@stacksjs/ts-cloud-core 0.1.1

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 (251) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +321 -0
  3. package/package.json +31 -0
  4. package/src/advanced-features.test.ts +465 -0
  5. package/src/aws/cloudformation.ts +421 -0
  6. package/src/aws/cloudfront.ts +158 -0
  7. package/src/aws/credentials.test.ts +132 -0
  8. package/src/aws/credentials.ts +545 -0
  9. package/src/aws/index.ts +87 -0
  10. package/src/aws/s3.test.ts +188 -0
  11. package/src/aws/s3.ts +1088 -0
  12. package/src/aws/signature.test.ts +670 -0
  13. package/src/aws/signature.ts +1155 -0
  14. package/src/backup/disaster-recovery.test.ts +726 -0
  15. package/src/backup/disaster-recovery.ts +500 -0
  16. package/src/backup/index.ts +34 -0
  17. package/src/backup/manager.test.ts +498 -0
  18. package/src/backup/manager.ts +432 -0
  19. package/src/cicd/circleci.ts +430 -0
  20. package/src/cicd/github-actions.ts +424 -0
  21. package/src/cicd/gitlab-ci.ts +255 -0
  22. package/src/cicd/index.ts +8 -0
  23. package/src/cli/history.ts +396 -0
  24. package/src/cli/index.ts +10 -0
  25. package/src/cli/progress.ts +458 -0
  26. package/src/cli/repl.ts +454 -0
  27. package/src/cli/suggestions.ts +327 -0
  28. package/src/cli/table.test.ts +319 -0
  29. package/src/cli/table.ts +332 -0
  30. package/src/cloudformation/builder.test.ts +327 -0
  31. package/src/cloudformation/builder.ts +378 -0
  32. package/src/cloudformation/builders/api-gateway.ts +449 -0
  33. package/src/cloudformation/builders/cache.ts +334 -0
  34. package/src/cloudformation/builders/cdn.ts +278 -0
  35. package/src/cloudformation/builders/compute.ts +485 -0
  36. package/src/cloudformation/builders/database.ts +392 -0
  37. package/src/cloudformation/builders/functions.ts +343 -0
  38. package/src/cloudformation/builders/messaging.ts +140 -0
  39. package/src/cloudformation/builders/monitoring.ts +300 -0
  40. package/src/cloudformation/builders/network.ts +264 -0
  41. package/src/cloudformation/builders/queue.ts +147 -0
  42. package/src/cloudformation/builders/security.ts +399 -0
  43. package/src/cloudformation/builders/storage.ts +285 -0
  44. package/src/cloudformation/index.ts +30 -0
  45. package/src/cloudformation/types.ts +173 -0
  46. package/src/compliance/aws-config.ts +543 -0
  47. package/src/compliance/cloudtrail.ts +376 -0
  48. package/src/compliance/compliance.test.ts +423 -0
  49. package/src/compliance/guardduty.ts +446 -0
  50. package/src/compliance/index.ts +66 -0
  51. package/src/compliance/security-hub.ts +456 -0
  52. package/src/containers/build-optimization.ts +416 -0
  53. package/src/containers/containers.test.ts +508 -0
  54. package/src/containers/image-scanning.ts +360 -0
  55. package/src/containers/index.ts +9 -0
  56. package/src/containers/registry.ts +293 -0
  57. package/src/containers/service-mesh.ts +520 -0
  58. package/src/database/database.test.ts +762 -0
  59. package/src/database/index.ts +9 -0
  60. package/src/database/migrations.ts +444 -0
  61. package/src/database/performance.ts +528 -0
  62. package/src/database/replicas.ts +534 -0
  63. package/src/database/users.ts +494 -0
  64. package/src/dependency-graph.ts +143 -0
  65. package/src/deployment/ab-testing.ts +582 -0
  66. package/src/deployment/blue-green.ts +452 -0
  67. package/src/deployment/canary.ts +500 -0
  68. package/src/deployment/deployment.test.ts +526 -0
  69. package/src/deployment/index.ts +61 -0
  70. package/src/deployment/progressive.ts +62 -0
  71. package/src/dns/dns.test.ts +641 -0
  72. package/src/dns/dnssec.ts +315 -0
  73. package/src/dns/index.ts +8 -0
  74. package/src/dns/resolver.ts +496 -0
  75. package/src/dns/routing.ts +593 -0
  76. package/src/email/advanced/analytics.ts +445 -0
  77. package/src/email/advanced/index.ts +11 -0
  78. package/src/email/advanced/rules.ts +465 -0
  79. package/src/email/advanced/scheduling.ts +352 -0
  80. package/src/email/advanced/search.ts +412 -0
  81. package/src/email/advanced/shared-mailboxes.ts +404 -0
  82. package/src/email/advanced/templates.ts +455 -0
  83. package/src/email/advanced/threading.ts +281 -0
  84. package/src/email/analytics.ts +467 -0
  85. package/src/email/bounce-handling.ts +425 -0
  86. package/src/email/email.test.ts +431 -0
  87. package/src/email/handlers/__tests__/inbound.test.ts +38 -0
  88. package/src/email/handlers/__tests__/outbound.test.ts +37 -0
  89. package/src/email/handlers/converter.ts +227 -0
  90. package/src/email/handlers/feedback.ts +228 -0
  91. package/src/email/handlers/inbound.ts +169 -0
  92. package/src/email/handlers/outbound.ts +178 -0
  93. package/src/email/index.ts +15 -0
  94. package/src/email/reputation.ts +303 -0
  95. package/src/email/templates.ts +352 -0
  96. package/src/errors/index.test.ts +434 -0
  97. package/src/errors/index.ts +416 -0
  98. package/src/health-checks/index.ts +40 -0
  99. package/src/index.ts +360 -0
  100. package/src/intrinsic-functions.ts +118 -0
  101. package/src/lambda/concurrency.ts +330 -0
  102. package/src/lambda/destinations.ts +345 -0
  103. package/src/lambda/dlq.ts +425 -0
  104. package/src/lambda/index.ts +11 -0
  105. package/src/lambda/lambda.test.ts +840 -0
  106. package/src/lambda/layers.ts +263 -0
  107. package/src/lambda/versions.ts +376 -0
  108. package/src/lambda/vpc.ts +399 -0
  109. package/src/local/config.ts +114 -0
  110. package/src/local/index.ts +6 -0
  111. package/src/local/mock-aws.ts +351 -0
  112. package/src/modules/ai.ts +340 -0
  113. package/src/modules/api.ts +478 -0
  114. package/src/modules/auth.ts +805 -0
  115. package/src/modules/cache.ts +417 -0
  116. package/src/modules/cdn.ts +1062 -0
  117. package/src/modules/communication.ts +1094 -0
  118. package/src/modules/compute.ts +3348 -0
  119. package/src/modules/database.ts +554 -0
  120. package/src/modules/deployment.ts +1079 -0
  121. package/src/modules/dns.ts +337 -0
  122. package/src/modules/email.ts +1538 -0
  123. package/src/modules/filesystem.ts +515 -0
  124. package/src/modules/index.ts +32 -0
  125. package/src/modules/messaging.ts +486 -0
  126. package/src/modules/monitoring.ts +2086 -0
  127. package/src/modules/network.ts +664 -0
  128. package/src/modules/parameter-store.ts +325 -0
  129. package/src/modules/permissions.ts +1081 -0
  130. package/src/modules/phone.ts +494 -0
  131. package/src/modules/queue.ts +1260 -0
  132. package/src/modules/redirects.ts +464 -0
  133. package/src/modules/registry.ts +699 -0
  134. package/src/modules/search.ts +401 -0
  135. package/src/modules/secrets.ts +416 -0
  136. package/src/modules/security.ts +731 -0
  137. package/src/modules/sms.ts +389 -0
  138. package/src/modules/storage.ts +1120 -0
  139. package/src/modules/workflow.ts +680 -0
  140. package/src/multi-account/config.ts +521 -0
  141. package/src/multi-account/index.ts +7 -0
  142. package/src/multi-account/manager.ts +427 -0
  143. package/src/multi-region/cross-region.ts +410 -0
  144. package/src/multi-region/index.ts +8 -0
  145. package/src/multi-region/manager.ts +483 -0
  146. package/src/multi-region/regions.ts +435 -0
  147. package/src/network-security/index.ts +48 -0
  148. package/src/observability/index.ts +9 -0
  149. package/src/observability/logs.ts +522 -0
  150. package/src/observability/metrics.ts +460 -0
  151. package/src/observability/observability.test.ts +782 -0
  152. package/src/observability/synthetics.ts +568 -0
  153. package/src/observability/xray.ts +358 -0
  154. package/src/phone/advanced/analytics.ts +349 -0
  155. package/src/phone/advanced/callbacks.ts +428 -0
  156. package/src/phone/advanced/index.ts +8 -0
  157. package/src/phone/advanced/ivr-builder.ts +504 -0
  158. package/src/phone/advanced/recording.ts +310 -0
  159. package/src/phone/handlers/__tests__/incoming-call.test.ts +40 -0
  160. package/src/phone/handlers/incoming-call.ts +117 -0
  161. package/src/phone/handlers/missed-call.ts +116 -0
  162. package/src/phone/handlers/voicemail.ts +179 -0
  163. package/src/phone/index.ts +9 -0
  164. package/src/presets/api-backend.ts +134 -0
  165. package/src/presets/data-pipeline.ts +204 -0
  166. package/src/presets/extend.test.ts +295 -0
  167. package/src/presets/extend.ts +297 -0
  168. package/src/presets/fullstack-app.ts +144 -0
  169. package/src/presets/index.ts +27 -0
  170. package/src/presets/jamstack.ts +135 -0
  171. package/src/presets/microservices.ts +167 -0
  172. package/src/presets/ml-api.ts +208 -0
  173. package/src/presets/nodejs-server.ts +104 -0
  174. package/src/presets/nodejs-serverless.ts +114 -0
  175. package/src/presets/realtime-app.ts +184 -0
  176. package/src/presets/static-site.ts +64 -0
  177. package/src/presets/traditional-web-app.ts +339 -0
  178. package/src/presets/wordpress.ts +138 -0
  179. package/src/preview/github.test.ts +249 -0
  180. package/src/preview/github.ts +297 -0
  181. package/src/preview/index.ts +37 -0
  182. package/src/preview/manager.test.ts +440 -0
  183. package/src/preview/manager.ts +326 -0
  184. package/src/preview/notifications.test.ts +582 -0
  185. package/src/preview/notifications.ts +341 -0
  186. package/src/queue/batch-processing.ts +402 -0
  187. package/src/queue/dlq-monitoring.ts +402 -0
  188. package/src/queue/fifo.ts +342 -0
  189. package/src/queue/index.ts +9 -0
  190. package/src/queue/management.ts +428 -0
  191. package/src/queue/queue.test.ts +429 -0
  192. package/src/resource-mgmt/index.ts +39 -0
  193. package/src/resource-naming.ts +62 -0
  194. package/src/s3/index.ts +523 -0
  195. package/src/schema/cloud-config.schema.json +554 -0
  196. package/src/schema/index.ts +68 -0
  197. package/src/security/certificate-manager.ts +492 -0
  198. package/src/security/index.ts +9 -0
  199. package/src/security/scanning.ts +545 -0
  200. package/src/security/secrets-manager.ts +476 -0
  201. package/src/security/secrets-rotation.ts +456 -0
  202. package/src/security/security.test.ts +738 -0
  203. package/src/sms/advanced/ab-testing.ts +389 -0
  204. package/src/sms/advanced/analytics.ts +336 -0
  205. package/src/sms/advanced/campaigns.ts +523 -0
  206. package/src/sms/advanced/chatbot.ts +224 -0
  207. package/src/sms/advanced/index.ts +10 -0
  208. package/src/sms/advanced/link-tracking.ts +248 -0
  209. package/src/sms/advanced/mms.ts +308 -0
  210. package/src/sms/handlers/__tests__/send.test.ts +40 -0
  211. package/src/sms/handlers/delivery-status.ts +133 -0
  212. package/src/sms/handlers/receive.ts +162 -0
  213. package/src/sms/handlers/send.ts +174 -0
  214. package/src/sms/index.ts +9 -0
  215. package/src/stack-diff.ts +389 -0
  216. package/src/static-site/index.ts +85 -0
  217. package/src/template-builder.ts +110 -0
  218. package/src/template-validator.ts +574 -0
  219. package/src/utils/cache.ts +291 -0
  220. package/src/utils/diff.ts +269 -0
  221. package/src/utils/hash.ts +227 -0
  222. package/src/utils/index.ts +8 -0
  223. package/src/utils/parallel.ts +294 -0
  224. package/src/validators/credentials.test.ts +274 -0
  225. package/src/validators/credentials.ts +233 -0
  226. package/src/validators/quotas.test.ts +434 -0
  227. package/src/validators/quotas.ts +217 -0
  228. package/test/ai.test.ts +327 -0
  229. package/test/api.test.ts +511 -0
  230. package/test/auth.test.ts +632 -0
  231. package/test/cache.test.ts +406 -0
  232. package/test/cdn.test.ts +247 -0
  233. package/test/compute.test.ts +861 -0
  234. package/test/database.test.ts +523 -0
  235. package/test/deployment.test.ts +499 -0
  236. package/test/dns.test.ts +270 -0
  237. package/test/email.test.ts +439 -0
  238. package/test/filesystem.test.ts +382 -0
  239. package/test/integration.test.ts +350 -0
  240. package/test/messaging.test.ts +514 -0
  241. package/test/monitoring.test.ts +634 -0
  242. package/test/network.test.ts +425 -0
  243. package/test/permissions.test.ts +488 -0
  244. package/test/queue.test.ts +484 -0
  245. package/test/registry.test.ts +306 -0
  246. package/test/security.test.ts +462 -0
  247. package/test/storage.test.ts +463 -0
  248. package/test/template-validator.test.ts +559 -0
  249. package/test/workflow.test.ts +592 -0
  250. package/tsconfig.json +16 -0
  251. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,428 @@
