@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,295 @@
1
+ /**
2
+ * Preset Extension and Composition Tests
3
+ */
4
+
5
+ import { describe, expect, it } from 'bun:test'
6
+ import {
7
+ extendPreset,
8
+ composePresets,
9
+ withMonitoring,
10
+ withSecurity,
11
+ withDatabase,
12
+ withCache,
13
+ withCDN,
14
+ withQueue,
15
+ } from './extend'
16
+ import type { CloudConfig } from '@stacksjs/ts-cloud-types'
17
+
18
+ describe('extendPreset', () => {
19
+ it('should extend base preset with new properties', () => {
20
+ const base: Partial<CloudConfig> = {
21
+ project: {
22
+ name: 'Base',
23
+ slug: 'base',
24
+ region: 'us-east-1',
25
+ },
26
+ }
27
+
28
+ const extension: Partial<CloudConfig> = {
29
+ infrastructure: {
30
+ storage: {
31
+ uploads: {},
32
+ },
33
+ },
34
+ }
35
+
36
+ const result = extendPreset(base, extension)
37
+
38
+ expect(result.project).toEqual(base.project)
39
+ expect(result.infrastructure?.storage).toEqual(extension.infrastructure?.storage)
40
+ })
41
+
42
+ it('should override base properties with extension properties', () => {
43
+ const base: Partial<CloudConfig> = {
44
+ project: {
45
+ name: 'Base',
46
+ slug: 'base',
47
+ region: 'us-east-1',
48
+ },
49
+ }
50
+
51
+ const extension: Partial<CloudConfig> = {
52
+ project: {
53
+ name: 'Base',
54
+ slug: 'base',
55
+ region: 'eu-west-1',
56
+ },
57
+ }
58
+
59
+ const result = extendPreset(base, extension)
60
+
61
+ expect(result.project?.name).toBe('Base')
62
+ expect(result.project?.slug).toBe('base')
63
+ expect(result.project?.region).toBe('eu-west-1')
64
+ })
65
+
66
+ it('should merge infrastructure configurations', () => {
67
+ const base: Partial<CloudConfig> = {
68
+ infrastructure: {
69
+ storage: {
70
+ uploads: {},
71
+ },
72
+ },
73
+ }
74
+
75
+ const extension: Partial<CloudConfig> = {
76
+ infrastructure: {
77
+ storage: {
78
+ assets: {},
79
+ },
80
+ },
81
+ }
82
+
83
+ const result = extendPreset(base, extension)
84
+
85
+ expect(result.infrastructure?.storage?.uploads).toBeDefined()
86
+ expect(result.infrastructure?.storage?.assets).toBeDefined()
87
+ })
88
+ })
89
+
90
+ describe('composePresets', () => {
91
+ it('should compose multiple presets in order', () => {
92
+ const preset1: Partial<CloudConfig> = {
93
+ project: {
94
+ name: 'Project',
95
+ slug: 'project',
96
+ region: 'us-west-2',
97
+ },
98
+ }
99
+
100
+ const preset2: Partial<CloudConfig> = {
101
+ project: {
102
+ name: 'Project',
103
+ slug: 'project',
104
+ region: 'us-east-1',
105
+ },
106
+ }
107
+
108
+ const preset3: Partial<CloudConfig> = {
109
+ infrastructure: {
110
+ storage: {},
111
+ },
112
+ }
113
+
114
+ const result = composePresets(preset1, preset2, preset3)
115
+
116
+ expect(result.project?.name).toBe('Project')
117
+ expect(result.project?.slug).toBe('project')
118
+ expect(result.project?.region).toBe('us-east-1')
119
+ expect(result.infrastructure?.storage).toBeDefined()
120
+ })
121
+
122
+ it('should apply later presets over earlier ones', () => {
123
+ const preset1: Partial<CloudConfig> = {
124
+ project: {
125
+ name: 'Project',
126
+ slug: 'project',
127
+ region: 'us-east-1',
128
+ },
129
+ }
130
+
131
+ const preset2: Partial<CloudConfig> = {
132
+ project: {
133
+ name: 'Project',
134
+ slug: 'project',
135
+ region: 'eu-west-1',
136
+ },
137
+ }
138
+
139
+ const result = composePresets(preset1, preset2)
140
+
141
+ expect(result.project?.region).toBe('eu-west-1')
142
+ })
143
+
144
+ it('should handle empty presets', () => {
145
+ const preset1: Partial<CloudConfig> = {
146
+ project: {
147
+ name: 'Test',
148
+ slug: 'test',
149
+ region: 'us-east-1',
150
+ },
151
+ }
152
+
153
+ const result = composePresets(preset1, {}, {})
154
+
155
+ expect(result.project?.name).toBe('Test')
156
+ })
157
+ })
158
+
159
+ describe('withMonitoring', () => {
160
+ it('should add monitoring configuration', () => {
161
+ const base: Partial<CloudConfig> = {
162
+ project: {
163
+ name: 'Test',
164
+ slug: 'test',
165
+ region: 'us-east-1',
166
+ },
167
+ }
168
+
169
+ const result = extendPreset(base, withMonitoring({
170
+ alarms: [],
171
+ }))
172
+
173
+ expect(result.infrastructure?.monitoring).toBeDefined()
174
+ })
175
+
176
+ it('should preserve existing infrastructure', () => {
177
+ const base: Partial<CloudConfig> = {
178
+ infrastructure: {
179
+ storage: {
180
+ uploads: {},
181
+ },
182
+ },
183
+ }
184
+
185
+ const result = extendPreset(base, withMonitoring({
186
+ alarms: [],
187
+ }))
188
+
189
+ expect(result.infrastructure?.storage?.uploads).toBeDefined()
190
+ expect(result.infrastructure?.monitoring).toBeDefined()
191
+ })
192
+ })
193
+
194
+ describe('withSecurity', () => {
195
+ it('should add security configuration', () => {
196
+ const base: Partial<CloudConfig> = {
197
+ project: {
198
+ name: 'Test',
199
+ slug: 'test',
200
+ region: 'us-east-1',
201
+ },
202
+ }
203
+
204
+ const result = extendPreset(base, withSecurity({
205
+ waf: { enabled: true },
206
+ }))
207
+
208
+ expect(result.infrastructure?.security).toBeDefined()
209
+ expect(result.infrastructure?.security?.waf?.enabled).toBe(true)
210
+ })
211
+ })
212
+
213
+ describe('withDatabase', () => {
214
+ it('should add database configuration', () => {
215
+ const base: Partial<CloudConfig> = {
216
+ project: {
217
+ name: 'Test',
218
+ slug: 'test',
219
+ region: 'us-east-1',
220
+ },
221
+ }
222
+
223
+ const result = extendPreset(base, withDatabase({
224
+ postgres: {
225
+ instanceClass: 'db.t3.micro',
226
+ },
227
+ }))
228
+
229
+ expect(result.infrastructure?.databases?.postgres).toBeDefined()
230
+ expect(result.infrastructure?.databases?.postgres?.instanceClass).toBe('db.t3.micro')
231
+ })
232
+ })
233
+
234
+ describe('withCache', () => {
235
+ it('should add cache configuration', () => {
236
+ const base: Partial<CloudConfig> = {
237
+ project: {
238
+ name: 'Test',
239
+ slug: 'test',
240
+ region: 'us-east-1',
241
+ },
242
+ }
243
+
244
+ const result = extendPreset(base, withCache({
245
+ redis: {
246
+ nodeType: 'cache.t3.small',
247
+ },
248
+ }))
249
+
250
+ expect(result.infrastructure?.cache?.redis).toBeDefined()
251
+ expect(result.infrastructure?.cache?.redis?.nodeType).toBe('cache.t3.small')
252
+ })
253
+ })
254
+
255
+ describe('withCDN', () => {
256
+ it('should add CDN configuration', () => {
257
+ const base: Partial<CloudConfig> = {
258
+ project: {
259
+ name: 'Test',
260
+ slug: 'test',
261
+ region: 'us-east-1',
262
+ },
263
+ }
264
+
265
+ const result = extendPreset(base, withCDN({
266
+ enabled: true,
267
+ compress: true,
268
+ }))
269
+
270
+ expect(result.infrastructure?.cdn).toBeDefined()
271
+ expect(result.infrastructure?.cdn?.enabled).toBe(true)
272
+ expect(result.infrastructure?.cdn?.compress).toBe(true)
273
+ })
274
+ })
275
+
276
+ describe('withQueue', () => {
277
+ it('should add queue configuration', () => {
278
+ const base: Partial<CloudConfig> = {
279
+ project: {
280
+ name: 'Test',
281
+ slug: 'test',
282
+ region: 'us-east-1',
283
+ },
284
+ }
285
+
286
+ const result = extendPreset(base, withQueue({
287
+ jobs: {
288
+ fifo: false,
289
+ },
290
+ }))
291
+
292
+ expect(result.infrastructure?.queues?.jobs).toBeDefined()
293
+ expect(result.infrastructure?.queues?.jobs?.fifo).toBe(false)
294
+ })
295
+ })
@@ -0,0 +1,297 @@
1
+ import type { CloudConfig } from '@stacksjs/ts-cloud-types'
2
+
3
+ /**
4
+ * Deep merge utility for combining CloudConfig objects
5
+ */
6
+ function deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>): T {
7
+ const result = { ...target }
8
+
9
+ for (const key in source) {
10
+ const sourceValue = source[key]
11
+ const targetValue = result[key]
12
+
13
+ if (sourceValue === undefined) {
14
+ continue
15
+ }
16
+
17
+ if (Array.isArray(sourceValue) && Array.isArray(targetValue)) {
18
+ // Merge arrays by concatenating
19
+ result[key] = [...targetValue, ...sourceValue] as any
20
+ }
21
+ else if (
22
+ typeof sourceValue === 'object'
23
+ && sourceValue !== null
24
+ && !Array.isArray(sourceValue)
25
+ && typeof targetValue === 'object'
26
+ && targetValue !== null
27
+ && !Array.isArray(targetValue)
28
+ ) {
29
+ // Recursively merge objects
30
+ result[key] = deepMerge(targetValue, sourceValue)
31
+ }
32
+ else {
33
+ // Override primitive values
34
+ result[key] = sourceValue as any
35
+ }
36
+ }
37
+
38
+ return result
39
+ }
40
+
41
+ /**
42
+ * Extend a base preset with custom configuration
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const myPreset = extendPreset(
47
+ * createNodeJsServerPreset({ name: 'My App', slug: 'my-app' }),
48
+ * {
49
+ * infrastructure: {
50
+ * compute: {
51
+ * server: {
52
+ * instanceType: 't3.large', // Override instance type
53
+ * autoScaling: {
54
+ * max: 20, // Increase max instances
55
+ * },
56
+ * },
57
+ * },
58
+ * database: {
59
+ * postgres: {
60
+ * instanceClass: 'db.r6g.xlarge', // Upgrade database
61
+ * },
62
+ * },
63
+ * },
64
+ * }
65
+ * )
66
+ * ```
67
+ */
68
+ export function extendPreset(
69
+ basePreset: Partial<CloudConfig>,
70
+ extensions: Partial<CloudConfig>,
71
+ ): Partial<CloudConfig> {
72
+ return deepMerge(basePreset as Record<string, any>, extensions as Record<string, any>) as Partial<CloudConfig>
73
+ }
74
+
75
+ /**
76
+ * Compose multiple presets together
77
+ * Later presets override earlier ones
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * const composedPreset = composePresets(
82
+ * createStaticSitePreset({ name: 'Site', slug: 'site', domain: 'example.com' }),
83
+ * createApiBackendPreset({ name: 'API', slug: 'api' }),
84
+ * {
85
+ * // Custom overrides
86
+ * infrastructure: {
87
+ * monitoring: {
88
+ * alarms: [{ metric: 'CustomMetric', threshold: 100 }],
89
+ * },
90
+ * },
91
+ * }
92
+ * )
93
+ * ```
94
+ */
95
+ export function composePresets(
96
+ ...presets: Partial<CloudConfig>[]
97
+ ): Partial<CloudConfig> {
98
+ return presets.reduce(
99
+ (acc, preset) => deepMerge(acc as Record<string, any>, preset as Record<string, any>),
100
+ {} as Record<string, any>,
101
+ ) as Partial<CloudConfig>
102
+ }
103
+
104
+ /**
105
+ * Create a custom preset by extending an existing one
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * const createMyCustomPreset = createPreset(
110
+ * (options) => createNodeJsServerPreset(options),
111
+ * {
112
+ * infrastructure: {
113
+ * monitoring: {
114
+ * dashboard: {
115
+ * name: 'custom-dashboard',
116
+ * widgets: [{ type: 'metric', metrics: ['CustomMetric'] }],
117
+ * },
118
+ * },
119
+ * },
120
+ * }
121
+ * )
122
+ *
123
+ * // Use it
124
+ * const myPreset = createMyCustomPreset({ name: 'App', slug: 'app' })
125
+ * ```
126
+ */
127
+ export function createPreset<TOptions extends Record<string, any>>(
128
+ basePresetFn: (options: TOptions) => Partial<CloudConfig>,
129
+ extensions: Partial<CloudConfig> | ((config: Partial<CloudConfig>, options: TOptions) => Partial<CloudConfig>),
130
+ ): (options: TOptions) => Partial<CloudConfig> {
131
+ return (options: TOptions) => {
132
+ const baseConfig = basePresetFn(options)
133
+
134
+ if (typeof extensions === 'function') {
135
+ return extendPreset(baseConfig, extensions(baseConfig, options))
136
+ }
137
+
138
+ return extendPreset(baseConfig, extensions)
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Merge infrastructure configurations selectively
144
+ * Useful for adding specific infrastructure to existing presets
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * const withRedis = mergeInfrastructure({
149
+ * cache: {
150
+ * redis: {
151
+ * nodeType: 'cache.t3.small',
152
+ * numCacheNodes: 2,
153
+ * },
154
+ * },
155
+ * })
156
+ *
157
+ * const myPreset = extendPreset(
158
+ * createApiBackendPreset({ name: 'API', slug: 'api' }),
159
+ * withRedis
160
+ * )
161
+ * ```
162
+ */
163
+ export function mergeInfrastructure(
164
+ infrastructure: Partial<CloudConfig['infrastructure']>,
165
+ ): Partial<CloudConfig> {
166
+ return {
167
+ infrastructure,
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Add monitoring configuration to any preset
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * const myPreset = extendPreset(
177
+ * createStaticSitePreset({ name: 'Site', slug: 'site' }),
178
+ * withMonitoring({
179
+ * dashboard: { name: 'my-dashboard' },
180
+ * alarms: [{ metric: 'Errors', threshold: 10 }],
181
+ * })
182
+ * )
183
+ * ```
184
+ */
185
+ export function withMonitoring(
186
+ monitoring: NonNullable<CloudConfig['infrastructure']>['monitoring'],
187
+ ): Partial<CloudConfig> {
188
+ return mergeInfrastructure({ monitoring })
189
+ }
190
+
191
+ /**
192
+ * Add security configuration to any preset
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * const myPreset = extendPreset(
197
+ * createApiBackendPreset({ name: 'API', slug: 'api' }),
198
+ * withSecurity({
199
+ * waf: { enabled: true, rules: ['rateLimit', 'sqlInjection'] },
200
+ * })
201
+ * )
202
+ * ```
203
+ */
204
+ export function withSecurity(
205
+ security: NonNullable<CloudConfig['infrastructure']>['security'],
206
+ ): Partial<CloudConfig> {
207
+ return mergeInfrastructure({ security })
208
+ }
209
+
210
+ /**
211
+ * Add database configuration to any preset
212
+ *
213
+ * @example
214
+ * ```typescript
215
+ * const myPreset = extendPreset(
216
+ * createNodeJsServerlessPreset({ name: 'App', slug: 'app' }),
217
+ * withDatabase({
218
+ * postgres: {
219
+ * engine: 'postgres',
220
+ * version: '15',
221
+ * instanceClass: 'db.t3.medium',
222
+ * multiAZ: true,
223
+ * },
224
+ * })
225
+ * )
226
+ * ```
227
+ */
228
+ export function withDatabase(
229
+ databases: NonNullable<CloudConfig['infrastructure']>['databases'],
230
+ ): Partial<CloudConfig> {
231
+ return mergeInfrastructure({ databases })
232
+ }
233
+
234
+ /**
235
+ * Add cache configuration to any preset
236
+ *
237
+ * @example
238
+ * ```typescript
239
+ * const myPreset = extendPreset(
240
+ * createApiBackendPreset({ name: 'API', slug: 'api' }),
241
+ * withCache({
242
+ * redis: {
243
+ * nodeType: 'cache.t3.small',
244
+ * numCacheNodes: 2,
245
+ * },
246
+ * })
247
+ * )
248
+ * ```
249
+ */
250
+ export function withCache(
251
+ cache: NonNullable<CloudConfig['infrastructure']>['cache'],
252
+ ): Partial<CloudConfig> {
253
+ return mergeInfrastructure({ cache })
254
+ }
255
+
256
+ /**
257
+ * Add CDN configuration to any preset
258
+ *
259
+ * @example
260
+ * ```typescript
261
+ * const myPreset = extendPreset(
262
+ * createNodeJsServerPreset({ name: 'App', slug: 'app' }),
263
+ * withCDN({
264
+ * enabled: true,
265
+ * compress: true,
266
+ * http3: true,
267
+ * })
268
+ * )
269
+ * ```
270
+ */
271
+ export function withCDN(
272
+ cdn: NonNullable<CloudConfig['infrastructure']>['cdn'],
273
+ ): Partial<CloudConfig> {
274
+ return mergeInfrastructure({ cdn })
275
+ }
276
+
277
+ /**
278
+ * Add queue configuration to any preset
279
+ *
280
+ * @example
281
+ * ```typescript
282
+ * const myPreset = extendPreset(
283
+ * createNodeJsServerlessPreset({ name: 'App', slug: 'app' }),
284
+ * withQueue({
285
+ * jobs: {
286
+ * fifo: false,
287
+ * deadLetterQueue: true,
288
+ * },
289
+ * })
290
+ * )
291
+ * ```
292
+ */
293
+ export function withQueue(
294
+ queues: NonNullable<CloudConfig['infrastructure']>['queues'],
295
+ ): Partial<CloudConfig> {
296
+ return mergeInfrastructure({ queues })
297
+ }