reqon-dsl 0.2.0 → 0.3.0

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 (396) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +22 -0
  3. package/dist/ast/nodes.d.ts +83 -4
  4. package/dist/ast/nodes.js +14 -0
  5. package/dist/auth/circuit-breaker.js +7 -6
  6. package/dist/auth/rate-limiter.d.ts +4 -0
  7. package/dist/auth/rate-limiter.js +9 -16
  8. package/dist/cli.d.ts +13 -0
  9. package/dist/cli.js +84 -4
  10. package/dist/config/constants.d.ts +141 -0
  11. package/dist/config/constants.js +128 -0
  12. package/dist/config/index.d.ts +4 -0
  13. package/dist/config/index.js +4 -0
  14. package/dist/control/index.d.ts +2 -0
  15. package/dist/control/index.js +1 -0
  16. package/dist/control/server.d.ts +88 -0
  17. package/dist/control/server.js +238 -0
  18. package/dist/control/types.d.ts +55 -0
  19. package/dist/control/types.js +7 -0
  20. package/dist/debug/cli-debugger.d.ts +17 -0
  21. package/dist/debug/cli-debugger.js +180 -0
  22. package/dist/debug/controller.d.ts +94 -0
  23. package/dist/debug/controller.js +45 -0
  24. package/dist/debug/index.d.ts +6 -0
  25. package/dist/debug/index.js +5 -0
  26. package/dist/errors/index.d.ts +67 -0
  27. package/dist/errors/index.js +89 -1
  28. package/dist/execution/index.d.ts +1 -1
  29. package/dist/execution/state.d.ts +24 -0
  30. package/dist/index.d.ts +21 -1
  31. package/dist/index.js +33 -2
  32. package/dist/interpreter/context.d.ts +14 -0
  33. package/dist/interpreter/context.js +15 -0
  34. package/dist/interpreter/evaluator.d.ts +63 -1
  35. package/dist/interpreter/evaluator.js +186 -39
  36. package/dist/interpreter/executor.d.ts +70 -14
  37. package/dist/interpreter/executor.js +503 -174
  38. package/dist/interpreter/fetch-handler.d.ts +9 -0
  39. package/dist/interpreter/fetch-handler.js +133 -24
  40. package/dist/interpreter/http.d.ts +5 -0
  41. package/dist/interpreter/http.js +26 -12
  42. package/dist/interpreter/index.d.ts +3 -1
  43. package/dist/interpreter/index.js +2 -0
  44. package/dist/interpreter/pagination.d.ts +11 -2
  45. package/dist/interpreter/pagination.js +95 -31
  46. package/dist/interpreter/signals.d.ts +8 -0
  47. package/dist/interpreter/signals.js +12 -0
  48. package/dist/interpreter/source-manager.d.ts +75 -0
  49. package/dist/interpreter/source-manager.js +157 -0
  50. package/dist/interpreter/step-handlers/apply-handler.d.ts +29 -0
  51. package/dist/interpreter/step-handlers/apply-handler.js +79 -0
  52. package/dist/interpreter/step-handlers/for-handler.d.ts +13 -0
  53. package/dist/interpreter/step-handlers/for-handler.js +71 -4
  54. package/dist/interpreter/step-handlers/index.d.ts +4 -2
  55. package/dist/interpreter/step-handlers/index.js +4 -2
  56. package/dist/interpreter/step-handlers/match-handler.d.ts +9 -0
  57. package/dist/interpreter/step-handlers/match-handler.js +43 -16
  58. package/dist/interpreter/step-handlers/pause-handler.d.ts +52 -0
  59. package/dist/interpreter/step-handlers/pause-handler.js +87 -0
  60. package/dist/interpreter/step-handlers/store-handler.d.ts +11 -1
  61. package/dist/interpreter/step-handlers/store-handler.js +45 -13
  62. package/dist/interpreter/step-handlers/types.d.ts +3 -0
  63. package/dist/interpreter/step-handlers/validate-handler.d.ts +2 -1
  64. package/dist/interpreter/step-handlers/validate-handler.js +4 -2
  65. package/dist/interpreter/step-handlers/webhook-handler.d.ts +3 -0
  66. package/dist/interpreter/step-handlers/webhook-handler.js +18 -2
  67. package/dist/interpreter/store-manager.d.ts +46 -0
  68. package/dist/interpreter/store-manager.js +66 -0
  69. package/dist/lexer/index.d.ts +11 -4
  70. package/dist/lexer/index.js +11 -4
  71. package/dist/lexer/tokens.d.ts +17 -1
  72. package/dist/lexer/tokens.js +36 -0
  73. package/dist/mcp/index.d.ts +11 -0
  74. package/dist/mcp/index.js +11 -0
  75. package/dist/mcp/server.d.ts +17 -0
  76. package/dist/mcp/server.js +451 -0
  77. package/dist/oas/index.d.ts +2 -0
  78. package/dist/oas/index.js +1 -0
  79. package/dist/oas/mock-generator.d.ts +12 -0
  80. package/dist/oas/mock-generator.js +187 -0
  81. package/dist/observability/events.d.ts +244 -0
  82. package/dist/observability/events.js +90 -0
  83. package/dist/observability/index.d.ts +15 -0
  84. package/dist/observability/index.js +12 -0
  85. package/dist/observability/logger.d.ts +106 -0
  86. package/dist/observability/logger.js +259 -0
  87. package/dist/observability/otel.d.ts +135 -0
  88. package/dist/observability/otel.js +386 -0
  89. package/dist/parser/action-parser.d.ts +105 -0
  90. package/dist/parser/action-parser.js +645 -0
  91. package/dist/parser/expressions.d.ts +13 -0
  92. package/dist/parser/expressions.js +72 -2
  93. package/dist/parser/fetch-parser.d.ts +27 -0
  94. package/dist/parser/fetch-parser.js +269 -0
  95. package/dist/parser/index.d.ts +17 -0
  96. package/dist/parser/index.js +17 -0
  97. package/dist/parser/parser.d.ts +44 -46
  98. package/dist/parser/parser.js +122 -1070
  99. package/dist/parser/pipeline-parser.d.ts +12 -0
  100. package/dist/parser/pipeline-parser.js +52 -0
  101. package/dist/parser/schedule-parser.d.ts +7 -0
  102. package/dist/parser/schedule-parser.js +137 -0
  103. package/dist/parser/source-parser.d.ts +9 -0
  104. package/dist/parser/source-parser.js +151 -0
  105. package/dist/pause/index.d.ts +14 -0
  106. package/dist/pause/index.js +11 -0
  107. package/dist/pause/manager.d.ts +118 -0
  108. package/dist/pause/manager.js +245 -0
  109. package/dist/pause/state.d.ts +93 -0
  110. package/dist/pause/state.js +103 -0
  111. package/dist/pause/store.d.ts +61 -0
  112. package/dist/pause/store.js +156 -0
  113. package/dist/plugin.d.ts +9 -12
  114. package/dist/plugin.js +10 -13
  115. package/dist/stores/factory.d.ts +1 -1
  116. package/dist/stores/factory.js +3 -2
  117. package/dist/stores/file.d.ts +26 -0
  118. package/dist/stores/file.js +64 -10
  119. package/dist/stores/index.d.ts +16 -1
  120. package/dist/stores/index.js +16 -1
  121. package/dist/stores/memory.d.ts +4 -0
  122. package/dist/stores/memory.js +11 -0
  123. package/dist/stores/types.d.ts +17 -0
  124. package/dist/stores/types.js +12 -0
  125. package/dist/trace/index.d.ts +16 -0
  126. package/dist/trace/index.js +12 -0
  127. package/dist/trace/recorder.d.ts +71 -0
  128. package/dist/trace/recorder.js +144 -0
  129. package/dist/trace/replay.d.ts +132 -0
  130. package/dist/trace/replay.js +264 -0
  131. package/dist/trace/state.d.ts +102 -0
  132. package/dist/trace/state.js +86 -0
  133. package/dist/trace/store.d.ts +69 -0
  134. package/dist/trace/store.js +225 -0
  135. package/dist/utils/index.d.ts +1 -0
  136. package/dist/utils/index.js +1 -0
  137. package/dist/utils/type-guards.d.ts +58 -0
  138. package/dist/utils/type-guards.js +92 -0
  139. package/dist/webhook/server.js +7 -6
  140. package/package.json +55 -6
  141. package/.claude/settings.local.json +0 -31
  142. package/.claude/skills/api-integration.md +0 -125
  143. package/.claude/skills/database-schema.md +0 -51
  144. package/.claude/skills/dsl-design.md +0 -80
  145. package/.claude/skills/property-testing.md +0 -143
  146. package/.claude/skills/reqon/SKILL.md +0 -44
  147. package/.claude/skills/reqon/references/examples.md +0 -206
  148. package/.claude/skills/reqon/references/syntax.md +0 -263
  149. package/.claude/skills/vscode-extension.md +0 -113
  150. package/.github/dependabot.yml +0 -32
  151. package/.github/pull_request_template.md +0 -21
  152. package/.github/workflows/ci.yml +0 -174
  153. package/.github/workflows/release.yml +0 -73
  154. package/CLAUDE.md +0 -72
  155. package/CONTRIBUTING.md +0 -161
  156. package/TODO.md +0 -51
  157. package/dist/auth/auth.test.d.ts +0 -1
  158. package/dist/auth/auth.test.js +0 -255
  159. package/dist/errors/errors.test.d.ts +0 -1
  160. package/dist/errors/errors.test.js +0 -165
  161. package/dist/execution/execution.test.d.ts +0 -1
  162. package/dist/execution/execution.test.js +0 -246
  163. package/dist/integration.test.d.ts +0 -1
  164. package/dist/integration.test.js +0 -168
  165. package/dist/interpreter/evaluator.test.d.ts +0 -1
  166. package/dist/interpreter/evaluator.test.js +0 -512
  167. package/dist/interpreter/http.test.d.ts +0 -1
  168. package/dist/interpreter/http.test.js +0 -299
  169. package/dist/interpreter/progress.test.d.ts +0 -1
  170. package/dist/interpreter/progress.test.js +0 -216
  171. package/dist/interpreter/schema-matcher.test.d.ts +0 -1
  172. package/dist/interpreter/schema-matcher.test.js +0 -122
  173. package/dist/lexer/lexer.d.ts +0 -24
  174. package/dist/lexer/lexer.js +0 -264
  175. package/dist/lexer/lexer.test.d.ts +0 -1
  176. package/dist/lexer/lexer.test.js +0 -259
  177. package/dist/loader/loader.test.d.ts +0 -1
  178. package/dist/loader/loader.test.js +0 -287
  179. package/dist/oas/oas.test.d.ts +0 -1
  180. package/dist/oas/oas.test.js +0 -218
  181. package/dist/parser/expressions.test.d.ts +0 -1
  182. package/dist/parser/expressions.test.js +0 -378
  183. package/dist/parser/match.test.d.ts +0 -1
  184. package/dist/parser/match.test.js +0 -254
  185. package/dist/parser/parser.test.d.ts +0 -1
  186. package/dist/parser/parser.test.js +0 -333
  187. package/dist/parser/schedule.test.d.ts +0 -1
  188. package/dist/parser/schedule.test.js +0 -241
  189. package/dist/scheduler/cron-parser.test.d.ts +0 -1
  190. package/dist/scheduler/cron-parser.test.js +0 -188
  191. package/dist/stores/file.test.d.ts +0 -1
  192. package/dist/stores/file.test.js +0 -165
  193. package/dist/stores/memory.test.d.ts +0 -1
  194. package/dist/stores/memory.test.js +0 -157
  195. package/dist/stores/stores.test.d.ts +0 -1
  196. package/dist/stores/stores.test.js +0 -158
  197. package/dist/sync/sync.test.d.ts +0 -1
  198. package/dist/sync/sync.test.js +0 -221
  199. package/docusaurus/README.md +0 -41
  200. package/docusaurus/docs/advanced/execution-state.md +0 -283
  201. package/docusaurus/docs/advanced/extending-reqon.md +0 -388
  202. package/docusaurus/docs/advanced/multi-file-missions.md +0 -250
  203. package/docusaurus/docs/advanced/parallel-execution.md +0 -353
  204. package/docusaurus/docs/api-reference.md +0 -443
  205. package/docusaurus/docs/authentication/api-key.md +0 -339
  206. package/docusaurus/docs/authentication/basic.md +0 -276
  207. package/docusaurus/docs/authentication/bearer.md +0 -282
  208. package/docusaurus/docs/authentication/oauth2.md +0 -317
  209. package/docusaurus/docs/authentication/overview.md +0 -251
  210. package/docusaurus/docs/cli.md +0 -229
  211. package/docusaurus/docs/core-concepts/actions.md +0 -286
  212. package/docusaurus/docs/core-concepts/missions.md +0 -264
  213. package/docusaurus/docs/core-concepts/schemas.md +0 -353
  214. package/docusaurus/docs/core-concepts/sources.md +0 -339
  215. package/docusaurus/docs/core-concepts/stores.md +0 -332
  216. package/docusaurus/docs/dsl-syntax/expressions.md +0 -361
  217. package/docusaurus/docs/dsl-syntax/fetch.md +0 -293
  218. package/docusaurus/docs/dsl-syntax/for-loops.md +0 -324
  219. package/docusaurus/docs/dsl-syntax/map.md +0 -345
  220. package/docusaurus/docs/dsl-syntax/match.md +0 -387
  221. package/docusaurus/docs/dsl-syntax/pipelines.md +0 -397
  222. package/docusaurus/docs/dsl-syntax/validate.md +0 -401
  223. package/docusaurus/docs/error-handling/dead-letter-queues.md +0 -399
  224. package/docusaurus/docs/error-handling/flow-control.md +0 -337
  225. package/docusaurus/docs/error-handling/retry-strategies.md +0 -368
  226. package/docusaurus/docs/examples.md +0 -488
  227. package/docusaurus/docs/getting-started.md +0 -256
  228. package/docusaurus/docs/http/circuit-breaker.md +0 -401
  229. package/docusaurus/docs/http/incremental-sync.md +0 -394
  230. package/docusaurus/docs/http/pagination.md +0 -361
  231. package/docusaurus/docs/http/rate-limiting.md +0 -383
  232. package/docusaurus/docs/http/requests.md +0 -328
  233. package/docusaurus/docs/http/retry.md +0 -402
  234. package/docusaurus/docs/intro.md +0 -90
  235. package/docusaurus/docs/openapi/loading-specs.md +0 -305
  236. package/docusaurus/docs/openapi/operation-calls.md +0 -314
  237. package/docusaurus/docs/openapi/overview.md +0 -212
  238. package/docusaurus/docs/openapi/response-validation.md +0 -344
  239. package/docusaurus/docs/scheduling/cron.md +0 -305
  240. package/docusaurus/docs/scheduling/daemon-mode.md +0 -317
  241. package/docusaurus/docs/scheduling/intervals.md +0 -289
  242. package/docusaurus/docs/scheduling/overview.md +0 -231
  243. package/docusaurus/docs/stores/custom-adapters.md +0 -376
  244. package/docusaurus/docs/stores/file.md +0 -236
  245. package/docusaurus/docs/stores/memory.md +0 -193
  246. package/docusaurus/docs/stores/overview.md +0 -274
  247. package/docusaurus/docs/stores/postgrest.md +0 -316
  248. package/docusaurus/docusaurus.config.ts +0 -148
  249. package/docusaurus/package-lock.json +0 -18029
  250. package/docusaurus/package.json +0 -47
  251. package/docusaurus/sidebars.ts +0 -155
  252. package/docusaurus/src/components/HomepageFeatures/index.tsx +0 -105
  253. package/docusaurus/src/components/HomepageFeatures/styles.module.css +0 -12
  254. package/docusaurus/src/css/custom.css +0 -169
  255. package/docusaurus/src/pages/index.module.css +0 -48
  256. package/docusaurus/src/pages/index.tsx +0 -110
  257. package/docusaurus/src/pages/markdown-page.md +0 -7
  258. package/docusaurus/static/.nojekyll +0 -0
  259. package/docusaurus/static/img/docusaurus-social-card.jpg +0 -0
  260. package/docusaurus/static/img/docusaurus.png +0 -0
  261. package/docusaurus/static/img/favicon.ico +0 -0
  262. package/docusaurus/static/img/logo.svg +0 -10
  263. package/docusaurus/static/img/undraw_docusaurus_mountain.svg +0 -171
  264. package/docusaurus/static/img/undraw_docusaurus_react.svg +0 -170
  265. package/docusaurus/static/img/undraw_docusaurus_tree.svg +0 -40
  266. package/docusaurus/tsconfig.json +0 -8
  267. package/examples/README.md +0 -112
  268. package/examples/error-handling/README.md +0 -150
  269. package/examples/error-handling/payment-processor.vague +0 -287
  270. package/examples/github-sync/README.md +0 -74
  271. package/examples/github-sync/fetch-issues.vague +0 -47
  272. package/examples/github-sync/fetch-prs.vague +0 -40
  273. package/examples/github-sync/mission.vague +0 -101
  274. package/examples/github-sync/normalize.vague +0 -70
  275. package/examples/jsonplaceholder/README.md +0 -28
  276. package/examples/jsonplaceholder/posts.vague +0 -48
  277. package/examples/petstore/README.md +0 -35
  278. package/examples/petstore/openapi.yaml +0 -97
  279. package/examples/petstore/sync.vague +0 -52
  280. package/examples/temporal-comparison/README.md +0 -297
  281. package/examples/temporal-comparison/reconciliation.vague +0 -355
  282. package/examples/temporal-comparison/temporal/activities/index.ts +0 -8
  283. package/examples/temporal-comparison/temporal/activities/shipstation.ts +0 -225
  284. package/examples/temporal-comparison/temporal/activities/shopify.ts +0 -257
  285. package/examples/temporal-comparison/temporal/activities/storage.ts +0 -198
  286. package/examples/temporal-comparison/temporal/activities/stripe.ts +0 -169
  287. package/examples/temporal-comparison/temporal/activities/validation.ts +0 -205
  288. package/examples/temporal-comparison/temporal/client/schedule.ts +0 -218
  289. package/examples/temporal-comparison/temporal/config/retry.ts +0 -63
  290. package/examples/temporal-comparison/temporal/types/index.ts +0 -129
  291. package/examples/temporal-comparison/temporal/workers/main.ts +0 -130
  292. package/examples/temporal-comparison/temporal/workflows/orderReconciliation.ts +0 -262
  293. package/examples/xero/README.md +0 -88
  294. package/examples/xero/invoices.vague +0 -189
  295. package/src/api-integration.test.ts +0 -954
  296. package/src/ast/index.ts +0 -1
  297. package/src/ast/nodes.ts +0 -310
  298. package/src/auth/auth.test.ts +0 -326
  299. package/src/auth/circuit-breaker.test.ts +0 -390
  300. package/src/auth/circuit-breaker.ts +0 -379
  301. package/src/auth/credentials.test.ts +0 -273
  302. package/src/auth/credentials.ts +0 -246
  303. package/src/auth/index.ts +0 -40
  304. package/src/auth/oauth2-provider.ts +0 -177
  305. package/src/auth/rate-limiter.ts +0 -459
  306. package/src/auth/token-store.ts +0 -177
  307. package/src/auth/types.ts +0 -159
  308. package/src/benchmark/e2e.bench.ts +0 -288
  309. package/src/benchmark/evaluator.bench.ts +0 -331
  310. package/src/benchmark/fixtures.ts +0 -295
  311. package/src/benchmark/index.ts +0 -108
  312. package/src/benchmark/lexer.bench.ts +0 -69
  313. package/src/benchmark/parser.bench.ts +0 -103
  314. package/src/benchmark/resilience.bench.ts +0 -193
  315. package/src/benchmark/store.bench.ts +0 -147
  316. package/src/benchmark/utils.ts +0 -230
  317. package/src/cli.ts +0 -313
  318. package/src/errors/errors.test.ts +0 -234
  319. package/src/errors/index.ts +0 -223
  320. package/src/execution/execution.test.ts +0 -307
  321. package/src/execution/index.ts +0 -21
  322. package/src/execution/state.ts +0 -207
  323. package/src/execution/store.ts +0 -188
  324. package/src/index.ts +0 -169
  325. package/src/integration.test.ts +0 -192
  326. package/src/interpreter/context.ts +0 -57
  327. package/src/interpreter/evaluator.test.ts +0 -796
  328. package/src/interpreter/evaluator.ts +0 -245
  329. package/src/interpreter/executor.ts +0 -946
  330. package/src/interpreter/fetch-handler.ts +0 -302
  331. package/src/interpreter/http.test.ts +0 -423
  332. package/src/interpreter/http.ts +0 -308
  333. package/src/interpreter/index.ts +0 -32
  334. package/src/interpreter/pagination.ts +0 -207
  335. package/src/interpreter/progress.test.ts +0 -276
  336. package/src/interpreter/schema-matcher.test.ts +0 -160
  337. package/src/interpreter/schema-matcher.ts +0 -168
  338. package/src/interpreter/signals.ts +0 -73
  339. package/src/interpreter/step-handlers/for-handler.ts +0 -65
  340. package/src/interpreter/step-handlers/index.ts +0 -17
  341. package/src/interpreter/step-handlers/map-handler.ts +0 -24
  342. package/src/interpreter/step-handlers/match-handler.ts +0 -101
  343. package/src/interpreter/step-handlers/store-handler.ts +0 -78
  344. package/src/interpreter/step-handlers/types.ts +0 -17
  345. package/src/interpreter/step-handlers/validate-handler.ts +0 -30
  346. package/src/interpreter/step-handlers/webhook-handler.ts +0 -142
  347. package/src/lexer/index.ts +0 -18
  348. package/src/lexer/lexer.test.ts +0 -316
  349. package/src/lexer/tokens.ts +0 -179
  350. package/src/loader/index.ts +0 -288
  351. package/src/loader/loader.test.ts +0 -360
  352. package/src/oas/index.ts +0 -4
  353. package/src/oas/loader.ts +0 -126
  354. package/src/oas/oas.test.ts +0 -254
  355. package/src/oas/validator.ts +0 -299
  356. package/src/parser/base.ts +0 -124
  357. package/src/parser/expressions.test.ts +0 -525
  358. package/src/parser/expressions.ts +0 -314
  359. package/src/parser/index.ts +0 -3
  360. package/src/parser/match.test.ts +0 -296
  361. package/src/parser/parser.test.ts +0 -739
  362. package/src/parser/parser.ts +0 -1469
  363. package/src/parser/schedule.test.ts +0 -287
  364. package/src/parser/webhook.test.ts +0 -248
  365. package/src/plugin.ts +0 -83
  366. package/src/scheduler/cron-parser.test.ts +0 -236
  367. package/src/scheduler/cron-parser.ts +0 -236
  368. package/src/scheduler/index.ts +0 -10
  369. package/src/scheduler/scheduler.ts +0 -443
  370. package/src/scheduler/types.ts +0 -71
  371. package/src/stores/factory.ts +0 -104
  372. package/src/stores/file.test.ts +0 -276
  373. package/src/stores/file.ts +0 -211
  374. package/src/stores/index.ts +0 -6
  375. package/src/stores/memory.test.ts +0 -238
  376. package/src/stores/memory.ts +0 -63
  377. package/src/stores/postgrest.test.ts +0 -488
  378. package/src/stores/postgrest.ts +0 -263
  379. package/src/stores/stores.test.ts +0 -197
  380. package/src/stores/types.ts +0 -58
  381. package/src/sync/index.ts +0 -16
  382. package/src/sync/state.ts +0 -126
  383. package/src/sync/store.ts +0 -139
  384. package/src/sync/sync.test.ts +0 -271
  385. package/src/utils/async.ts +0 -10
  386. package/src/utils/file.ts +0 -106
  387. package/src/utils/index.ts +0 -14
  388. package/src/utils/logger.ts +0 -53
  389. package/src/utils/path.ts +0 -47
  390. package/src/webhook/index.ts +0 -15
  391. package/src/webhook/server.test.ts +0 -253
  392. package/src/webhook/server.ts +0 -389
  393. package/src/webhook/store.ts +0 -239
  394. package/src/webhook/types.ts +0 -93
  395. package/tsconfig.json +0 -17
  396. package/vitest.config.ts +0 -39
