@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,434 @@
1
+ /**
2
+ * Service Quota Validation Tests
3
+ */
4
+
5
+ import { describe, expect, it } from 'bun:test'
6
+ import {
7
+ DEFAULT_SERVICE_LIMITS,
8
+ checkServiceQuotas,
9
+ getQuotaUsageSummary,
10
+ suggestQuotaIncrease,
11
+ } from './quotas'
12
+
13
+ describe('DEFAULT_SERVICE_LIMITS', () => {
14
+ it('should have EC2 limits', () => {
15
+ expect(DEFAULT_SERVICE_LIMITS.ec2).toBeDefined()
16
+ expect(DEFAULT_SERVICE_LIMITS.ec2['Running On-Demand Instances']).toBe(20)
17
+ expect(DEFAULT_SERVICE_LIMITS.ec2['VPCs']).toBe(5)
18
+ expect(DEFAULT_SERVICE_LIMITS.ec2['Security Groups']).toBe(500)
19
+ })
20
+
21
+ it('should have RDS limits', () => {
22
+ expect(DEFAULT_SERVICE_LIMITS.rds).toBeDefined()
23
+ expect(DEFAULT_SERVICE_LIMITS.rds['DB Instances']).toBe(40)
24
+ })
25
+
26
+ it('should have S3 limits', () => {
27
+ expect(DEFAULT_SERVICE_LIMITS.s3).toBeDefined()
28
+ expect(DEFAULT_SERVICE_LIMITS.s3['Buckets']).toBe(100)
29
+ })
30
+
31
+ it('should have Lambda limits', () => {
32
+ expect(DEFAULT_SERVICE_LIMITS.lambda).toBeDefined()
33
+ expect(DEFAULT_SERVICE_LIMITS.lambda['Concurrent Executions']).toBe(1000)
34
+ })
35
+
36
+ it('should have DynamoDB limits', () => {
37
+ expect(DEFAULT_SERVICE_LIMITS.dynamodb).toBeDefined()
38
+ expect(DEFAULT_SERVICE_LIMITS.dynamodb['Tables']).toBe(256)
39
+ })
40
+ })
41
+
42
+ describe('checkServiceQuotas', () => {
43
+ it('should return empty array for minimal config', async () => {
44
+ const quotas = await checkServiceQuotas({
45
+ project: { name: 'Test', slug: 'test' },
46
+ })
47
+
48
+ expect(Array.isArray(quotas)).toBe(true)
49
+ expect(quotas.length).toBe(0)
50
+ })
51
+
52
+ it('should check EC2 instance quotas', async () => {
53
+ const quotas = await checkServiceQuotas({
54
+ project: { name: 'Test', slug: 'test' },
55
+ infrastructure: {
56
+ compute: {
57
+ server: {
58
+ autoScaling: {
59
+ max: 10,
60
+ },
61
+ },
62
+ },
63
+ },
64
+ })
65
+
66
+ const ec2Quota = quotas.find(q => q.quotaName === 'Running On-Demand Instances')
67
+ expect(ec2Quota).toBeDefined()
68
+ expect(ec2Quota?.service).toBe('EC2')
69
+ expect(ec2Quota?.currentValue).toBe(10)
70
+ expect(ec2Quota?.limit).toBe(20)
71
+ expect(ec2Quota?.percentage).toBe(50)
72
+ expect(ec2Quota?.warning).toBe(false)
73
+ })
74
+
75
+ it('should warn when EC2 quota exceeds 80%', async () => {
76
+ const quotas = await checkServiceQuotas({
77
+ project: { name: 'Test', slug: 'test' },
78
+ infrastructure: {
79
+ compute: {
80
+ server: {
81
+ autoScaling: {
82
+ max: 17,
83
+ },
84
+ },
85
+ },
86
+ },
87
+ })
88
+
89
+ const ec2Quota = quotas.find(q => q.quotaName === 'Running On-Demand Instances')
90
+ expect(ec2Quota?.warning).toBe(true)
91
+ })
92
+
93
+ it('should check VPC quotas', async () => {
94
+ const quotas = await checkServiceQuotas({
95
+ project: { name: 'Test', slug: 'test' },
96
+ infrastructure: {
97
+ network: {
98
+ vpc: {},
99
+ },
100
+ },
101
+ })
102
+
103
+ const vpcQuota = quotas.find(q => q.quotaName === 'VPCs')
104
+ expect(vpcQuota).toBeDefined()
105
+ expect(vpcQuota?.service).toBe('EC2')
106
+ expect(vpcQuota?.currentValue).toBe(1)
107
+ expect(vpcQuota?.limit).toBe(5)
108
+ })
109
+
110
+ it('should check RDS database quotas', async () => {
111
+ const quotas = await checkServiceQuotas({
112
+ project: { name: 'Test', slug: 'test' },
113
+ infrastructure: {
114
+ database: {
115
+ postgres: {},
116
+ },
117
+ },
118
+ })
119
+
120
+ const rdsQuota = quotas.find(q => q.quotaName === 'DB Instances')
121
+ expect(rdsQuota).toBeDefined()
122
+ expect(rdsQuota?.service).toBe('RDS')
123
+ expect(rdsQuota?.currentValue).toBe(1)
124
+ expect(rdsQuota?.limit).toBe(40)
125
+ })
126
+
127
+ it('should check S3 bucket quotas', async () => {
128
+ const quotas = await checkServiceQuotas({
129
+ project: { name: 'Test', slug: 'test' },
130
+ infrastructure: {
131
+ storage: {
132
+ uploads: {},
133
+ assets: {},
134
+ backups: {},
135
+ },
136
+ },
137
+ })
138
+
139
+ const s3Quota = quotas.find(q => q.quotaName === 'Buckets')
140
+ expect(s3Quota).toBeDefined()
141
+ expect(s3Quota?.service).toBe('S3')
142
+ expect(s3Quota?.currentValue).toBe(3)
143
+ expect(s3Quota?.limit).toBe(100)
144
+ })
145
+
146
+ it('should warn when S3 bucket quota exceeds 80%', async () => {
147
+ const storage: any = {}
148
+ for (let i = 0; i < 85; i++) {
149
+ storage[`bucket${i}`] = {}
150
+ }
151
+
152
+ const quotas = await checkServiceQuotas({
153
+ project: { name: 'Test', slug: 'test' },
154
+ infrastructure: {
155
+ storage,
156
+ },
157
+ })
158
+
159
+ const s3Quota = quotas.find(q => q.quotaName === 'Buckets')
160
+ expect(s3Quota?.warning).toBe(true)
161
+ })
162
+
163
+ it('should check Lambda function quotas', async () => {
164
+ const quotas = await checkServiceQuotas({
165
+ project: { name: 'Test', slug: 'test' },
166
+ infrastructure: {
167
+ functions: {
168
+ api: [
169
+ { name: 'handler1', runtime: 'nodejs20.x' },
170
+ { name: 'handler2', runtime: 'nodejs20.x' },
171
+ ],
172
+ },
173
+ },
174
+ })
175
+
176
+ const lambdaQuota = quotas.find(q => q.quotaName === 'Functions (estimated)')
177
+ expect(lambdaQuota).toBeDefined()
178
+ expect(lambdaQuota?.service).toBe('Lambda')
179
+ expect(lambdaQuota?.currentValue).toBe(2)
180
+ expect(lambdaQuota?.limit).toBe(1000)
181
+ })
182
+
183
+ it('should check DynamoDB table quotas', async () => {
184
+ const quotas = await checkServiceQuotas({
185
+ project: { name: 'Test', slug: 'test' },
186
+ infrastructure: {
187
+ database: {
188
+ dynamodb: {
189
+ tables: [
190
+ { name: 'users', partitionKey: 'id' },
191
+ { name: 'posts', partitionKey: 'id' },
192
+ ],
193
+ },
194
+ },
195
+ },
196
+ })
197
+
198
+ const dynamoQuota = quotas.find(q => q.quotaName === 'Tables')
199
+ expect(dynamoQuota).toBeDefined()
200
+ expect(dynamoQuota?.service).toBe('DynamoDB')
201
+ expect(dynamoQuota?.currentValue).toBe(2)
202
+ expect(dynamoQuota?.limit).toBe(256)
203
+ })
204
+
205
+ it('should check multiple quotas at once', async () => {
206
+ const quotas = await checkServiceQuotas({
207
+ project: { name: 'Test', slug: 'test' },
208
+ infrastructure: {
209
+ compute: {
210
+ server: {
211
+ autoScaling: { max: 5 },
212
+ },
213
+ },
214
+ network: {
215
+ vpc: {},
216
+ },
217
+ storage: {
218
+ uploads: {},
219
+ },
220
+ database: {
221
+ postgres: {},
222
+ },
223
+ },
224
+ })
225
+
226
+ expect(quotas.length).toBeGreaterThan(0)
227
+ expect(quotas.some(q => q.service === 'EC2')).toBe(true)
228
+ expect(quotas.some(q => q.service === 'S3')).toBe(true)
229
+ expect(quotas.some(q => q.service === 'RDS')).toBe(true)
230
+ })
231
+ })
232
+
233
+ describe('getQuotaUsageSummary', () => {
234
+ it('should return message for empty quotas', () => {
235
+ const summary = getQuotaUsageSummary([])
236
+
237
+ expect(summary).toBe('No quotas to check')
238
+ })
239
+
240
+ it('should format quota usage summary', () => {
241
+ const summary = getQuotaUsageSummary([
242
+ {
243
+ service: 'EC2',
244
+ quotaName: 'Running Instances',
245
+ currentValue: 10,
246
+ limit: 20,
247
+ percentage: 50,
248
+ warning: false,
249
+ },
250
+ {
251
+ service: 'S3',
252
+ quotaName: 'Buckets',
253
+ currentValue: 5,
254
+ limit: 100,
255
+ percentage: 5,
256
+ warning: false,
257
+ },
258
+ ])
259
+
260
+ expect(summary).toContain('Service Quota Usage')
261
+ expect(summary).toContain('EC2')
262
+ expect(summary).toContain('Running Instances')
263
+ expect(summary).toContain('10/20')
264
+ expect(summary).toContain('50.0%')
265
+ expect(summary).toContain('S3')
266
+ expect(summary).toContain('Buckets')
267
+ })
268
+
269
+ it('should show warning indicator for quota warnings', () => {
270
+ const summary = getQuotaUsageSummary([
271
+ {
272
+ service: 'EC2',
273
+ quotaName: 'Running Instances',
274
+ currentValue: 18,
275
+ limit: 20,
276
+ percentage: 90,
277
+ warning: true,
278
+ },
279
+ ])
280
+
281
+ expect(summary).toContain('⚠')
282
+ })
283
+
284
+ it('should show checkmark for normal usage', () => {
285
+ const summary = getQuotaUsageSummary([
286
+ {
287
+ service: 'EC2',
288
+ quotaName: 'Running Instances',
289
+ currentValue: 5,
290
+ limit: 20,
291
+ percentage: 25,
292
+ warning: false,
293
+ },
294
+ ])
295
+
296
+ expect(summary).toContain('✓')
297
+ })
298
+
299
+ it('should group quotas by service', () => {
300
+ const summary = getQuotaUsageSummary([
301
+ {
302
+ service: 'EC2',
303
+ quotaName: 'Instances',
304
+ currentValue: 5,
305
+ limit: 20,
306
+ percentage: 25,
307
+ warning: false,
308
+ },
309
+ {
310
+ service: 'EC2',
311
+ quotaName: 'VPCs',
312
+ currentValue: 2,
313
+ limit: 5,
314
+ percentage: 40,
315
+ warning: false,
316
+ },
317
+ {
318
+ service: 'S3',
319
+ quotaName: 'Buckets',
320
+ currentValue: 3,
321
+ limit: 100,
322
+ percentage: 3,
323
+ warning: false,
324
+ },
325
+ ])
326
+
327
+ // Should have EC2 section with 2 items
328
+ const ec2Section = summary.split('S3:')[0]
329
+ expect(ec2Section).toContain('Instances')
330
+ expect(ec2Section).toContain('VPCs')
331
+ })
332
+ })
333
+
334
+ describe('suggestQuotaIncrease', () => {
335
+ it('should return empty array for no warnings', () => {
336
+ const suggestions = suggestQuotaIncrease([
337
+ {
338
+ service: 'EC2',
339
+ quotaName: 'Instances',
340
+ currentValue: 5,
341
+ limit: 20,
342
+ percentage: 25,
343
+ warning: false,
344
+ },
345
+ ])
346
+
347
+ expect(suggestions).toEqual([])
348
+ })
349
+
350
+ it('should suggest increase for quotas at 100%', () => {
351
+ const suggestions = suggestQuotaIncrease([
352
+ {
353
+ service: 'EC2',
354
+ quotaName: 'Instances',
355
+ currentValue: 20,
356
+ limit: 20,
357
+ percentage: 100,
358
+ warning: true,
359
+ },
360
+ ])
361
+
362
+ expect(suggestions).toHaveLength(1)
363
+ expect(suggestions[0]).toContain('Request quota increase')
364
+ expect(suggestions[0]).toContain('EC2')
365
+ expect(suggestions[0]).toContain('Instances')
366
+ expect(suggestions[0]).toContain('20')
367
+ })
368
+
369
+ it('should suggest consideration for quotas with warnings', () => {
370
+ const suggestions = suggestQuotaIncrease([
371
+ {
372
+ service: 'S3',
373
+ quotaName: 'Buckets',
374
+ currentValue: 85,
375
+ limit: 100,
376
+ percentage: 85,
377
+ warning: true,
378
+ },
379
+ ])
380
+
381
+ expect(suggestions).toHaveLength(1)
382
+ expect(suggestions[0]).toContain('Consider requesting quota increase')
383
+ expect(suggestions[0]).toContain('S3')
384
+ expect(suggestions[0]).toContain('Buckets')
385
+ expect(suggestions[0]).toContain('85.0%')
386
+ })
387
+
388
+ it('should provide multiple suggestions', () => {
389
+ const suggestions = suggestQuotaIncrease([
390
+ {
391
+ service: 'EC2',
392
+ quotaName: 'Instances',
393
+ currentValue: 20,
394
+ limit: 20,
395
+ percentage: 100,
396
+ warning: true,
397
+ },
398
+ {
399
+ service: 'S3',
400
+ quotaName: 'Buckets',
401
+ currentValue: 85,
402
+ limit: 100,
403
+ percentage: 85,
404
+ warning: true,
405
+ },
406
+ ])
407
+
408
+ expect(suggestions).toHaveLength(2)
409
+ })
410
+
411
+ it('should prioritize 100% quotas over warnings', () => {
412
+ const suggestions = suggestQuotaIncrease([
413
+ {
414
+ service: 'EC2',
415
+ quotaName: 'Instances',
416
+ currentValue: 20,
417
+ limit: 20,
418
+ percentage: 100,
419
+ warning: true,
420
+ },
421
+ {
422
+ service: 'S3',
423
+ quotaName: 'Buckets',
424
+ currentValue: 85,
425
+ limit: 100,
426
+ percentage: 85,
427
+ warning: true,
428
+ },
429
+ ])
430
+
431
+ expect(suggestions[0]).toContain('Request quota increase')
432
+ expect(suggestions[1]).toContain('Consider requesting')
433
+ })
434
+ })
@@ -0,0 +1,217 @@
1
+ /**
2
+ * AWS Service Quota Checking
3
+ * Check if deployment will exceed service limits
4
+ */
5
+
6
+ import { DebugLogger } from '../errors'
7
+
8
+ export interface ServiceQuota {
9
+ service: string
10
+ quotaName: string
11
+ currentValue: number
12
+ limit: number
13
+ percentage: number
14
+ warning: boolean
15
+ }
16
+
17
+ /**
18
+ * Common AWS service limits by region
19
+ */
20
+ export const DEFAULT_SERVICE_LIMITS = {
21
+ ec2: {
22
+ 'Running On-Demand Instances': 20,
23
+ 'Elastic IPs': 5,
24
+ 'VPCs': 5,
25
+ 'Internet Gateways': 5,
26
+ 'NAT Gateways': 5,
27
+ 'Security Groups': 500,
28
+ },
29
+ rds: {
30
+ 'DB Instances': 40,
31
+ 'DB Snapshots': 100,
32
+ 'DB Parameter Groups': 50,
33
+ },
34
+ lambda: {
35
+ 'Concurrent Executions': 1000,
36
+ 'Function Storage': 75 * 1024 * 1024 * 1024 as number, // 75 GB
37
+ },
38
+ s3: {
39
+ 'Buckets': 100,
40
+ },
41
+ cloudformation: {
42
+ 'Stacks': 200,
43
+ 'StackSets': 100,
44
+ },
45
+ elasticache: {
46
+ 'Nodes': 100,
47
+ 'Clusters': 100,
48
+ },
49
+ dynamodb: {
50
+ 'Tables': 256,
51
+ },
52
+ ecs: {
53
+ 'Clusters': 10000,
54
+ 'Services per Cluster': 2000,
55
+ },
56
+ }
57
+
58
+ /**
59
+ * Check service quotas for deployment
60
+ */
61
+ export async function checkServiceQuotas(config: any): Promise<ServiceQuota[]> {
62
+ const quotas: ServiceQuota[] = []
63
+
64
+ DebugLogger.verbose('Checking AWS service quotas...')
65
+
66
+ // Check EC2 quotas
67
+ if (config.infrastructure?.compute?.server) {
68
+ const instanceCount = config.infrastructure.compute.server.autoScaling?.max || 1
69
+
70
+ quotas.push({
71
+ service: 'EC2',
72
+ quotaName: 'Running On-Demand Instances',
73
+ currentValue: instanceCount,
74
+ limit: DEFAULT_SERVICE_LIMITS.ec2['Running On-Demand Instances'],
75
+ percentage: (instanceCount / DEFAULT_SERVICE_LIMITS.ec2['Running On-Demand Instances']) * 100,
76
+ warning: instanceCount > DEFAULT_SERVICE_LIMITS.ec2['Running On-Demand Instances'] * 0.8,
77
+ })
78
+ }
79
+
80
+ // Check VPC quotas
81
+ if (config.infrastructure?.network?.vpc) {
82
+ quotas.push({
83
+ service: 'EC2',
84
+ quotaName: 'VPCs',
85
+ currentValue: 1,
86
+ limit: DEFAULT_SERVICE_LIMITS.ec2.VPCs,
87
+ percentage: (1 / DEFAULT_SERVICE_LIMITS.ec2.VPCs) * 100,
88
+ warning: false,
89
+ })
90
+ }
91
+
92
+ // Check RDS quotas
93
+ if (config.infrastructure?.database?.postgres || config.infrastructure?.database?.mysql) {
94
+ quotas.push({
95
+ service: 'RDS',
96
+ quotaName: 'DB Instances',
97
+ currentValue: 1,
98
+ limit: DEFAULT_SERVICE_LIMITS.rds['DB Instances'],
99
+ percentage: (1 / DEFAULT_SERVICE_LIMITS.rds['DB Instances']) * 100,
100
+ warning: false,
101
+ })
102
+ }
103
+
104
+ // Check S3 quotas
105
+ if (config.infrastructure?.storage) {
106
+ const bucketCount = Object.keys(config.infrastructure.storage).length
107
+
108
+ quotas.push({
109
+ service: 'S3',
110
+ quotaName: 'Buckets',
111
+ currentValue: bucketCount,
112
+ limit: DEFAULT_SERVICE_LIMITS.s3.Buckets,
113
+ percentage: (bucketCount / DEFAULT_SERVICE_LIMITS.s3.Buckets) * 100,
114
+ warning: bucketCount > DEFAULT_SERVICE_LIMITS.s3.Buckets * 0.8,
115
+ })
116
+ }
117
+
118
+ // Check Lambda quotas
119
+ if (config.infrastructure?.functions) {
120
+ let functionCount = 0
121
+ for (const category of Object.values(config.infrastructure.functions)) {
122
+ if (Array.isArray(category)) {
123
+ functionCount += category.length
124
+ }
125
+ }
126
+
127
+ // Lambda doesn't have a function count limit, but we can check storage
128
+ quotas.push({
129
+ service: 'Lambda',
130
+ quotaName: 'Functions (estimated)',
131
+ currentValue: functionCount,
132
+ limit: 1000, // Soft limit
133
+ percentage: (functionCount / 1000) * 100,
134
+ warning: functionCount > 800,
135
+ })
136
+ }
137
+
138
+ // Check DynamoDB quotas
139
+ if (config.infrastructure?.database?.dynamodb) {
140
+ const tableCount = config.infrastructure.database.dynamodb.tables?.length || 0
141
+
142
+ quotas.push({
143
+ service: 'DynamoDB',
144
+ quotaName: 'Tables',
145
+ currentValue: tableCount,
146
+ limit: DEFAULT_SERVICE_LIMITS.dynamodb.Tables,
147
+ percentage: (tableCount / DEFAULT_SERVICE_LIMITS.dynamodb.Tables) * 100,
148
+ warning: tableCount > DEFAULT_SERVICE_LIMITS.dynamodb.Tables * 0.8,
149
+ })
150
+ }
151
+
152
+ // Log warnings
153
+ const warnings = quotas.filter(q => q.warning)
154
+ if (warnings.length > 0) {
155
+ DebugLogger.warn('Service quota warnings detected:')
156
+ for (const warning of warnings) {
157
+ DebugLogger.warn(` ${warning.service} - ${warning.quotaName}: ${warning.currentValue}/${warning.limit} (${warning.percentage.toFixed(1)}%)`)
158
+ }
159
+ }
160
+ else {
161
+ DebugLogger.verbose('All service quotas are within limits')
162
+ }
163
+
164
+ return quotas
165
+ }
166
+
167
+ /**
168
+ * Get quota usage summary
169
+ */
170
+ export function getQuotaUsageSummary(quotas: ServiceQuota[]): string {
171
+ if (quotas.length === 0) {
172
+ return 'No quotas to check'
173
+ }
174
+
175
+ let summary = 'Service Quota Usage:\n\n'
176
+
177
+ const byService = quotas.reduce((acc, quota) => {
178
+ if (!acc[quota.service]) {
179
+ acc[quota.service] = []
180
+ }
181
+ acc[quota.service].push(quota)
182
+ return acc
183
+ }, {} as Record<string, ServiceQuota[]>)
184
+
185
+ for (const [service, serviceQuotas] of Object.entries(byService)) {
186
+ summary += `${service}:\n`
187
+ for (const quota of serviceQuotas) {
188
+ const indicator = quota.warning ? '⚠' : '✓'
189
+ summary += ` ${indicator} ${quota.quotaName}: ${quota.currentValue}/${quota.limit} (${quota.percentage.toFixed(1)}%)\n`
190
+ }
191
+ summary += '\n'
192
+ }
193
+
194
+ return summary
195
+ }
196
+
197
+ /**
198
+ * Suggest quota increase if needed
199
+ */
200
+ export function suggestQuotaIncrease(quotas: ServiceQuota[]): string[] {
201
+ const suggestions: string[] = []
202
+
203
+ for (const quota of quotas) {
204
+ if (quota.percentage >= 100) {
205
+ suggestions.push(
206
+ `Request quota increase for ${quota.service} - ${quota.quotaName} (currently ${quota.limit})`,
207
+ )
208
+ }
209
+ else if (quota.warning) {
210
+ suggestions.push(
211
+ `Consider requesting quota increase for ${quota.service} - ${quota.quotaName} (${quota.percentage.toFixed(1)}% used)`,
212
+ )
213
+ }
214
+ }
215
+
216
+ return suggestions
217
+ }