@stacksjs/ts-cloud 0.7.5 → 0.7.7

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.
Files changed (57) hide show
  1. package/dist/aws/index.js +259 -0
  2. package/dist/bin/cli.js +231 -231
  3. package/dist/chunk-0wxyppza.js +146 -0
  4. package/dist/chunk-3knnr7wh.js +601 -0
  5. package/dist/chunk-3rsfns7x.js +10427 -0
  6. package/dist/chunk-93hjhs78.js +1752 -0
  7. package/dist/chunk-arsh1g5h.js +1749 -0
  8. package/dist/chunk-b82pbxyp.js +1572 -0
  9. package/dist/chunk-c6rgvg1j.js +46124 -0
  10. package/dist/chunk-d2p5n2aq.js +23 -0
  11. package/dist/chunk-d7p84vz5.js +1699 -0
  12. package/dist/chunk-eq08r166.js +8 -0
  13. package/dist/chunk-hsk6fe6x.js +371 -0
  14. package/dist/chunk-mj1tmhte.js +13 -0
  15. package/dist/chunk-pegd7rmf.js +10 -0
  16. package/dist/chunk-pqfzdg68.js +12 -0
  17. package/dist/chunk-qpj3edwz.js +601 -0
  18. package/dist/chunk-tjjgajbh.js +1032 -0
  19. package/dist/chunk-tnztxpcb.js +630 -0
  20. package/dist/chunk-tt4kxske.js +1788 -0
  21. package/dist/chunk-v0bahtg2.js +4 -0
  22. package/dist/chunk-vd87cpvn.js +1754 -0
  23. package/dist/chunk-x07dz3sd.js +12828 -0
  24. package/dist/chunk-xzn0ntr0.js +4616 -0
  25. package/dist/chunk-zn0nxxa8.js +1779 -0
  26. package/dist/deploy/index.js +87 -0
  27. package/dist/dns/index.js +31 -0
  28. package/dist/drivers/index.d.ts +2 -0
  29. package/dist/drivers/index.js +108 -0
  30. package/dist/drivers/shared/box-provision.d.ts +102 -0
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.js +4383 -91358
  33. package/dist/push/index.js +509 -0
  34. package/dist/ui/index.html +3 -3
  35. package/dist/ui/server/actions.html +3 -3
  36. package/dist/ui/server/backups.html +3 -3
  37. package/dist/ui/server/database.html +3 -3
  38. package/dist/ui/server/deployments.html +3 -3
  39. package/dist/ui/server/firewall.html +3 -3
  40. package/dist/ui/server/logs.html +3 -3
  41. package/dist/ui/server/services.html +3 -3
  42. package/dist/ui/server/sites.html +3 -3
  43. package/dist/ui/server/ssh-keys.html +3 -3
  44. package/dist/ui/server/terminal.html +3 -3
  45. package/dist/ui/server/workers.html +3 -3
  46. package/dist/ui/serverless/alarms.html +3 -3
  47. package/dist/ui/serverless/assets.html +3 -3
  48. package/dist/ui/serverless/data.html +3 -3
  49. package/dist/ui/serverless/deployments.html +3 -3
  50. package/dist/ui/serverless/functions.html +3 -3
  51. package/dist/ui/serverless/logs.html +3 -3
  52. package/dist/ui/serverless/queues.html +3 -3
  53. package/dist/ui/serverless/scheduler.html +3 -3
  54. package/dist/ui/serverless/secrets.html +3 -3
  55. package/dist/ui/serverless/traces.html +3 -3
  56. package/dist/ui/serverless.html +3 -3
  57. package/package.json +3 -3