@@ -1,50 +1,87 @@
1
+ /**
2
+ * ---
3
+ * purpose: Mission executor - orchestrates pipeline execution
4
+ * inputs:
5
+ * - ReqonProgram - parsed AST
6
+ * - ExecutorConfig - auth, stores, callbacks, debug settings
7
+ * outputs:
8
+ * - ExecutionResult - success/errors, stores, duration
9
+ * related:
10
+ * - ./context.ts - execution state (variables, stores, sources)
11
+ * - ./evaluator.ts - expression evaluation
12
+ * - ./fetch-handler.ts - HTTP requests
13
+ * - ./step-handlers/ - individual step type handlers
14
+ * - ./source-manager.ts - auth provider management
15
+ * ---
16
+ */
1
17
  import { isParallelStage } from '../ast/nodes.js';
2
- import { createContext, setVariable } from './context.js';
18
+ import { createContext, childContext, setVariable } from './context.js';
3
19
  import { evaluate } from './evaluator.js';
4
- import { HttpClient, BearerAuthProvider, OAuth2AuthProvider } from './http.js';
5
- import { createStore, resolveStoreType } from '../stores/index.js';
6
- import { loadOAS } from '../oas/index.js';
20
+ import { SourceManager } from './source-manager.js';
21
+ import { StoreManager } from './store-manager.js';
7
22
  import { AdaptiveRateLimiter } from '../auth/rate-limiter.js';
