@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,554 @@
1
+ import type {
2
+ DynamoDBTable,
3
+ RDSDBInstance,
4
+ RDSDBParameterGroup,
5
+ RDSDBSubnetGroup,
6
+ } from '@stacksjs/ts-cloud-aws-types'
7
+ import type { EnvironmentType } from '@stacksjs/ts-cloud-types'
8
+ import { Fn } from '../intrinsic-functions'
9
+ import { generateLogicalId, generateResourceName } from '../resource-naming'
10
+
11
+ export interface RDSOptions {
12
+ slug: string
13
+ environment: EnvironmentType
14
+ dbInstanceIdentifier?: string
15
+ dbInstanceClass?: string
16
+ instanceClass?: string // Alias for dbInstanceClass
17
+ allocatedStorage?: number
18
+ storageType?: 'gp2' | 'gp3' | 'io1' | 'io2'
19
+ masterUsername?: string
20
+ masterUserPassword?: string
21
+ masterPassword?: string // Alias for masterUserPassword
22
+ databaseName?: string
23
+ subnetIds?: string[]
24
+ securityGroupIds?: string[]
25
+ encrypted?: boolean
26
+ kmsKeyId?: string
27
+ multiAz?: boolean
28
+ backupRetentionDays?: number
29
+ publiclyAccessible?: boolean
30
+ enableCloudwatchLogs?: boolean
31
+ deletionProtection?: boolean
32
+ }
33
+
34
+ export interface DynamoDBTableOptions {
35
+ slug: string
36
+ environment: EnvironmentType
37
+ tableName?: string
38
+ partitionKey: {
39
+ name: string
40
+ type: 'S' | 'N' | 'B'
41
+ }
42
+ sortKey?: {
43
+ name: string
44
+ type: 'S' | 'N' | 'B'
45
+ }
46
+ billingMode?: 'PROVISIONED' | 'PAY_PER_REQUEST'
47
+ readCapacity?: number
48
+ writeCapacity?: number
49
+ streamEnabled?: boolean
50
+ streamViewType?: 'NEW_IMAGE' | 'OLD_IMAGE' | 'NEW_AND_OLD_IMAGES' | 'KEYS_ONLY'
51
+ encrypted?: boolean
52
+ kmsKeyId?: string
53
+ pointInTimeRecovery?: boolean
54
+ ttlAttribute?: string
55
+ }
56
+
57
+ export interface GlobalSecondaryIndexOptions {
58
+ indexName: string
59
+ partitionKey: {
60
+ name: string
61
+ type: 'S' | 'N' | 'B'
62
+ }
63
+ sortKey?: {
64
+ name: string
65
+ type: 'S' | 'N' | 'B'
66
+ }
67
+ projectionType?: 'ALL' | 'KEYS_ONLY' | 'INCLUDE'
68
+ nonKeyAttributes?: string[]
69
+ readCapacity?: number
70
+ writeCapacity?: number
71
+ }
72
+
73
+ /**
74
+ * Database Module - RDS + DynamoDB
75
+ * Provides clean API for relational (RDS) and NoSQL (DynamoDB) databases
76
+ */
77
+ export class Database {
78
+ /**
79
+ * Create a PostgreSQL RDS instance
80
+ */
81
+ static createPostgres(options: RDSOptions): {
82
+ dbInstance: RDSDBInstance
83
+ subnetGroup?: RDSDBSubnetGroup
84
+ logicalId: string
85
+ subnetGroupId?: string
86
+ } {
87
+ return Database.createRDSInstance('postgres', '16.2', options)
88
+ }
89
+
90
+ /**
91
+ * Create a MySQL RDS instance
92
+ */
93
+ static createMysql(options: RDSOptions): {
94
+ dbInstance: RDSDBInstance
95
+ subnetGroup?: RDSDBSubnetGroup
96
+ logicalId: string
97
+ subnetGroupId?: string
98
+ } {
99
+ return Database.createRDSInstance('mysql', '8.0.35', options)
100
+ }
101
+
102
+ /**
103
+ * Create an RDS instance (internal helper)
104
+ */
105
+ private static createRDSInstance(
106
+ engine: 'postgres' | 'mysql',
107
+ engineVersion: string,
108
+ options: RDSOptions,
109
+ ): {
110
+ dbInstance: RDSDBInstance
111
+ subnetGroup?: RDSDBSubnetGroup
112
+ logicalId: string
113
+ subnetGroupId?: string
114
+ } {
115
+ const {
116
+ slug,
117
+ environment,
118
+ instanceClass = 'db.t3.micro',
119
+ allocatedStorage = 20,
120
+ storageType = 'gp3',
121
+ masterUsername = 'admin',
122
+ masterPassword,
123
+ databaseName,
124
+ subnetIds,
125
+ securityGroupIds,
126
+ encrypted = true,
127
+ kmsKeyId,
128
+ multiAz = false,
129
+ backupRetentionDays = 7,
130
+ publiclyAccessible = false,
131
+ enableCloudwatchLogs = true,
132
+ deletionProtection = true,
133
+ } = options
134
+
135
+ const resourceName = generateResourceName({
136
+ slug,
137
+ environment,
138
+ resourceType: `${engine}-db`,
139
+ })
140
+
141
+ const logicalId = generateLogicalId(resourceName)
142
+
143
+ // Create subnet group if subnets provided
144
+ let subnetGroup: RDSDBSubnetGroup | undefined
145
+ let subnetGroupId: string | undefined
146
+
147
+ if (subnetIds && subnetIds.length > 0) {
148
+ const subnetGroupName = generateResourceName({
149
+ slug,
150
+ environment,
151
+ resourceType: 'db-subnet-group',
152
+ })
153
+
154
+ subnetGroupId = generateLogicalId(subnetGroupName)
155
+
156
+ subnetGroup = {
157
+ Type: 'AWS::RDS::DBSubnetGroup',
158
+ Properties: {
159
+ DBSubnetGroupName: subnetGroupName,
160
+ DBSubnetGroupDescription: `Subnet group for ${resourceName}`,
161
+ SubnetIds: subnetIds,
162
+ Tags: [
163
+ { Key: 'Name', Value: subnetGroupName },
164
+ { Key: 'Environment', Value: environment },
165
+ ],
166
+ },
167
+ }
168
+ }
169
+
170
+ const dbInstance: RDSDBInstance = {
171
+ Type: 'AWS::RDS::DBInstance',
172
+ Properties: {
173
+ DBInstanceIdentifier: resourceName,
174
+ DBInstanceClass: instanceClass,
175
+ Engine: engine,
176
+ EngineVersion: engineVersion,
177
+ MasterUsername: masterUsername,
178
+ MasterUserPassword: masterPassword,
179
+ AllocatedStorage: allocatedStorage,
180
+ StorageType: storageType,
181
+ StorageEncrypted: encrypted,
182
+ MultiAZ: multiAz,
183
+ BackupRetentionPeriod: backupRetentionDays,
184
+ PubliclyAccessible: publiclyAccessible,
185
+ DeletionProtection: deletionProtection,
186
+ Tags: [
187
+ { Key: 'Name', Value: resourceName },
188
+ { Key: 'Environment', Value: environment },
189
+ ],
190
+ },
191
+ }
192
+
193
+ if (databaseName) {
194
+ dbInstance.Properties.DBName = databaseName
195
+ }
196
+
197
+ if (kmsKeyId) {
198
+ dbInstance.Properties.KmsKeyId = kmsKeyId
199
+ }
200
+
201
+ if (subnetGroupId) {
202
+ dbInstance.Properties.DBSubnetGroupName = Fn.Ref(subnetGroupId) as unknown as string
203
+ }
204
+
205
+ if (securityGroupIds && securityGroupIds.length > 0) {
206
+ dbInstance.Properties.VPCSecurityGroups = securityGroupIds
207
+ }
208
+
209
+ if (enableCloudwatchLogs) {
210
+ dbInstance.Properties.EnableCloudwatchLogsExports = engine === 'postgres'
211
+ ? ['postgresql']
212
+ : ['error', 'general', 'slowquery']
213
+ }
214
+
215
+ return {
216
+ dbInstance,
217
+ subnetGroup,
218
+ logicalId,
219
+ subnetGroupId,
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Create a read replica
225
+ */
226
+ static createReadReplica(
227
+ sourceDbLogicalId: string,
228
+ options: Omit<RDSOptions, 'masterUsername' | 'masterPassword' | 'databaseName'>,
229
+ ): {
230
+ replica: RDSDBInstance
231
+ logicalId: string
232
+ } {
233
+ const {
234
+ slug,
235
+ environment,
236
+ instanceClass = 'db.t3.micro',
237
+ securityGroupIds,
238
+ publiclyAccessible = false,
239
+ } = options
240
+
241
+ const resourceName = generateResourceName({
242
+ slug,
243
+ environment,
244
+ resourceType: 'db-replica',
245
+ })
246
+
247
+ const logicalId = generateLogicalId(resourceName)
248
+
249
+ const replica: RDSDBInstance = {
250
+ Type: 'AWS::RDS::DBInstance',
251
+ Properties: {
252
+ DBInstanceIdentifier: resourceName,
253
+ DBInstanceClass: instanceClass,
254
+ SourceDBInstanceIdentifier: Fn.Ref(sourceDbLogicalId) as unknown as string,
255
+ PubliclyAccessible: publiclyAccessible,
256
+ Tags: [
257
+ { Key: 'Name', Value: resourceName },
258
+ { Key: 'Environment', Value: environment },
259
+ { Key: 'Type', Value: 'ReadReplica' },
260
+ ],
261
+ },
262
+ }
263
+
264
+ if (securityGroupIds && securityGroupIds.length > 0) {
265
+ replica.Properties.VPCSecurityGroups = securityGroupIds
266
+ }
267
+
268
+ return { replica, logicalId }
269
+ }
270
+
271
+ /**
272
+ * Create a DB parameter group
273
+ */
274
+ static createParameterGroup(
275
+ engine: 'postgres' | 'mysql',
276
+ version: string,
277
+ options: {
278
+ slug: string
279
+ environment: EnvironmentType
280
+ parameters?: Record<string, string>
281
+ },
282
+ ): {
283
+ parameterGroup: RDSDBParameterGroup
284
+ logicalId: string
285
+ } {
286
+ const { slug, environment, parameters = {} } = options
287
+
288
+ const resourceName = generateResourceName({
289
+ slug,
290
+ environment,
291
+ resourceType: 'db-params',
292
+ })
293
+
294
+ const logicalId = generateLogicalId(resourceName)
295
+
296
+ // Determine parameter group family
297
+ const family = engine === 'postgres'
298
+ ? `postgres${version.split('.')[0]}`
299
+ : `mysql${version.split('.')[0]}.${version.split('.')[1]}`
300
+
301
+ const parameterGroup: RDSDBParameterGroup = {
302
+ Type: 'AWS::RDS::DBParameterGroup',
303
+ Properties: {
304
+ DBParameterGroupName: resourceName,
305
+ Description: `Parameter group for ${resourceName}`,
306
+ Family: family,
307
+ Parameters: parameters,
308
+ Tags: [
309
+ { Key: 'Name', Value: resourceName },
310
+ { Key: 'Environment', Value: environment },
311
+ ],
312
+ },
313
+ }
314
+
315
+ return { parameterGroup, logicalId }
316
+ }
317
+
318
+ /**
319
+ * Enable backup for RDS instance
320
+ */
321
+ static enableBackup(
322
+ dbInstance: RDSDBInstance,
323
+ retentionDays: number = 7,
324
+ ): RDSDBInstance {
325
+ dbInstance.Properties.BackupRetentionPeriod = retentionDays
326
+ return dbInstance
327
+ }
328
+
329
+ /**
330
+ * Create a DynamoDB table
331
+ */
332
+ static createTable(options: DynamoDBTableOptions): {
333
+ table: DynamoDBTable
334
+ logicalId: string
335
+ } {
336
+ const {
337
+ slug,
338
+ environment,
339
+ tableName,
340
+ partitionKey,
341
+ sortKey,
342
+ billingMode = 'PAY_PER_REQUEST',
343
+ readCapacity = 5,
344
+ writeCapacity = 5,
345
+ streamEnabled = false,
346
+ streamViewType = 'NEW_AND_OLD_IMAGES',
347
+ encrypted = true,
348
+ kmsKeyId,
349
+ pointInTimeRecovery = true,
350
+ ttlAttribute,
351
+ } = options
352
+
353
+ const resourceName = tableName || generateResourceName({
354
+ slug,
355
+ environment,
356
+ resourceType: 'table',
357
+ })
358
+
359
+ const logicalId = generateLogicalId(resourceName)
360
+
361
+ // Build attribute definitions
362
+ const attributeDefinitions: DynamoDBTable['Properties']['AttributeDefinitions'] = [
363
+ {
364
+ AttributeName: partitionKey.name,
365
+ AttributeType: partitionKey.type,
366
+ },
367
+ ]
368
+
369
+ if (sortKey) {
370
+ attributeDefinitions.push({
371
+ AttributeName: sortKey.name,
372
+ AttributeType: sortKey.type,
373
+ })
374
+ }
375
+
376
+ // Build key schema
377
+ const keySchema: DynamoDBTable['Properties']['KeySchema'] = [
378
+ {
379
+ AttributeName: partitionKey.name,
380
+ KeyType: 'HASH',
381
+ },
382
+ ]
383
+
384
+ if (sortKey) {
385
+ keySchema.push({
386
+ AttributeName: sortKey.name,
387
+ KeyType: 'RANGE',
388
+ })
389
+ }
390
+
391
+ const table: DynamoDBTable = {
392
+ Type: 'AWS::DynamoDB::Table',
393
+ Properties: {
394
+ TableName: resourceName,
395
+ BillingMode: billingMode,
396
+ AttributeDefinitions: attributeDefinitions,
397
+ KeySchema: keySchema,
398
+ Tags: [
399
+ { Key: 'Name', Value: resourceName },
400
+ { Key: 'Environment', Value: environment },
401
+ ],
402
+ },
403
+ }
404
+
405
+ if (billingMode === 'PROVISIONED') {
406
+ table.Properties.ProvisionedThroughput = {
407
+ ReadCapacityUnits: readCapacity,
408
+ WriteCapacityUnits: writeCapacity,
409
+ }
410
+ }
411
+
412
+ if (streamEnabled) {
413
+ table.Properties.StreamSpecification = {
414
+ StreamViewType: streamViewType,
415
+ }
416
+ }
417
+
418
+ if (encrypted) {
419
+ table.Properties.SSESpecification = {
420
+ SSEEnabled: true,
421
+ SSEType: kmsKeyId ? 'KMS' : 'AES256',
422
+ KMSMasterKeyId: kmsKeyId,
423
+ }
424
+ }
425
+
426
+ if (pointInTimeRecovery) {
427
+ table.Properties.PointInTimeRecoverySpecification = {
428
+ PointInTimeRecoveryEnabled: true,
429
+ }
430
+ }
431
+
432
+ if (ttlAttribute) {
433
+ table.Properties.TimeToLiveSpecification = {
434
+ AttributeName: ttlAttribute,
435
+ Enabled: true,
436
+ }
437
+ }
438
+
439
+ return { table, logicalId }
440
+ }
441
+
442
+ /**
443
+ * Add a global secondary index to a DynamoDB table
444
+ */
445
+ static addGlobalSecondaryIndex(
446
+ table: DynamoDBTable,
447
+ index: GlobalSecondaryIndexOptions,
448
+ ): DynamoDBTable {
449
+ const {
450
+ indexName,
451
+ partitionKey,
452
+ sortKey,
453
+ projectionType = 'ALL',
454
+ nonKeyAttributes,
455
+ readCapacity = 5,
456
+ writeCapacity = 5,
457
+ } = index
458
+
459
+ // Add attribute definitions if not already present
460
+ if (!table.Properties.AttributeDefinitions.some(attr => attr.AttributeName === partitionKey.name)) {
461
+ table.Properties.AttributeDefinitions.push({
462
+ AttributeName: partitionKey.name,
463
+ AttributeType: partitionKey.type,
464
+ })
465
+ }
466
+
467
+ if (sortKey && !table.Properties.AttributeDefinitions.some(attr => attr.AttributeName === sortKey.name)) {
468
+ table.Properties.AttributeDefinitions.push({
469
+ AttributeName: sortKey.name,
470
+ AttributeType: sortKey.type,
471
+ })
472
+ }
473
+
474
+ // Build GSI key schema
475
+ const gsiKeySchema: { AttributeName: string, KeyType: 'HASH' | 'RANGE' }[] = [
476
+ {
477
+ AttributeName: partitionKey.name,
478
+ KeyType: 'HASH',
479
+ },
480
+ ]
481
+
482
+ if (sortKey) {
483
+ gsiKeySchema.push({
484
+ AttributeName: sortKey.name,
485
+ KeyType: 'RANGE',
486
+ })
487
+ }
488
+
489
+ // Build GSI
490
+ const gsi = {
491
+ IndexName: indexName,
492
+ KeySchema: gsiKeySchema,
493
+ Projection: {
494
+ ProjectionType: projectionType as 'ALL' | 'KEYS_ONLY' | 'INCLUDE',
495
+ NonKeyAttributes: projectionType === 'INCLUDE' ? nonKeyAttributes : undefined,
496
+ },
497
+ ProvisionedThroughput: undefined as { ReadCapacityUnits: number, WriteCapacityUnits: number } | undefined,
498
+ }
499
+
500
+ if (table.Properties.BillingMode === 'PROVISIONED') {
501
+ gsi.ProvisionedThroughput = {
502
+ ReadCapacityUnits: readCapacity,
503
+ WriteCapacityUnits: writeCapacity,
504
+ }
505
+ }
506
+
507
+ if (!table.Properties.GlobalSecondaryIndexes) {
508
+ table.Properties.GlobalSecondaryIndexes = []
509
+ }
510
+
511
+ table.Properties.GlobalSecondaryIndexes.push(gsi)
512
+
513
+ return table
514
+ }
515
+
516
+ /**
517
+ * Enable streams on a DynamoDB table
518
+ */
519
+ static enableStreams(
520
+ table: DynamoDBTable,
521
+ viewType: 'NEW_IMAGE' | 'OLD_IMAGE' | 'NEW_AND_OLD_IMAGES' | 'KEYS_ONLY' = 'NEW_AND_OLD_IMAGES',
522
+ ): DynamoDBTable {
523
+ table.Properties.StreamSpecification = {
524
+ StreamViewType: viewType,
525
+ }
526
+ return table
527
+ }
528
+
529
+ /**
530
+ * Common RDS instance classes
531
+ */
532
+ static readonly InstanceClasses = {
533
+ // T3 - Burstable performance
534
+ T3_Micro: 'db.t3.micro',
535
+ T3_Small: 'db.t3.small',
536
+ T3_Medium: 'db.t3.medium',
537
+ T3_Large: 'db.t3.large',
538
+
539
+ // T4g - Arm-based burstable
540
+ T4g_Micro: 'db.t4g.micro',
541
+ T4g_Small: 'db.t4g.small',
542
+ T4g_Medium: 'db.t4g.medium',
543
+
544
+ // M5 - General purpose
545
+ M5_Large: 'db.m5.large',
546
+ M5_XLarge: 'db.m5.xlarge',
547
+ M5_2XLarge: 'db.m5.2xlarge',
548
+
549
+ // R5 - Memory optimized
550
+ R5_Large: 'db.r5.large',
551
+ R5_XLarge: 'db.r5.xlarge',
552
+ R5_2XLarge: 'db.r5.2xlarge',
553
+ } as const
554
+ }