@@ -0,0 +1,1779 @@
1
+ import {
2
+ Route53Client
3
+ } from "./chunk-tnztxpcb.js";
4
+ import {
5
+ S3Client
6
+ } from "./chunk-vd87cpvn.js";
7
+ import {
8
+ AWSClient
9
+ } from "./chunk-arsh1g5h.js";
10
+ import {
11
+ __require
12
+ } from "./chunk-v0bahtg2.js";
13
+
14
+ // src/aws/ses.ts
15
+ class SESClient {
16
+ client;
17
+ region;
18
+ constructor(region = "us-east-1", credentials) {
19
+ this.region = region;
20
+ this.client = new AWSClient(credentials);
21
+ }
22
+ async createEmailIdentity(params) {
23
+ const result = await this.client.request({
24
+ service: "email",
25
+ region: this.region,
26
+ method: "POST",
27
+ path: "/v2/email/identities",
28
+ headers: {
29
+ "Content-Type": "application/json"
30
+ },
31
+ body: JSON.stringify(params)
32
+ });
33
+ return result;
34
+ }
35
+ async getEmailIdentity(emailIdentity) {
36
+ const result = await this.client.request({
37
+ service: "email",
38
+ region: this.region,
39
+ method: "GET",
40
+ path: `/v2/email/identities/${encodeURIComponent(emailIdentity)}`,
41
+ headers: {
42
+ "Content-Type": "application/json"
43
+ }
44
+ });
45
+ return {
46
+ IdentityType: result.IdentityType,
47
+ IdentityName: emailIdentity,
48
+ SendingEnabled: result.VerifiedForSendingStatus,
49
+ VerificationStatus: result.VerificationStatus,
50
+ DkimAttributes: result.DkimAttributes,
51
+ MailFromAttributes: result.MailFromAttributes
52
+ };
53
+ }
54
+ async putEmailIdentityMailFromAttributes(emailIdentity, params) {
55
+ await this.client.request({
56
+ service: "email",
57
+ region: this.region,
58
+ method: "PUT",
59
+ path: `/v2/email/identities/${encodeURIComponent(emailIdentity)}/mail-from`,
60
+ headers: {
61
+ "Content-Type": "application/json"
62
+ },
63
+ body: JSON.stringify(params)
64
+ });
65
+ }
66
+ async listEmailIdentities(params) {
67
+ let path = "/v2/email/identities";
68
+ const queryParams = [];
69
+ if (params?.PageSize) {
70
+ queryParams.push(`PageSize=${params.PageSize}`);
71
+ }
72
+ if (params?.NextToken) {
73
+ queryParams.push(`NextToken=${encodeURIComponent(params.NextToken)}`);
74
+ }
75
+ if (queryParams.length > 0) {
76
+ path += `?${queryParams.join("&")}`;
77
+ }
78
+ const result = await this.client.request({
79
+ service: "email",
80
+ region: this.region,
81
+ method: "GET",
82
+ path,
83
+ headers: {
84
+ "Content-Type": "application/json"
85
+ }
86
+ });
87
+ return {
88
+ EmailIdentities: result.EmailIdentities,
89
+ NextToken: result.NextToken
90
+ };
91
+ }
92
+ async deleteEmailIdentity(emailIdentity) {
93
+ await this.client.request({
94
+ service: "email",
95
+ region: this.region,
96
+ method: "DELETE",
97
+ path: `/v2/email/identities/${encodeURIComponent(emailIdentity)}`,
98
+ headers: {
99
+ "Content-Type": "application/json"
100
+ }
101
+ });
102
+ }
103
+ async putEmailIdentityDkimAttributes(params) {
104
+ await this.client.request({
105
+ service: "email",
106
+ region: this.region,
107
+ method: "PUT",
108
+ path: `/v2/email/identities/${encodeURIComponent(params.EmailIdentity)}/dkim`,
109
+ headers: {
110
+ "Content-Type": "application/json"
111
+ },
112
+ body: JSON.stringify({ SigningEnabled: params.SigningEnabled })
113
+ });
114
+ }
115
+ async sendEmail(params) {
116
+ const result = await this.client.request({
117
+ service: "email",
118
+ region: this.region,
119
+ method: "POST",
120
+ path: "/v2/email/outbound-emails",
121
+ headers: {
122
+ "Content-Type": "application/json"
123
+ },
124
+ body: JSON.stringify(params)
125
+ });
126
+ return {
127
+ MessageId: result.MessageId
128
+ };
129
+ }
130
+ async sendBulkEmail(params) {
131
+ const result = await this.client.request({
132
+ service: "email",
133
+ region: this.region,
134
+ method: "POST",
135
+ path: "/v2/email/outbound-bulk-emails",
136
+ headers: {
137
+ "Content-Type": "application/json"
138
+ },
139
+ body: JSON.stringify(params)
140
+ });
141
+ return {
142
+ BulkEmailEntryResults: result.BulkEmailEntryResults
143
+ };
144
+ }
145
+ async createEmailTemplate(params) {
146
+ await this.client.request({
147
+ service: "email",
148
+ region: this.region,
149
+ method: "POST",
150
+ path: "/v2/email/templates",
151
+ headers: {
152
+ "Content-Type": "application/json"
153
+ },
154
+ body: JSON.stringify(params)
155
+ });
156
+ }
157
+ async getEmailTemplate(templateName) {
158
+ const result = await this.client.request({
159
+ service: "email",
160
+ region: this.region,
161
+ method: "GET",
162
+ path: `/v2/email/templates/${encodeURIComponent(templateName)}`,
163
+ headers: {
164
+ "Content-Type": "application/json"
165
+ }
166
+ });
167
+ return result;
168
+ }
169
+ async deleteEmailTemplate(templateName) {
170
+ await this.client.request({
171
+ service: "email",
172
+ region: this.region,
173
+ method: "DELETE",
174
+ path: `/v2/email/templates/${encodeURIComponent(templateName)}`,
175
+ headers: {
176
+ "Content-Type": "application/json"
177
+ }
178
+ });
179
+ }
180
+ async listEmailTemplates(params) {
181
+ let path = "/v2/email/templates";
182
+ const queryParams = [];
183
+ if (params?.PageSize) {
184
+ queryParams.push(`PageSize=${params.PageSize}`);
185
+ }
186
+ if (params?.NextToken) {
187
+ queryParams.push(`NextToken=${encodeURIComponent(params.NextToken)}`);
188
+ }
189
+ if (queryParams.length > 0) {
190
+ path += `?${queryParams.join("&")}`;
191
+ }
192
+ const result = await this.client.request({
193
+ service: "email",
194
+ region: this.region,
195
+ method: "GET",
196
+ path,
197
+ headers: {
198
+ "Content-Type": "application/json"
199
+ }
200
+ });
201
+ return result;
202
+ }
203
+ async getSendStatistics() {
204
+ const result = await this.client.request({
205
+ service: "ses",
206
+ region: this.region,
207
+ method: "POST",
208
+ path: "/",
209
+ headers: {
210
+ "Content-Type": "application/x-www-form-urlencoded"
211
+ },
212
+ body: "Action=GetSendStatistics&Version=2010-12-01"
213
+ });
214
+ return {
215
+ SendDataPoints: result.GetSendStatisticsResponse?.GetSendStatisticsResult?.SendDataPoints?.member
216
+ };
217
+ }
218
+ async getSendQuota() {
219
+ const result = await this.client.request({
220
+ service: "ses",
221
+ region: this.region,
222
+ method: "POST",
223
+ path: "/",
224
+ headers: {
225
+ "Content-Type": "application/x-www-form-urlencoded"
226
+ },
227
+ body: "Action=GetSendQuota&Version=2010-12-01"
228
+ });
229
+ const quota = result.GetSendQuotaResponse?.GetSendQuotaResult;
230
+ return {
231
+ Max24HourSend: quota?.Max24HourSend ? Number(quota.Max24HourSend) : undefined,
232
+ MaxSendRate: quota?.MaxSendRate ? Number(quota.MaxSendRate) : undefined,
233
+ SentLast24Hours: quota?.SentLast24Hours ? Number(quota.SentLast24Hours) : undefined
234
+ };
235
+ }
236
+ async verifyDomain(domain) {
237
+ const result = await this.createEmailIdentity({
238
+ EmailIdentity: domain
239
+ });
240
+ return {
241
+ dkimTokens: result.DkimAttributes?.Tokens,
242
+ verificationStatus: result.DkimAttributes?.Status
243
+ };
244
+ }
245
+ async sendSimpleEmail(params) {
246
+ const toAddresses = Array.isArray(params.to) ? params.to : [params.to];
247
+ const replyToAddresses = params.replyTo ? Array.isArray(params.replyTo) ? params.replyTo : [params.replyTo] : undefined;
248
+ const body = {};
249
+ if (params.text) {
250
+ body.Text = { Data: params.text };
251
+ }
252
+ if (params.html) {
253
+ body.Html = { Data: params.html };
254
+ }
255
+ return this.sendEmail({
256
+ FromEmailAddress: params.from,
257
+ Destination: {
258
+ ToAddresses: toAddresses
259
+ },
260
+ Content: {
261
+ Simple: {
262
+ Subject: { Data: params.subject },
263
+ Body: body
264
+ }
265
+ },
266
+ ReplyToAddresses: replyToAddresses
267
+ });
268
+ }
269
+ async sendTemplatedEmail(params) {
270
+ const toAddresses = Array.isArray(params.to) ? params.to : [params.to];
271
+ const replyToAddresses = params.replyTo ? Array.isArray(params.replyTo) ? params.replyTo : [params.replyTo] : undefined;
272
+ return this.sendEmail({
273
+ FromEmailAddress: params.from,
274
+ Destination: {
275
+ ToAddresses: toAddresses
276
+ },
277
+ Content: {
278
+ Template: {
279
+ TemplateName: params.templateName,
280
+ TemplateData: JSON.stringify(params.templateData)
281
+ }
282
+ },
283
+ ReplyToAddresses: replyToAddresses
284
+ });
285
+ }
286
+ async getDkimRecords(domain) {
287
+ const identity = await this.getEmailIdentity(domain);
288
+ if (!identity.DkimAttributes?.Tokens) {
289
+ return [];
290
+ }
291
+ return identity.DkimAttributes.Tokens.map((token) => ({
292
+ name: `${token}._domainkey.${domain}`,
293
+ type: "CNAME",
294
+ value: `${token}.dkim.amazonses.com`
295
+ }));
296
+ }
297
+ async isDomainVerified(domain) {
298
+ try {
299
+ const identity = await this.getEmailIdentity(domain);
300
+ return identity.VerificationStatus === "SUCCESS" && identity.SendingEnabled === true;
301
+ } catch {
302
+ return false;
303
+ }
304
+ }
305
+ async waitForDomainVerification(domain, maxAttempts = 60, delayMs = 30000) {
306
+ for (let i = 0;i < maxAttempts; i++) {
307
+ const isVerified = await this.isDomainVerified(domain);
308
+ if (isVerified) {
309
+ return true;
310
+ }
311
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
312
+ }
313
+ return false;
314
+ }
315
+ buildFormBody(params) {
316
+ const entries = Object.entries(params).filter(([, value]) => value !== undefined).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
317
+ return entries.join("&");
318
+ }
319
+ async createReceiptRuleSet(ruleSetName) {
320
+ await this.client.request({
321
+ service: "ses",
322
+ region: this.region,
323
+ method: "POST",
324
+ path: "/",
325
+ headers: {
326
+ "Content-Type": "application/x-www-form-urlencoded"
327
+ },
328
+ body: this.buildFormBody({
329
+ Action: "CreateReceiptRuleSet",
330
+ Version: "2010-12-01",
331
+ RuleSetName: ruleSetName
332
+ })
333
+ });
334
+ }
335
+ async deleteReceiptRuleSet(ruleSetName) {
336
+ await this.client.request({
337
+ service: "ses",
338
+ region: this.region,
339
+ method: "POST",
340
+ path: "/",
341
+ headers: {
342
+ "Content-Type": "application/x-www-form-urlencoded"
343
+ },
344
+ body: this.buildFormBody({
345
+ Action: "DeleteReceiptRuleSet",
346
+ Version: "2010-12-01",
347
+ RuleSetName: ruleSetName
348
+ })
349
+ });
350
+ }
351
+ async setActiveReceiptRuleSet(ruleSetName) {
352
+ await this.client.request({
353
+ service: "ses",
354
+ region: this.region,
355
+ method: "POST",
356
+ path: "/",
357
+ headers: {
358
+ "Content-Type": "application/x-www-form-urlencoded"
359
+ },
360
+ body: this.buildFormBody({
361
+ Action: "SetActiveReceiptRuleSet",
362
+ Version: "2010-12-01",
363
+ RuleSetName: ruleSetName
364
+ })
365
+ });
366
+ }
367
+ async listReceiptRuleSets(nextToken) {
368
+ const formParams = {
369
+ Action: "ListReceiptRuleSets",
370
+ Version: "2010-12-01"
371
+ };
372
+ if (nextToken) {
373
+ formParams.NextToken = nextToken;
374
+ }
375
+ const result = await this.client.request({
376
+ service: "ses",
377
+ region: this.region,
378
+ method: "POST",
379
+ path: "/",
380
+ headers: {
381
+ "Content-Type": "application/x-www-form-urlencoded"
382
+ },
383
+ body: this.buildFormBody(formParams)
384
+ });
385
+ const ruleSetsResult = result?.ListReceiptRuleSetsResponse?.ListReceiptRuleSetsResult || result?.ListReceiptRuleSetsResult;
386
+ const ruleSets = ruleSetsResult?.RuleSets?.member;
387
+ return {
388
+ RuleSets: Array.isArray(ruleSets) ? ruleSets : ruleSets ? [ruleSets] : [],
389
+ NextToken: ruleSetsResult?.NextToken
390
+ };
391
+ }
392
+ async describeReceiptRuleSet(ruleSetName) {
393
+ const result = await this.client.request({
394
+ service: "ses",
395
+ region: this.region,
396
+ method: "POST",
397
+ path: "/",
398
+ headers: {
399
+ "Content-Type": "application/x-www-form-urlencoded"
400
+ },
401
+ body: this.buildFormBody({
402
+ Action: "DescribeReceiptRuleSet",
403
+ Version: "2010-12-01",
404
+ RuleSetName: ruleSetName
405
+ })
406
+ });
407
+ const response = result?.DescribeReceiptRuleSetResponse?.DescribeReceiptRuleSetResult || result?.DescribeReceiptRuleSetResult;
408
+ const rules = response?.Rules?.member;
409
+ return {
410
+ Metadata: response?.Metadata,
411
+ Rules: Array.isArray(rules) ? rules : rules ? [rules] : []
412
+ };
413
+ }
414
+ async createReceiptRule(params) {
415
+ const formParams = {
416
+ Action: "CreateReceiptRule",
417
+ Version: "2010-12-01",
418
+ RuleSetName: params.RuleSetName,
419
+ "Rule.Name": params.Rule.Name,
420
+ "Rule.Enabled": params.Rule.Enabled !== false ? "true" : "false"
421
+ };
422
+ if (params.Rule.TlsPolicy) {
423
+ formParams["Rule.TlsPolicy"] = params.Rule.TlsPolicy;
424
+ }
425
+ if (params.Rule.ScanEnabled !== undefined) {
426
+ formParams["Rule.ScanEnabled"] = params.Rule.ScanEnabled ? "true" : "false";
427
+ }
428
+ if (params.After) {
429
+ formParams.After = params.After;
430
+ }
431
+ if (params.Rule.Recipients) {
432
+ params.Rule.Recipients.forEach((recipient, index) => {
433
+ formParams[`Rule.Recipients.member.${index + 1}`] = recipient;
434
+ });
435
+ }
436
+ params.Rule.Actions.forEach((action, index) => {
437
+ const actionNum = index + 1;
438
+ if (action.S3Action) {
439
+ formParams[`Rule.Actions.member.${actionNum}.S3Action.BucketName`] = action.S3Action.BucketName;
440
+ if (action.S3Action.ObjectKeyPrefix) {
441
+ formParams[`Rule.Actions.member.${actionNum}.S3Action.ObjectKeyPrefix`] = action.S3Action.ObjectKeyPrefix;
442
+ }
443
+ if (action.S3Action.KmsKeyArn) {
444
+ formParams[`Rule.Actions.member.${actionNum}.S3Action.KmsKeyArn`] = action.S3Action.KmsKeyArn;
445
+ }
446
+ }
447
+ if (action.LambdaAction) {
448
+ formParams[`Rule.Actions.member.${actionNum}.LambdaAction.FunctionArn`] = action.LambdaAction.FunctionArn;
449
+ formParams[`Rule.Actions.member.${actionNum}.LambdaAction.InvocationType`] = action.LambdaAction.InvocationType || "Event";
450
+ }
451
+ if (action.SNSAction) {
452
+ formParams[`Rule.Actions.member.${actionNum}.SNSAction.TopicArn`] = action.SNSAction.TopicArn;
453
+ if (action.SNSAction.Encoding) {
454
+ formParams[`Rule.Actions.member.${actionNum}.SNSAction.Encoding`] = action.SNSAction.Encoding;
455
+ }
456
+ }
457
+ if (action.StopAction) {
458
+ formParams[`Rule.Actions.member.${actionNum}.StopAction.Scope`] = action.StopAction.Scope;
459
+ if (action.StopAction.TopicArn) {
460
+ formParams[`Rule.Actions.member.${actionNum}.StopAction.TopicArn`] = action.StopAction.TopicArn;
461
+ }
462
+ }
463
+ });
464
+ await this.client.request({
465
+ service: "ses",
466
+ region: this.region,
467
+ method: "POST",
468
+ path: "/",
469
+ headers: {
470
+ "Content-Type": "application/x-www-form-urlencoded"
471
+ },
472
+ body: this.buildFormBody(formParams)
473
+ });
474
+ }
475
+ async deleteReceiptRule(ruleSetName, ruleName) {
476
+ await this.client.request({
477
+ service: "ses",
478
+ region: this.region,
479
+ method: "POST",
480
+ path: "/",
481
+ headers: {
482
+ "Content-Type": "application/x-www-form-urlencoded"
483
+ },
484
+ body: this.buildFormBody({
485
+ Action: "DeleteReceiptRule",
486
+ Version: "2010-12-01",
487
+ RuleSetName: ruleSetName,
488
+ RuleName: ruleName
489
+ })
490
+ });
491
+ }
492
+ async receiptRuleSetExists(ruleSetName) {
493
+ try {
494
+ await this.describeReceiptRuleSet(ruleSetName);
495
+ return true;
496
+ } catch (error) {
497
+ if (error.code === "RuleSetDoesNotExist" || error.statusCode === 400) {
498
+ return false;
499
+ }
500
+ throw error;
501
+ }
502
+ }
503
+ async getActiveReceiptRuleSet() {
504
+ const result = await this.client.request({
505
+ service: "ses",
506
+ region: this.region,
507
+ method: "POST",
508
+ path: "/",
509
+ headers: {
510
+ "Content-Type": "application/x-www-form-urlencoded"
511
+ },
512
+ body: this.buildFormBody({
513
+ Action: "DescribeActiveReceiptRuleSet",
514
+ Version: "2010-12-01"
515
+ })
516
+ });
517
+ const response = result?.DescribeActiveReceiptRuleSetResponse?.DescribeActiveReceiptRuleSetResult || result?.DescribeActiveReceiptRuleSetResult;
518
+ if (!response?.Metadata) {
519
+ return null;
520
+ }
521
+ const rules = response?.Rules?.member;
522
+ return {
523
+ Metadata: response?.Metadata,
524
+ Rules: Array.isArray(rules) ? rules : rules ? [rules] : []
525
+ };
526
+ }
527
+ async updateReceiptRule(params) {
528
+ const formParams = {
529
+ Action: "UpdateReceiptRule",
530
+ Version: "2010-12-01",
531
+ RuleSetName: params.RuleSetName,
532
+ "Rule.Name": params.Rule.Name
533
+ };
534
+ if (params.Rule.Enabled !== undefined) {
535
+ formParams["Rule.Enabled"] = params.Rule.Enabled ? "true" : "false";
536
+ }
537
+ if (params.Rule.TlsPolicy) {
538
+ formParams["Rule.TlsPolicy"] = params.Rule.TlsPolicy;
539
+ }
540
+ if (params.Rule.ScanEnabled !== undefined) {
541
+ formParams["Rule.ScanEnabled"] = params.Rule.ScanEnabled ? "true" : "false";
542
+ }
543
+ if (params.Rule.Recipients) {
544
+ params.Rule.Recipients.forEach((recipient, index) => {
545
+ formParams[`Rule.Recipients.member.${index + 1}`] = recipient;
546
+ });
547
+ }
548
+ params.Rule.Actions.forEach((action, index) => {
549
+ const actionNum = index + 1;
550
+ if (action.S3Action) {
551
+ formParams[`Rule.Actions.member.${actionNum}.S3Action.BucketName`] = action.S3Action.BucketName;
552
+ if (action.S3Action.ObjectKeyPrefix) {
553
+ formParams[`Rule.Actions.member.${actionNum}.S3Action.ObjectKeyPrefix`] = action.S3Action.ObjectKeyPrefix;
554
+ }
555
+ if (action.S3Action.KmsKeyArn) {
556
+ formParams[`Rule.Actions.member.${actionNum}.S3Action.KmsKeyArn`] = action.S3Action.KmsKeyArn;
557
+ }
558
+ }
559
+ if (action.LambdaAction) {
560
+ formParams[`Rule.Actions.member.${actionNum}.LambdaAction.FunctionArn`] = action.LambdaAction.FunctionArn;
561
+ formParams[`Rule.Actions.member.${actionNum}.LambdaAction.InvocationType`] = action.LambdaAction.InvocationType || "Event";
562
+ }
563
+ if (action.SNSAction) {
564
+ formParams[`Rule.Actions.member.${actionNum}.SNSAction.TopicArn`] = action.SNSAction.TopicArn;
565
+ if (action.SNSAction.Encoding) {
566
+ formParams[`Rule.Actions.member.${actionNum}.SNSAction.Encoding`] = action.SNSAction.Encoding;
567
+ }
568
+ }
569
+ if (action.StopAction) {
570
+ formParams[`Rule.Actions.member.${actionNum}.StopAction.Scope`] = action.StopAction.Scope;
571
+ if (action.StopAction.TopicArn) {
572
+ formParams[`Rule.Actions.member.${actionNum}.StopAction.TopicArn`] = action.StopAction.TopicArn;
573
+ }
574
+ }
575
+ });
576
+ await this.client.request({
577
+ service: "ses",
578
+ region: this.region,
579
+ method: "POST",
580
+ path: "/",
581
+ headers: {
582
+ "Content-Type": "application/x-www-form-urlencoded"
583
+ },
584
+ body: this.buildFormBody(formParams)
585
+ });
586
+ }
587
+ async sendRawEmail(params) {
588
+ const rawMessageBase64 = Buffer.from(params.rawMessage).toString("base64");
589
+ const formParams = {
590
+ Action: "SendRawEmail",
591
+ Version: "2010-12-01",
592
+ Source: params.source,
593
+ "RawMessage.Data": rawMessageBase64
594
+ };
595
+ params.destinations.forEach((dest, index) => {
596
+ formParams[`Destinations.member.${index + 1}`] = dest;
597
+ });
598
+ const result = await this.client.request({
599
+ service: "ses",
600
+ region: this.region,
601
+ method: "POST",
602
+ path: "/",
603
+ headers: {
604
+ "Content-Type": "application/x-www-form-urlencoded"
605
+ },
606
+ body: this.buildFormBody(formParams)
607
+ });
608
+ const response = result?.SendRawEmailResponse?.SendRawEmailResult || result?.SendRawEmailResult;
609
+ return {
610
+ MessageId: response?.MessageId
611
+ };
612
+ }
613
+ async getSuppressedDestination(emailAddress) {
614
+ try {
615
+ const result = await this.client.request({
616
+ service: "ses",
617
+ region: this.region,
618
+ method: "GET",
619
+ path: `/v2/email/suppression/addresses/${encodeURIComponent(emailAddress)}`
620
+ });
621
+ return result?.SuppressedDestination || null;
622
+ } catch (error) {
623
+ if (error.message?.includes("404") || error.message?.includes("NotFoundException")) {
624
+ return null;
625
+ }
626
+ throw error;
627
+ }
628
+ }
629
+ async deleteSuppressedDestination(emailAddress) {
630
+ await this.client.request({
631
+ service: "ses",
632
+ region: this.region,
633
+ method: "DELETE",
634
+ path: `/v2/email/suppression/addresses/${encodeURIComponent(emailAddress)}`
635
+ });
636
+ }
637
+ }
638
+
639
+ // src/aws/iam.ts
640
+ function buildQueryParams(action, params) {
641
+ const queryParams = [`Action=${action}`, "Version=2010-05-08"];
642
+ for (const [key, value] of Object.entries(params)) {
643
+ if (value === undefined || value === null)
644
+ continue;
645
+ if (Array.isArray(value)) {
646
+ value.forEach((item, index) => {
647
+ if (typeof item === "object" && item !== null) {
648
+ for (const [subKey, subValue] of Object.entries(item)) {
649
+ queryParams.push(`${key}.member.${index + 1}.${subKey}=${encodeURIComponent(String(subValue))}`);
650
+ }
651
+ } else {
652
+ queryParams.push(`${key}.member.${index + 1}=${encodeURIComponent(String(item))}`);
653
+ }
654
+ });
655
+ } else if (typeof value === "object") {
656
+ for (const [subKey, subValue] of Object.entries(value)) {
657
+ if (subValue !== undefined && subValue !== null) {
658
+ queryParams.push(`${key}.${subKey}=${encodeURIComponent(String(subValue))}`);
659
+ }
660
+ }
661
+ } else {
662
+ queryParams.push(`${key}=${encodeURIComponent(String(value))}`);
663
+ }
664
+ }
665
+ return queryParams.join("&");
666
+ }
667
+ function parseXmlValue(xml, tag) {
668
+ const regex = new RegExp(`<${tag}>([^<]*)</${tag}>`);
669
+ const match = xml.match(regex);
670
+ return match ? match[1] : undefined;
671
+ }
672
+ function parseXmlArray(xml, containerTag, itemTag) {
673
+ const containerRegex = new RegExp(`<${containerTag}>([\\s\\S]*?)</${containerTag}>`);
674
+ const containerMatch = xml.match(containerRegex);
675
+ if (!containerMatch)
676
+ return [];
677
+ const items = [];
678
+ const itemRegex = new RegExp(`<${itemTag}>([\\s\\S]*?)</${itemTag}>`, "g");
679
+ let match;
680
+ while ((match = itemRegex.exec(containerMatch[1])) !== null) {
681
+ items.push(match[1]);
682
+ }
683
+ return items;
684
+ }
685
+
686
+ class IAMClient {
687
+ client;
688
+ region;
689
+ constructor(region = "us-east-1") {
690
+ this.client = new AWSClient;
691
+ this.region = region;
692
+ }
693
+ async request(action, params = {}) {
694
+ const body = buildQueryParams(action, params);
695
+ const response = await this.client.request({
696
+ service: "iam",
697
+ region: this.region,
698
+ method: "POST",
699
+ path: "/",
700
+ headers: {
701
+ "Content-Type": "application/x-www-form-urlencoded"
702
+ },
703
+ body
704
+ });
705
+ return response;
706
+ }
707
+ async createUser(params) {
708
+ const response = await this.request("CreateUser", params);
709
+ return this.parseUser(response);
710
+ }
711
+ async getUser(params = {}) {
712
+ const response = await this.request("GetUser", params);
713
+ return this.parseUser(response);
714
+ }
715
+ async listUsers(params = {}) {
716
+ const response = await this.request("ListUsers", params);
717
+ const users = this.parseUsers(response);
718
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
719
+ const marker = parseXmlValue(response, "Marker");
720
+ return { Users: users, IsTruncated: isTruncated, Marker: marker };
721
+ }
722
+ async updateUser(params) {
723
+ await this.request("UpdateUser", params);
724
+ }
725
+ async deleteUser(params) {
726
+ await this.request("DeleteUser", params);
727
+ }
728
+ parseUser(xml) {
729
+ return {
730
+ UserName: parseXmlValue(xml, "UserName") || "",
731
+ UserId: parseXmlValue(xml, "UserId") || "",
732
+ Arn: parseXmlValue(xml, "Arn") || "",
733
+ Path: parseXmlValue(xml, "Path"),
734
+ CreateDate: parseXmlValue(xml, "CreateDate"),
735
+ PasswordLastUsed: parseXmlValue(xml, "PasswordLastUsed")
736
+ };
737
+ }
738
+ parseUsers(xml) {
739
+ const memberXmls = parseXmlArray(xml, "Users", "member");
740
+ return memberXmls.map((memberXml) => ({
741
+ UserName: parseXmlValue(memberXml, "UserName") || "",
742
+ UserId: parseXmlValue(memberXml, "UserId") || "",
743
+ Arn: parseXmlValue(memberXml, "Arn") || "",
744
+ Path: parseXmlValue(memberXml, "Path"),
745
+ CreateDate: parseXmlValue(memberXml, "CreateDate"),
746
+ PasswordLastUsed: parseXmlValue(memberXml, "PasswordLastUsed")
747
+ }));
748
+ }
749
+ async createGroup(params) {
750
+ const response = await this.request("CreateGroup", params);
751
+ return this.parseGroup(response);
752
+ }
753
+ async getGroup(params) {
754
+ const response = await this.request("GetGroup", params);
755
+ const group = this.parseGroup(response);
756
+ const users = this.parseUsers(response);
757
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
758
+ const marker = parseXmlValue(response, "Marker");
759
+ return { Group: group, Users: users, IsTruncated: isTruncated, Marker: marker };
760
+ }
761
+ async listGroups(params = {}) {
762
+ const response = await this.request("ListGroups", params);
763
+ const groups = this.parseGroups(response);
764
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
765
+ const marker = parseXmlValue(response, "Marker");
766
+ return { Groups: groups, IsTruncated: isTruncated, Marker: marker };
767
+ }
768
+ async updateGroup(params) {
769
+ await this.request("UpdateGroup", params);
770
+ }
771
+ async deleteGroup(params) {
772
+ await this.request("DeleteGroup", params);
773
+ }
774
+ async addUserToGroup(params) {
775
+ await this.request("AddUserToGroup", params);
776
+ }
777
+ async removeUserFromGroup(params) {
778
+ await this.request("RemoveUserFromGroup", params);
779
+ }
780
+ async listGroupsForUser(params) {
781
+ const response = await this.request("ListGroupsForUser", params);
782
+ const groups = this.parseGroups(response);
783
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
784
+ const marker = parseXmlValue(response, "Marker");
785
+ return { Groups: groups, IsTruncated: isTruncated, Marker: marker };
786
+ }
787
+ parseGroup(xml) {
788
+ return {
789
+ GroupName: parseXmlValue(xml, "GroupName") || "",
790
+ GroupId: parseXmlValue(xml, "GroupId") || "",
791
+ Arn: parseXmlValue(xml, "Arn") || "",
792
+ Path: parseXmlValue(xml, "Path"),
793
+ CreateDate: parseXmlValue(xml, "CreateDate")
794
+ };
795
+ }
796
+ parseGroups(xml) {
797
+ const memberXmls = parseXmlArray(xml, "Groups", "member");
798
+ return memberXmls.map((memberXml) => ({
799
+ GroupName: parseXmlValue(memberXml, "GroupName") || "",
800
+ GroupId: parseXmlValue(memberXml, "GroupId") || "",
801
+ Arn: parseXmlValue(memberXml, "Arn") || "",
802
+ Path: parseXmlValue(memberXml, "Path"),
803
+ CreateDate: parseXmlValue(memberXml, "CreateDate")
804
+ }));
805
+ }
806
+ async createRole(params) {
807
+ const response = await this.request("CreateRole", params);
808
+ return this.parseRole(response);
809
+ }
810
+ async getRole(params) {
811
+ const response = await this.request("GetRole", params);
812
+ return this.parseRole(response);
813
+ }
814
+ async listRoles(params = {}) {
815
+ const response = await this.request("ListRoles", params);
816
+ const roles = this.parseRoles(response);
817
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
818
+ const marker = parseXmlValue(response, "Marker");
819
+ return { Roles: roles, IsTruncated: isTruncated, Marker: marker };
820
+ }
821
+ async updateRole(params) {
822
+ await this.request("UpdateRole", params);
823
+ }
824
+ async updateRoleDescription(params) {
825
+ const response = await this.request("UpdateRoleDescription", params);
826
+ return this.parseRole(response);
827
+ }
828
+ async updateAssumeRolePolicy(params) {
829
+ await this.request("UpdateAssumeRolePolicy", params);
830
+ }
831
+ async deleteRole(params) {
832
+ await this.request("DeleteRole", params);
833
+ }
834
+ async tagRole(params) {
835
+ await this.request("TagRole", params);
836
+ }
837
+ async untagRole(params) {
838
+ await this.request("UntagRole", params);
839
+ }
840
+ async listRoleTags(params) {
841
+ const response = await this.request("ListRoleTags", params);
842
+ const tags = this.parseTags(response);
843
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
844
+ const marker = parseXmlValue(response, "Marker");
845
+ return { Tags: tags, IsTruncated: isTruncated, Marker: marker };
846
+ }
847
+ parseRole(xml) {
848
+ return {
849
+ RoleName: parseXmlValue(xml, "RoleName") || "",
850
+ RoleId: parseXmlValue(xml, "RoleId") || "",
851
+ Arn: parseXmlValue(xml, "Arn") || "",
852
+ Path: parseXmlValue(xml, "Path"),
853
+ CreateDate: parseXmlValue(xml, "CreateDate"),
854
+ AssumeRolePolicyDocument: parseXmlValue(xml, "AssumeRolePolicyDocument"),
855
+ Description: parseXmlValue(xml, "Description"),
856
+ MaxSessionDuration: parseXmlValue(xml, "MaxSessionDuration") ? Number.parseInt(parseXmlValue(xml, "MaxSessionDuration"), 10) : undefined
857
+ };
858
+ }
859
+ parseRoles(xml) {
860
+ const memberXmls = parseXmlArray(xml, "Roles", "member");
861
+ return memberXmls.map((memberXml) => ({
862
+ RoleName: parseXmlValue(memberXml, "RoleName") || "",
863
+ RoleId: parseXmlValue(memberXml, "RoleId") || "",
864
+ Arn: parseXmlValue(memberXml, "Arn") || "",
865
+ Path: parseXmlValue(memberXml, "Path"),
866
+ CreateDate: parseXmlValue(memberXml, "CreateDate"),
867
+ AssumeRolePolicyDocument: parseXmlValue(memberXml, "AssumeRolePolicyDocument"),
868
+ Description: parseXmlValue(memberXml, "Description"),
869
+ MaxSessionDuration: parseXmlValue(memberXml, "MaxSessionDuration") ? Number.parseInt(parseXmlValue(memberXml, "MaxSessionDuration"), 10) : undefined
870
+ }));
871
+ }
872
+ parseTags(xml) {
873
+ const memberXmls = parseXmlArray(xml, "Tags", "member");
874
+ return memberXmls.map((memberXml) => ({
875
+ Key: parseXmlValue(memberXml, "Key") || "",
876
+ Value: parseXmlValue(memberXml, "Value") || ""
877
+ }));
878
+ }
879
+ async createPolicy(params) {
880
+ const response = await this.request("CreatePolicy", params);
881
+ return this.parsePolicy(response);
882
+ }
883
+ async getPolicy(params) {
884
+ const response = await this.request("GetPolicy", params);
885
+ return this.parsePolicy(response);
886
+ }
887
+ async getPolicyVersion(params) {
888
+ const response = await this.request("GetPolicyVersion", params);
889
+ return {
890
+ VersionId: parseXmlValue(response, "VersionId") || "",
891
+ IsDefaultVersion: parseXmlValue(response, "IsDefaultVersion") === "true",
892
+ CreateDate: parseXmlValue(response, "CreateDate"),
893
+ Document: parseXmlValue(response, "Document")
894
+ };
895
+ }
896
+ async listPolicies(params = {}) {
897
+ const response = await this.request("ListPolicies", params);
898
+ const policies = this.parsePolicies(response);
899
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
900
+ const marker = parseXmlValue(response, "Marker");
901
+ return { Policies: policies, IsTruncated: isTruncated, Marker: marker };
902
+ }
903
+ async listPolicyVersions(params) {
904
+ const response = await this.request("ListPolicyVersions", params);
905
+ const versions = this.parsePolicyVersions(response);
906
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
907
+ const marker = parseXmlValue(response, "Marker");
908
+ return { Versions: versions, IsTruncated: isTruncated, Marker: marker };
909
+ }
910
+ async createPolicyVersion(params) {
911
+ const response = await this.request("CreatePolicyVersion", params);
912
+ return {
913
+ VersionId: parseXmlValue(response, "VersionId") || "",
914
+ IsDefaultVersion: parseXmlValue(response, "IsDefaultVersion") === "true",
915
+ CreateDate: parseXmlValue(response, "CreateDate")
916
+ };
917
+ }
918
+ async deletePolicyVersion(params) {
919
+ await this.request("DeletePolicyVersion", params);
920
+ }
921
+ async setDefaultPolicyVersion(params) {
922
+ await this.request("SetDefaultPolicyVersion", params);
923
+ }
924
+ async deletePolicy(params) {
925
+ await this.request("DeletePolicy", params);
926
+ }
927
+ async attachUserPolicy(params) {
928
+ await this.request("AttachUserPolicy", params);
929
+ }
930
+ async detachUserPolicy(params) {
931
+ await this.request("DetachUserPolicy", params);
932
+ }
933
+ async attachGroupPolicy(params) {
934
+ await this.request("AttachGroupPolicy", params);
935
+ }
936
+ async detachGroupPolicy(params) {
937
+ await this.request("DetachGroupPolicy", params);
938
+ }
939
+ async attachRolePolicy(params) {
940
+ await this.request("AttachRolePolicy", params);
941
+ }
942
+ async detachRolePolicy(params) {
943
+ await this.request("DetachRolePolicy", params);
944
+ }
945
+ async listAttachedUserPolicies(params) {
946
+ const response = await this.request("ListAttachedUserPolicies", params);
947
+ const policies = this.parseAttachedPolicies(response);
948
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
949
+ const marker = parseXmlValue(response, "Marker");
950
+ return { AttachedPolicies: policies, IsTruncated: isTruncated, Marker: marker };
951
+ }
952
+ async listAttachedGroupPolicies(params) {
953
+ const response = await this.request("ListAttachedGroupPolicies", params);
954
+ const policies = this.parseAttachedPolicies(response);
955
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
956
+ const marker = parseXmlValue(response, "Marker");
957
+ return { AttachedPolicies: policies, IsTruncated: isTruncated, Marker: marker };
958
+ }
959
+ async listAttachedRolePolicies(params) {
960
+ const response = await this.request("ListAttachedRolePolicies", params);
961
+ const policies = this.parseAttachedPolicies(response);
962
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
963
+ const marker = parseXmlValue(response, "Marker");
964
+ return { AttachedPolicies: policies, IsTruncated: isTruncated, Marker: marker };
965
+ }
966
+ parsePolicy(xml) {
967
+ return {
968
+ PolicyName: parseXmlValue(xml, "PolicyName") || "",
969
+ PolicyId: parseXmlValue(xml, "PolicyId") || "",
970
+ Arn: parseXmlValue(xml, "Arn") || "",
971
+ Path: parseXmlValue(xml, "Path"),
972
+ DefaultVersionId: parseXmlValue(xml, "DefaultVersionId"),
973
+ AttachmentCount: parseXmlValue(xml, "AttachmentCount") ? Number.parseInt(parseXmlValue(xml, "AttachmentCount"), 10) : undefined,
974
+ PermissionsBoundaryUsageCount: parseXmlValue(xml, "PermissionsBoundaryUsageCount") ? Number.parseInt(parseXmlValue(xml, "PermissionsBoundaryUsageCount"), 10) : undefined,
975
+ IsAttachable: parseXmlValue(xml, "IsAttachable") === "true",
976
+ Description: parseXmlValue(xml, "Description"),
977
+ CreateDate: parseXmlValue(xml, "CreateDate"),
978
+ UpdateDate: parseXmlValue(xml, "UpdateDate")
979
+ };
980
+ }
981
+ parsePolicies(xml) {
982
+ const memberXmls = parseXmlArray(xml, "Policies", "member");
983
+ return memberXmls.map((memberXml) => ({
984
+ PolicyName: parseXmlValue(memberXml, "PolicyName") || "",
985
+ PolicyId: parseXmlValue(memberXml, "PolicyId") || "",
986
+ Arn: parseXmlValue(memberXml, "Arn") || "",
987
+ Path: parseXmlValue(memberXml, "Path"),
988
+ DefaultVersionId: parseXmlValue(memberXml, "DefaultVersionId"),
989
+ AttachmentCount: parseXmlValue(memberXml, "AttachmentCount") ? Number.parseInt(parseXmlValue(memberXml, "AttachmentCount"), 10) : undefined,
990
+ PermissionsBoundaryUsageCount: parseXmlValue(memberXml, "PermissionsBoundaryUsageCount") ? Number.parseInt(parseXmlValue(memberXml, "PermissionsBoundaryUsageCount"), 10) : undefined,
991
+ IsAttachable: parseXmlValue(memberXml, "IsAttachable") === "true",
992
+ Description: parseXmlValue(memberXml, "Description"),
993
+ CreateDate: parseXmlValue(memberXml, "CreateDate"),
994
+ UpdateDate: parseXmlValue(memberXml, "UpdateDate")
995
+ }));
996
+ }
997
+ parsePolicyVersions(xml) {
998
+ const memberXmls = parseXmlArray(xml, "Versions", "member");
999
+ return memberXmls.map((memberXml) => ({
1000
+ VersionId: parseXmlValue(memberXml, "VersionId") || "",
1001
+ IsDefaultVersion: parseXmlValue(memberXml, "IsDefaultVersion") === "true",
1002
+ CreateDate: parseXmlValue(memberXml, "CreateDate")
1003
+ }));
1004
+ }
1005
+ parseAttachedPolicies(xml) {
1006
+ const memberXmls = parseXmlArray(xml, "AttachedPolicies", "member");
1007
+ return memberXmls.map((memberXml) => ({
1008
+ PolicyName: parseXmlValue(memberXml, "PolicyName") || "",
1009
+ PolicyArn: parseXmlValue(memberXml, "PolicyArn") || ""
1010
+ }));
1011
+ }
1012
+ async putUserPolicy(params) {
1013
+ await this.request("PutUserPolicy", params);
1014
+ }
1015
+ async getUserPolicy(params) {
1016
+ const response = await this.request("GetUserPolicy", params);
1017
+ return {
1018
+ UserName: parseXmlValue(response, "UserName") || "",
1019
+ PolicyName: parseXmlValue(response, "PolicyName") || "",
1020
+ PolicyDocument: decodeURIComponent(parseXmlValue(response, "PolicyDocument") || "")
1021
+ };
1022
+ }
1023
+ async deleteUserPolicy(params) {
1024
+ await this.request("DeleteUserPolicy", params);
1025
+ }
1026
+ async listUserPolicies(params) {
1027
+ const response = await this.request("ListUserPolicies", params);
1028
+ const policyNames = parseXmlArray(response, "PolicyNames", "member");
1029
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
1030
+ const marker = parseXmlValue(response, "Marker");
1031
+ return { PolicyNames: policyNames, IsTruncated: isTruncated, Marker: marker };
1032
+ }
1033
+ async putGroupPolicy(params) {
1034
+ await this.request("PutGroupPolicy", params);
1035
+ }
1036
+ async getGroupPolicy(params) {
1037
+ const response = await this.request("GetGroupPolicy", params);
1038
+ return {
1039
+ GroupName: parseXmlValue(response, "GroupName") || "",
1040
+ PolicyName: parseXmlValue(response, "PolicyName") || "",
1041
+ PolicyDocument: decodeURIComponent(parseXmlValue(response, "PolicyDocument") || "")
1042
+ };
1043
+ }
1044
+ async deleteGroupPolicy(params) {
1045
+ await this.request("DeleteGroupPolicy", params);
1046
+ }
1047
+ async listGroupPolicies(params) {
1048
+ const response = await this.request("ListGroupPolicies", params);
1049
+ const policyNames = parseXmlArray(response, "PolicyNames", "member");
1050
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
1051
+ const marker = parseXmlValue(response, "Marker");
1052
+ return { PolicyNames: policyNames, IsTruncated: isTruncated, Marker: marker };
1053
+ }
1054
+ async putRolePolicy(params) {
1055
+ await this.request("PutRolePolicy", params);
1056
+ }
1057
+ async getRolePolicy(params) {
1058
+ const response = await this.request("GetRolePolicy", params);
1059
+ return {
1060
+ RoleName: parseXmlValue(response, "RoleName") || "",
1061
+ PolicyName: parseXmlValue(response, "PolicyName") || "",
1062
+ PolicyDocument: decodeURIComponent(parseXmlValue(response, "PolicyDocument") || "")
1063
+ };
1064
+ }
1065
+ async deleteRolePolicy(params) {
1066
+ await this.request("DeleteRolePolicy", params);
1067
+ }
1068
+ async listRolePolicies(params) {
1069
+ const response = await this.request("ListRolePolicies", params);
1070
+ const policyNames = parseXmlArray(response, "PolicyNames", "member");
1071
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
1072
+ const marker = parseXmlValue(response, "Marker");
1073
+ return { PolicyNames: policyNames, IsTruncated: isTruncated, Marker: marker };
1074
+ }
1075
+ async createAccessKey(params = {}) {
1076
+ const response = await this.request("CreateAccessKey", params);
1077
+ if (typeof response === "object") {
1078
+ const accessKey = response?.CreateAccessKeyResult?.AccessKey || response?.AccessKey;
1079
+ if (accessKey) {
1080
+ return {
1081
+ AccessKey: {
1082
+ UserName: accessKey.UserName || "",
1083
+ AccessKeyId: accessKey.AccessKeyId || "",
1084
+ Status: accessKey.Status || "Active",
1085
+ SecretAccessKey: accessKey.SecretAccessKey || "",
1086
+ CreateDate: accessKey.CreateDate
1087
+ }
1088
+ };
1089
+ }
1090
+ }
1091
+ return {
1092
+ AccessKey: {
1093
+ UserName: parseXmlValue(response, "UserName") || "",
1094
+ AccessKeyId: parseXmlValue(response, "AccessKeyId") || "",
1095
+ Status: parseXmlValue(response, "Status") || "Active",
1096
+ SecretAccessKey: parseXmlValue(response, "SecretAccessKey") || "",
1097
+ CreateDate: parseXmlValue(response, "CreateDate")
1098
+ }
1099
+ };
1100
+ }
1101
+ async listAccessKeys(params = {}) {
1102
+ const response = await this.request("ListAccessKeys", params);
1103
+ const keys = this.parseAccessKeys(response);
1104
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
1105
+ const marker = parseXmlValue(response, "Marker");
1106
+ return { AccessKeyMetadata: keys, IsTruncated: isTruncated, Marker: marker };
1107
+ }
1108
+ async updateAccessKey(params) {
1109
+ await this.request("UpdateAccessKey", params);
1110
+ }
1111
+ async deleteAccessKey(params) {
1112
+ await this.request("DeleteAccessKey", params);
1113
+ }
1114
+ async getAccessKeyLastUsed(params) {
1115
+ const response = await this.request("GetAccessKeyLastUsed", params);
1116
+ return {
1117
+ UserName: parseXmlValue(response, "UserName") || "",
1118
+ AccessKeyLastUsed: {
1119
+ LastUsedDate: parseXmlValue(response, "LastUsedDate"),
1120
+ ServiceName: parseXmlValue(response, "ServiceName"),
1121
+ Region: parseXmlValue(response, "Region")
1122
+ }
1123
+ };
1124
+ }
1125
+ parseAccessKeys(xml) {
1126
+ const memberXmls = parseXmlArray(xml, "AccessKeyMetadata", "member");
1127
+ return memberXmls.map((memberXml) => ({
1128
+ UserName: parseXmlValue(memberXml, "UserName"),
1129
+ AccessKeyId: parseXmlValue(memberXml, "AccessKeyId") || "",
1130
+ Status: parseXmlValue(memberXml, "Status") || "Active",
1131
+ CreateDate: parseXmlValue(memberXml, "CreateDate")
1132
+ }));
1133
+ }
1134
+ async createInstanceProfile(params) {
1135
+ const response = await this.request("CreateInstanceProfile", params);
1136
+ return this.parseInstanceProfile(response);
1137
+ }
1138
+ async getInstanceProfile(params) {
1139
+ const response = await this.request("GetInstanceProfile", params);
1140
+ return this.parseInstanceProfile(response);
1141
+ }
1142
+ async listInstanceProfiles(params = {}) {
1143
+ const response = await this.request("ListInstanceProfiles", params);
1144
+ const profiles = this.parseInstanceProfiles(response);
1145
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
1146
+ const marker = parseXmlValue(response, "Marker");
1147
+ return { InstanceProfiles: profiles, IsTruncated: isTruncated, Marker: marker };
1148
+ }
1149
+ async listInstanceProfilesForRole(params) {
1150
+ const response = await this.request("ListInstanceProfilesForRole", params);
1151
+ const profiles = this.parseInstanceProfiles(response);
1152
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
1153
+ const marker = parseXmlValue(response, "Marker");
1154
+ return { InstanceProfiles: profiles, IsTruncated: isTruncated, Marker: marker };
1155
+ }
1156
+ async addRoleToInstanceProfile(params) {
1157
+ await this.request("AddRoleToInstanceProfile", params);
1158
+ }
1159
+ async removeRoleFromInstanceProfile(params) {
1160
+ await this.request("RemoveRoleFromInstanceProfile", params);
1161
+ }
1162
+ async deleteInstanceProfile(params) {
1163
+ await this.request("DeleteInstanceProfile", params);
1164
+ }
1165
+ parseInstanceProfile(xml) {
1166
+ return {
1167
+ InstanceProfileName: parseXmlValue(xml, "InstanceProfileName") || "",
1168
+ InstanceProfileId: parseXmlValue(xml, "InstanceProfileId") || "",
1169
+ Arn: parseXmlValue(xml, "Arn") || "",
1170
+ Path: parseXmlValue(xml, "Path"),
1171
+ CreateDate: parseXmlValue(xml, "CreateDate")
1172
+ };
1173
+ }
1174
+ parseInstanceProfiles(xml) {
1175
+ const memberXmls = parseXmlArray(xml, "InstanceProfiles", "member");
1176
+ return memberXmls.map((memberXml) => ({
1177
+ InstanceProfileName: parseXmlValue(memberXml, "InstanceProfileName") || "",
1178
+ InstanceProfileId: parseXmlValue(memberXml, "InstanceProfileId") || "",
1179
+ Arn: parseXmlValue(memberXml, "Arn") || "",
1180
+ Path: parseXmlValue(memberXml, "Path"),
1181
+ CreateDate: parseXmlValue(memberXml, "CreateDate")
1182
+ }));
1183
+ }
1184
+ async getAccountPasswordPolicy() {
1185
+ const response = await this.request("GetAccountPasswordPolicy");
1186
+ return {
1187
+ MinimumPasswordLength: parseXmlValue(response, "MinimumPasswordLength") ? Number.parseInt(parseXmlValue(response, "MinimumPasswordLength"), 10) : undefined,
1188
+ RequireSymbols: parseXmlValue(response, "RequireSymbols") === "true",
1189
+ RequireNumbers: parseXmlValue(response, "RequireNumbers") === "true",
1190
+ RequireUppercaseCharacters: parseXmlValue(response, "RequireUppercaseCharacters") === "true",
1191
+ RequireLowercaseCharacters: parseXmlValue(response, "RequireLowercaseCharacters") === "true",
1192
+ AllowUsersToChangePassword: parseXmlValue(response, "AllowUsersToChangePassword") === "true",
1193
+ ExpirePasswords: parseXmlValue(response, "ExpirePasswords") === "true",
1194
+ MaxPasswordAge: parseXmlValue(response, "MaxPasswordAge") ? Number.parseInt(parseXmlValue(response, "MaxPasswordAge"), 10) : undefined,
1195
+ PasswordReusePrevention: parseXmlValue(response, "PasswordReusePrevention") ? Number.parseInt(parseXmlValue(response, "PasswordReusePrevention"), 10) : undefined,
1196
+ HardExpiry: parseXmlValue(response, "HardExpiry") === "true"
1197
+ };
1198
+ }
1199
+ async updateAccountPasswordPolicy(params) {
1200
+ await this.request("UpdateAccountPasswordPolicy", params);
1201
+ }
1202
+ async deleteAccountPasswordPolicy() {
1203
+ await this.request("DeleteAccountPasswordPolicy");
1204
+ }
1205
+ async getAccountSummary() {
1206
+ const response = await this.request("GetAccountSummary");
1207
+ const summary = {};
1208
+ const entries = parseXmlArray(response, "SummaryMap", "entry");
1209
+ for (const entry of entries) {
1210
+ const key = parseXmlValue(entry, "key");
1211
+ const value = parseXmlValue(entry, "value");
1212
+ if (key && value) {
1213
+ summary[key] = Number.parseInt(value, 10);
1214
+ }
1215
+ }
1216
+ return summary;
1217
+ }
1218
+ async listAccountAliases() {
1219
+ const response = await this.request("ListAccountAliases");
1220
+ const aliases = parseXmlArray(response, "AccountAliases", "member");
1221
+ const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
1222
+ const marker = parseXmlValue(response, "Marker");
1223
+ return { AccountAliases: aliases, IsTruncated: isTruncated, Marker: marker };
1224
+ }
1225
+ async createAccountAlias(params) {
1226
+ await this.request("CreateAccountAlias", params);
1227
+ }
1228
+ async deleteAccountAlias(params) {
1229
+ await this.request("DeleteAccountAlias", params);
1230
+ }
1231
+ async simulatePrincipalPolicy(params) {
1232
+ const response = await this.request("SimulatePrincipalPolicy", params);
1233
+ return this.parseSimulationResults(response);
1234
+ }
1235
+ parseSimulationResults(xml) {
1236
+ const resultXmls = parseXmlArray(xml, "EvaluationResults", "member");
1237
+ const evaluationResults = resultXmls.map((resultXml) => {
1238
+ const matchedStatementXmls = parseXmlArray(resultXml, "MatchedStatements", "member");
1239
+ const matchedStatements = matchedStatementXmls.map((stmtXml) => ({
1240
+ SourcePolicyId: parseXmlValue(stmtXml, "SourcePolicyId"),
1241
+ SourcePolicyType: parseXmlValue(stmtXml, "SourcePolicyType")
1242
+ }));
1243
+ return {
1244
+ EvalActionName: parseXmlValue(resultXml, "EvalActionName") || "",
1245
+ EvalResourceName: parseXmlValue(resultXml, "EvalResourceName"),
1246
+ EvalDecision: parseXmlValue(resultXml, "EvalDecision") || "",
1247
+ MatchedStatements: matchedStatements.length > 0 ? matchedStatements : undefined
1248
+ };
1249
+ });
1250
+ const isTruncated = parseXmlValue(xml, "IsTruncated") === "true";
1251
+ const marker = parseXmlValue(xml, "Marker");
1252
+ return { EvaluationResults: evaluationResults, IsTruncated: isTruncated, Marker: marker };
1253
+ }
1254
+ }
1255
+
1256
+ // src/aws/email.ts
1257
+ class EmailClient {
1258
+ ses;
1259
+ s3;
1260
+ iam;
1261
+ route53;
1262
+ region;
1263
+ domain;
1264
+ defaultFrom;
1265
+ constructor(options = {}) {
1266
+ this.region = options.region || "us-east-1";
1267
+ this.domain = options.domain;
1268
+ this.defaultFrom = options.defaultFrom;
1269
+ this.ses = new SESClient(this.region);
1270
+ this.s3 = new S3Client(this.region);
1271
+ this.iam = new IAMClient(this.region);
1272
+ this.route53 = new Route53Client(this.region);
1273
+ }
1274
+ async send(options) {
1275
+ const from = options.from || this.defaultFrom;
1276
+ if (!from) {
1277
+ throw new Error("From address is required. Set defaultFrom in constructor or provide from in options.");
1278
+ }
1279
+ const fromAddress = options.fromName ? `${options.fromName} <${from}>` : from;
1280
+ const toAddresses = Array.isArray(options.to) ? options.to : [options.to];
1281
+ if (!options.attachments || options.attachments.length === 0) {
1282
+ const result2 = await this.ses.sendSimpleEmail({
1283
+ from: fromAddress,
1284
+ to: toAddresses,
1285
+ subject: options.subject,
1286
+ text: options.text,
1287
+ html: options.html,
1288
+ replyTo: options.replyTo
1289
+ });
1290
+ return { messageId: result2.MessageId || "" };
1291
+ }
1292
+ const rawEmail = this.buildRawEmail(options, fromAddress, toAddresses);
1293
+ const result = await this.ses.sendEmail({
1294
+ FromEmailAddress: fromAddress,
1295
+ Destination: {
1296
+ ToAddresses: toAddresses,
1297
+ CcAddresses: options.cc ? Array.isArray(options.cc) ? options.cc : [options.cc] : undefined,
1298
+ BccAddresses: options.bcc ? Array.isArray(options.bcc) ? options.bcc : [options.bcc] : undefined
1299
+ },
1300
+ Content: {
1301
+ Raw: {
1302
+ Data: Buffer.from(rawEmail).toString("base64")
1303
+ }
1304
+ }
1305
+ });
1306
+ return { messageId: result.MessageId || "" };
1307
+ }
1308
+ async sendTemplate(options) {
1309
+ const from = options.from || this.defaultFrom;
1310
+ if (!from) {
1311
+ throw new Error("From address is required");
1312
+ }
1313
+ const fromAddress = options.fromName ? `${options.fromName} <${from}>` : from;
1314
+ const result = await this.ses.sendTemplatedEmail({
1315
+ from: fromAddress,
1316
+ to: options.to,
1317
+ templateName: options.templateName,
1318
+ templateData: options.templateData,
1319
+ replyTo: options.replyTo
1320
+ });
1321
+ return { messageId: result.MessageId || "" };
1322
+ }
1323
+ async sendBulk(options) {
1324
+ const from = options.from || this.defaultFrom;
1325
+ if (!from) {
1326
+ throw new Error("From address is required");
1327
+ }
1328
+ const entries = options.recipients.map((r) => ({
1329
+ Destination: {
1330
+ ToAddresses: Array.isArray(r.to) ? r.to : [r.to]
1331
+ },
1332
+ ReplacementEmailContent: r.templateData ? {
1333
+ ReplacementTemplate: {
1334
+ ReplacementTemplateData: JSON.stringify(r.templateData)
1335
+ }
1336
+ } : undefined
1337
+ }));
1338
+ const result = await this.ses.sendBulkEmail({
1339
+ FromEmailAddress: from,
1340
+ BulkEmailEntries: entries,
1341
+ DefaultContent: {
1342
+ Template: {
1343
+ TemplateName: options.templateName,
1344
+ TemplateData: JSON.stringify(options.defaultTemplateData)
1345
+ }
1346
+ }
1347
+ });
1348
+ return {
1349
+ results: result.BulkEmailEntryResults?.map((r) => ({
1350
+ status: r.Status || "UNKNOWN",
1351
+ messageId: r.MessageId,
1352
+ error: r.Error
1353
+ })) || []
1354
+ };
1355
+ }
1356
+ async setupDomain(domain) {
1357
+ let identity;
1358
+ try {
1359
+ identity = await this.ses.getEmailIdentity(domain);
1360
+ } catch {
1361
+ const createResult = await this.ses.createEmailIdentity({ EmailIdentity: domain });
1362
+ identity = {
1363
+ VerificationStatus: createResult.DkimAttributes?.Status,
1364
+ DkimAttributes: createResult.DkimAttributes,
1365
+ SendingEnabled: createResult.VerifiedForSendingStatus
1366
+ };
1367
+ }
1368
+ try {
1369
+ await this.ses.putEmailIdentityDkimAttributes({
1370
+ EmailIdentity: domain,
1371
+ SigningEnabled: true
1372
+ });
1373
+ } catch {}
1374
+ try {
1375
+ await this.ses.putEmailIdentityMailFromAttributes(domain, {
1376
+ MailFromDomain: `mail.${domain}`,
1377
+ BehaviorOnMxFailure: "USE_DEFAULT_VALUE"
1378
+ });
1379
+ } catch {}
1380
+ const dkimRecords = await this.ses.getDkimRecords(domain);
1381
+ return {
1382
+ domainVerified: identity.SendingEnabled === true,
1383
+ dkimStatus: identity.DkimAttributes?.Status || "UNKNOWN",
1384
+ dkimTokens: identity.DkimAttributes?.Tokens,
1385
+ mailFromStatus: identity.MailFromAttributes?.MailFromDomainStatus
1386
+ };
1387
+ }
1388
+ async getDnsRecords(domain) {
1389
+ const records = [];
1390
+ const dkimRecords = await this.ses.getDkimRecords(domain);
1391
+ for (const record of dkimRecords) {
1392
+ records.push({
1393
+ type: record.type,
1394
+ name: record.name,
1395
+ value: record.value,
1396
+ ttl: 1800
1397
+ });
1398
+ }
1399
+ records.push({
1400
+ type: "MX",
1401
+ name: domain,
1402
+ value: `inbound-smtp.${this.region}.amazonaws.com`,
1403
+ priority: 10,
1404
+ ttl: 3600
1405
+ });
1406
+ records.push({
1407
+ type: "MX",
1408
+ name: `mail.${domain}`,
1409
+ value: `feedback-smtp.${this.region}.amazonses.com`,
1410
+ priority: 10,
1411
+ ttl: 3600
1412
+ });
1413
+ records.push({
1414
+ type: "TXT",
1415
+ name: `mail.${domain}`,
1416
+ value: "v=spf1 include:amazonses.com ~all",
1417
+ ttl: 3600
1418
+ });
1419
+ records.push({
1420
+ type: "TXT",
1421
+ name: `_dmarc.${domain}`,
1422
+ value: `v=DMARC1;p=quarantine;pct=25;rua=mailto:dmarcreports@${domain}`,
1423
+ ttl: 3600
1424
+ });
1425
+ return records;
1426
+ }
1427
+ async isDomainReady(domain) {
1428
+ try {
1429
+ const identity = await this.ses.getEmailIdentity(domain);
1430
+ return identity.SendingEnabled === true && identity.DkimAttributes?.Status === "SUCCESS";
1431
+ } catch {
1432
+ return false;
1433
+ }
1434
+ }
1435
+ async setupReceiving(config) {
1436
+ const policy = {
1437
+ Version: "2012-10-17",
1438
+ Statement: [
1439
+ {
1440
+ Sid: "AllowSESPuts",
1441
+ Effect: "Allow",
1442
+ Principal: { Service: "ses.amazonaws.com" },
1443
+ Action: "s3:PutObject",
1444
+ Resource: `arn:aws:s3:::${config.bucketName}/*`,
1445
+ Condition: {
1446
+ StringEquals: { "AWS:SourceAccount": config.accountId }
1447
+ }
1448
+ }
1449
+ ]
1450
+ };
1451
+ await this.s3.putBucketPolicy(config.bucketName, policy);
1452
+ try {
1453
+ await this.ses.createReceiptRuleSet(config.ruleSetName);
1454
+ } catch (e) {
1455
+ if (!e.message?.includes("already exists") && e.code !== "AlreadyExists") {
1456
+ throw e;
1457
+ }
1458
+ }
1459
+ const actions = [
1460
+ {
1461
+ S3Action: {
1462
+ BucketName: config.bucketName,
1463
+ ObjectKeyPrefix: config.prefix || "inbox/"
1464
+ }
1465
+ }
1466
+ ];
1467
+ if (config.lambdaArn) {
1468
+ actions.push({
1469
+ LambdaAction: {
1470
+ FunctionArn: config.lambdaArn,
1471
+ InvocationType: "Event"
1472
+ }
1473
+ });
1474
+ }
1475
+ try {
1476
+ await this.ses.createReceiptRule({
1477
+ RuleSetName: config.ruleSetName,
1478
+ Rule: {
1479
+ Name: config.ruleName,
1480
+ Enabled: true,
1481
+ TlsPolicy: "Optional",
1482
+ Recipients: config.recipients || [config.domain],
1483
+ ScanEnabled: config.scanEnabled !== false,
1484
+ Actions: actions
1485
+ }
1486
+ });
1487
+ } catch (e) {
1488
+ if (e.message?.includes("already exists")) {
1489
+ await this.ses.deleteReceiptRule(config.ruleSetName, config.ruleName);
1490
+ await this.ses.createReceiptRule({
1491
+ RuleSetName: config.ruleSetName,
1492
+ Rule: {
1493
+ Name: config.ruleName,
1494
+ Enabled: true,
1495
+ TlsPolicy: "Optional",
1496
+ Recipients: config.recipients || [config.domain],
1497
+ ScanEnabled: config.scanEnabled !== false,
1498
+ Actions: actions
1499
+ }
1500
+ });
1501
+ } else {
1502
+ throw e;
1503
+ }
1504
+ }
1505
+ await this.ses.setActiveReceiptRuleSet(config.ruleSetName);
1506
+ }
1507
+ async getIncomingEmails(options) {
1508
+ const objects = await this.s3.list({
1509
+ bucket: options.bucket,
1510
+ prefix: options.prefix || "incoming/",
1511
+ maxKeys: options.maxResults || 100
1512
+ });
1513
+ return objects.map((obj) => ({
1514
+ key: obj.Key || "",
1515
+ lastModified: obj.LastModified || "",
1516
+ size: obj.Size || 0
1517
+ }));
1518
+ }
1519
+ async readEmail(options) {
1520
+ return await this.s3.getObject(options.bucket, options.key);
1521
+ }
1522
+ async createSmtpCredentials(options) {
1523
+ const iamUsername = `ses-smtp-${options.username.replace(/[^a-zA-Z0-9]/g, "-")}`;
1524
+ const { AWSClient: AWSClient2 } = await import("./chunk-mj1tmhte.js");
1525
+ const client = new AWSClient2;
1526
+ const buildBody = (action, params) => {
1527
+ const allParams = { Action: action, Version: "2010-05-08", ...params };
1528
+ return Object.entries(allParams).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
1529
+ };
1530
+ try {
1531
+ await client.request({
1532
+ service: "iam",
1533
+ region: "us-east-1",
1534
+ method: "POST",
1535
+ path: "/",
1536
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1537
+ body: buildBody("CreateUser", { UserName: iamUsername })
1538
+ });
1539
+ } catch (e) {
1540
+ if (!e.message?.includes("EntityAlreadyExists")) {
1541
+ throw e;
1542
+ }
1543
+ }
1544
+ const policy = {
1545
+ Version: "2012-10-17",
1546
+ Statement: [
1547
+ {
1548
+ Effect: "Allow",
1549
+ Action: "ses:SendRawEmail",
1550
+ Resource: "*",
1551
+ Condition: {
1552
+ StringLike: {
1553
+ "ses:FromAddress": `*@${options.domain}`
1554
+ }
1555
+ }
1556
+ }
1557
+ ]
1558
+ };
1559
+ const policyName = `ses-smtp-policy-${options.domain.replace(/\./g, "-")}`;
1560
+ try {
1561
+ await client.request({
1562
+ service: "iam",
1563
+ region: "us-east-1",
1564
+ method: "POST",
1565
+ path: "/",
1566
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1567
+ body: buildBody("PutUserPolicy", {
1568
+ UserName: iamUsername,
1569
+ PolicyName: policyName,
1570
+ PolicyDocument: JSON.stringify(policy)
1571
+ })
1572
+ });
1573
+ } catch {}
1574
+ const keyResponse = await client.request({
1575
+ service: "iam",
1576
+ region: "us-east-1",
1577
+ method: "POST",
1578
+ path: "/",
1579
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1580
+ body: buildBody("CreateAccessKey", { UserName: iamUsername })
1581
+ });
1582
+ const accessKey = keyResponse?.CreateAccessKeyResult?.AccessKey;
1583
+ if (!accessKey?.AccessKeyId || !accessKey?.SecretAccessKey) {
1584
+ throw new Error("Failed to create access key");
1585
+ }
1586
+ const smtpPassword = this.deriveSmtpPassword(accessKey.SecretAccessKey);
1587
+ return {
1588
+ username: accessKey.AccessKeyId,
1589
+ password: smtpPassword,
1590
+ server: `email-smtp.${this.region}.amazonaws.com`,
1591
+ port: 587
1592
+ };
1593
+ }
1594
+ deriveSmtpPassword(secretAccessKey) {
1595
+ const crypto = __require("node:crypto");
1596
+ const message = "SendRawEmail";
1597
+ const versionInBytes = Buffer.from([4]);
1598
+ let signature = crypto.createHmac("sha256", `AWS4${secretAccessKey}`).update("11111111").digest();
1599
+ signature = crypto.createHmac("sha256", signature).update(this.region).digest();
1600
+ signature = crypto.createHmac("sha256", signature).update("ses").digest();
1601
+ signature = crypto.createHmac("sha256", signature).update("aws4_request").digest();
1602
+ signature = crypto.createHmac("sha256", signature).update(message).digest();
1603
+ const signatureWithVersion = Buffer.concat([versionInBytes, signature]);
1604
+ return signatureWithVersion.toString("base64");
1605
+ }
1606
+ async createTemplate(options) {
1607
+ await this.ses.createEmailTemplate({
1608
+ TemplateName: options.name,
1609
+ TemplateContent: {
1610
+ Subject: options.subject,
1611
+ Text: options.text,
1612
+ Html: options.html
1613
+ }
1614
+ });
1615
+ }
1616
+ async getTemplate(name) {
1617
+ try {
1618
+ const result = await this.ses.getEmailTemplate(name);
1619
+ return {
1620
+ name: result.TemplateName || name,
1621
+ subject: result.TemplateContent?.Subject,
1622
+ text: result.TemplateContent?.Text,
1623
+ html: result.TemplateContent?.Html
1624
+ };
1625
+ } catch {
1626
+ return null;
1627
+ }
1628
+ }
1629
+ async deleteTemplate(name) {
1630
+ await this.ses.deleteEmailTemplate(name);
1631
+ }
1632
+ async listTemplates() {
1633
+ const result = await this.ses.listEmailTemplates();
1634
+ return result.TemplatesMetadata?.map((t) => ({
1635
+ name: t.TemplateName || "",
1636
+ createdAt: t.CreatedTimestamp
1637
+ })) || [];
1638
+ }
1639
+ async deploy(config) {
1640
+ const ruleSetName = `${config.appName}-${config.environment}-email-rules`;
1641
+ const ruleName = `${config.appName}-inbound-email`;
1642
+ const bucketName = config.storage?.bucketName || `${config.appName}-${config.environment}-email`;
1643
+ const domainSetup = await this.setupDomain(config.domain);
1644
+ const buckets = await this.s3.listBuckets();
1645
+ const bucketExists = buckets.Buckets?.some((b) => b.Name === bucketName);
1646
+ if (!bucketExists) {
1647
+ await this.s3.createBucket(bucketName);
1648
+ }
1649
+ await this.setupReceiving({
1650
+ domain: config.domain,
1651
+ ruleSetName,
1652
+ ruleName,
1653
+ bucketName,
1654
+ prefix: config.storage?.prefix || "inbox/",
1655
+ accountId: config.accountId,
1656
+ recipients: config.catchAll ? [config.domain] : config.mailboxes,
1657
+ scanEnabled: true
1658
+ });
1659
+ const dnsRecords = await this.getDnsRecords(config.domain);
1660
+ return {
1661
+ success: true,
1662
+ domainVerified: domainSetup.domainVerified,
1663
+ dkimStatus: domainSetup.dkimStatus,
1664
+ receiptRuleSet: ruleSetName,
1665
+ storageBucket: bucketName,
1666
+ dnsRecords
1667
+ };
1668
+ }
1669
+ async undeploy(config) {
1670
+ const ruleSetName = `${config.appName}-${config.environment}-email-rules`;
1671
+ const ruleName = `${config.appName}-inbound-email`;
1672
+ const bucketName = `${config.appName}-${config.environment}-email`;
1673
+ try {
1674
+ await this.ses.deleteReceiptRule(ruleSetName, ruleName);
1675
+ } catch {}
1676
+ try {
1677
+ await this.ses.deleteReceiptRuleSet(ruleSetName);
1678
+ } catch {}
1679
+ if (config.deleteBucket) {
1680
+ try {
1681
+ await this.s3.emptyAndDeleteBucket(bucketName);
1682
+ } catch {}
1683
+ }
1684
+ }
1685
+ async getSendingStats() {
1686
+ const quota = await this.ses.getSendQuota();
1687
+ return {
1688
+ sentLast24Hours: quota.SentLast24Hours || 0,
1689
+ maxSendRate: quota.MaxSendRate || 0,
1690
+ max24HourSend: quota.Max24HourSend || 0
1691
+ };
1692
+ }
1693
+ buildRawEmail(options, from, to) {
1694
+ const boundary = `----=_Part_${Date.now()}_${Math.random().toString(36).substring(2)}`;
1695
+ let email = "";
1696
+ email += `From: ${from}\r
1697
+ `;
1698
+ email += `To: ${to.join(", ")}\r
1699
+ `;
1700
+ if (options.cc) {
1701
+ const ccList = Array.isArray(options.cc) ? options.cc : [options.cc];
1702
+ email += `Cc: ${ccList.join(", ")}\r
1703
+ `;
1704
+ }
1705
+ email += `Subject: ${options.subject}\r
1706
+ `;
1707
+ email += `MIME-Version: 1.0\r
1708
+ `;
1709
+ email += `Content-Type: multipart/mixed; boundary="${boundary}"\r
1710
+ `;
1711
+ email += `\r
1712
+ `;
1713
+ if (options.text || options.html) {
1714
+ email += `--${boundary}\r
1715
+ `;
1716
+ if (options.html && options.text) {
1717
+ const altBoundary = `----=_Alt_${Date.now()}`;
1718
+ email += `Content-Type: multipart/alternative; boundary="${altBoundary}"\r
1719
+ `;
1720
+ email += `\r
1721
+ `;
1722
+ email += `--${altBoundary}\r
1723
+ `;
1724
+ email += `Content-Type: text/plain; charset="UTF-8"\r
1725
+ `;
1726
+ email += `\r
1727
+ `;
1728
+ email += `${options.text}\r
1729
+ `;
1730
+ email += `--${altBoundary}\r
1731
+ `;
1732
+ email += `Content-Type: text/html; charset="UTF-8"\r
1733
+ `;
1734
+ email += `\r
1735
+ `;
1736
+ email += `${options.html}\r
1737
+ `;
1738
+ email += `--${altBoundary}--\r
1739
+ `;
1740
+ } else if (options.html) {
1741
+ email += `Content-Type: text/html; charset="UTF-8"\r
1742
+ `;
1743
+ email += `\r
1744
+ `;
1745
+ email += `${options.html}\r
1746
+ `;
1747
+ } else {
1748
+ email += `Content-Type: text/plain; charset="UTF-8"\r
1749
+ `;
1750
+ email += `\r
1751
+ `;
1752
+ email += `${options.text}\r
1753
+ `;
1754
+ }
1755
+ }
1756
+ if (options.attachments) {
1757
+ for (const attachment of options.attachments) {
1758
+ email += `--${boundary}\r
1759
+ `;
1760
+ email += `Content-Type: ${attachment.contentType || "application/octet-stream"}; name="${attachment.filename}"\r
1761
+ `;
1762
+ email += `Content-Transfer-Encoding: base64\r
1763
+ `;
1764
+ email += `Content-Disposition: attachment; filename="${attachment.filename}"\r
1765
+ `;
1766
+ email += `\r
1767
+ `;
1768
+ email += `${attachment.content}\r
1769
+ `;
1770
+ }
1771
+ }
1772
+ email += `--${boundary}--\r
1773
+ `;
1774
+ return email;
1775
+ }
1776
+ }
1777
+ var email = new EmailClient;
1778
+
1779
+ export { SESClient, IAMClient, EmailClient, email };