@stacksjs/ts-cloud-core 0.1.7 → 0.1.9

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 (225) hide show
  1. package/package.json +7 -6
  2. package/src/advanced-features.test.ts +465 -0
  3. package/src/aws/cloudformation.ts +421 -0
  4. package/src/aws/cloudfront.ts +158 -0
  5. package/src/aws/credentials.test.ts +132 -0
  6. package/src/aws/credentials.ts +545 -0
  7. package/src/aws/index.ts +87 -0
  8. package/src/aws/s3.test.ts +188 -0
  9. package/src/aws/s3.ts +1088 -0
  10. package/src/aws/signature.test.ts +670 -0
  11. package/src/aws/signature.ts +1155 -0
  12. package/src/backup/disaster-recovery.test.ts +726 -0
  13. package/src/backup/disaster-recovery.ts +500 -0
  14. package/src/backup/index.ts +34 -0
  15. package/src/backup/manager.test.ts +498 -0
  16. package/src/backup/manager.ts +432 -0
  17. package/src/cicd/circleci.ts +430 -0
  18. package/src/cicd/github-actions.ts +424 -0
  19. package/src/cicd/gitlab-ci.ts +255 -0
  20. package/src/cicd/index.ts +8 -0
  21. package/src/cli/history.ts +396 -0
  22. package/src/cli/index.ts +10 -0
  23. package/src/cli/progress.ts +458 -0
  24. package/src/cli/repl.ts +454 -0
  25. package/src/cli/suggestions.ts +327 -0
  26. package/src/cli/table.test.ts +319 -0
  27. package/src/cli/table.ts +332 -0
  28. package/src/cloudformation/builder.test.ts +327 -0
  29. package/src/cloudformation/builder.ts +378 -0
  30. package/src/cloudformation/builders/api-gateway.ts +449 -0
  31. package/src/cloudformation/builders/cache.ts +334 -0
  32. package/src/cloudformation/builders/cdn.ts +278 -0
  33. package/src/cloudformation/builders/compute.ts +485 -0
  34. package/src/cloudformation/builders/database.ts +392 -0
  35. package/src/cloudformation/builders/functions.ts +343 -0
  36. package/src/cloudformation/builders/messaging.ts +140 -0
  37. package/src/cloudformation/builders/monitoring.ts +300 -0
  38. package/src/cloudformation/builders/network.ts +264 -0
  39. package/src/cloudformation/builders/queue.ts +147 -0
  40. package/src/cloudformation/builders/security.ts +399 -0
  41. package/src/cloudformation/builders/storage.ts +285 -0
  42. package/src/cloudformation/index.ts +30 -0
  43. package/src/cloudformation/types.ts +173 -0
  44. package/src/compliance/aws-config.ts +543 -0
  45. package/src/compliance/cloudtrail.ts +376 -0
  46. package/src/compliance/compliance.test.ts +423 -0
  47. package/src/compliance/guardduty.ts +446 -0
  48. package/src/compliance/index.ts +66 -0
  49. package/src/compliance/security-hub.ts +456 -0
  50. package/src/containers/build-optimization.ts +416 -0
  51. package/src/containers/containers.test.ts +508 -0
  52. package/src/containers/image-scanning.ts +360 -0
  53. package/src/containers/index.ts +9 -0
  54. package/src/containers/registry.ts +293 -0
  55. package/src/containers/service-mesh.ts +520 -0
  56. package/src/database/database.test.ts +762 -0
  57. package/src/database/index.ts +9 -0
  58. package/src/database/migrations.ts +444 -0
  59. package/src/database/performance.ts +528 -0
  60. package/src/database/replicas.ts +534 -0
  61. package/src/database/users.ts +494 -0
  62. package/src/dependency-graph.ts +143 -0
  63. package/src/deployment/ab-testing.ts +582 -0
  64. package/src/deployment/blue-green.ts +452 -0
  65. package/src/deployment/canary.ts +500 -0
  66. package/src/deployment/deployment.test.ts +526 -0
  67. package/src/deployment/index.ts +61 -0
  68. package/src/deployment/progressive.ts +62 -0
  69. package/src/dns/dns.test.ts +641 -0
  70. package/src/dns/dnssec.ts +315 -0
  71. package/src/dns/index.ts +8 -0
  72. package/src/dns/resolver.ts +496 -0
  73. package/src/dns/routing.ts +593 -0
  74. package/src/email/advanced/analytics.ts +445 -0
  75. package/src/email/advanced/index.ts +11 -0
  76. package/src/email/advanced/rules.ts +465 -0
  77. package/src/email/advanced/scheduling.ts +352 -0
  78. package/src/email/advanced/search.ts +412 -0
  79. package/src/email/advanced/shared-mailboxes.ts +404 -0
  80. package/src/email/advanced/templates.ts +455 -0
  81. package/src/email/advanced/threading.ts +281 -0
  82. package/src/email/analytics.ts +467 -0
  83. package/src/email/bounce-handling.ts +425 -0
  84. package/src/email/email.test.ts +431 -0
  85. package/src/email/handlers/__tests__/inbound.test.ts +38 -0
  86. package/src/email/handlers/__tests__/outbound.test.ts +37 -0
  87. package/src/email/handlers/converter.ts +227 -0
  88. package/src/email/handlers/feedback.ts +228 -0
  89. package/src/email/handlers/inbound.ts +169 -0
  90. package/src/email/handlers/outbound.ts +178 -0
  91. package/src/email/index.ts +15 -0
  92. package/src/email/reputation.ts +303 -0
  93. package/src/email/templates.ts +352 -0
  94. package/src/errors/index.test.ts +434 -0
  95. package/src/errors/index.ts +416 -0
  96. package/src/health-checks/index.ts +40 -0
  97. package/src/index.ts +360 -0
  98. package/src/intrinsic-functions.ts +118 -0
  99. package/src/lambda/concurrency.ts +330 -0
  100. package/src/lambda/destinations.ts +345 -0
  101. package/src/lambda/dlq.ts +425 -0
  102. package/src/lambda/index.ts +11 -0
  103. package/src/lambda/lambda.test.ts +840 -0
  104. package/src/lambda/layers.ts +263 -0
  105. package/src/lambda/versions.ts +376 -0
  106. package/src/lambda/vpc.ts +399 -0
  107. package/src/local/config.ts +114 -0
  108. package/src/local/index.ts +6 -0
  109. package/src/local/mock-aws.ts +351 -0
  110. package/src/modules/ai.ts +340 -0
  111. package/src/modules/api.ts +478 -0
  112. package/src/modules/auth.ts +805 -0
  113. package/src/modules/cache.ts +417 -0
  114. package/src/modules/cdn.ts +1062 -0
  115. package/src/modules/communication.ts +1094 -0
  116. package/src/modules/compute.ts +3348 -0
  117. package/src/modules/database.ts +554 -0
  118. package/src/modules/deployment.ts +1079 -0
  119. package/src/modules/dns.ts +337 -0
  120. package/src/modules/email.ts +1538 -0
  121. package/src/modules/filesystem.ts +515 -0
  122. package/src/modules/index.ts +32 -0
  123. package/src/modules/messaging.ts +486 -0
  124. package/src/modules/monitoring.ts +2086 -0
  125. package/src/modules/network.ts +664 -0
  126. package/src/modules/parameter-store.ts +325 -0
  127. package/src/modules/permissions.ts +1081 -0
  128. package/src/modules/phone.ts +494 -0
  129. package/src/modules/queue.ts +1260 -0
  130. package/src/modules/redirects.ts +464 -0
  131. package/src/modules/registry.ts +699 -0
  132. package/src/modules/search.ts +401 -0
  133. package/src/modules/secrets.ts +416 -0
  134. package/src/modules/security.ts +731 -0
  135. package/src/modules/sms.ts +389 -0
  136. package/src/modules/storage.ts +1120 -0
  137. package/src/modules/workflow.ts +680 -0
  138. package/src/multi-account/config.ts +521 -0
  139. package/src/multi-account/index.ts +7 -0
  140. package/src/multi-account/manager.ts +427 -0
  141. package/src/multi-region/cross-region.ts +410 -0
  142. package/src/multi-region/index.ts +8 -0
  143. package/src/multi-region/manager.ts +483 -0
  144. package/src/multi-region/regions.ts +435 -0
  145. package/src/network-security/index.ts +48 -0
  146. package/src/observability/index.ts +9 -0
  147. package/src/observability/logs.ts +522 -0
  148. package/src/observability/metrics.ts +460 -0
  149. package/src/observability/observability.test.ts +782 -0
  150. package/src/observability/synthetics.ts +568 -0
  151. package/src/observability/xray.ts +358 -0
  152. package/src/phone/advanced/analytics.ts +349 -0
  153. package/src/phone/advanced/callbacks.ts +428 -0
  154. package/src/phone/advanced/index.ts +8 -0
  155. package/src/phone/advanced/ivr-builder.ts +504 -0
  156. package/src/phone/advanced/recording.ts +310 -0
  157. package/src/phone/handlers/__tests__/incoming-call.test.ts +40 -0
  158. package/src/phone/handlers/incoming-call.ts +117 -0
  159. package/src/phone/handlers/missed-call.ts +116 -0
  160. package/src/phone/handlers/voicemail.ts +179 -0
  161. package/src/phone/index.ts +9 -0
  162. package/src/presets/api-backend.ts +134 -0
  163. package/src/presets/data-pipeline.ts +204 -0
  164. package/src/presets/extend.test.ts +295 -0
  165. package/src/presets/extend.ts +297 -0
  166. package/src/presets/fullstack-app.ts +144 -0
  167. package/src/presets/index.ts +27 -0
  168. package/src/presets/jamstack.ts +135 -0
  169. package/src/presets/microservices.ts +167 -0
  170. package/src/presets/ml-api.ts +208 -0
  171. package/src/presets/nodejs-server.ts +104 -0
  172. package/src/presets/nodejs-serverless.ts +114 -0
  173. package/src/presets/realtime-app.ts +184 -0
  174. package/src/presets/static-site.ts +64 -0
  175. package/src/presets/traditional-web-app.ts +339 -0
  176. package/src/presets/wordpress.ts +138 -0
  177. package/src/preview/github.test.ts +249 -0
  178. package/src/preview/github.ts +297 -0
  179. package/src/preview/index.ts +37 -0
  180. package/src/preview/manager.test.ts +440 -0
  181. package/src/preview/manager.ts +326 -0
  182. package/src/preview/notifications.test.ts +582 -0
  183. package/src/preview/notifications.ts +341 -0
  184. package/src/queue/batch-processing.ts +402 -0
  185. package/src/queue/dlq-monitoring.ts +402 -0
  186. package/src/queue/fifo.ts +342 -0
  187. package/src/queue/index.ts +9 -0
  188. package/src/queue/management.ts +428 -0
  189. package/src/queue/queue.test.ts +429 -0
  190. package/src/resource-mgmt/index.ts +39 -0
  191. package/src/resource-naming.ts +62 -0
  192. package/src/s3/index.ts +523 -0
  193. package/src/schema/cloud-config.schema.json +554 -0
  194. package/src/schema/index.ts +68 -0
  195. package/src/security/certificate-manager.ts +492 -0
  196. package/src/security/index.ts +9 -0
  197. package/src/security/scanning.ts +545 -0
  198. package/src/security/secrets-manager.ts +476 -0
  199. package/src/security/secrets-rotation.ts +456 -0
  200. package/src/security/security.test.ts +738 -0
  201. package/src/sms/advanced/ab-testing.ts +389 -0
  202. package/src/sms/advanced/analytics.ts +336 -0
  203. package/src/sms/advanced/campaigns.ts +523 -0
  204. package/src/sms/advanced/chatbot.ts +224 -0
  205. package/src/sms/advanced/index.ts +10 -0
  206. package/src/sms/advanced/link-tracking.ts +248 -0
  207. package/src/sms/advanced/mms.ts +308 -0
  208. package/src/sms/handlers/__tests__/send.test.ts +40 -0
  209. package/src/sms/handlers/delivery-status.ts +133 -0
  210. package/src/sms/handlers/receive.ts +162 -0
  211. package/src/sms/handlers/send.ts +174 -0
  212. package/src/sms/index.ts +9 -0
  213. package/src/stack-diff.ts +389 -0
  214. package/src/static-site/index.ts +85 -0
  215. package/src/template-builder.ts +110 -0
  216. package/src/template-validator.ts +574 -0
  217. package/src/utils/cache.ts +291 -0
  218. package/src/utils/diff.ts +269 -0
  219. package/src/utils/hash.ts +227 -0
  220. package/src/utils/index.ts +8 -0
  221. package/src/utils/parallel.ts +294 -0
  222. package/src/validators/credentials.test.ts +274 -0
  223. package/src/validators/credentials.ts +233 -0
  224. package/src/validators/quotas.test.ts +434 -0
  225. package/src/validators/quotas.ts +217 -0
