@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,592 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import { Workflow } from '../src/modules/workflow'
3
+ import { TemplateBuilder } from '../src/template-builder'
4
+ import type { EnvironmentType } from '@stacksjs/ts-cloud-types'
5
+
6
+ const slug = 'test-app'
7
+ const environment: EnvironmentType = 'development'
8
+
9
+ describe('workflow Module - State Machine', () => {
10
+ test('should create a standard state machine with auto-generated role', () => {
11
+ const definition = {
12
+ StartAt: 'HelloWorld',
13
+ States: {
14
+ HelloWorld: Workflow.createPassState({ result: 'Hello World', end: true }),
15
+ },
16
+ }
17
+
18
+ const { stateMachine, logicalId, role, roleLogicalId } = Workflow.createStateMachine({
19
+ slug,
20
+ environment,
21
+ definition,
22
+ })
23
+
24
+ expect(stateMachine.Type).toBe('AWS::StepFunctions::StateMachine')
25
+ expect(stateMachine.Properties?.StateMachineName).toContain('test-app')
26
+ expect(stateMachine.Properties?.StateMachineType).toBe('STANDARD')
27
+ expect(stateMachine.Properties?.DefinitionString).toBe(JSON.stringify(definition))
28
+ expect(logicalId).toBeTruthy()
29
+ expect(role).toBeDefined()
30
+ expect(roleLogicalId).toBeTruthy()
31
+ expect(role?.Type).toBe('AWS::IAM::Role')
32
+ })
33
+
34
+ test('should create an express state machine', () => {
35
+ const definition = {
36
+ StartAt: 'HelloWorld',
37
+ States: {
38
+ HelloWorld: Workflow.createPassState({ result: 'Hello World', end: true }),
39
+ },
40
+ }
41
+
42
+ const { stateMachine } = Workflow.createStateMachine({
43
+ slug,
44
+ environment,
45
+ type: 'EXPRESS',
46
+ definition,
47
+ })
48
+
49
+ expect(stateMachine.Properties?.StateMachineType).toBe('EXPRESS')
50
+ })
51
+
52
+ test('should create a state machine with custom role ARN', () => {
53
+ const definition = {
54
+ StartAt: 'HelloWorld',
55
+ States: {
56
+ HelloWorld: Workflow.createPassState({ result: 'Hello World', end: true }),
57
+ },
58
+ }
59
+
60
+ const customRoleArn = 'arn:aws:iam::123456789012:role/custom-role'
61
+
62
+ const { stateMachine, role } = Workflow.createStateMachine({
63
+ slug,
64
+ environment,
65
+ definition,
66
+ roleArn: customRoleArn,
67
+ })
68
+
69
+ expect(stateMachine.Properties?.RoleArn).toBe(customRoleArn)
70
+ expect(role).toBeUndefined()
71
+ })
72
+
73
+ test('should create a state machine with logging configuration', () => {
74
+ const definition = {
75
+ StartAt: 'HelloWorld',
76
+ States: {
77
+ HelloWorld: Workflow.createPassState({ result: 'Hello World', end: true }),
78
+ },
79
+ }
80
+
81
+ const { stateMachine } = Workflow.createStateMachine({
82
+ slug,
83
+ environment,
84
+ definition,
85
+ loggingConfiguration: {
86
+ level: 'ALL',
87
+ includeExecutionData: true,
88
+ destinations: ['arn:aws:logs:us-east-1:123456789012:log-group:/aws/stepfunctions/test'],
89
+ },
90
+ })
91
+
92
+ expect(stateMachine.Properties?.LoggingConfiguration).toBeDefined()
93
+ expect(stateMachine.Properties?.LoggingConfiguration?.Level).toBe('ALL')
94
+ expect(stateMachine.Properties?.LoggingConfiguration?.IncludeExecutionData).toBe(true)
95
+ })
96
+
97
+ test('should create a state machine with tracing enabled', () => {
98
+ const definition = {
99
+ StartAt: 'HelloWorld',
100
+ States: {
101
+ HelloWorld: Workflow.createPassState({ result: 'Hello World', end: true }),
102
+ },
103
+ }
104
+
105
+ const { stateMachine } = Workflow.createStateMachine({
106
+ slug,
107
+ environment,
108
+ definition,
109
+ tracingConfiguration: {
110
+ enabled: true,
111
+ },
112
+ })
113
+
114
+ expect(stateMachine.Properties?.TracingConfiguration).toBeDefined()
115
+ expect(stateMachine.Properties?.TracingConfiguration?.Enabled).toBe(true)
116
+ })
117
+ })
118
+
119
+ describe('workflow Module - Task States', () => {
120
+ test('should create a Lambda task state', () => {
121
+ const functionArn = 'arn:aws:lambda:us-east-1:123456789012:function:my-function'
122
+
123
+ const task = Workflow.createLambdaTask(functionArn, {
124
+ parameters: { input: 'test' },
125
+ resultPath: '$.result',
126
+ next: 'NextState',
127
+ })
128
+
129
+ expect(task.Type).toBe('Task')
130
+ expect(task.Resource).toBe('arn:aws:states:::lambda:invoke')
131
+ expect(task.Parameters).toBeDefined()
132
+ expect(task.Parameters?.FunctionName).toBe(functionArn)
133
+ expect(task.ResultPath).toBe('$.result')
134
+ expect(task.Next).toBe('NextState')
135
+ })
136
+
137
+ test('should create a DynamoDB GetItem task state', () => {
138
+ const task = Workflow.createDynamoDBTask(
139
+ 'GetItem',
140
+ 'my-table',
141
+ {
142
+ Key: {
143
+ id: { S: 'test-id' },
144
+ },
145
+ },
146
+ {
147
+ resultPath: '$.dbResult',
148
+ end: true,
149
+ },
150
+ )
151
+
152
+ expect(task.Type).toBe('Task')
153
+ expect(task.Resource).toBe('arn:aws:states:::dynamodb:getItem')
154
+ expect(task.Parameters?.TableName).toBe('my-table')
155
+ expect(task.Parameters?.Key).toBeDefined()
156
+ expect(task.End).toBe(true)
157
+ })
158
+
159
+ test('should create a DynamoDB PutItem task state', () => {
160
+ const task = Workflow.createDynamoDBTask(
161
+ 'PutItem',
162
+ 'my-table',
163
+ {
164
+ Item: {
165
+ id: { S: 'test-id' },
166
+ name: { S: 'Test' },
167
+ },
168
+ },
169
+ )
170
+
171
+ expect(task.Resource).toBe('arn:aws:states:::dynamodb:putItem')
172
+ })
173
+
174
+ test('should create an SNS publish task state', () => {
175
+ const topicArn = 'arn:aws:sns:us-east-1:123456789012:my-topic'
176
+
177
+ const task = Workflow.createSNSPublishTask(
178
+ topicArn,
179
+ { message: 'Hello SNS' },
180
+ {
181
+ resultPath: null,
182
+ end: true,
183
+ },
184
+ )
185
+
186
+ expect(task.Type).toBe('Task')
187
+ expect(task.Resource).toBe('arn:aws:states:::sns:publish')
188
+ expect(task.Parameters?.TopicArn).toBe(topicArn)
189
+ expect(task.Parameters?.Message).toEqual({ message: 'Hello SNS' })
190
+ expect(task.ResultPath).toBeNull()
191
+ })
192
+
193
+ test('should create an SQS send message task state', () => {
194
+ const queueUrl = 'https://sqs.us-east-1.amazonaws.com/123456789012/my-queue'
195
+
196
+ const task = Workflow.createSQSSendMessageTask(
197
+ queueUrl,
198
+ { body: 'Hello SQS' },
199
+ {
200
+ next: 'NextState',
201
+ },
202
+ )
203
+
204
+ expect(task.Type).toBe('Task')
205
+ expect(task.Resource).toBe('arn:aws:states:::sqs:sendMessage')
206
+ expect(task.Parameters?.QueueUrl).toBe(queueUrl)
207
+ expect(task.Parameters?.MessageBody).toEqual({ body: 'Hello SQS' })
208
+ expect(task.Next).toBe('NextState')
209
+ })
210
+ })
211
+
212
+ describe('workflow Module - Control States', () => {
213
+ test('should create a Pass state', () => {
214
+ const pass = Workflow.createPassState({
215
+ result: { message: 'Processing' },
216
+ resultPath: '$.status',
217
+ next: 'NextState',
218
+ })
219
+
220
+ expect(pass.Type).toBe('Pass')
221
+ expect(pass.Result).toEqual({ message: 'Processing' })
222
+ expect(pass.ResultPath).toBe('$.status')
223
+ expect(pass.Next).toBe('NextState')
224
+ })
225
+
226
+ test('should create a Wait state with seconds', () => {
227
+ const wait = Workflow.createWaitState({
228
+ seconds: 60,
229
+ next: 'NextState',
230
+ })
231
+
232
+ expect(wait.Type).toBe('Wait')
233
+ expect(wait.Seconds).toBe(60)
234
+ expect(wait.Next).toBe('NextState')
235
+ })
236
+
237
+ test('should create a Wait state with timestamp', () => {
238
+ const wait = Workflow.createWaitState({
239
+ timestamp: '2024-01-01T00:00:00Z',
240
+ next: 'NextState',
241
+ })
242
+
243
+ expect(wait.Type).toBe('Wait')
244
+ expect(wait.Timestamp).toBe('2024-01-01T00:00:00Z')
245
+ })
246
+
247
+ test('should create a Choice state', () => {
248
+ const choice = Workflow.createChoiceState(
249
+ [
250
+ {
251
+ Variable: '$.status',
252
+ StringEquals: 'success',
253
+ Next: 'SuccessState',
254
+ },
255
+ {
256
+ Variable: '$.status',
257
+ StringEquals: 'error',
258
+ Next: 'ErrorState',
259
+ },
260
+ ],
261
+ 'DefaultState',
262
+ )
263
+
264
+ expect(choice.Type).toBe('Choice')
265
+ expect(choice.Choices).toHaveLength(2)
266
+ expect(choice.Default).toBe('DefaultState')
267
+ })
268
+
269
+ test('should create a Parallel state', () => {
270
+ const branch1 = {
271
+ StartAt: 'Branch1Task',
272
+ States: {
273
+ Branch1Task: Workflow.createPassState({ result: 'Branch 1', end: true }),
274
+ },
275
+ }
276
+
277
+ const branch2 = {
278
+ StartAt: 'Branch2Task',
279
+ States: {
280
+ Branch2Task: Workflow.createPassState({ result: 'Branch 2', end: true }),
281
+ },
282
+ }
283
+
284
+ const parallel = Workflow.createParallelState([branch1, branch2], {
285
+ resultPath: '$.parallelResults',
286
+ next: 'NextState',
287
+ })
288
+
289
+ expect(parallel.Type).toBe('Parallel')
290
+ expect(parallel.Branches).toHaveLength(2)
291
+ expect(parallel.ResultPath).toBe('$.parallelResults')
292
+ expect(parallel.Next).toBe('NextState')
293
+ })
294
+
295
+ test('should create a Map state', () => {
296
+ const iterator = {
297
+ StartAt: 'ProcessItem',
298
+ States: {
299
+ ProcessItem: Workflow.createPassState({ result: 'Processed', end: true }),
300
+ },
301
+ }
302
+
303
+ const map = Workflow.createMapState(iterator, {
304
+ itemsPath: '$.items',
305
+ maxConcurrency: 5,
306
+ resultPath: '$.results',
307
+ end: true,
308
+ })
309
+
310
+ expect(map.Type).toBe('Map')
311
+ expect(map.ItemsPath).toBe('$.items')
312
+ expect(map.MaxConcurrency).toBe(5)
313
+ expect(map.Iterator).toEqual(iterator)
314
+ expect(map.End).toBe(true)
315
+ })
316
+
317
+ test('should create a Succeed state', () => {
318
+ const succeed = Workflow.createSucceedState()
319
+
320
+ expect(succeed.Type).toBe('Succeed')
321
+ })
322
+
323
+ test('should create a Fail state', () => {
324
+ const fail = Workflow.createFailState('ErrorCode', 'Error message')
325
+
326
+ expect(fail.Type).toBe('Fail')
327
+ expect(fail.Error).toBe('ErrorCode')
328
+ expect(fail.Cause).toBe('Error message')
329
+ })
330
+ })
331
+
332
+ describe('workflow Module - Error Handling', () => {
333
+ test('should create standard retry policy', () => {
334
+ const retry = Workflow.RetryPolicies.standard()
335
+
336
+ expect(retry.ErrorEquals).toContain('States.ALL')
337
+ expect(retry.IntervalSeconds).toBe(2)
338
+ expect(retry.MaxAttempts).toBe(3)
339
+ expect(retry.BackoffRate).toBe(2.0)
340
+ })
341
+
342
+ test('should create aggressive retry policy', () => {
343
+ const retry = Workflow.RetryPolicies.aggressive()
344
+
345
+ expect(retry.ErrorEquals).toContain('States.TaskFailed')
346
+ expect(retry.IntervalSeconds).toBe(1)
347
+ expect(retry.MaxAttempts).toBe(5)
348
+ expect(retry.BackoffRate).toBe(1.5)
349
+ })
350
+
351
+ test('should create custom retry policy', () => {
352
+ const retry = Workflow.RetryPolicies.custom(
353
+ ['CustomError'],
354
+ 5,
355
+ 10,
356
+ 1.8,
357
+ )
358
+
359
+ expect(retry.ErrorEquals).toEqual(['CustomError'])
360
+ expect(retry.IntervalSeconds).toBe(5)
361
+ expect(retry.MaxAttempts).toBe(10)
362
+ expect(retry.BackoffRate).toBe(1.8)
363
+ })
364
+
365
+ test('should create catch all policy', () => {
366
+ const catchPolicy = Workflow.CatchPolicies.all('ErrorHandler')
367
+
368
+ expect(catchPolicy.ErrorEquals).toContain('States.ALL')
369
+ expect(catchPolicy.Next).toBe('ErrorHandler')
370
+ expect(catchPolicy.ResultPath).toBe('$.error')
371
+ })
372
+
373
+ test('should create specific catch policy', () => {
374
+ const catchPolicy = Workflow.CatchPolicies.specific(
375
+ ['CustomError', 'AnotherError'],
376
+ 'SpecificHandler',
377
+ '$.customError',
378
+ )
379
+
380
+ expect(catchPolicy.ErrorEquals).toEqual(['CustomError', 'AnotherError'])
381
+ expect(catchPolicy.Next).toBe('SpecificHandler')
382
+ expect(catchPolicy.ResultPath).toBe('$.customError')
383
+ })
384
+
385
+ test('should apply retry and catch to task', () => {
386
+ const task = Workflow.createLambdaTask(
387
+ 'arn:aws:lambda:us-east-1:123456789012:function:my-function',
388
+ {
389
+ retry: [Workflow.RetryPolicies.standard()],
390
+ catch: [Workflow.CatchPolicies.all('ErrorHandler')],
391
+ end: true,
392
+ },
393
+ )
394
+
395
+ expect(task.Retry).toHaveLength(1)
396
+ expect(task.Catch).toHaveLength(1)
397
+ expect(task.Retry?.[0].ErrorEquals).toContain('States.ALL')
398
+ expect(task.Catch?.[0].Next).toBe('ErrorHandler')
399
+ })
400
+ })
401
+
402
+ describe('workflow Module - Common Patterns', () => {
403
+ test('should create sequential workflow pattern', () => {
404
+ const definition = Workflow.Patterns.sequential(slug, environment, [
405
+ { name: 'Step1', state: Workflow.createPassState({ result: 'Step 1' }) },
406
+ { name: 'Step2', state: Workflow.createPassState({ result: 'Step 2' }) },
407
+ { name: 'Step3', state: Workflow.createPassState({ result: 'Step 3' }) },
408
+ ])
409
+
410
+ expect(definition.StartAt).toBe('Step1')
411
+ expect(Object.keys(definition.States)).toHaveLength(3)
412
+ expect(definition.States.Step1.Next).toBe('Step2')
413
+ expect(definition.States.Step2.Next).toBe('Step3')
414
+ expect(definition.States.Step3.End).toBe(true)
415
+ })
416
+
417
+ test('should create fan-out workflow pattern', () => {
418
+ const branch1 = {
419
+ StartAt: 'Branch1',
420
+ States: {
421
+ Branch1: Workflow.createPassState({ result: 'Branch 1', end: true }),
422
+ },
423
+ }
424
+
425
+ const branch2 = {
426
+ StartAt: 'Branch2',
427
+ States: {
428
+ Branch2: Workflow.createPassState({ result: 'Branch 2', end: true }),
429
+ },
430
+ }
431
+
432
+ const definition = Workflow.Patterns.fanout(slug, environment, [
433
+ { name: 'Branch1', definition: branch1 },
434
+ { name: 'Branch2', definition: branch2 },
435
+ ])
436
+
437
+ expect(definition.StartAt).toBe('Parallel')
438
+ expect(definition.States.Parallel.Type).toBe('Parallel')
439
+ const parallelState = definition.States.Parallel as { Type: 'Parallel', Branches: unknown[], End: boolean }
440
+ expect(parallelState.Branches).toHaveLength(2)
441
+ expect(parallelState.End).toBe(true)
442
+ })
443
+
444
+ test('should create map workflow pattern', () => {
445
+ const itemProcessor = {
446
+ StartAt: 'ProcessItem',
447
+ States: {
448
+ ProcessItem: Workflow.createPassState({ result: 'Processed', end: true }),
449
+ },
450
+ }
451
+
452
+ const definition = Workflow.Patterns.map(slug, environment, itemProcessor, 10)
453
+
454
+ expect(definition.StartAt).toBe('Map')
455
+ expect(definition.States.Map.Type).toBe('Map')
456
+ const mapState = definition.States.Map as { Type: 'Map', Iterator: unknown, MaxConcurrency: number, End: boolean }
457
+ expect(mapState.Iterator).toEqual(itemProcessor)
458
+ expect(mapState.MaxConcurrency).toBe(10)
459
+ expect(mapState.End).toBe(true)
460
+ })
461
+
462
+ test('should create error handling workflow pattern', () => {
463
+ const mainTask = Workflow.createLambdaTask(
464
+ 'arn:aws:lambda:us-east-1:123456789012:function:my-function',
465
+ )
466
+
467
+ const errorHandler = Workflow.createFailState('ProcessingError', 'Failed to process')
468
+
469
+ const definition = Workflow.Patterns.withErrorHandling(
470
+ slug,
471
+ environment,
472
+ mainTask,
473
+ errorHandler,
474
+ )
475
+
476
+ expect(definition.StartAt).toBe('Main')
477
+ const mainState = definition.States.Main as { Catch?: Array<{ Next: string }> }
478
+ expect(mainState.Catch).toHaveLength(1)
479
+ expect(mainState.Catch?.[0].Next).toBe('ErrorHandler')
480
+ expect(definition.States.ErrorHandler).toEqual(errorHandler)
481
+ expect(definition.States.Success.Type).toBe('Succeed')
482
+ })
483
+ })
484
+
485
+ describe('workflow Module - Integration with TemplateBuilder', () => {
486
+ test('should add state machine to template', () => {
487
+ const builder = new TemplateBuilder()
488
+
489
+ const definition = {
490
+ StartAt: 'HelloWorld',
491
+ States: {
492
+ HelloWorld: Workflow.createPassState({ result: 'Hello World', end: true }),
493
+ },
494
+ }
495
+
496
+ const { stateMachine, logicalId, role, roleLogicalId } = Workflow.createStateMachine({
497
+ slug,
498
+ environment,
499
+ definition,
500
+ })
501
+
502
+ if (role && roleLogicalId) {
503
+ builder.addResource(roleLogicalId, role)
504
+ }
505
+
506
+ builder.addResource(logicalId, stateMachine)
507
+
508
+ const template = builder.build()
509
+
510
+ expect(template.Resources[logicalId]).toBeDefined()
511
+ expect(template.Resources[logicalId].Type).toBe('AWS::StepFunctions::StateMachine')
512
+
513
+ if (roleLogicalId) {
514
+ expect(template.Resources[roleLogicalId]).toBeDefined()
515
+ expect(template.Resources[roleLogicalId].Type).toBe('AWS::IAM::Role')
516
+ }
517
+ })
518
+
519
+ test('should create complex workflow with multiple state types', () => {
520
+ const builder = new TemplateBuilder()
521
+
522
+ const definition = {
523
+ StartAt: 'Initialize',
524
+ States: {
525
+ Initialize: Workflow.createPassState({
526
+ result: { status: 'initialized' },
527
+ resultPath: '$.init',
528
+ next: 'Wait',
529
+ }),
530
+ Wait: Workflow.createWaitState({
531
+ seconds: 5,
532
+ next: 'ProcessItems',
533
+ }),
534
+ ProcessItems: Workflow.createMapState(
535
+ {
536
+ StartAt: 'ProcessItem',
537
+ States: {
538
+ ProcessItem: Workflow.createLambdaTask(
539
+ 'arn:aws:lambda:us-east-1:123456789012:function:processor',
540
+ { end: true },
541
+ ),
542
+ },
543
+ },
544
+ {
545
+ maxConcurrency: 5,
546
+ next: 'CheckResults',
547
+ },
548
+ ),
549
+ CheckResults: Workflow.createChoiceState(
550
+ [
551
+ {
552
+ Variable: '$.success',
553
+ BooleanEquals: true,
554
+ Next: 'Success',
555
+ },
556
+ {
557
+ Variable: '$.success',
558
+ BooleanEquals: false,
559
+ Next: 'Failed',
560
+ },
561
+ ],
562
+ ),
563
+ Success: Workflow.createSucceedState(),
564
+ Failed: Workflow.createFailState('ProcessingFailed', 'Items failed to process'),
565
+ },
566
+ }
567
+
568
+ const { stateMachine, logicalId, role, roleLogicalId } = Workflow.createStateMachine({
569
+ slug,
570
+ environment,
571
+ definition,
572
+ })
573
+
574
+ if (role && roleLogicalId) {
575
+ builder.addResource(roleLogicalId, role)
576
+ }
577
+
578
+ builder.addResource(logicalId, stateMachine)
579
+
580
+ const template = builder.build()
581
+ const parsedDefinition = JSON.parse(
582
+ template.Resources[logicalId]!.Properties!.DefinitionString as string,
583
+ )
584
+
585
+ expect(parsedDefinition.States.Initialize.Type).toBe('Pass')
586
+ expect(parsedDefinition.States.Wait.Type).toBe('Wait')
587
+ expect(parsedDefinition.States.ProcessItems.Type).toBe('Map')
588
+ expect(parsedDefinition.States.CheckResults.Type).toBe('Choice')
589
+ expect(parsedDefinition.States.Success.Type).toBe('Succeed')
590
+ expect(parsedDefinition.States.Failed.Type).toBe('Fail')
591
+ })
592
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "composite": true,
7
+ "declaration": true,
8
+ "declarationMap": true
9
+ },
10
+ "include": ["src/**/*"],
11
+ "exclude": ["node_modules", "dist"],
12
+ "references": [
13
+ { "path": "../types" },
14
+ { "path": "../aws-types" }
15
+ ]
16
+ }