@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,582 @@
1
+ /**
2
+ * A/B Testing Infrastructure
3
+ * Traffic splitting based on user attributes, headers, or cookies
4
+ */
5
+
6
+ export interface ABTest {
7
+ id: string
8
+ name: string
9
+ description?: string
10
+ variants: ABVariant[]
11
+ routingStrategy: RoutingStrategy
12
+ startTime: Date
13
+ endTime?: Date
14
+ status: 'draft' | 'active' | 'paused' | 'completed'
15
+ metrics?: ABMetrics
16
+ winner?: string
17
+ }
18
+
19
+ export interface ABVariant {
20
+ id: string
21
+ name: string
22
+ description?: string
23
+ trafficPercentage: number
24
+ targetGroupArn?: string
25
+ functionVersionArn?: string
26
+ originId?: string // For CloudFront
27
+ weight: number
28
+ }
29
+
30
+ export interface RoutingStrategy {
31
+ type: 'random' | 'cookie' | 'header' | 'geo' | 'device' | 'user-attribute'
32
+ cookieName?: string
33
+ headerName?: string
34
+ attributeName?: string
35
+ stickySession?: boolean
36
+ sessionDuration?: number // minutes
37
+ }
38
+
39
+ export interface ABMetrics {
40
+ variants: Record<string, VariantMetrics>
41
+ totalRequests: number
42
+ startTime: Date
43
+ lastUpdated: Date
44
+ }
45
+
46
+ export interface VariantMetrics {
47
+ requests: number
48
+ conversions: number
49
+ conversionRate: number
50
+ averageLatency: number
51
+ errorRate: number
52
+ revenue?: number
53
+ customMetrics?: Record<string, number>
54
+ }
55
+
56
+ export interface ABTestResult {
57
+ testId: string
58
+ winningVariant: string
59
+ confidence: number // 0-100%
60
+ improvement: number // Percentage improvement over control
61
+ statisticalSignificance: boolean
62
+ metrics: ABMetrics
63
+ recommendation: string
64
+ }
65
+
66
+ /**
67
+ * A/B testing manager
68
+ */
69
+ export class ABTestManager {
70
+ private tests: Map<string, ABTest> = new Map()
71
+ private testCounter = 0
72
+
73
+ /**
74
+ * Create A/B test
75
+ */
76
+ createTest(test: Omit<ABTest, 'id'>): ABTest {
77
+ const id = `abtest-${Date.now()}-${this.testCounter++}`
78
+
79
+ const abTest: ABTest = {
80
+ id,
81
+ ...test,
82
+ }
83
+
84
+ this.tests.set(id, abTest)
85
+
86
+ return abTest
87
+ }
88
+
89
+ /**
90
+ * Create simple A/B test (control vs variant)
91
+ */
92
+ createSimpleABTest(options: {
93
+ name: string
94
+ description?: string
95
+ controlTargetGroupArn: string
96
+ variantTargetGroupArn: string
97
+ variantTrafficPercentage?: number
98
+ stickySession?: boolean
99
+ }): ABTest {
100
+ const variantPercentage = options.variantTrafficPercentage || 50
101
+
102
+ return this.createTest({
103
+ name: options.name,
104
+ description: options.description,
105
+ variants: [
106
+ {
107
+ id: 'control',
108
+ name: 'Control',
109
+ description: 'Original version',
110
+ trafficPercentage: 100 - variantPercentage,
111
+ targetGroupArn: options.controlTargetGroupArn,
112
+ weight: 100 - variantPercentage,
113
+ },
114
+ {
115
+ id: 'variant-a',
116
+ name: 'Variant A',
117
+ description: 'Test version',
118
+ trafficPercentage: variantPercentage,
119
+ targetGroupArn: options.variantTargetGroupArn,
120
+ weight: variantPercentage,
121
+ },
122
+ ],
123
+ routingStrategy: {
124
+ type: options.stickySession ? 'cookie' : 'random',
125
+ cookieName: options.stickySession ? 'ab_variant' : undefined,
126
+ stickySession: options.stickySession ?? false,
127
+ sessionDuration: 1440, // 24 hours
128
+ },
129
+ startTime: new Date(),
130
+ status: 'draft',
131
+ })
132
+ }
133
+
134
+ /**
135
+ * Create multivariate test
136
+ */
137
+ createMultivariateTest(options: {
138
+ name: string
139
+ description?: string
140
+ variants: Array<{
141
+ name: string
142
+ description?: string
143
+ targetGroupArn: string
144
+ trafficPercentage: number
145
+ }>
146
+ routingStrategy?: RoutingStrategy
147
+ }): ABTest {
148
+ // Validate percentages sum to 100
149
+ const totalPercentage = options.variants.reduce((sum, v) => sum + v.trafficPercentage, 0)
150
+ if (totalPercentage !== 100) {
151
+ throw new Error(`Traffic percentages must sum to 100, got ${totalPercentage}`)
152
+ }
153
+
154
+ return this.createTest({
155
+ name: options.name,
156
+ description: options.description,
157
+ variants: options.variants.map((v, index) => ({
158
+ id: `variant-${index}`,
159
+ name: v.name,
160
+ description: v.description,
161
+ trafficPercentage: v.trafficPercentage,
162
+ targetGroupArn: v.targetGroupArn,
163
+ weight: v.trafficPercentage,
164
+ })),
165
+ routingStrategy: options.routingStrategy || {
166
+ type: 'random',
167
+ stickySession: false,
168
+ },
169
+ startTime: new Date(),
170
+ status: 'draft',
171
+ })
172
+ }
173
+
174
+ /**
175
+ * Create header-based A/B test
176
+ */
177
+ createHeaderBasedTest(options: {
178
+ name: string
179
+ controlTargetGroupArn: string
180
+ variantTargetGroupArn: string
181
+ headerName: string
182
+ headerValue: string
183
+ }): ABTest {
184
+ return this.createTest({
185
+ name: options.name,
186
+ description: `Route based on ${options.headerName} header`,
187
+ variants: [
188
+ {
189
+ id: 'control',
190
+ name: 'Control',
191
+ trafficPercentage: 50,
192
+ targetGroupArn: options.controlTargetGroupArn,
193
+ weight: 50,
194
+ },
195
+ {
196
+ id: 'variant-a',
197
+ name: 'Variant A',
198
+ trafficPercentage: 50,
199
+ targetGroupArn: options.variantTargetGroupArn,
200
+ weight: 50,
201
+ },
202
+ ],
203
+ routingStrategy: {
204
+ type: 'header',
205
+ headerName: options.headerName,
206
+ stickySession: false,
207
+ },
208
+ startTime: new Date(),
209
+ status: 'draft',
210
+ })
211
+ }
212
+
213
+ /**
214
+ * Create geo-based A/B test
215
+ */
216
+ createGeoBasedTest(options: {
217
+ name: string
218
+ controlTargetGroupArn: string
219
+ variantTargetGroupArn: string
220
+ regions: string[] // For variant (e.g., ['US', 'CA'])
221
+ }): ABTest {
222
+ return this.createTest({
223
+ name: options.name,
224
+ description: `Route based on geographic location`,
225
+ variants: [
226
+ {
227
+ id: 'control',
228
+ name: 'Control (Rest of World)',
229
+ trafficPercentage: 50,
230
+ targetGroupArn: options.controlTargetGroupArn,
231
+ weight: 50,
232
+ },
233
+ {
234
+ id: 'variant-a',
235
+ name: `Variant A (${options.regions.join(', ')})`,
236
+ trafficPercentage: 50,
237
+ targetGroupArn: options.variantTargetGroupArn,
238
+ weight: 50,
239
+ },
240
+ ],
241
+ routingStrategy: {
242
+ type: 'geo',
243
+ stickySession: true,
244
+ sessionDuration: 1440,
245
+ },
246
+ startTime: new Date(),
247
+ status: 'draft',
248
+ })
249
+ }
250
+
251
+ /**
252
+ * Start A/B test
253
+ */
254
+ startTest(testId: string): void {
255
+ const test = this.tests.get(testId)
256
+
257
+ if (!test) {
258
+ throw new Error(`Test not found: ${testId}`)
259
+ }
260
+
261
+ if (test.status !== 'draft' && test.status !== 'paused') {
262
+ throw new Error(`Cannot start test in ${test.status} status`)
263
+ }
264
+
265
+ test.status = 'active'
266
+ test.startTime = new Date()
267
+
268
+ console.log(`Started A/B test: ${test.name}`)
269
+ console.log(` Variants: ${test.variants.length}`)
270
+ test.variants.forEach((v) => {
271
+ console.log(` - ${v.name}: ${v.trafficPercentage}%`)
272
+ })
273
+ }
274
+
275
+ /**
276
+ * Pause A/B test
277
+ */
278
+ pauseTest(testId: string): void {
279
+ const test = this.tests.get(testId)
280
+
281
+ if (!test) {
282
+ throw new Error(`Test not found: ${testId}`)
283
+ }
284
+
285
+ test.status = 'paused'
286
+ console.log(`Paused A/B test: ${test.name}`)
287
+ }
288
+
289
+ /**
290
+ * Update traffic split
291
+ */
292
+ updateTrafficSplit(testId: string, variantId: string, newPercentage: number): void {
293
+ const test = this.tests.get(testId)
294
+
295
+ if (!test) {
296
+ throw new Error(`Test not found: ${testId}`)
297
+ }
298
+
299
+ const variant = test.variants.find(v => v.id === variantId)
300
+
301
+ if (!variant) {
302
+ throw new Error(`Variant not found: ${variantId}`)
303
+ }
304
+
305
+ const oldPercentage = variant.trafficPercentage
306
+ variant.trafficPercentage = newPercentage
307
+ variant.weight = newPercentage
308
+
309
+ console.log(`Updated ${variant.name} traffic: ${oldPercentage}% → ${newPercentage}%`)
310
+ }
311
+
312
+ /**
313
+ * Analyze test results
314
+ */
315
+ analyzeResults(testId: string): ABTestResult {
316
+ const test = this.tests.get(testId)
317
+
318
+ if (!test) {
319
+ throw new Error(`Test not found: ${testId}`)
320
+ }
321
+
322
+ if (!test.metrics) {
323
+ // Simulate metrics collection
324
+ test.metrics = this.collectMetrics(test)
325
+ }
326
+
327
+ // Find winning variant (highest conversion rate)
328
+ let winningVariant = test.variants[0]
329
+ let highestConversionRate = 0
330
+
331
+ for (const variant of test.variants) {
332
+ const metrics = test.metrics.variants[variant.id]
333
+ if (metrics && metrics.conversionRate > highestConversionRate) {
334
+ highestConversionRate = metrics.conversionRate
335
+ winningVariant = variant
336
+ }
337
+ }
338
+
339
+ const controlMetrics = test.metrics.variants['control'] || test.metrics.variants[test.variants[0].id]
340
+ const winnerMetrics = test.metrics.variants[winningVariant.id]
341
+
342
+ const improvement
343
+ = ((winnerMetrics.conversionRate - controlMetrics.conversionRate) / controlMetrics.conversionRate) * 100
344
+
345
+ // Simple statistical significance check (would use proper chi-square test in production)
346
+ const minSampleSize = 100
347
+ const statisticalSignificance
348
+ = winnerMetrics.requests > minSampleSize && controlMetrics.requests > minSampleSize && Math.abs(improvement) > 10
349
+
350
+ return {
351
+ testId,
352
+ winningVariant: winningVariant.name,
353
+ confidence: statisticalSignificance ? 95 : 75,
354
+ improvement,
355
+ statisticalSignificance,
356
+ metrics: test.metrics,
357
+ recommendation: this.generateRecommendation(improvement, statisticalSignificance, winningVariant.name),
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Declare winner and route all traffic
363
+ */
364
+ declareWinner(testId: string, variantId: string): void {
365
+ const test = this.tests.get(testId)
366
+
367
+ if (!test) {
368
+ throw new Error(`Test not found: ${testId}`)
369
+ }
370
+
371
+ const winner = test.variants.find(v => v.id === variantId)
372
+
373
+ if (!winner) {
374
+ throw new Error(`Variant not found: ${variantId}`)
375
+ }
376
+
377
+ // Route all traffic to winner
378
+ test.variants.forEach((v) => {
379
+ if (v.id === variantId) {
380
+ v.trafficPercentage = 100
381
+ v.weight = 100
382
+ }
383
+ else {
384
+ v.trafficPercentage = 0
385
+ v.weight = 0
386
+ }
387
+ })
388
+
389
+ test.status = 'completed'
390
+ test.endTime = new Date()
391
+ test.winner = variantId
392
+
393
+ console.log(`Declared winner: ${winner.name}`)
394
+ console.log(` All traffic now routed to ${winner.name}`)
395
+ }
396
+
397
+ /**
398
+ * Collect metrics for test
399
+ */
400
+ private collectMetrics(test: ABTest): ABMetrics {
401
+ const variantMetrics: Record<string, VariantMetrics> = {}
402
+
403
+ // Simulate metric collection
404
+ let totalRequests = 0
405
+
406
+ for (const variant of test.variants) {
407
+ const requests = Math.floor(Math.random() * 1000) + 500
408
+ const conversions = Math.floor(requests * (Math.random() * 0.1 + 0.05)) // 5-15% conversion
409
+ const conversionRate = (conversions / requests) * 100
410
+
411
+ variantMetrics[variant.id] = {
412
+ requests,
413
+ conversions,
414
+ conversionRate,
415
+ averageLatency: 150 + Math.random() * 100,
416
+ errorRate: Math.random() * 0.5,
417
+ revenue: conversions * (Math.random() * 50 + 100), // $100-150 per conversion
418
+ }
419
+
420
+ totalRequests += requests
421
+ }
422
+
423
+ return {
424
+ variants: variantMetrics,
425
+ totalRequests,
426
+ startTime: test.startTime,
427
+ lastUpdated: new Date(),
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Generate recommendation
433
+ */
434
+ private generateRecommendation(
435
+ improvement: number,
436
+ significant: boolean,
437
+ winnerName: string,
438
+ ): string {
439
+ if (!significant) {
440
+ return 'Continue test - sample size too small or no significant difference detected'
441
+ }
442
+
443
+ if (improvement > 20) {
444
+ return `Strong winner detected - ${winnerName} shows ${improvement.toFixed(1)}% improvement. Recommend deploying to all traffic.`
445
+ }
446
+
447
+ if (improvement > 10) {
448
+ return `Moderate improvement - ${winnerName} shows ${improvement.toFixed(1)}% improvement. Consider deploying.`
449
+ }
450
+
451
+ if (improvement > 0) {
452
+ return `Minor improvement - ${winnerName} shows ${improvement.toFixed(1)}% improvement. May not be worth the complexity.`
453
+ }
454
+
455
+ return `No improvement detected - consider reverting to control variant`
456
+ }
457
+
458
+ /**
459
+ * Get test
460
+ */
461
+ getTest(id: string): ABTest | undefined {
462
+ return this.tests.get(id)
463
+ }
464
+
465
+ /**
466
+ * List tests
467
+ */
468
+ listTests(): ABTest[] {
469
+ return Array.from(this.tests.values())
470
+ }
471
+
472
+ /**
473
+ * Generate CloudFormation for ALB weighted routing
474
+ */
475
+ generateALBListenerRuleCF(test: ABTest): any {
476
+ return {
477
+ Type: 'AWS::ElasticLoadBalancingV2::ListenerRule',
478
+ Properties: {
479
+ ListenerArn: { Ref: 'LoadBalancerListener' },
480
+ Priority: 1,
481
+ Conditions: [
482
+ {
483
+ Field: 'path-pattern',
484
+ Values: ['/*'],
485
+ },
486
+ ],
487
+ Actions: [
488
+ {
489
+ Type: 'forward',
490
+ ForwardConfig: {
491
+ TargetGroups: test.variants.map(variant => ({
492
+ TargetGroupArn: variant.targetGroupArn,
493
+ Weight: variant.weight,
494
+ })),
495
+ TargetGroupStickinessConfig: {
496
+ Enabled: test.routingStrategy.stickySession || false,
497
+ DurationSeconds: test.routingStrategy.sessionDuration
498
+ ? test.routingStrategy.sessionDuration * 60
499
+ : undefined,
500
+ },
501
+ },
502
+ },
503
+ ],
504
+ },
505
+ }
506
+ }
507
+
508
+ /**
509
+ * Generate Lambda@Edge function for A/B testing
510
+ */
511
+ generateLambdaEdgeFunction(test: ABTest): string {
512
+ return `'use strict';
513
+
514
+ exports.handler = (event, context, callback) => {
515
+ const request = event.Records[0].cf.request;
516
+ const headers = request.headers;
517
+
518
+ // Check for existing variant cookie
519
+ let variant = null;
520
+ if (headers.cookie) {
521
+ const cookies = headers.cookie[0].value.split(';');
522
+ for (const cookie of cookies) {
523
+ const [key, value] = cookie.trim().split('=');
524
+ if (key === '${test.routingStrategy.cookieName || 'ab_variant'}') {
525
+ variant = value;
526
+ break;
527
+ }
528
+ }
529
+ }
530
+
531
+ // Assign variant if not already assigned
532
+ if (!variant) {
533
+ const random = Math.random() * 100;
534
+ let cumulative = 0;
535
+
536
+ ${test.variants
537
+ .map((v, i) => {
538
+ return `if (random < ${v.trafficPercentage + (i > 0 ? test.variants.slice(0, i).reduce((sum, v) => sum + v.trafficPercentage, 0) : 0)}) {
539
+ variant = '${v.id}';
540
+ }`
541
+ })
542
+ .join(' else ')}
543
+ }
544
+
545
+ // Set variant cookie
546
+ const response = {
547
+ status: '200',
548
+ statusDescription: 'OK',
549
+ headers: {
550
+ 'set-cookie': [{
551
+ key: 'Set-Cookie',
552
+ value: \`${test.routingStrategy.cookieName || 'ab_variant'}=\${variant}; Path=/; Max-Age=${(test.routingStrategy.sessionDuration || 1440) * 60}\`
553
+ }]
554
+ }
555
+ };
556
+
557
+ // Route to appropriate origin based on variant
558
+ ${test.variants
559
+ .map(
560
+ v => `if (variant === '${v.id}') {
561
+ request.origin.custom.domainName = '${v.originId}';
562
+ }`,
563
+ )
564
+ .join(' else ')}
565
+
566
+ callback(null, request);
567
+ };`
568
+ }
569
+
570
+ /**
571
+ * Clear all data
572
+ */
573
+ clear(): void {
574
+ this.tests.clear()
575
+ this.testCounter = 0
576
+ }
577
+ }
578
+
579
+ /**
580
+ * Global A/B testing manager instance
581
+ */
582
+ export const abTestManager: ABTestManager = new ABTestManager()