wirejs-deploy-amplify-basic 0.1.171 → 0.1.173

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.
@@ -16,6 +16,7 @@ import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb';
16
16
  import { AnyPrincipal, PolicyStatement } from 'aws-cdk-lib/aws-iam';
17
17
  import { Rule, RuleTargetInput, Schedule } from 'aws-cdk-lib/aws-events';
18
18
  import { LambdaFunction as LambdaFunctionTarget } from 'aws-cdk-lib/aws-events-targets';
19
+ import { SESClient, VerifyEmailIdentityCommand, GetIdentityVerificationAttributesCommand } from '@aws-sdk/client-ses';
19
20
 
20
21
  import { TableDefinition, indexName, DeploymentConfig } from 'wirejs-resources';
21
22
  import { TableIndexes } from './constructs/table-indexes';
@@ -236,11 +237,15 @@ for (const resource of generated) {
236
237
  */
237
238
  function cronExpressionToSchedule(expression: string): Schedule {
238
239
  const [minute, hour, dayOfMonth, month, dayOfWeek] = expression.trim().split(/\s+/);
239
- // EventBridge requires exactly one of day-of-month or day-of-week to be '?'.
240
- // If neither is wildcarded, prefer keeping day-of-month and set weekday to '?'.
241
- const ebDayOfMonth = dayOfWeek !== '*' && dayOfWeek !== '?' ? '?' : dayOfMonth;
242
- const ebDayOfWeek = ebDayOfMonth === '?' ? dayOfWeek : '?';
243
- return Schedule.cron({ minute, hour, day: ebDayOfMonth, month, weekDay: ebDayOfWeek });
240
+ // CDK's Schedule.cron() does not accept both `day` and `weekDay` simultaneously.
241
+ // If dayOfWeek is specified (not '*' or '?'), use it; otherwise use dayOfMonth.
242
+ const useWeekDay = dayOfWeek !== '*' && dayOfWeek !== '?';
243
+ return Schedule.cron({
244
+ minute,
245
+ hour,
246
+ month,
247
+ ...(useWeekDay ? { weekDay: dayOfWeek } : { day: dayOfMonth }),
248
+ });
244
249
  }
245
250
 
246
251
  /**
@@ -305,4 +310,75 @@ api.addToRolePolicy(new PolicyStatement({
305
310
  }));
306
311
 
307
312
 
313
+ /**
314
+ * SES permissions for EmailSender resources
315
+ */
316
+ function isEmailSender(resource: any): resource is {
317
+ type: 'EmailSender';
318
+ options: { absoluteId: string; from: string };
319
+ } {
320
+ return resource.type === 'EmailSender';
321
+ }
322
+
323
+ const emailSenderResources = generated.filter(isEmailSender);
324
+ if (emailSenderResources.length > 0) {
325
+ const senderAddresses = emailSenderResources.map(r => r.options.from);
326
+ api.addToRolePolicy(new PolicyStatement({
327
+ actions: [
328
+ 'ses:SendEmail',
329
+ 'ses:SendRawEmail',
330
+ ],
331
+ resources: [
332
+ `arn:${backend.stack.partition}:ses:${backend.stack.region}:${backend.stack.account}:identity/*`,
333
+ ],
334
+ }));
335
+
336
+ // Initiate SES email verification for each sender address during deployment.
337
+ // This sends a verification email to each address automatically so customers
338
+ // don't need to manually trigger it from the AWS console.
339
+ const sesClient = new SESClient();
340
+ const verificationResults: { address: string; status: string }[] = [];
341
+
342
+ for (const address of senderAddresses) {
343
+ try {
344
+ // Check current verification status first
345
+ const statusResult = await sesClient.send(
346
+ new GetIdentityVerificationAttributesCommand({ Identities: [address] })
347
+ );
348
+ const currentStatus = statusResult.VerificationAttributes?.[address]?.VerificationStatus;
349
+
350
+ if (currentStatus === 'Success') {
351
+ verificationResults.push({ address, status: 'already verified ✅' });
352
+ } else {
353
+ // Trigger a (new) verification email
354
+ await sesClient.send(new VerifyEmailIdentityCommand({ EmailAddress: address }));
355
+ verificationResults.push({ address, status: 'verification email sent 📧' });
356
+ }
357
+ } catch (err: any) {
358
+ verificationResults.push({ address, status: `could not initiate automatically — ${err?.message ?? err}` });
359
+ }
360
+ }
361
+
362
+ console.log(`
363
+ ⚠️ AWS SES Email Verification
364
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
365
+ Your app uses EmailSender with the following sender address(es):
366
+
367
+ ${verificationResults.map(r => ` • ${r.address} — ${r.status}`).join('\n')}
368
+
369
+ ${verificationResults.some(r => r.status.includes('sent'))
370
+ ? 'Check your inbox and click the verification link in each email from AWS SES.'
371
+ : ''}
372
+ AWS SES starts in "sandbox" mode — you must also verify recipient addresses
373
+ or request production access to send to arbitrary recipients.
374
+
375
+ To request production (sandbox removal) access:
376
+ https://docs.aws.amazon.com/ses/latest/dg/request-production-access.html
377
+
378
+ See docs/amplify.md in your project for more details.
379
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
380
+ `);
381
+ }
382
+
383
+
308
384
  backend.addOutput({ custom: { api: apiUrl.url } });
@@ -3,6 +3,6 @@
3
3
  "dependencies": {
4
4
  "jsdom": "^25.0.1",
5
5
  "wirejs-dom": "^1.0.44",
6
- "wirejs-resources": "^0.1.171"
6
+ "wirejs-resources": "^0.1.173"
7
7
  }
8
8
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from 'wirejs-resources';
2
2
  export { FileService } from './services/file.js';
3
3
  export { AuthenticationService } from './services/authentication.js';
4
+ export { EmailSender } from './services/email-sender.js';
4
5
  export { LLM } from './services/llm.js';
5
6
  export { DistributedTable } from './resources/distributed-table.js';
6
7
  export { RealtimeService } from './services/realtime.js';
package/dist/index.js CHANGED
@@ -6,6 +6,8 @@ import { FileService } from './services/file.js';
6
6
  export { FileService } from './services/file.js';
7
7
  import { AuthenticationService } from './services/authentication.js';
8
8
  export { AuthenticationService } from './services/authentication.js';
9
+ import { EmailSender } from './services/email-sender.js';
10
+ export { EmailSender } from './services/email-sender.js';
9
11
  import { LLM } from './services/llm.js';
10
12
  export { LLM } from './services/llm.js';
11
13
  import { DistributedTable } from './resources/distributed-table.js';
@@ -23,6 +25,7 @@ overrides.AuthenticationService = AuthenticationService;
23
25
  overrides.BackgroundJob = BackgroundJob;
24
26
  overrides.CronJob = CronJob;
25
27
  overrides.DistributedTable = DistributedTable;
28
+ overrides.EmailSender = EmailSender;
26
29
  overrides.Endpoint = Endpoint;
27
30
  overrides.FileService = FileService;
28
31
  overrides.LLM = LLM;
@@ -0,0 +1,7 @@
1
+ import { Resource, EmailSender as BaseEmailSender, EmailMessage } from 'wirejs-resources';
2
+ export declare class EmailSender extends BaseEmailSender {
3
+ constructor(scope: Resource | string, id: string, options: {
4
+ from: string;
5
+ });
6
+ send(message: EmailMessage): Promise<void>;
7
+ }
@@ -0,0 +1,24 @@
1
+ import { SESClient, SendEmailCommand, } from '@aws-sdk/client-ses';
2
+ import { EmailSender as BaseEmailSender, } from 'wirejs-resources';
3
+ import { addResource } from '../resource-collector.js';
4
+ const ses = new SESClient();
5
+ export class EmailSender extends BaseEmailSender {
6
+ constructor(scope, id, options) {
7
+ super(scope, id, options);
8
+ addResource('EmailSender', { absoluteId: this.absoluteId, from: options.from });
9
+ }
10
+ async send(message) {
11
+ const toAddresses = Array.isArray(message.to) ? message.to : [message.to];
12
+ await ses.send(new SendEmailCommand({
13
+ Source: this.from,
14
+ Destination: { ToAddresses: toAddresses },
15
+ Message: {
16
+ Subject: { Data: message.subject },
17
+ Body: {
18
+ Text: { Data: message.body },
19
+ ...(message.html !== undefined ? { Html: { Data: message.html } } : {}),
20
+ },
21
+ },
22
+ }));
23
+ }
24
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wirejs-deploy-amplify-basic",
3
- "version": "0.1.171",
3
+ "version": "0.1.173",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -32,6 +32,7 @@
32
32
  "@aws-sdk/client-cognito-identity-provider": "^3.741.0",
33
33
  "@aws-sdk/client-dynamodb": "^3.774.0",
34
34
  "@aws-sdk/client-s3": "^3.738.0",
35
+ "@aws-sdk/client-ses": "^3.738.0",
35
36
  "@aws-sdk/credential-provider-node": "^3.806.0",
36
37
  "@aws-sdk/client-lambda": "3.821.0",
37
38
  "@aws-sdk/lib-dynamodb": "^3.778.0",
@@ -44,7 +45,7 @@
44
45
  "recursive-copy": "^2.0.14",
45
46
  "rimraf": "^6.0.1",
46
47
  "wirejs-dom": "^1.0.44",
47
- "wirejs-resources": "^0.1.171"
48
+ "wirejs-resources": "^0.1.173"
48
49
  },
49
50
  "devDependencies": {
50
51
  "@aws-amplify/backend": "^1.14.0",