8
23
  import { CircuitBreaker } from '../auth/circuit-breaker.js';
9
24
  import { createExecutionState, findResumePoint, FileExecutionStore, } from '../execution/index.js';
10
- import { FileSyncStore, } from '../sync/index.js';
25
+ import { FileSyncStore } from '../sync/index.js';
11
26
  import { FetchHandler } from './fetch-handler.js';
12
- import { ForHandler, MapHandler, ValidateHandler, StoreHandler, MatchHandler, WebhookHandler, SkipSignal, RetrySignal, JumpSignal, QueueSignal, } from './step-handlers/index.js';
27
+ import { ForHandler, MapHandler, ValidateHandler, StoreHandler, MatchHandler, ApplyHandler, WebhookHandler, PauseHandler, SkipSignal, AbortError, RetrySignal, JumpSignal, QueueSignal, } from './step-handlers/index.js';
28
+ import { createStructuredLogger } from '../observability/index.js';
29
+ import { PauseSignal } from './signals.js';
30
+ import { FileTraceStore, createTraceRecorder, } from '../trace/index.js';
31
+ import { FilePauseStore, createPauseManager, } from '../pause/index.js';
13
32
  export class MissionExecutor {
14
33
  config;
15
34
  ctx;
16
35
  errors = [];
17
36
  actionsRun = [];
18
- oasSources = new Map();
19
- sourceConfigs = new Map();
37
+ transforms = new Map();
20
38
  rateLimiter;
21
39
  circuitBreaker;
40
+ sourceManager;
41
+ storeManager;
22
42
  executionStore;
23
43
  executionState;
24
44
  syncStore;
25
45
  missionName;
46
+ eventEmitter;
47
+ logger;
48
+ stepIndex = 0;
49
+ debugController;
50
+ traceRecorder;
51
+ traceStore;
52
+ pauseManager;
53
+ pauseStore;
54
+ currentStageIndex = 0;
55
+ currentPauseId;
26
56
  constructor(config = {}) {
27
57
  this.config = config;
28
58
  this.ctx = createContext();
29
59
  this.rateLimiter = new AdaptiveRateLimiter();
30
60
  this.circuitBreaker = new CircuitBreaker();
61
+ // Initialize managers (logger set after verbose callbacks configured)
62
+ this.sourceManager = new SourceManager({ auth: config.auth, missionDir: config.missionDir }, { rateLimiter: this.rateLimiter, circuitBreaker: this.circuitBreaker });
63
+ this.storeManager = new StoreManager({
64
+ customStores: config.stores,
65
+ developmentMode: config.developmentMode,
66
+ dataDir: config.dataDir,
67
+ });
31
68
  // Set up rate limit callbacks with default logging if verbose
32
69
  const callbacks = config.rateLimitCallbacks ?? {};
33
70
  if (config.verbose && !callbacks.onRateLimited) {
34
71
  callbacks.onRateLimited = (event) => {
35
- console.log(`[Reqon] Rate limited on ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} - ` +
72
+ this.log(`Rate limited on ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} - ` +
36
73
  `waiting ${event.waitSeconds}s (strategy: ${event.strategy})`);
37
74
  };
38
75
  }
39
76
  if (config.verbose && !callbacks.onResumed) {
40
77
  callbacks.onResumed = (event) => {
41
- console.log(`[Reqon] Rate limit cleared for ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} ` +
78
+ this.log(`Rate limit cleared for ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} ` +
42
79
  `(waited ${event.waitedSeconds}s)`);
43
80
  };
44
81
  }
45
82
  if (config.verbose && !callbacks.onWaiting) {
46
83
  callbacks.onWaiting = (event) => {
47
- console.log(`[Reqon] Still waiting for ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} - ` +
84
+ this.log(`Still waiting for ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} - ` +
48
85
  `${event.waitSeconds}s remaining (elapsed: ${event.elapsedSeconds}s)`);
49
86
  };
50
87
  }
@@ -53,33 +90,71 @@ export class MissionExecutor {
53
90
  const cbCallbacks = config.circuitBreakerCallbacks ?? {};
54
91
  if (config.verbose && !cbCallbacks.onOpen) {
55
92
  cbCallbacks.onOpen = (event) => {
56
- console.log(`[Reqon] Circuit breaker OPEN for ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} - ` +
93
+ this.log(`Circuit breaker OPEN for ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} - ` +
57
94
  `${event.failures} failures (${event.reason ?? 'threshold exceeded'})`);
58
95
  };
59
96
  }
60
97
  if (config.verbose && !cbCallbacks.onHalfOpen) {
61
98
  cbCallbacks.onHalfOpen = (event) => {
62
- console.log(`[Reqon] Circuit breaker HALF-OPEN for ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} - ` +
99
+ this.log(`Circuit breaker HALF-OPEN for ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} - ` +
63
100
  `testing recovery`);
64
101
  };
65
102
  }
66
103
  if (config.verbose && !cbCallbacks.onClose) {
67
104
  cbCallbacks.onClose = (event) => {
68
- console.log(`[Reqon] Circuit breaker CLOSED for ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} - ` +
105
+ this.log(`Circuit breaker CLOSED for ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} - ` +
69
106
  `recovery successful`);
70
107
  };
71
108
  }
72
109
  if (config.verbose && !cbCallbacks.onRejected) {
73
110
  cbCallbacks.onRejected = (event) => {
74
- console.log(`[Reqon] Request REJECTED by circuit breaker for ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} - ` +
111
+ this.log(`Request REJECTED by circuit breaker for ${event.source}${event.endpoint ? `:${event.endpoint}` : ''} - ` +
75
112
  `retry in ${Math.ceil(event.nextAttemptIn / 1000)}s`);
76
113
  };
77
114
  }
78
115
  this.circuitBreaker.setCallbacks(cbCallbacks);
79
116
  // Initialize execution store if persistence enabled
80
117
  if (config.persistState) {
81
- this.executionStore = config.executionStore ?? new FileExecutionStore(`${config.dataDir ?? '.reqon-data'}/executions`);
118
+ this.executionStore =
119
+ config.executionStore ??
120
+ new FileExecutionStore(`${config.dataDir ?? '.reqon-data'}/executions`);
121
+ }
122
+ // Initialize event emitter if provided
123
+ this.eventEmitter = config.eventEmitter;
124
+ // Initialize logger if verbose or provided
125
+ if (config.logger) {
126
+ this.logger = config.logger;
127
+ }
128
+ else if (config.verbose) {
129
+ this.logger = createStructuredLogger({
130
+ prefix: 'Reqon',
131
+ level: 'debug',
132
+ context: {},
133
+ });
82
134
  }
135
+ // Update managers with log function now that logger is configured
136
+ this.sourceManager = new SourceManager({ auth: config.auth, missionDir: config.missionDir, log: (msg) => this.log(msg) }, { rateLimiter: this.rateLimiter, circuitBreaker: this.circuitBreaker });
137
+ this.storeManager = new StoreManager({
138
+ customStores: config.stores,
139
+ developmentMode: config.developmentMode,
140
+ dataDir: config.dataDir,
141
+ log: (msg) => this.log(msg),
142
+ });
143
+ // Initialize debug controller if provided
144
+ this.debugController = config.debugController;
145
+ // Initialize trace store
146
+ this.traceStore =
147
+ config.traceStore ?? new FileTraceStore(`${config.dataDir ?? '.reqon-data'}/traces`);
148
+ // Initialize pause store and manager
149
+ this.pauseStore =
150
+ config.pauseStore ?? new FilePauseStore(`${config.dataDir ?? '.reqon-data'}/pauses`);
151
+ this.pauseManager =
152
+ config.pauseManager ??
153
+ createPauseManager({
154
+ store: this.pauseStore,
155
+ webhookServer: config.webhookServer,
156
+ log: (msg) => this.log(msg),
157
+ });
83
158
  }
84
159
  async execute(program) {
85
160
  const startTime = Date.now();
@@ -96,6 +171,18 @@ export class MissionExecutor {
96
171
  }
97
172
  // Initialize or resume execution state
98
173
  await this.initializeExecutionState(mission);
174
+ // Initialize trace recorder if tracing is enabled
175
+ if (mission.trace && this.traceStore && this.executionState) {
176
+ this.traceRecorder = createTraceRecorder({
177
+ executionId: this.executionState.id,
178
+ mission: mission.name,
179
+ mode: mission.trace.mode,
180
+ store: this.traceStore,
181
+ metadata: this.config.metadata,
182
+ streaming: true, // Stream snapshots as they happen
183
+ });
184
+ this.log(`Tracing enabled (mode: ${mission.trace.mode})`);
185
+ }
99
186
  try {
100
187
  await this.executeMission(mission);
101
188
  // Mark execution as completed
@@ -107,22 +194,32 @@ export class MissionExecutor {
107
194
  }
108
195
  }
109
196
  catch (error) {
110
- this.errors.push({
111
- action: 'mission',
112
- step: 'execute',
113
- message: error.message,
114
- details: error,
115
- });
116
- // Mark execution as failed
117
- if (this.executionState) {
118
- this.executionState.status = 'failed';
119
- this.executionState.completedAt = new Date();
120
- this.executionState.duration = Date.now() - startTime;
121
- await this.saveExecutionState();
197
+ // PauseSignal is not an error - execution was intentionally paused
198
+ if (error instanceof PauseSignal) {
199
+ this.log('Execution paused');
200
+ this.currentPauseId = error.pauseId;
201
+ // State is already set to 'paused' in checkPause() or pause handler
202
+ // Don't record as error, just let execution end
203
+ }
204
+ else {
205
+ this.errors.push({
206
+ action: 'mission',
207
+ step: 'execute',
208
+ message: error.message,
209
+ details: error,
210
+ });
211
+ // Mark execution as failed
212
+ if (this.executionState) {
213
+ this.executionState.status = 'failed';
214
+ this.executionState.completedAt = new Date();
215
+ this.executionState.duration = Date.now() - startTime;
216
+ await this.saveExecutionState();
217
+ }
122
218
  }
123
219
  }
124
220
  const duration = Date.now() - startTime;
125
- const success = this.errors.length === 0;
221
+ const isPaused = this.executionState?.status === 'paused';
222
+ const success = this.errors.length === 0 && !isPaused;
126
223
  // Emit onExecutionComplete callback - count stages in a single pass
127
224
  const stageCounts = this.executionState?.stages.reduce((acc, s) => {
128
225
  if (s.status === 'completed')
@@ -142,6 +239,34 @@ export class MissionExecutor {
142
239
  stagesFailed,
143
240
  errors: this.errors,
144
241
  });
242
+ // Emit mission.complete, mission.paused, or mission.failed event
243
+ if (isPaused) {
244
+ this.eventEmitter?.emit('mission.paused', {
245
+ stagesCompleted,
246
+ executionId: this.executionState?.id,
247
+ });
248
+ }
249
+ else if (success) {
250
+ this.eventEmitter?.emit('mission.complete', {
251
+ success: true,
252
+ stagesCompleted,
253
+ stagesFailed,
254
+ stagesSkipped: this.executionState?.stages.filter((s) => s.status === 'skipped').length ?? 0,
255
+ errorCount: this.errors.length,
256
+ });
257
+ }
258
+ else {
259
+ const failedStage = this.executionState?.stages.find((s) => s.status === 'failed');
260
+ this.eventEmitter?.emit('mission.failed', {
261
+ error: this.errors[0]?.message ?? 'Unknown error',
262
+ failedStage: failedStage?.action,
263
+ stagesCompleted,
264
+ });
265
+ }
266
+ // Finalize trace if enabled
267
+ if (this.traceRecorder) {
268
+ await this.traceRecorder.finalize(success);
269
+ }
145
270
  return {
146
271
  success,
147
272
  duration,
@@ -150,6 +275,8 @@ export class MissionExecutor {
150
275
  stores: this.ctx.stores,
151
276
  executionId: this.executionState?.id,
152
277
  state: this.executionState,
278
+ traceId: this.traceRecorder ? this.executionState?.id : undefined,
279
+ pauseId: this.currentPauseId,
153
280
  };
154
281
  }
155
282
  async initializeExecutionState(mission) {
@@ -190,6 +317,13 @@ export class MissionExecutor {
190
317
  isResume,
191
318
  metadata: this.config.metadata,
192
319
  });
320
+ // Emit mission.start event
321
+ this.eventEmitter?.emit('mission.start', {
322
+ stageCount: mission.pipeline.stages.length,
323
+ isResume,
324
+ resumeFromStage: isResume ? findResumePoint(this.executionState) : undefined,
325
+ metadata: this.config.metadata,
326
+ });
193
327
  }
194
328
  async saveExecutionState() {
195
329
  if (this.executionStore && this.executionState) {
@@ -227,20 +361,23 @@ export class MissionExecutor {
227
361
  this.log(`Executing mission: ${mission.name}`);
228
362
  this.missionName = mission.name;
229
363
  // Initialize sync store
230
- this.syncStore = this.config.syncStore ?? new FileSyncStore(mission.name, `${this.config.dataDir ?? '.reqon-data'}/sync`);
231
- // Initialize sources (HTTP clients)
232
- for (const source of mission.sources) {
233
- await this.initializeSource(source);
234
- }
235
- // Initialize stores
236
- for (const store of mission.stores) {
237
- await this.initializeStore(store);
238
- }
364
+ this.syncStore =
365
+ this.config.syncStore ??
366
+ new FileSyncStore(mission.name, `${this.config.dataDir ?? '.reqon-data'}/sync`);
367
+ // Initialize sources using SourceManager
368
+ await this.sourceManager.initializeSources(mission.sources, this.ctx);
369
+ // Initialize stores using StoreManager
370
+ await this.storeManager.initializeStores(mission.stores, this.ctx);
239
371
  // Initialize schemas (for match step schema matching)
240
372
  for (const schema of mission.schemas) {
241
373
  this.ctx.schemas.set(schema.name, schema);
242
374
  this.log(`Registered schema: ${schema.name}`);
243
375
  }
376
+ // Initialize transforms
377
+ for (const transform of mission.transforms) {
378
+ this.transforms.set(transform.name, transform);
379
+ this.log(`Registered transform: ${transform.name}`);
380
+ }
244
381
  // Build action lookup
245
382
  const actions = new Map();
246
383
  for (const action of mission.actions) {
@@ -256,6 +393,8 @@ export class MissionExecutor {
256
393
  // Execute pipeline
257
394
  for (let i = 0; i < mission.pipeline.stages.length; i++) {
258
395
  const stage = mission.pipeline.stages[i];
396
+ // Check for pause request at safe point (between stages)
397
+ await this.checkPause();
259
398
  // Skip already completed stages when resuming
260
399
  if (i < resumeIndex) {
261
400
  this.log(`Skipping ${this.getStageName(stage)} (already completed)`);
@@ -268,9 +407,12 @@ export class MissionExecutor {
268
407
  this.log(`Skipping ${this.getStageName(stage)} (condition not met)`);
269
408
  this.updateStageState(i, { status: 'skipped' });
270
409
  await this.saveExecutionState();
410
+ this.updateControlServerState();
271
411
  continue;
272
412
  }
273
413
  }
414
+ // Track current stage index for pause handler
415
+ this.currentStageIndex = i;
274
416
  // Execute stage (parallel or sequential)
275
417
  if (isParallelStage(stage)) {
276
418
  await this.executeParallelStage(i, stage, actions, mission);
@@ -278,6 +420,8 @@ export class MissionExecutor {
278
420
  else if (stage.action) {
279
421
  await this.executeSequentialStage(i, stage.action, actions, mission);
280
422
  }
423
+ // Update control server with latest state after each stage
424
+ this.updateControlServerState();
281
425
  }
282
426
  }
283
427
  getStageName(stage) {
@@ -303,6 +447,13 @@ export class MissionExecutor {
303
447
  stageName: actionName,
304
448
  totalStages: mission.pipeline.stages.length,
305
449
  });
450
+ // Emit stage.start event
451
+ this.eventEmitter?.emit('stage.start', {
452
+ stageIndex,
453
+ stageName: actionName,
454
+ totalStages: mission.pipeline.stages.length,
455
+ isParallel: false,
456
+ });
306
457
  try {
307
458
  await this.executeAction(action);
308
459
  this.actionsRun.push(action.name);
@@ -319,6 +470,12 @@ export class MissionExecutor {
319
470
  success: true,
320
471
  duration: Date.now() - stageStartTime,
321
472
  });
473
+ // Emit stage.complete event
474
+ this.eventEmitter?.emit('stage.complete', {
475
+ stageIndex,
476
+ stageName: actionName,
477
+ success: true,
478
+ });
322
479
  }
323
480
  catch (error) {
324
481
  // Mark stage as failed
@@ -338,6 +495,13 @@ export class MissionExecutor {
338
495
  duration: Date.now() - stageStartTime,
339
496
  error: error.message,
340
497
  });
498
+ // Emit stage.complete event (failure)
499
+ this.eventEmitter?.emit('stage.complete', {
500
+ stageIndex,
501
+ stageName: actionName,
502
+ success: false,
503
+ error: error.message,
504
+ });
341
505
  throw error; // Re-throw to stop execution
342
506
  }
343
507
  }
@@ -365,10 +529,18 @@ export class MissionExecutor {
365
529
  stageName,
366
530
  totalStages: mission.pipeline.stages.length,
367
531
  });
532
+ // Emit stage.start event (parallel)
533
+ this.eventEmitter?.emit('stage.start', {
534
+ stageIndex,
535
+ stageName,
536
+ totalStages: mission.pipeline.stages.length,
537
+ isParallel: true,
538
+ parallelActions: actionNames,
539
+ });
368
540
  this.log(`Executing parallel stage: ${stageName}`);
369
541
  try {
370
542
  // Execute all actions in parallel
371
- const results = await Promise.allSettled(actionDefs.map(action => this.executeAction(action)));
543
+ const results = await Promise.allSettled(actionDefs.map((action) => this.executeAction(action)));
372
544
  // Check for failures
373
545
  const failures = [];
374
546
  for (let i = 0; i < results.length; i++) {
@@ -381,7 +553,7 @@ export class MissionExecutor {
381
553
  }
382
554
  }
383
555
  if (failures.length > 0) {
384
- const errorMsg = failures.map(f => `${f.name}: ${f.error.message}`).join('; ');
556
+ const errorMsg = failures.map((f) => `${f.name}: ${f.error.message}`).join('; ');
385
557
  throw new Error(`Parallel stage failed: ${errorMsg}`);
386
558
  }
387
559
  // Mark stage as completed
@@ -397,6 +569,12 @@ export class MissionExecutor {
397
569
  success: true,
398
570
  duration: Date.now() - stageStartTime,
399
571
  });
572
+ // Emit stage.complete event (success)
573
+ this.eventEmitter?.emit('stage.complete', {
574
+ stageIndex,
575
+ stageName,
576
+ success: true,
577
+ });
400
578
  }
401
579
  catch (error) {
402
580
  // Mark stage as failed
@@ -416,154 +594,129 @@ export class MissionExecutor {
416
594
  duration: Date.now() - stageStartTime,
417
595
  error: error.message,
418
596
  });
419
- throw error; // Re-throw to stop execution
420
- }
421
- }
422
- async initializeSource(source) {
423
- // Store source config for later reference
424
- this.sourceConfigs.set(source.name, source);
425
- const authConfig = this.config.auth?.[source.name];
426
- let authProvider;
427
- if (authConfig) {
428
- if (authConfig.type === 'bearer' && authConfig.token) {
429
- authProvider = new BearerAuthProvider(authConfig.token);
430
- }
431
- else if (authConfig.type === 'oauth2' && authConfig.accessToken) {
432
- authProvider = new OAuth2AuthProvider({
433
- accessToken: authConfig.accessToken,
434
- refreshToken: authConfig.refreshToken,
435
- tokenEndpoint: authConfig.tokenEndpoint,
436
- clientId: authConfig.clientId,
437
- clientSecret: authConfig.clientSecret,
438
- });
439
- }
440
- }
441
- // If source has OAS spec, load it
442
- let baseUrl = source.config.base;
443
- if (source.specPath) {
444
- try {
445
- const oasSource = await loadOAS(source.specPath);
446
- this.oasSources.set(source.name, oasSource);
447
- // Use base URL from OAS if not explicitly provided
448
- if (!baseUrl) {
449
- baseUrl = oasSource.baseUrl;
450
- }
451
- this.log(`Loaded OAS spec for ${source.name}: ${oasSource.operations.size} operations`);
452
- }
453
- catch (error) {
454
- throw new Error(`Failed to load OAS spec for ${source.name}: ${error.message}`);
455
- }
456
- }
457
- if (!baseUrl) {
458
- throw new Error(`Source ${source.name} has no base URL (provide 'base' or OAS spec with servers)`);
459
- }
460
- // Configure rate limiter for this source
461
- if (source.config.rateLimit) {
462
- this.rateLimiter.configure(source.name, {
463
- strategy: source.config.rateLimit.strategy,
464
- maxWait: source.config.rateLimit.maxWait,
465
- fallbackRpm: source.config.rateLimit.fallbackRpm,
466
- });
467
- this.log(`Rate limit config for ${source.name}: strategy=${source.config.rateLimit.strategy ?? 'pause'}, ` +
468
- `maxWait=${source.config.rateLimit.maxWait ?? 300}s`);
469
- }
470
- // Configure circuit breaker for this source
471
- if (source.config.circuitBreaker) {
472
- this.circuitBreaker.configure(source.name, {
473
- failureThreshold: source.config.circuitBreaker.failureThreshold,
474
- // Convert seconds to milliseconds for the circuit breaker
475
- resetTimeout: source.config.circuitBreaker.resetTimeout
476
- ? source.config.circuitBreaker.resetTimeout * 1000
477
- : undefined,
478
- successThreshold: source.config.circuitBreaker.successThreshold,
479
- failureWindow: source.config.circuitBreaker.failureWindow
480
- ? source.config.circuitBreaker.failureWindow * 1000
481
- : undefined,
597
+ // Emit stage.complete event (failure)
598
+ this.eventEmitter?.emit('stage.complete', {
599
+ stageIndex,
600
+ stageName,
601
+ success: false,
602
+ error: error.message,
482
603
  });
483
- this.log(`Circuit breaker config for ${source.name}: ` +
484
- `failureThreshold=${source.config.circuitBreaker.failureThreshold ?? 5}, ` +
485
- `resetTimeout=${source.config.circuitBreaker.resetTimeout ?? 30}s`);
486
- }
487
- const client = new HttpClient({
488
- baseUrl,
489
- auth: authProvider,
490
- rateLimiter: this.rateLimiter,
491
- circuitBreaker: this.circuitBreaker,
492
- sourceName: source.name,
493
- });
494
- this.ctx.sources.set(source.name, client);
495
- this.log(`Initialized source: ${source.name}`);
496
- }
497
- async initializeStore(store) {
498
- // Check for custom store adapter
499
- if (this.config.stores?.[store.name]) {
500
- this.ctx.stores.set(store.name, this.config.stores[store.name]);
501
- this.log(`Initialized store: ${store.name} (custom adapter)`);
502
- return;
604
+ throw error; // Re-throw to stop execution
503
605
  }
504
- // Use store factory to create appropriate adapter
505
- const developmentMode = this.config.developmentMode ?? true;
506
- const storeType = resolveStoreType(store.storeType, developmentMode);
507
- const adapter = createStore({
508
- type: storeType,
509
- name: store.target,
510
- baseDir: this.config.dataDir,
511
- });
512
- this.ctx.stores.set(store.name, adapter);
513
- this.log(`Initialized store: ${store.name} (${storeType}${storeType !== store.storeType ? ` <- ${store.storeType}` : ''})`);
514
606
  }
515
607
  async executeAction(action) {
516
608
  this.log(`Executing action: ${action.name}`);
609
+ // Create a child context for this action with its own response scope
610
+ // This allows parallel actions to have independent response values
611
+ const actionCtx = childContext(this.ctx);
517
612
  for (const step of action.steps) {
518
- await this.executeStep(step, action.name);
613
+ await this.executeStep(step, action.name, actionCtx);
519
614
  }
520
615
  }
521
616
  async executeStep(step, actionName, ctx) {
522
617
  // Use provided context or default to this.ctx
618
+ // NOTE: ctx is used for action-scoped operations (response, variables)
619
+ // this.ctx is still used for mission-level resources (stores, sources)
523
620
  const execCtx = ctx ?? this.ctx;
524
- const originalCtx = this.ctx;
525
- // Temporarily use the provided context
526
- if (ctx) {
527
- this.ctx = ctx;
621
+ // Track step index for events
622
+ const currentStepIndex = this.stepIndex++;
623
+ const stepType = this.getStepType(step.type);
624
+ // Emit step.start event
625
+ this.eventEmitter?.emit('step.start', {
626
+ actionName,
627
+ stepIndex: currentStepIndex,
628
+ stepType,
629
+ });
630
+ const stepStartTime = Date.now();
631
+ // Record trace snapshot before step
632
+ if (this.traceRecorder) {
633
+ await this.traceRecorder.recordBeforeStep(actionName, currentStepIndex, stepType, execCtx);
634
+ }
635
+ // Debug pause point - before executing step
636
+ if (this.debugController) {
637
+ const location = {
638
+ action: actionName,
639
+ stepIndex: currentStepIndex,
640
+ stepType,
641
+ };
642
+ if (this.debugController.shouldPause(location)) {
643
+ const snapshot = this.captureDebugSnapshot(actionName, currentStepIndex, stepType, { type: 'step' }, execCtx);
644
+ const command = await this.debugController.pause(snapshot);
645
+ this.handleDebugCommand(command);
646
+ }
528
647
  }
529
648
  try {
530
649
  switch (step.type) {
531
650
  case 'FetchStep':
532
- await this.executeFetch(step);
651
+ await this.executeFetch(step, execCtx);
533
652
  break;
534
653
  case 'ForStep':
535
- await this.executeFor(step, actionName);
654
+ await this.executeFor(step, actionName, execCtx);
536
655
  break;
537
656
  case 'MapStep':
538
- await this.executeMap(step);
657
+ await this.executeMap(step, execCtx);
539
658
  break;
540
659
  case 'ValidateStep':
541
- await this.executeValidate(step);
660
+ await this.executeValidate(step, execCtx);
542
661
  break;
543
662
  case 'StoreStep':
544
- await this.executeStore(step);
663
+ await this.executeStore(step, execCtx);
545
664
  break;
546
665
  case 'MatchStep':
547
- await this.executeMatch(step, actionName);
666
+ await this.executeMatch(step, actionName, execCtx);
548
667
  break;
549
668
  case 'LetStep':
550
- await this.executeLet(step);
669
+ await this.executeLet(step, execCtx);
670
+ break;
671
+ case 'ApplyStep':
672
+ await this.executeApply(step, execCtx);
551
673
  break;
552
674
  case 'WebhookStep':
553
- await this.executeWebhook(step);
675
+ await this.executeWebhook(step, execCtx);
676
+ break;
677
+ case 'PauseStep':
678
+ await this.executePause(step, actionName, currentStepIndex, execCtx);
554
679
  break;
555
680
  default:
556
681
  throw new Error(`Unknown step type: ${step.type}`);
557
682
  }
683
+ const stepDuration = Date.now() - stepStartTime;
684
+ // Record trace snapshot after step
685
+ if (this.traceRecorder) {
686
+ await this.traceRecorder.recordAfterStep(actionName, currentStepIndex, stepType, execCtx, stepDuration);
687
+ }
688
+ // Emit step.complete event (success)
689
+ this.eventEmitter?.emit('step.complete', {
690
+ actionName,
691
+ stepIndex: currentStepIndex,
692
+ stepType,
693
+ success: true,
694
+ });
558
695
  }
559
696
  catch (error) {
560
697
  // Re-throw flow control signals without recording as errors
561
698
  if (error instanceof SkipSignal ||
562
699
  error instanceof RetrySignal ||
563
700
  error instanceof JumpSignal ||
564
- error instanceof QueueSignal) {
701
+ error instanceof QueueSignal ||
702
+ error instanceof PauseSignal) {
703
+ // Emit step.complete for flow control (not an error)
704
+ this.eventEmitter?.emit('step.complete', {
705
+ actionName,
706
+ stepIndex: currentStepIndex,
707
+ stepType,
708
+ success: true, // Flow control is not a failure
709
+ });
565
710
  throw error;
566
711
  }
712
+ // Emit step.complete event (failure)
713
+ this.eventEmitter?.emit('step.complete', {
714
+ actionName,
715
+ stepIndex: currentStepIndex,
716
+ stepType,
717
+ success: false,
718
+ error: error.message,
719
+ });
567
720
  // AbortError is a controlled abort, still record it
568
721
  this.errors.push({
569
722
  action: actionName,
@@ -573,91 +726,267 @@ export class MissionExecutor {
573
726
  });
574
727
  throw error;
575
728
  }
576
- finally {
577
- // Restore original context
578
- if (ctx) {
579
- this.ctx = originalCtx;
580
- }
581
- }
582
729
  }
583
- async executeFetch(step) {
730
+ async executeFetch(step, ctx) {
584
731
  const fetchHandler = new FetchHandler({
585
- ctx: this.ctx,
586
- oasSources: this.oasSources,
587
- sourceConfigs: this.sourceConfigs,
732
+ ctx,
733
+ oasSources: this.sourceManager.getAllOASSources(),
734
+ sourceConfigs: this.sourceManager.getAllSourceConfigs(),
588
735
  syncStore: this.syncStore,
589
736
  missionName: this.missionName,
590
737
  executionId: this.executionState?.id,
591
738
  dryRun: this.config.dryRun,
592
739
  log: (msg) => this.log(msg),
740
+ emit: this.eventEmitter
741
+ ? (type, payload) => this.eventEmitter.emit(type, payload)
742
+ : undefined,
593
743
  });
594
744
  const result = await fetchHandler.execute(step);
595
- this.ctx.response = result.data;
745
+ ctx.response = result.data;
596
746
  // Update sync checkpoint after successful fetch
597
747
  if (result.checkpointKey && this.syncStore) {
598
748
  await fetchHandler.recordCheckpoint(result.checkpointKey, step, result.data);
599
749
  }
600
750
  }
601
- async executeFor(step, actionName) {
751
+ async executeFor(step, actionName, ctx) {
602
752
  const handler = new ForHandler({
603
- ctx: this.ctx,
753
+ ctx,
604
754
  log: (msg) => this.log(msg),
755
+ emit: this.eventEmitter
756
+ ? (type, payload) => this.eventEmitter.emit(type, payload)
757
+ : undefined,
605
758
  executeStep: (s, a, c) => this.executeStep(s, a, c),
606
759
  actionName,
760
+ debugController: this.debugController,
761
+ captureDebugSnapshot: this.debugController
762
+ ? (action, stepIndex, stepType, pauseReason, ctx) => this.captureDebugSnapshot(action, stepIndex, stepType, pauseReason, ctx)
763
+ : undefined,
764
+ handleDebugCommand: this.debugController
765
+ ? (cmd) => this.handleDebugCommand(cmd)
766
+ : undefined,
767
+ checkPause: this.config.controlServer ? () => this.checkPause() : undefined,
607
768
  });
608
769
  await handler.execute(step);
609
770
  }
610
- async executeMap(step) {
771
+ async executeMap(step, ctx) {
611
772
  const handler = new MapHandler({
612
- ctx: this.ctx,
773
+ ctx,
613
774
  log: (msg) => this.log(msg),
775
+ emit: this.eventEmitter
776
+ ? (type, payload) => this.eventEmitter.emit(type, payload)
777
+ : undefined,
614
778
  });
615
779
  await handler.execute(step);
616
780
  }
617
- async executeValidate(step) {
781
+ async executeValidate(step, ctx) {
618
782
  const handler = new ValidateHandler({
619
- ctx: this.ctx,
783
+ ctx,
620
784
  log: (msg) => this.log(msg),
785
+ emit: this.eventEmitter
786
+ ? (type, payload) => this.eventEmitter.emit(type, payload)
787
+ : undefined,
621
788
  });
622
789
  await handler.execute(step);
623
790
  }
624
- async executeStore(step) {
791
+ async executeStore(step, ctx) {
625
792
  const handler = new StoreHandler({
626
- ctx: this.ctx,
793
+ ctx,
627
794
  log: (msg) => this.log(msg),
795
+ emit: this.eventEmitter
796
+ ? (type, payload) => this.eventEmitter.emit(type, payload)
797
+ : undefined,
628
798
  });
629
799
  await handler.execute(step);
630
800
  }
631
- async executeMatch(step, actionName) {
801
+ async executeMatch(step, actionName, ctx) {
632
802
  const handler = new MatchHandler({
633
- ctx: this.ctx,
803
+ ctx,
634
804
  log: (msg) => this.log(msg),
805
+ emit: this.eventEmitter
806
+ ? (type, payload) => this.eventEmitter.emit(type, payload)
807
+ : undefined,
635
808
  executeStep: (s, a, c) => this.executeStep(s, a, c),
636
809
  actionName,
810
+ debugController: this.debugController,
811
+ captureDebugSnapshot: this.debugController
812
+ ? (action, stepIndex, stepType, pauseReason, execCtx) => this.captureDebugSnapshot(action, stepIndex, stepType, pauseReason, execCtx)
813
+ : undefined,
814
+ handleDebugCommand: this.debugController
815
+ ? (cmd) => this.handleDebugCommand(cmd)
816
+ : undefined,
637
817
  });
638
818
  await handler.execute(step);
639
819
  // Flow control signals (SkipSignal, RetrySignal, etc.) will propagate up
640
820
  }
641
- async executeLet(step) {
642
- const value = evaluate(step.value, this.ctx);
643
- setVariable(this.ctx, step.name, value);
821
+ async executeLet(step, ctx) {
822
+ const value = evaluate(step.value, ctx);
823
+ setVariable(ctx, step.name, value);
644
824
  this.log(`Set variable '${step.name}' = ${JSON.stringify(value)}`);
645
825
  }
646
- async executeWebhook(step) {
826
+ async executeApply(step, ctx) {
827
+ const transform = this.transforms.get(step.transform);
828
+ if (!transform) {
829
+ throw new Error(`Transform '${step.transform}' not found`);
830
+ }
831
+ const handler = new ApplyHandler({
832
+ ctx,
833
+ log: (msg) => this.log(msg),
834
+ transform,
835
+ });
836
+ await handler.execute(step);
837
+ }
838
+ async executeWebhook(step, ctx) {
647
839
  if (!this.config.webhookServer) {
648
840
  throw new Error('Webhook server not configured. Use --webhook flag or configure webhookServer in executor config.');
649
841
  }
650
842
  const handler = new WebhookHandler({
651
- ctx: this.ctx,
843
+ ctx,
652
844
  webhookServer: this.config.webhookServer,
653
845
  executionId: this.executionState?.id ?? 'ephemeral',
654
846
  log: (msg) => this.log(msg),
847
+ emit: this.eventEmitter
848
+ ? (type, payload) => this.eventEmitter.emit(type, payload)
849
+ : undefined,
655
850
  });
656
851
  await handler.execute(step);
657
852
  }
853
+ async executePause(step, actionName, stepIndex, ctx) {
854
+ if (!this.pauseManager) {
855
+ throw new Error('Pause manager not configured');
856
+ }
857
+ // Mark execution state as paused before creating pause
858
+ if (this.executionState) {
859
+ this.executionState.status = 'paused';
860
+ await this.saveExecutionState();
861
+ }
862
+ const handler = new PauseHandler({
863
+ ctx,
864
+ log: (msg) => this.log(msg),
865
+ emit: this.eventEmitter
866
+ ? (type, payload) => this.eventEmitter.emit(type, payload)
867
+ : undefined,
868
+ pauseManager: this.pauseManager,
869
+ executionId: this.executionState?.id ?? 'ephemeral',
870
+ mission: this.missionName ?? 'unknown',
871
+ actionName,
872
+ stageIndex: this.currentStageIndex,
873
+ stepIndex,
874
+ });
875
+ // This will throw PauseSignal
876
+ await handler.execute(step);
877
+ }
658
878
  log(message) {
659
- if (this.config.verbose) {
879
+ if (this.logger) {
880
+ this.logger.info(message);
881
+ }
882
+ else if (this.config.verbose) {
660
883
  console.log(`[Reqon] ${message}`);
661
884
  }
662
885
  }
886
+ /**
887
+ * Check if pause has been requested and handle it
888
+ * Should be called at safe pause points (between stages, loop iterations)
889
+ */
890
+ async checkPause() {
891
+ if (!this.config.controlServer?.isPauseRequested()) {
892
+ return;
893
+ }
894
+ this.log('Pause requested - saving state and pausing execution');
895
+ // Save state as paused
896
+ if (this.executionState) {
897
+ this.executionState.status = 'paused';
898
+ await this.saveExecutionState();
899
+ }
900
+ // Clear the pause request (it's been handled)
901
+ this.config.controlServer.clearPauseRequest();
902
+ // Throw pause signal to stop execution
903
+ throw new PauseSignal();
904
+ }
905
+ /**
906
+ * Update control server with current state
907
+ */
908
+ updateControlServerState() {
909
+ if (this.config.controlServer && this.executionState) {
910
+ this.config.controlServer.updateState(this.executionState);
911
+ }
912
+ }
913
+ getStepType(stepType) {
914
+ const mapping = {
915
+ FetchStep: 'fetch',
916
+ ForStep: 'for',
917
+ MapStep: 'map',
918
+ ValidateStep: 'validate',
919
+ StoreStep: 'store',
920
+ MatchStep: 'match',
921
+ LetStep: 'let',
922
+ WebhookStep: 'webhook',
923
+ PauseStep: 'pause',
924
+ };
925
+ return mapping[stepType] ?? 'fetch';
926
+ }
927
+ /** Get the event emitter (for external access) */
928
+ getEventEmitter() {
929
+ return this.eventEmitter;
930
+ }
931
+ /** Get the structured logger (for external access) */
932
+ getLogger() {
933
+ return this.logger;
934
+ }
935
+ /** Get the debug controller (for external access) */
936
+ getDebugController() {
937
+ return this.debugController;
938
+ }
939
+ /** Capture current execution state for debugging */
940
+ captureDebugSnapshot(action, stepIndex, stepType, pauseReason, ctx) {
941
+ // Collect variables from context chain
942
+ const variables = {};
943
+ let current = ctx;
944
+ while (current) {
945
+ for (const [key, value] of current.variables) {
946
+ if (!(key in variables)) {
947
+ variables[key] = value;
948
+ }
949
+ }
950
+ current = current.parent;
951
+ }
952
+ // Collect store info
953
+ const stores = {};
954
+ for (const [name, _store] of ctx.stores) {
955
+ stores[name] = {
956
+ type: ctx.storeTypes.get(name) ?? 'unknown',
957
+ count: -1, // Would need async call to get count
958
+ };
959
+ }
960
+ return {
961
+ mission: this.missionName ?? 'unknown',
962
+ action,
963
+ stepIndex,
964
+ stepType,
965
+ pauseReason,
966
+ variables,
967
+ stores,
968
+ response: ctx.response,
969
+ };
970
+ }
971
+ /** Handle debug command and update state */
972
+ handleDebugCommand(cmd) {
973
+ if (!this.debugController)
974
+ return;
975
+ switch (cmd.type) {
976
+ case 'abort':
977
+ throw new AbortError('Execution aborted by debugger');
978
+ case 'continue':
979
+ this.debugController.mode = 'run';
980
+ break;
981
+ case 'step':
982
+ this.debugController.mode = 'step';
983
+ break;
984
+ case 'step-into':
985
+ this.debugController.mode = 'step-into';
986
+ break;
987
+ case 'step-over':
988
+ this.debugController.mode = 'step-over';
989
+ break;
990
+ }
991
+ }
663
992
  }