@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,486 @@
1
+ import type {
2
+ SNSSubscription,
3
+ SNSTopic,
4
+ SNSTopicPolicy,
5
+ } from '@stacksjs/ts-cloud-aws-types'
6
+ import type { EnvironmentType } from '@stacksjs/ts-cloud-types'
7
+ import { Fn } from '../intrinsic-functions'
8
+ import { generateLogicalId, generateResourceName } from '../resource-naming'
9
+
10
+ export interface TopicOptions {
11
+ slug: string
12
+ environment: EnvironmentType
13
+ topicName?: string
14
+ displayName?: string
15
+ encrypted?: boolean
16
+ kmsKeyId?: string
17
+ }
18
+
19
+ export interface SubscriptionOptions {
20
+ slug: string
21
+ environment: EnvironmentType
22
+ protocol: 'http' | 'https' | 'email' | 'email-json' | 'sms' | 'sqs' | 'application' | 'lambda' | 'firehose'
23
+ endpoint: string
24
+ filterPolicy?: Record<string, unknown>
25
+ rawMessageDelivery?: boolean
26
+ }
27
+
28
+ export interface TopicPolicyOptions {
29
+ slug: string
30
+ environment: EnvironmentType
31
+ allowedPrincipals?: string | string[]
32
+ allowedServices?: string | string[]
33
+ actions?: string | string[]
34
+ }
35
+
36
+ /**
37
+ * Messaging Module - SNS (Simple Notification Service)
38
+ * Provides clean API for pub/sub messaging, notifications, and event routing
39
+ */
40
+ export class Messaging {
41
+ /**
42
+ * Create an SNS topic
43
+ */
44
+ static createTopic(options: TopicOptions): {
45
+ topic: SNSTopic
46
+ logicalId: string
47
+ } {
48
+ const {
49
+ slug,
50
+ environment,
51
+ topicName,
52
+ displayName,
53
+ encrypted = false,
54
+ kmsKeyId,
55
+ } = options
56
+
57
+ const resourceName = topicName || generateResourceName({
58
+ slug,
59
+ environment,
60
+ resourceType: 'topic',
61
+ })
62
+
63
+ const logicalId = generateLogicalId(resourceName)
64
+
65
+ const topic: SNSTopic = {
66
+ Type: 'AWS::SNS::Topic',
67
+ Properties: {
68
+ TopicName: resourceName,
69
+ DisplayName: displayName || resourceName,
70
+ Tags: [
71
+ { Key: 'Name', Value: resourceName },
72
+ { Key: 'Environment', Value: environment },
73
+ ],
74
+ },
75
+ }
76
+
77
+ if (encrypted && kmsKeyId) {
78
+ topic.Properties!.KmsMasterKeyId = kmsKeyId
79
+ }
80
+
81
+ return { topic, logicalId }
82
+ }
83
+
84
+ /**
85
+ * Subscribe to a topic
86
+ */
87
+ static subscribe(
88
+ topicLogicalId: string,
89
+ options: SubscriptionOptions,
90
+ ): {
91
+ subscription: SNSSubscription
92
+ logicalId: string
93
+ } {
94
+ const {
95
+ slug,
96
+ environment,
97
+ protocol,
98
+ endpoint,
99
+ filterPolicy,
100
+ rawMessageDelivery = false,
101
+ } = options
102
+
103
+ const resourceName = generateResourceName({
104
+ slug,
105
+ environment,
106
+ resourceType: 'subscription',
107
+ })
108
+
109
+ // Create unique ID by including protocol and using the endpoint
110
+ // Extract alphanumeric chars and take last 12 chars to ensure uniqueness
111
+ const endpointClean = endpoint.replace(/[^a-zA-Z0-9]/g, '')
112
+ const endpointHash = endpointClean.length > 12 ? endpointClean.slice(-12) : endpointClean
113
+ const logicalId = generateLogicalId(`${resourceName}-${protocol}-${endpointHash}`)
114
+
115
+ const subscription: SNSSubscription = {
116
+ Type: 'AWS::SNS::Subscription',
117
+ Properties: {
118
+ TopicArn: Fn.Ref(topicLogicalId) as unknown as string,
119
+ Protocol: protocol,
120
+ Endpoint: endpoint,
121
+ RawMessageDelivery: rawMessageDelivery,
122
+ },
123
+ }
124
+
125
+ if (filterPolicy) {
126
+ subscription.Properties.FilterPolicy = filterPolicy
127
+ }
128
+
129
+ return { subscription, logicalId }
130
+ }
131
+
132
+ /**
133
+ * Subscribe email to topic
134
+ */
135
+ static subscribeEmail(
136
+ topicLogicalId: string,
137
+ email: string,
138
+ options: {
139
+ slug: string
140
+ environment: EnvironmentType
141
+ filterPolicy?: Record<string, unknown>
142
+ },
143
+ ): {
144
+ subscription: SNSSubscription
145
+ logicalId: string
146
+ } {
147
+ return Messaging.subscribe(topicLogicalId, {
148
+ ...options,
149
+ protocol: 'email',
150
+ endpoint: email,
151
+ })
152
+ }
153
+
154
+ /**
155
+ * Subscribe Lambda function to topic
156
+ */
157
+ static subscribeLambda(
158
+ topicLogicalId: string,
159
+ functionArn: string,
160
+ options: {
161
+ slug: string
162
+ environment: EnvironmentType
163
+ filterPolicy?: Record<string, unknown>
164
+ },
165
+ ): {
166
+ subscription: SNSSubscription
167
+ logicalId: string
168
+ } {
169
+ return Messaging.subscribe(topicLogicalId, {
170
+ ...options,
171
+ protocol: 'lambda',
172
+ endpoint: functionArn,
173
+ rawMessageDelivery: true,
174
+ })
175
+ }
176
+
177
+ /**
178
+ * Subscribe SQS queue to topic
179
+ */
180
+ static subscribeSqs(
181
+ topicLogicalId: string,
182
+ queueArn: string,
183
+ options: {
184
+ slug: string
185
+ environment: EnvironmentType
186
+ filterPolicy?: Record<string, unknown>
187
+ rawMessageDelivery?: boolean
188
+ },
189
+ ): {
190
+ subscription: SNSSubscription
191
+ logicalId: string
192
+ } {
193
+ return Messaging.subscribe(topicLogicalId, {
194
+ ...options,
195
+ protocol: 'sqs',
196
+ endpoint: queueArn,
197
+ })
198
+ }
199
+
200
+ /**
201
+ * Subscribe HTTP/HTTPS endpoint to topic
202
+ */
203
+ static subscribeHttp(
204
+ topicLogicalId: string,
205
+ url: string,
206
+ options: {
207
+ slug: string
208
+ environment: EnvironmentType
209
+ filterPolicy?: Record<string, unknown>
210
+ },
211
+ ): {
212
+ subscription: SNSSubscription
213
+ logicalId: string
214
+ } {
215
+ const protocol = url.startsWith('https://') ? 'https' : 'http'
216
+
217
+ return Messaging.subscribe(topicLogicalId, {
218
+ ...options,
219
+ protocol,
220
+ endpoint: url,
221
+ })
222
+ }
223
+
224
+ /**
225
+ * Subscribe SMS to topic
226
+ */
227
+ static subscribeSms(
228
+ topicLogicalId: string,
229
+ phoneNumber: string,
230
+ options: {
231
+ slug: string
232
+ environment: EnvironmentType
233
+ },
234
+ ): {
235
+ subscription: SNSSubscription
236
+ logicalId: string
237
+ } {
238
+ return Messaging.subscribe(topicLogicalId, {
239
+ ...options,
240
+ protocol: 'sms',
241
+ endpoint: phoneNumber,
242
+ })
243
+ }
244
+
245
+ /**
246
+ * Create a topic policy
247
+ */
248
+ static setTopicPolicy(
249
+ topicLogicalId: string,
250
+ options: TopicPolicyOptions,
251
+ ): {
252
+ policy: SNSTopicPolicy
253
+ logicalId: string
254
+ } {
255
+ const {
256
+ slug,
257
+ environment,
258
+ allowedPrincipals,
259
+ allowedServices,
260
+ actions = 'SNS:Publish',
261
+ } = options
262
+
263
+ const resourceName = generateResourceName({
264
+ slug,
265
+ environment,
266
+ resourceType: 'topic-policy',
267
+ })
268
+
269
+ const logicalId = generateLogicalId(resourceName)
270
+
271
+ const principal: Record<string, unknown> = {}
272
+
273
+ if (allowedPrincipals) {
274
+ principal.AWS = allowedPrincipals
275
+ }
276
+
277
+ if (allowedServices) {
278
+ principal.Service = allowedServices
279
+ }
280
+
281
+ const policy: SNSTopicPolicy = {
282
+ Type: 'AWS::SNS::TopicPolicy',
283
+ Properties: {
284
+ Topics: [Fn.Ref(topicLogicalId) as unknown as string],
285
+ PolicyDocument: {
286
+ Version: '2012-10-17',
287
+ Statement: [
288
+ {
289
+ Effect: 'Allow',
290
+ Principal: principal,
291
+ Action: actions,
292
+ Resource: Fn.Ref(topicLogicalId) as unknown as string,
293
+ },
294
+ ],
295
+ },
296
+ },
297
+ }
298
+
299
+ return { policy, logicalId }
300
+ }
301
+
302
+ /**
303
+ * Allow CloudWatch Alarms to publish to topic
304
+ */
305
+ static allowCloudWatchAlarms(
306
+ topicLogicalId: string,
307
+ options: {
308
+ slug: string
309
+ environment: EnvironmentType
310
+ },
311
+ ): {
312
+ policy: SNSTopicPolicy
313
+ logicalId: string
314
+ } {
315
+ return Messaging.setTopicPolicy(topicLogicalId, {
316
+ ...options,
317
+ allowedServices: 'cloudwatch.amazonaws.com',
318
+ actions: 'SNS:Publish',
319
+ })
320
+ }
321
+
322
+ /**
323
+ * Allow EventBridge to publish to topic
324
+ */
325
+ static allowEventBridge(
326
+ topicLogicalId: string,
327
+ options: {
328
+ slug: string
329
+ environment: EnvironmentType
330
+ },
331
+ ): {
332
+ policy: SNSTopicPolicy
333
+ logicalId: string
334
+ } {
335
+ return Messaging.setTopicPolicy(topicLogicalId, {
336
+ ...options,
337
+ allowedServices: 'events.amazonaws.com',
338
+ actions: 'SNS:Publish',
339
+ })
340
+ }
341
+
342
+ /**
343
+ * Allow S3 to publish to topic
344
+ */
345
+ static allowS3(
346
+ topicLogicalId: string,
347
+ options: {
348
+ slug: string
349
+ environment: EnvironmentType
350
+ },
351
+ ): {
352
+ policy: SNSTopicPolicy
353
+ logicalId: string
354
+ } {
355
+ return Messaging.setTopicPolicy(topicLogicalId, {
356
+ ...options,
357
+ allowedServices: 's3.amazonaws.com',
358
+ actions: 'SNS:Publish',
359
+ })
360
+ }
361
+
362
+ /**
363
+ * Enable encryption on topic
364
+ */
365
+ static enableEncryption(
366
+ topic: SNSTopic,
367
+ kmsKeyId: string,
368
+ ): SNSTopic {
369
+ if (!topic.Properties) {
370
+ topic.Properties = {}
371
+ }
372
+
373
+ topic.Properties.KmsMasterKeyId = kmsKeyId
374
+
375
+ return topic
376
+ }
377
+
378
+ /**
379
+ * Add inline subscription to topic
380
+ */
381
+ static addInlineSubscription(
382
+ topic: SNSTopic,
383
+ protocol: SubscriptionOptions['protocol'],
384
+ endpoint: string,
385
+ ): SNSTopic {
386
+ if (!topic.Properties) {
387
+ topic.Properties = {}
388
+ }
389
+
390
+ if (!topic.Properties.Subscription) {
391
+ topic.Properties.Subscription = []
392
+ }
393
+
394
+ topic.Properties.Subscription.push({
395
+ Protocol: protocol,
396
+ Endpoint: endpoint,
397
+ })
398
+
399
+ return topic
400
+ }
401
+
402
+ /**
403
+ * Common filter policy patterns
404
+ */
405
+ static readonly FilterPolicies = {
406
+ /**
407
+ * Filter by event type
408
+ */
409
+ eventType: (types: string[]): { eventType: string[] } => ({
410
+ eventType: types,
411
+ }),
412
+
413
+ /**
414
+ * Filter by status
415
+ */
416
+ status: (statuses: string[]): { status: string[] } => ({
417
+ status: statuses,
418
+ }),
419
+
420
+ /**
421
+ * Filter by numeric range
422
+ */
423
+ numericRange: (attribute: string, min: number, max: number): Record<string, Array<{ numeric: (string | number)[] }>> => ({
424
+ [attribute]: [{ numeric: ['>=', min, '<=', max] }],
425
+ }),
426
+
427
+ /**
428
+ * Filter by string prefix
429
+ */
430
+ prefix: (attribute: string, prefixValue: string): Record<string, Array<{ prefix: string }>> => ({
431
+ [attribute]: [{ prefix: prefixValue }],
432
+ }),
433
+
434
+ /**
435
+ * Filter by multiple attributes (AND logic)
436
+ */
437
+ and: (...policies: Record<string, unknown>[]): Record<string, unknown> => {
438
+ return Object.assign({}, ...policies)
439
+ },
440
+
441
+ /**
442
+ * Filter by exists/not exists
443
+ */
444
+ exists: (attribute: string, existsValue: boolean): Record<string, Array<{ exists: boolean }>> => ({
445
+ [attribute]: [{ exists: existsValue }],
446
+ }),
447
+ } as const
448
+
449
+ /**
450
+ * Common use cases for SNS topics
451
+ */
452
+ static readonly UseCases = {
453
+ /**
454
+ * Create alert topic for CloudWatch alarms
455
+ */
456
+ createAlertTopic: (options: TopicOptions): { topic: SNSTopic; logicalId: string } => {
457
+ return Messaging.createTopic({
458
+ ...options,
459
+ topicName: options.topicName || `${options.slug}-${options.environment}-alerts`,
460
+ displayName: options.displayName || 'Alert Notifications',
461
+ })
462
+ },
463
+
464
+ /**
465
+ * Create event fanout topic for distributing events
466
+ */
467
+ createEventFanout: (options: TopicOptions): { topic: SNSTopic; logicalId: string } => {
468
+ return Messaging.createTopic({
469
+ ...options,
470
+ topicName: options.topicName || `${options.slug}-${options.environment}-events`,
471
+ displayName: options.displayName || 'Event Fanout',
472
+ })
473
+ },
474
+
475
+ /**
476
+ * Create notification topic for user notifications
477
+ */
478
+ createNotificationTopic: (options: TopicOptions): { topic: SNSTopic; logicalId: string } => {
479
+ return Messaging.createTopic({
480
+ ...options,
481
+ topicName: options.topicName || `${options.slug}-${options.environment}-notifications`,
482
+ displayName: options.displayName || 'User Notifications',
483
+ })
484
+ },
485
+ } as const
486
+ }