1
+ /**
2
+ * Callback Requests and Queue Management
3
+ *
4
+ * Provides callback scheduling and queue management
5
+ */
6
+
7
+ export interface CallbackRequest {
8
+ id: string
9
+ phoneNumber: string
10
+ customerName?: string
11
+ reason?: string
12
+ preferredTime?: string
13
+ queueId?: string
14
+ priority: number
15
+ status: 'pending' | 'scheduled' | 'in-progress' | 'completed' | 'failed' | 'cancelled'
16
+ attempts: number
17
+ maxAttempts: number
18
+ createdAt: string
19
+ scheduledFor?: string
20
+ completedAt?: string
21
+ notes?: string
22
+ }
23
+
24
+ export interface QueuePosition {
25
+ position: number
26
+ estimatedWaitTime: number
27
+ callersAhead: number
28
+ }
29
+
30
+ /**
31
+ * Callback Module
32
+ */
33
+ export class Callbacks {
34
+ /**
35
+ * Lambda code for callback request handling
36
+ */
37
+ static CallbackRequestCode = `
38
+ const { DynamoDBClient, PutItemCommand, QueryCommand, UpdateItemCommand } = require('@aws-sdk/client-dynamodb');
39
+ const { ConnectClient, StartOutboundVoiceContactCommand } = require('@aws-sdk/client-connect');
40
+ const { SNSClient, PublishCommand } = require('@aws-sdk/client-sns');
41
+
42
+ const dynamodb = new DynamoDBClient({});
43
+ const connect = new ConnectClient({});
44
+ const sns = new SNSClient({});
45
+
46
+ const CALLBACKS_TABLE = process.env.CALLBACKS_TABLE;
47
+ const CONNECT_INSTANCE_ID = process.env.CONNECT_INSTANCE_ID;
48
+ const CONTACT_FLOW_ID = process.env.CONTACT_FLOW_ID;
49
+ const SOURCE_PHONE_NUMBER = process.env.SOURCE_PHONE_NUMBER;
50
+ const NOTIFICATION_TOPIC_ARN = process.env.NOTIFICATION_TOPIC_ARN;
51
+
52
+ exports.handler = async (event) => {
53
+ console.log('Callback request event:', JSON.stringify(event, null, 2));
54
+
55
+ const { httpMethod, body, pathParameters } = event;
56
+
57
+ try {
58
+ switch (httpMethod) {
59
+ case 'POST':
60
+ return await createCallbackRequest(JSON.parse(body || '{}'));
61
+ case 'GET':
62
+ if (pathParameters?.id) {
63
+ return await getCallbackRequest(pathParameters.id);
64
+ }
65
+ return await listCallbackRequests(event.queryStringParameters);
66
+ case 'DELETE':
67
+ return await cancelCallbackRequest(pathParameters?.id);
68
+ default:
69
+ return { statusCode: 405, body: JSON.stringify({ error: 'Method not allowed' }) };
70
+ }
71
+ } catch (error) {
72
+ console.error('Error:', error);
73
+ return { statusCode: 500, body: JSON.stringify({ error: error.message }) };
74
+ }
75
+ };
76
+
77
+ async function createCallbackRequest(data) {
78
+ const id = \`cb-\${Date.now()}-\${Math.random().toString(36).substr(2, 9)}\`;
79
+ const now = new Date().toISOString();
80
+
81
+ const callback = {
82
+ id: { S: id },
83
+ phoneNumber: { S: data.phoneNumber },
84
+ customerName: { S: data.customerName || '' },
85
+ reason: { S: data.reason || '' },
86
+ preferredTime: { S: data.preferredTime || '' },
87
+ queueId: { S: data.queueId || '' },
88
+ priority: { N: String(data.priority || 5) },
89
+ status: { S: 'pending' },
90
+ attempts: { N: '0' },
91
+ maxAttempts: { N: String(data.maxAttempts || 3) },
92
+ createdAt: { S: now },
93
+ ttl: { N: String(Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60) },
94
+ };
95
+
96
+ await dynamodb.send(new PutItemCommand({
97
+ TableName: CALLBACKS_TABLE,
98
+ Item: callback,
99
+ }));
100
+
101
+ // Send notification
102
+ if (NOTIFICATION_TOPIC_ARN) {
103
+ await sns.send(new PublishCommand({
104
+ TopicArn: NOTIFICATION_TOPIC_ARN,
105
+ Subject: 'New Callback Request',
106
+ Message: JSON.stringify({
107
+ type: 'callback_request',
108
+ id,
109
+ phoneNumber: data.phoneNumber,
110
+ customerName: data.customerName,
111
+ reason: data.reason,
112
+ timestamp: now,
113
+ }, null, 2),
114
+ }));
115
+ }
116
+
117
+ return {
118
+ statusCode: 201,
119
+ headers: { 'Content-Type': 'application/json' },
120
+ body: JSON.stringify({ id, status: 'pending', createdAt: now }),
121
+ };
122
+ }
123
+
124
+ async function getCallbackRequest(id) {
125
+ const result = await dynamodb.send(new GetItemCommand({
126
+ TableName: CALLBACKS_TABLE,
127
+ Key: { id: { S: id } },
128
+ }));
129
+
130
+ if (!result.Item) {
131
+ return { statusCode: 404, body: JSON.stringify({ error: 'Not found' }) };
132
+ }
133
+
134
+ return {
135
+ statusCode: 200,
136
+ headers: { 'Content-Type': 'application/json' },
137
+ body: JSON.stringify(unmarshallCallback(result.Item)),
138
+ };
139
+ }
140
+
141
+ async function listCallbackRequests(params) {
142
+ const result = await dynamodb.send(new ScanCommand({
143
+ TableName: CALLBACKS_TABLE,
144
+ FilterExpression: '#status = :status',
145
+ ExpressionAttributeNames: { '#status': 'status' },
146
+ ExpressionAttributeValues: {
147
+ ':status': { S: params?.status || 'pending' },
148
+ },
149
+ }));
150
+
151
+ const callbacks = (result.Items || []).map(unmarshallCallback);
152
+ callbacks.sort((a, b) => a.priority - b.priority);
153
+
154
+ return {
155
+ statusCode: 200,
156
+ headers: { 'Content-Type': 'application/json' },
157
+ body: JSON.stringify(callbacks),
158
+ };
159
+ }
160
+
161
+ async function cancelCallbackRequest(id) {
162
+ await dynamodb.send(new UpdateItemCommand({
163
+ TableName: CALLBACKS_TABLE,
164
+ Key: { id: { S: id } },
165
+ UpdateExpression: 'SET #status = :status',
166
+ ExpressionAttributeNames: { '#status': 'status' },
167
+ ExpressionAttributeValues: {
168
+ ':status': { S: 'cancelled' },
169
+ },
170
+ }));
171
+
172
+ return { statusCode: 200, body: JSON.stringify({ id, status: 'cancelled' }) };
173
+ }
174
+
175
+ function unmarshallCallback(item) {
176
+ return {
177
+ id: item.id.S,
178
+ phoneNumber: item.phoneNumber.S,
179
+ customerName: item.customerName?.S,
180
+ reason: item.reason?.S,
181
+ preferredTime: item.preferredTime?.S,
182
+ queueId: item.queueId?.S,
183
+ priority: parseInt(item.priority?.N || '5'),
184
+ status: item.status.S,
185
+ attempts: parseInt(item.attempts?.N || '0'),
186
+ maxAttempts: parseInt(item.maxAttempts?.N || '3'),
187
+ createdAt: item.createdAt.S,
188
+ scheduledFor: item.scheduledFor?.S,
189
+ completedAt: item.completedAt?.S,
190
+ };
191
+ }
192
+ `
193
+
194
+ /**
195
+ * Lambda code for processing callbacks
196
+ */
197
+ static CallbackProcessorCode = `
198
+ const { DynamoDBClient, ScanCommand, UpdateItemCommand } = require('@aws-sdk/client-dynamodb');
199
+ const { ConnectClient, StartOutboundVoiceContactCommand } = require('@aws-sdk/client-connect');
200
+
201
+ const dynamodb = new DynamoDBClient({});
202
+ const connect = new ConnectClient({});
203
+
204
+ const CALLBACKS_TABLE = process.env.CALLBACKS_TABLE;
205
+ const CONNECT_INSTANCE_ID = process.env.CONNECT_INSTANCE_ID;
206
+ const CONTACT_FLOW_ID = process.env.CONTACT_FLOW_ID;
207
+ const SOURCE_PHONE_NUMBER = process.env.SOURCE_PHONE_NUMBER;
208
+ const QUEUE_ID = process.env.QUEUE_ID;
209
+
210
+ exports.handler = async (event) => {
211
+ console.log('Callback processor event:', JSON.stringify(event, null, 2));
212
+
213
+ try {
214
+ // Get pending callbacks
215
+ const result = await dynamodb.send(new ScanCommand({
216
+ TableName: CALLBACKS_TABLE,
217
+ FilterExpression: '#status = :pending',
218
+ ExpressionAttributeNames: { '#status': 'status' },
219
+ ExpressionAttributeValues: {
220
+ ':pending': { S: 'pending' },
221
+ },
222
+ }));
223
+
224
+ const callbacks = result.Items || [];
225
+
226
+ // Sort by priority and creation time
227
+ callbacks.sort((a, b) => {
228
+ const priorityDiff = parseInt(a.priority?.N || '5') - parseInt(b.priority?.N || '5');
229
+ if (priorityDiff !== 0) return priorityDiff;
230
+ return new Date(a.createdAt.S) - new Date(b.createdAt.S);
231
+ });
232
+
233
+ // Process up to 5 callbacks at a time
234
+ const toProcess = callbacks.slice(0, 5);
235
+
236
+ for (const callback of toProcess) {
237
+ const id = callback.id.S;
238
+ const phoneNumber = callback.phoneNumber.S;
239
+ const attempts = parseInt(callback.attempts?.N || '0');
240
+ const maxAttempts = parseInt(callback.maxAttempts?.N || '3');
241
+
242
+ // Check if preferred time has passed
243
+ if (callback.preferredTime?.S) {
244
+ const preferredTime = new Date(callback.preferredTime.S);
245
+ if (preferredTime > new Date()) {
246
+ console.log(\`Skipping callback \${id} - preferred time not reached\`);
247
+ continue;
248
+ }
249
+ }
250
+
251
+ try {
252
+ // Update status to in-progress
253
+ await dynamodb.send(new UpdateItemCommand({
254
+ TableName: CALLBACKS_TABLE,
255
+ Key: { id: { S: id } },
256
+ UpdateExpression: 'SET #status = :status, attempts = :attempts',
257
+ ExpressionAttributeNames: { '#status': 'status' },
258
+ ExpressionAttributeValues: {
259
+ ':status': { S: 'in-progress' },
260
+ ':attempts': { N: String(attempts + 1) },
261
+ },
262
+ }));
263
+
264
+ // Initiate outbound call
265
+ await connect.send(new StartOutboundVoiceContactCommand({
266
+ InstanceId: CONNECT_INSTANCE_ID,
267
+ ContactFlowId: CONTACT_FLOW_ID,
268
+ DestinationPhoneNumber: phoneNumber,
269
+ SourcePhoneNumber: SOURCE_PHONE_NUMBER,
270
+ QueueId: QUEUE_ID,
271
+ Attributes: {
272
+ callbackId: id,
273
+ customerName: callback.customerName?.S || '',
274
+ reason: callback.reason?.S || '',
275
+ },
276
+ }));
277
+
278
+ console.log(\`Initiated callback to \${phoneNumber}\`);
279
+
280
+ // Mark as completed (will be updated by call result handler)
281
+ await dynamodb.send(new UpdateItemCommand({
282
+ TableName: CALLBACKS_TABLE,
283
+ Key: { id: { S: id } },
284
+ UpdateExpression: 'SET #status = :status, completedAt = :now',
285
+ ExpressionAttributeNames: { '#status': 'status' },
286
+ ExpressionAttributeValues: {
287
+ ':status': { S: 'completed' },
288
+ ':now': { S: new Date().toISOString() },
289
+ },
290
+ }));
291
+
292
+ } catch (callError) {
293
+ console.error(\`Failed to call \${phoneNumber}:\`, callError);
294
+
295
+ // Check if max attempts reached
296
+ if (attempts + 1 >= maxAttempts) {
297
+ await dynamodb.send(new UpdateItemCommand({
298
+ TableName: CALLBACKS_TABLE,
299
+ Key: { id: { S: id } },
300
+ UpdateExpression: 'SET #status = :status, error = :error',
301
+ ExpressionAttributeNames: { '#status': 'status' },
302
+ ExpressionAttributeValues: {
303
+ ':status': { S: 'failed' },
304
+ ':error': { S: callError.message },
305
+ },
306
+ }));
307
+ } else {
308
+ // Reset to pending for retry
309
+ await dynamodb.send(new UpdateItemCommand({
310
+ TableName: CALLBACKS_TABLE,
311
+ Key: { id: { S: id } },
312
+ UpdateExpression: 'SET #status = :status',
313
+ ExpressionAttributeNames: { '#status': 'status' },
314
+ ExpressionAttributeValues: {
315
+ ':status': { S: 'pending' },
316
+ },
317
+ }));
318
+ }
319
+ }
320
+ }
321
+
322
+ return { statusCode: 200, processed: toProcess.length };
323
+ } catch (error) {
324
+ console.error('Error processing callbacks:', error);
325
+ return { statusCode: 500, error: error.message };
326
+ }
327
+ };
328
+ `
329
+
330
+ /**
331
+ * Create callbacks DynamoDB table
332
+ */
333
+ static createCallbacksTable(config: { slug: string }): Record<string, any> {
334
+ return {
335
+ [`${config.slug}CallbacksTable`]: {
336
+ Type: 'AWS::DynamoDB::Table',
337
+ Properties: {
338
+ TableName: `${config.slug}-callbacks`,
339
+ BillingMode: 'PAY_PER_REQUEST',
340
+ AttributeDefinitions: [
341
+ { AttributeName: 'id', AttributeType: 'S' },
342
+ ],
343
+ KeySchema: [
344
+ { AttributeName: 'id', KeyType: 'HASH' },
345
+ ],
346
+ TimeToLiveSpecification: {
347
+ AttributeName: 'ttl',
348
+ Enabled: true,
349
+ },
350
+ },
351
+ },
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Create callback request Lambda
357
+ */
358
+ static createCallbackRequestLambda(config: {
359
+ slug: string
360
+ roleArn: string
361
+ callbacksTable: string
362
+ notificationTopicArn?: string
363
+ }): Record<string, any> {
364
+ return {
365
+ [`${config.slug}CallbackRequestLambda`]: {
366
+ Type: 'AWS::Lambda::Function',
367
+ Properties: {
368
+ FunctionName: `${config.slug}-callback-request`,
369
+ Runtime: 'nodejs20.x',
370
+ Handler: 'index.handler',
371
+ Role: config.roleArn,
372
+ Timeout: 30,
373
+ MemorySize: 256,
374
+ Code: {
375
+ ZipFile: Callbacks.CallbackRequestCode,
376
+ },
377
+ Environment: {
378
+ Variables: {
379
+ CALLBACKS_TABLE: config.callbacksTable,
380
+ NOTIFICATION_TOPIC_ARN: config.notificationTopicArn || '',
381
+ },
382
+ },
383
+ },
384
+ },
385
+ }
386
+ }
387
+
388
+ /**
389
+ * Create callback processor Lambda
390
+ */
391
+ static createCallbackProcessorLambda(config: {
392
+ slug: string
393
+ roleArn: string
394
+ callbacksTable: string
395
+ connectInstanceId: string
396
+ contactFlowId: string
397
+ sourcePhoneNumber: string
398
+ queueId?: string
399
+ }): Record<string, any> {
400
+ return {
401
+ [`${config.slug}CallbackProcessorLambda`]: {
402
+ Type: 'AWS::Lambda::Function',
403
+ Properties: {
404
+ FunctionName: `${config.slug}-callback-processor`,
405
+ Runtime: 'nodejs20.x',
406
+ Handler: 'index.handler',
407
+ Role: config.roleArn,
408
+ Timeout: 300,
409
+ MemorySize: 256,
410
+ Code: {
411
+ ZipFile: Callbacks.CallbackProcessorCode,
412
+ },
413
+ Environment: {
414
+ Variables: {
415
+ CALLBACKS_TABLE: config.callbacksTable,
416
+ CONNECT_INSTANCE_ID: config.connectInstanceId,
417
+ CONTACT_FLOW_ID: config.contactFlowId,
418
+ SOURCE_PHONE_NUMBER: config.sourcePhoneNumber,
419
+ QUEUE_ID: config.queueId || '',
420
+ },
421
+ },
422
+ },
423
+ },
424
+ }
425
+ }
426
+ }
427
+
428
+ export default Callbacks
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Phone Advanced Features
3
+ */
4
+
5
+ export * from './recording'
6
+ export * from './ivr-builder'
7
+ export * from './analytics'
8
+ export * from './callbacks'