@@ -0,0 +1,494 @@
1
+ /**
2
+ * Phone/Voice Module for CloudFormation
3
+ *
4
+ * Provides CloudFormation resources for Amazon Connect phone infrastructure
5
+ */
6
+
7
+ import { handler as incomingCallHandler } from '../phone/handlers/incoming-call'
8
+ import { handler as voicemailHandler } from '../phone/handlers/voicemail'
9
+ import { handler as missedCallHandler } from '../phone/handlers/missed-call'
10
+
11
+ export interface PhoneConfig {
12
+ slug: string
13
+ environment: string
14
+ instanceAlias: string
15
+ inboundCallsEnabled?: boolean
16
+ outboundCallsEnabled?: boolean
17
+ voicemailEnabled?: boolean
18
+ transcriptionEnabled?: boolean
19
+ notificationTopicArn?: string
20
+ webhookUrl?: string
21
+ }
22
+
23
+ export class Phone {
24
+ /**
25
+ * Lambda code for phone handlers
26
+ */
27
+ static LambdaCode: {
28
+ incomingCall: string;
29
+ voicemail: string;
30
+ missedCall: string;
31
+ } = {
32
+ incomingCall: incomingCallHandler,
33
+ voicemail: voicemailHandler,
34
+ missedCall: missedCallHandler,
35
+ }
36
+
37
+ /**
38
+ * Create Amazon Connect instance CloudFormation resource
39
+ */
40
+ static createConnectInstance(config: PhoneConfig): Record<string, any> {
41
+ const { slug, environment, instanceAlias, inboundCallsEnabled = true, outboundCallsEnabled = true } = config
42
+
43
+ return {
44
+ [`${slug}ConnectInstance`]: {
45
+ Type: 'AWS::Connect::Instance',
46
+ Properties: {
47
+ InstanceAlias: instanceAlias,
48
+ IdentityManagementType: 'CONNECT_MANAGED',
49
+ Attributes: {
50
+ InboundCalls: inboundCallsEnabled,
51
+ OutboundCalls: outboundCallsEnabled,
52
+ ContactflowLogs: true,
53
+ ContactLens: true,
54
+ AutoResolveBestVoices: true,
55
+ UseCustomTTSVoices: false,
56
+ EarlyMedia: true,
57
+ },
58
+ },
59
+ },
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Create hours of operation resource
65
+ */
66
+ static createHoursOfOperation(config: {
67
+ slug: string
68
+ instanceArn: string
69
+ name: string
70
+ timezone: string
71
+ schedule: Array<{
72
+ day: string
73
+ startHour: number
74
+ startMinute: number
75
+ endHour: number
76
+ endMinute: number
77
+ }>
78
+ }): Record<string, any> {
79
+ return {
80
+ [`${config.slug}HoursOfOperation`]: {
81
+ Type: 'AWS::Connect::HoursOfOperation',
82
+ Properties: {
83
+ InstanceArn: config.instanceArn,
84
+ Name: config.name,
85
+ TimeZone: config.timezone,
86
+ Config: config.schedule.map(s => ({
87
+ Day: s.day,
88
+ StartTime: { Hours: s.startHour, Minutes: s.startMinute },
89
+ EndTime: { Hours: s.endHour, Minutes: s.endMinute },
90
+ })),
91
+ },
92
+ },
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Create queue resource
98
+ */
99
+ static createQueue(config: {
100
+ slug: string
101
+ instanceArn: string
102
+ name: string
103
+ hoursOfOperationArn: string
104
+ maxContacts?: number
105
+ }): Record<string, any> {
106
+ return {
107
+ [`${config.slug}Queue`]: {
108
+ Type: 'AWS::Connect::Queue',
109
+ Properties: {
110
+ InstanceArn: config.instanceArn,
111
+ Name: config.name,
112
+ HoursOfOperationArn: config.hoursOfOperationArn,
113
+ MaxContacts: config.maxContacts || 10,
114
+ },
115
+ },
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Create contact flow resource
121
+ */
122
+ static createContactFlow(config: {
123
+ slug: string
124
+ instanceArn: string
125
+ name: string
126
+ type: 'CONTACT_FLOW' | 'CUSTOMER_QUEUE' | 'CUSTOMER_HOLD' | 'CUSTOMER_WHISPER' | 'AGENT_HOLD' | 'AGENT_WHISPER' | 'OUTBOUND_WHISPER' | 'AGENT_TRANSFER' | 'QUEUE_TRANSFER'
127
+ content: string
128
+ }): Record<string, any> {
129
+ return {
130
+ [`${config.slug}ContactFlow`]: {
131
+ Type: 'AWS::Connect::ContactFlow',
132
+ Properties: {
133
+ InstanceArn: config.instanceArn,
134
+ Name: config.name,
135
+ Type: config.type,
136
+ Content: config.content,
137
+ },
138
+ },
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Create basic IVR contact flow content
144
+ */
145
+ static createBasicIvrFlow(config: {
146
+ greeting: string
147
+ queueArn: string
148
+ voicemailLambdaArn?: string
149
+ }): string {
150
+ const flow = {
151
+ Version: '2019-10-30',
152
+ StartAction: 'greeting',
153
+ Actions: [
154
+ {
155
+ Identifier: 'greeting',
156
+ Type: 'MessageParticipant',
157
+ Parameters: {
158
+ Text: config.greeting,
159
+ },
160
+ Transitions: {
161
+ NextAction: 'transfer_to_queue',
162
+ Errors: [{ NextAction: 'disconnect' }],
163
+ },
164
+ },
165
+ {
166
+ Identifier: 'transfer_to_queue',
167
+ Type: 'TransferToQueue',
168
+ Parameters: {
169
+ QueueId: config.queueArn,
170
+ },
171
+ Transitions: {
172
+ NextAction: config.voicemailLambdaArn ? 'voicemail' : 'disconnect',
173
+ Errors: [{ NextAction: 'disconnect' }],
174
+ },
175
+ },
176
+ ...(config.voicemailLambdaArn
177
+ ? [
178
+ {
179
+ Identifier: 'voicemail',
180
+ Type: 'InvokeLambdaFunction',
181
+ Parameters: {
182
+ LambdaFunctionARN: config.voicemailLambdaArn,
183
+ },
184
+ Transitions: {
185
+ NextAction: 'disconnect',
186
+ Errors: [{ NextAction: 'disconnect' }],
187
+ },
188
+ },
189
+ ]
190
+ : []),
191
+ {
192
+ Identifier: 'disconnect',
193
+ Type: 'DisconnectParticipant',
194
+ Parameters: {},
195
+ Transitions: {},
196
+ },
197
+ ],
198
+ }
199
+
200
+ return JSON.stringify(flow)
201
+ }
202
+
203
+ /**
204
+ * Create Lambda role for phone handlers
205
+ */
206
+ static createPhoneLambdaRole(config: { slug: string }): Record<string, any> {
207
+ return {
208
+ [`${config.slug}PhoneLambdaRole`]: {
209
+ Type: 'AWS::IAM::Role',
210
+ Properties: {
211
+ RoleName: `${config.slug}-phone-lambda-role`,
212
+ AssumeRolePolicyDocument: {
213
+ Version: '2012-10-17',
214
+ Statement: [
215
+ {
216
+ Effect: 'Allow',
217
+ Principal: { Service: 'lambda.amazonaws.com' },
218
+ Action: 'sts:AssumeRole',
219
+ },
220
+ ],
221
+ },
222
+ ManagedPolicyArns: ['arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'],
223
+ Policies: [
224
+ {
225
+ PolicyName: 'PhoneLambdaPolicy',
226
+ PolicyDocument: {
227
+ Version: '2012-10-17',
228
+ Statement: [
229
+ {
230
+ Effect: 'Allow',
231
+ Action: ['dynamodb:PutItem', 'dynamodb:UpdateItem', 'dynamodb:GetItem'],
232
+ Resource: '*',
233
+ },
234
+ {
235
+ Effect: 'Allow',
236
+ Action: ['sns:Publish'],
237
+ Resource: '*',
238
+ },
239
+ {
240
+ Effect: 'Allow',
241
+ Action: ['s3:GetObject', 's3:PutObject'],
242
+ Resource: '*',
243
+ },
244
+ {
245
+ Effect: 'Allow',
246
+ Action: ['transcribe:StartTranscriptionJob', 'transcribe:GetTranscriptionJob'],
247
+ Resource: '*',
248
+ },
249
+ {
250
+ Effect: 'Allow',
251
+ Action: ['connect:*'],
252
+ Resource: '*',
253
+ },
254
+ ],
255
+ },
256
+ },
257
+ ],
258
+ },
259
+ },
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Create incoming call Lambda function
265
+ */
266
+ static createIncomingCallLambda(config: {
267
+ slug: string
268
+ roleArn: string
269
+ notificationTopicArn?: string
270
+ callLogTable?: string
271
+ webhookUrl?: string
272
+ }): Record<string, any> {
273
+ return {
274
+ [`${config.slug}IncomingCallLambda`]: {
275
+ Type: 'AWS::Lambda::Function',
276
+ Properties: {
277
+ FunctionName: `${config.slug}-incoming-call`,
278
+ Runtime: 'nodejs20.x',
279
+ Handler: 'index.handler',
280
+ Role: config.roleArn,
281
+ Timeout: 30,
282
+ MemorySize: 256,
283
+ Code: {
284
+ ZipFile: Phone.LambdaCode.incomingCall,
285
+ },
286
+ Environment: {
287
+ Variables: {
288
+ NOTIFICATION_TOPIC_ARN: config.notificationTopicArn || '',
289
+ CALL_LOG_TABLE: config.callLogTable || '',
290
+ WEBHOOK_URL: config.webhookUrl || '',
291
+ },
292
+ },
293
+ },
294
+ },
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Create voicemail Lambda function
300
+ */
301
+ static createVoicemailLambda(config: {
302
+ slug: string
303
+ roleArn: string
304
+ voicemailBucket: string
305
+ notificationTopicArn?: string
306
+ callLogTable?: string
307
+ transcriptionEnabled?: boolean
308
+ }): Record<string, any> {
309
+ return {
310
+ [`${config.slug}VoicemailLambda`]: {
311
+ Type: 'AWS::Lambda::Function',
312
+ Properties: {
313
+ FunctionName: `${config.slug}-voicemail`,
314
+ Runtime: 'nodejs20.x',
315
+ Handler: 'index.handler',
316
+ Role: config.roleArn,
317
+ Timeout: 300, // 5 minutes for transcription
318
+ MemorySize: 512,
319
+ Code: {
320
+ ZipFile: Phone.LambdaCode.voicemail,
321
+ },
322
+ Environment: {
323
+ Variables: {
324
+ VOICEMAIL_BUCKET: config.voicemailBucket,
325
+ NOTIFICATION_TOPIC_ARN: config.notificationTopicArn || '',
326
+ CALL_LOG_TABLE: config.callLogTable || '',
327
+ TRANSCRIPTION_ENABLED: config.transcriptionEnabled ? 'true' : 'false',
328
+ },
329
+ },
330
+ },
331
+ },
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Create missed call Lambda function
337
+ */
338
+ static createMissedCallLambda(config: {
339
+ slug: string
340
+ roleArn: string
341
+ notificationTopicArn?: string
342
+ callLogTable?: string
343
+ webhookUrl?: string
344
+ }): Record<string, any> {
345
+ return {
346
+ [`${config.slug}MissedCallLambda`]: {
347
+ Type: 'AWS::Lambda::Function',
348
+ Properties: {
349
+ FunctionName: `${config.slug}-missed-call`,
350
+ Runtime: 'nodejs20.x',
351
+ Handler: 'index.handler',
352
+ Role: config.roleArn,
353
+ Timeout: 30,
354
+ MemorySize: 256,
355
+ Code: {
356
+ ZipFile: Phone.LambdaCode.missedCall,
357
+ },
358
+ Environment: {
359
+ Variables: {
360
+ NOTIFICATION_TOPIC_ARN: config.notificationTopicArn || '',
361
+ CALL_LOG_TABLE: config.callLogTable || '',
362
+ WEBHOOK_URL: config.webhookUrl || '',
363
+ },
364
+ },
365
+ },
366
+ },
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Create call log DynamoDB table
372
+ */
373
+ static createCallLogTable(config: { slug: string }): Record<string, any> {
374
+ return {
375
+ [`${config.slug}CallLogTable`]: {
376
+ Type: 'AWS::DynamoDB::Table',
377
+ Properties: {
378
+ TableName: `${config.slug}-call-log`,
379
+ BillingMode: 'PAY_PER_REQUEST',
380
+ AttributeDefinitions: [{ AttributeName: 'contactId', AttributeType: 'S' }],
381
+ KeySchema: [{ AttributeName: 'contactId', KeyType: 'HASH' }],
382
+ TimeToLiveSpecification: {
383
+ AttributeName: 'ttl',
384
+ Enabled: true,
385
+ },
386
+ },
387
+ },
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Create complete phone setup
393
+ */
394
+ static createCompleteSetup(config: PhoneConfig): {
395
+ resources: Record<string, any>
396
+ outputs: Record<string, any>
397
+ } {
398
+ const resources: Record<string, any> = {}
399
+ const outputs: Record<string, any> = {}
400
+
401
+ // Create IAM role
402
+ Object.assign(resources, Phone.createPhoneLambdaRole({ slug: config.slug }))
403
+
404
+ // Create call log table
405
+ Object.assign(resources, Phone.createCallLogTable({ slug: config.slug }))
406
+
407
+ // Create notification topic
408
+ resources[`${config.slug}PhoneNotificationTopic`] = {
409
+ Type: 'AWS::SNS::Topic',
410
+ Properties: {
411
+ TopicName: `${config.slug}-phone-notifications`,
412
+ },
413
+ }
414
+
415
+ // Create voicemail bucket
416
+ resources[`${config.slug}VoicemailBucket`] = {
417
+ Type: 'AWS::S3::Bucket',
418
+ Properties: {
419
+ BucketName: `${config.slug}-voicemails`,
420
+ LifecycleConfiguration: {
421
+ Rules: [
422
+ {
423
+ Id: 'DeleteOldVoicemails',
424
+ Status: 'Enabled',
425
+ ExpirationInDays: 90,
426
+ },
427
+ ],
428
+ },
429
+ },
430
+ }
431
+
432
+ // Create Lambda functions
433
+ const roleArn = { 'Fn::GetAtt': [`${config.slug}PhoneLambdaRole`, 'Arn'] }
434
+ const topicArn = { Ref: `${config.slug}PhoneNotificationTopic` }
435
+ const callLogTable = { Ref: `${config.slug}CallLogTable` }
436
+ const voicemailBucket = { Ref: `${config.slug}VoicemailBucket` }
437
+
438
+ Object.assign(
439
+ resources,
440
+ Phone.createIncomingCallLambda({
441
+ slug: config.slug,
442
+ roleArn: roleArn as any,
443
+ notificationTopicArn: topicArn as any,
444
+ callLogTable: callLogTable as any,
445
+ webhookUrl: config.webhookUrl,
446
+ }),
447
+ )
448
+
449
+ if (config.voicemailEnabled !== false) {
450
+ Object.assign(
451
+ resources,
452
+ Phone.createVoicemailLambda({
453
+ slug: config.slug,
454
+ roleArn: roleArn as any,
455
+ voicemailBucket: voicemailBucket as any,
456
+ notificationTopicArn: topicArn as any,
457
+ callLogTable: callLogTable as any,
458
+ transcriptionEnabled: config.transcriptionEnabled,
459
+ }),
460
+ )
461
+ }
462
+
463
+ Object.assign(
464
+ resources,
465
+ Phone.createMissedCallLambda({
466
+ slug: config.slug,
467
+ roleArn: roleArn as any,
468
+ notificationTopicArn: topicArn as any,
469
+ callLogTable: callLogTable as any,
470
+ webhookUrl: config.webhookUrl,
471
+ }),
472
+ )
473
+
474
+ // Outputs
475
+ outputs[`${config.slug}PhoneNotificationTopicArn`] = {
476
+ Description: 'Phone notification topic ARN',
477
+ Value: { Ref: `${config.slug}PhoneNotificationTopic` },
478
+ }
479
+
480
+ outputs[`${config.slug}CallLogTableName`] = {
481
+ Description: 'Call log table name',
482
+ Value: { Ref: `${config.slug}CallLogTable` },
483
+ }
484
+
485
+ outputs[`${config.slug}VoicemailBucketName`] = {
486
+ Description: 'Voicemail bucket name',
487
+ Value: { Ref: `${config.slug}VoicemailBucket` },
488
+ }
489
+
490
+ return { resources, outputs }
491
+ }
492
+ }
493
+
494
+ export default Phone