reqon-dsl 0.2.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 (388) hide show
  1. package/.claude/settings.local.json +31 -0
  2. package/.claude/skills/api-integration.md +125 -0
  3. package/.claude/skills/database-schema.md +51 -0
  4. package/.claude/skills/dsl-design.md +80 -0
  5. package/.claude/skills/property-testing.md +143 -0
  6. package/.claude/skills/reqon/SKILL.md +44 -0
  7. package/.claude/skills/reqon/references/examples.md +206 -0
  8. package/.claude/skills/reqon/references/syntax.md +263 -0
  9. package/.claude/skills/vscode-extension.md +113 -0
  10. package/.github/dependabot.yml +32 -0
  11. package/.github/pull_request_template.md +21 -0
  12. package/.github/workflows/ci.yml +174 -0
  13. package/.github/workflows/release.yml +73 -0
  14. package/CLAUDE.md +72 -0
  15. package/CONTRIBUTING.md +161 -0
  16. package/README.md +235 -0
  17. package/TODO.md +51 -0
  18. package/dist/ast/index.d.ts +1 -0
  19. package/dist/ast/index.js +1 -0
  20. package/dist/ast/nodes.d.ts +237 -0
  21. package/dist/ast/nodes.js +12 -0
  22. package/dist/auth/auth.test.d.ts +1 -0
  23. package/dist/auth/auth.test.js +255 -0
  24. package/dist/auth/circuit-breaker.d.ts +115 -0
  25. package/dist/auth/circuit-breaker.js +267 -0
  26. package/dist/auth/credentials.d.ts +91 -0
  27. package/dist/auth/credentials.js +169 -0
  28. package/dist/auth/index.d.ts +5 -0
  29. package/dist/auth/index.js +8 -0
  30. package/dist/auth/oauth2-provider.d.ts +41 -0
  31. package/dist/auth/oauth2-provider.js +131 -0
  32. package/dist/auth/rate-limiter.d.ts +61 -0
  33. package/dist/auth/rate-limiter.js +380 -0
  34. package/dist/auth/token-store.d.ts +30 -0
  35. package/dist/auth/token-store.js +148 -0
  36. package/dist/auth/types.d.ts +142 -0
  37. package/dist/auth/types.js +1 -0
  38. package/dist/cli.d.ts +2 -0
  39. package/dist/cli.js +270 -0
  40. package/dist/errors/errors.test.d.ts +1 -0
  41. package/dist/errors/errors.test.js +165 -0
  42. package/dist/errors/index.d.ts +83 -0
  43. package/dist/errors/index.js +159 -0
  44. package/dist/execution/execution.test.d.ts +1 -0
  45. package/dist/execution/execution.test.js +246 -0
  46. package/dist/execution/index.d.ts +4 -0
  47. package/dist/execution/index.js +2 -0
  48. package/dist/execution/state.d.ts +136 -0
  49. package/dist/execution/state.js +82 -0
  50. package/dist/execution/store.d.ts +52 -0
  51. package/dist/execution/store.js +120 -0
  52. package/dist/index.d.ts +27 -0
  53. package/dist/index.js +57 -0
  54. package/dist/integration.test.d.ts +1 -0
  55. package/dist/integration.test.js +168 -0
  56. package/dist/interpreter/context.d.ts +15 -0
  57. package/dist/interpreter/context.js +29 -0
  58. package/dist/interpreter/evaluator.d.ts +5 -0
  59. package/dist/interpreter/evaluator.js +223 -0
  60. package/dist/interpreter/evaluator.test.d.ts +1 -0
  61. package/dist/interpreter/evaluator.test.js +512 -0
  62. package/dist/interpreter/executor.d.ts +131 -0
  63. package/dist/interpreter/executor.js +663 -0
  64. package/dist/interpreter/fetch-handler.d.ts +43 -0
  65. package/dist/interpreter/fetch-handler.js +203 -0
  66. package/dist/interpreter/http.d.ts +57 -0
  67. package/dist/interpreter/http.js +210 -0
  68. package/dist/interpreter/http.test.d.ts +1 -0
  69. package/dist/interpreter/http.test.js +299 -0
  70. package/dist/interpreter/index.d.ts +7 -0
  71. package/dist/interpreter/index.js +7 -0
  72. package/dist/interpreter/pagination.d.ts +63 -0
  73. package/dist/interpreter/pagination.js +155 -0
  74. package/dist/interpreter/progress.test.d.ts +1 -0
  75. package/dist/interpreter/progress.test.js +216 -0
  76. package/dist/interpreter/schema-matcher.d.ts +16 -0
  77. package/dist/interpreter/schema-matcher.js +136 -0
  78. package/dist/interpreter/schema-matcher.test.d.ts +1 -0
  79. package/dist/interpreter/schema-matcher.test.js +122 -0
  80. package/dist/interpreter/signals.d.ts +57 -0
  81. package/dist/interpreter/signals.js +73 -0
  82. package/dist/interpreter/step-handlers/for-handler.d.ts +17 -0
  83. package/dist/interpreter/step-handlers/for-handler.js +51 -0
  84. package/dist/interpreter/step-handlers/index.d.ts +8 -0
  85. package/dist/interpreter/step-handlers/index.js +8 -0
  86. package/dist/interpreter/step-handlers/map-handler.d.ts +10 -0
  87. package/dist/interpreter/step-handlers/map-handler.js +20 -0
  88. package/dist/interpreter/step-handlers/match-handler.d.ts +27 -0
  89. package/dist/interpreter/step-handlers/match-handler.js +61 -0
  90. package/dist/interpreter/step-handlers/store-handler.d.ts +13 -0
  91. package/dist/interpreter/step-handlers/store-handler.js +66 -0
  92. package/dist/interpreter/step-handlers/types.d.ts +15 -0
  93. package/dist/interpreter/step-handlers/types.js +1 -0
  94. package/dist/interpreter/step-handlers/validate-handler.d.ts +10 -0
  95. package/dist/interpreter/step-handlers/validate-handler.js +26 -0
  96. package/dist/interpreter/step-handlers/webhook-handler.d.ts +36 -0
  97. package/dist/interpreter/step-handlers/webhook-handler.js +104 -0
  98. package/dist/lexer/index.d.ts +10 -0
  99. package/dist/lexer/index.js +12 -0
  100. package/dist/lexer/lexer.d.ts +24 -0
  101. package/dist/lexer/lexer.js +264 -0
  102. package/dist/lexer/lexer.test.d.ts +1 -0
  103. package/dist/lexer/lexer.test.js +259 -0
  104. package/dist/lexer/tokens.d.ts +69 -0
  105. package/dist/lexer/tokens.js +146 -0
  106. package/dist/loader/index.d.ts +36 -0
  107. package/dist/loader/index.js +220 -0
  108. package/dist/loader/loader.test.d.ts +1 -0
  109. package/dist/loader/loader.test.js +287 -0
  110. package/dist/oas/index.d.ts +4 -0
  111. package/dist/oas/index.js +2 -0
  112. package/dist/oas/loader.d.ts +21 -0
  113. package/dist/oas/loader.js +82 -0
  114. package/dist/oas/oas.test.d.ts +1 -0
  115. package/dist/oas/oas.test.js +218 -0
  116. package/dist/oas/validator.d.ts +12 -0
  117. package/dist/oas/validator.js +227 -0
  118. package/dist/parser/base.d.ts +33 -0
  119. package/dist/parser/base.js +97 -0
  120. package/dist/parser/expressions.d.ts +27 -0
  121. package/dist/parser/expressions.js +248 -0
  122. package/dist/parser/expressions.test.d.ts +1 -0
  123. package/dist/parser/expressions.test.js +378 -0
  124. package/dist/parser/index.d.ts +3 -0
  125. package/dist/parser/index.js +3 -0
  126. package/dist/parser/match.test.d.ts +1 -0
  127. package/dist/parser/match.test.js +254 -0
  128. package/dist/parser/parser.d.ts +68 -0
  129. package/dist/parser/parser.js +1229 -0
  130. package/dist/parser/parser.test.d.ts +1 -0
  131. package/dist/parser/parser.test.js +333 -0
  132. package/dist/parser/schedule.test.d.ts +1 -0
  133. package/dist/parser/schedule.test.js +241 -0
  134. package/dist/plugin.d.ts +35 -0
  135. package/dist/plugin.js +68 -0
  136. package/dist/scheduler/cron-parser.d.ts +32 -0
  137. package/dist/scheduler/cron-parser.js +198 -0
  138. package/dist/scheduler/cron-parser.test.d.ts +1 -0
  139. package/dist/scheduler/cron-parser.test.js +188 -0
  140. package/dist/scheduler/index.d.ts +3 -0
  141. package/dist/scheduler/index.js +2 -0
  142. package/dist/scheduler/scheduler.d.ts +81 -0
  143. package/dist/scheduler/scheduler.js +376 -0
  144. package/dist/scheduler/types.d.ts +65 -0
  145. package/dist/scheduler/types.js +1 -0
  146. package/dist/stores/factory.d.ts +36 -0
  147. package/dist/stores/factory.js +73 -0
  148. package/dist/stores/file.d.ts +60 -0
  149. package/dist/stores/file.js +173 -0
  150. package/dist/stores/file.test.d.ts +1 -0
  151. package/dist/stores/file.test.js +165 -0
  152. package/dist/stores/index.d.ts +6 -0
  153. package/dist/stores/index.js +5 -0
  154. package/dist/stores/memory.d.ts +19 -0
  155. package/dist/stores/memory.js +51 -0
  156. package/dist/stores/memory.test.d.ts +1 -0
  157. package/dist/stores/memory.test.js +157 -0
  158. package/dist/stores/postgrest.d.ts +55 -0
  159. package/dist/stores/postgrest.js +217 -0
  160. package/dist/stores/stores.test.d.ts +1 -0
  161. package/dist/stores/stores.test.js +158 -0
  162. package/dist/stores/types.d.ts +31 -0
  163. package/dist/stores/types.js +26 -0
  164. package/dist/sync/index.d.ts +4 -0
  165. package/dist/sync/index.js +2 -0
  166. package/dist/sync/state.d.ts +69 -0
  167. package/dist/sync/state.js +66 -0
  168. package/dist/sync/store.d.ts +49 -0
  169. package/dist/sync/store.js +93 -0
  170. package/dist/sync/sync.test.d.ts +1 -0
  171. package/dist/sync/sync.test.js +221 -0
  172. package/dist/utils/async.d.ts +7 -0
  173. package/dist/utils/async.js +9 -0
  174. package/dist/utils/file.d.ts +38 -0
  175. package/dist/utils/file.js +92 -0
  176. package/dist/utils/index.d.ts +4 -0
  177. package/dist/utils/index.js +4 -0
  178. package/dist/utils/logger.d.ts +34 -0
  179. package/dist/utils/logger.js +39 -0
  180. package/dist/utils/path.d.ts +12 -0
  181. package/dist/utils/path.js +41 -0
  182. package/dist/webhook/index.d.ts +8 -0
  183. package/dist/webhook/index.js +7 -0
  184. package/dist/webhook/server.d.ts +84 -0
  185. package/dist/webhook/server.js +319 -0
  186. package/dist/webhook/store.d.ts +67 -0
  187. package/dist/webhook/store.js +193 -0
  188. package/dist/webhook/types.d.ts +88 -0
  189. package/dist/webhook/types.js +6 -0
  190. package/docusaurus/README.md +41 -0
  191. package/docusaurus/docs/advanced/execution-state.md +283 -0
  192. package/docusaurus/docs/advanced/extending-reqon.md +388 -0
  193. package/docusaurus/docs/advanced/multi-file-missions.md +250 -0
  194. package/docusaurus/docs/advanced/parallel-execution.md +353 -0
  195. package/docusaurus/docs/api-reference.md +443 -0
  196. package/docusaurus/docs/authentication/api-key.md +339 -0
  197. package/docusaurus/docs/authentication/basic.md +276 -0
  198. package/docusaurus/docs/authentication/bearer.md +282 -0
  199. package/docusaurus/docs/authentication/oauth2.md +317 -0
  200. package/docusaurus/docs/authentication/overview.md +251 -0
  201. package/docusaurus/docs/cli.md +229 -0
  202. package/docusaurus/docs/core-concepts/actions.md +286 -0
  203. package/docusaurus/docs/core-concepts/missions.md +264 -0
  204. package/docusaurus/docs/core-concepts/schemas.md +353 -0
  205. package/docusaurus/docs/core-concepts/sources.md +339 -0
  206. package/docusaurus/docs/core-concepts/stores.md +332 -0
  207. package/docusaurus/docs/dsl-syntax/expressions.md +361 -0
  208. package/docusaurus/docs/dsl-syntax/fetch.md +293 -0
  209. package/docusaurus/docs/dsl-syntax/for-loops.md +324 -0
  210. package/docusaurus/docs/dsl-syntax/map.md +345 -0
  211. package/docusaurus/docs/dsl-syntax/match.md +387 -0
  212. package/docusaurus/docs/dsl-syntax/pipelines.md +397 -0
  213. package/docusaurus/docs/dsl-syntax/validate.md +401 -0
  214. package/docusaurus/docs/error-handling/dead-letter-queues.md +399 -0
  215. package/docusaurus/docs/error-handling/flow-control.md +337 -0
  216. package/docusaurus/docs/error-handling/retry-strategies.md +368 -0
  217. package/docusaurus/docs/examples.md +488 -0
  218. package/docusaurus/docs/getting-started.md +256 -0
  219. package/docusaurus/docs/http/circuit-breaker.md +401 -0
  220. package/docusaurus/docs/http/incremental-sync.md +394 -0
  221. package/docusaurus/docs/http/pagination.md +361 -0
  222. package/docusaurus/docs/http/rate-limiting.md +383 -0
  223. package/docusaurus/docs/http/requests.md +328 -0
  224. package/docusaurus/docs/http/retry.md +402 -0
  225. package/docusaurus/docs/intro.md +90 -0
  226. package/docusaurus/docs/openapi/loading-specs.md +305 -0
  227. package/docusaurus/docs/openapi/operation-calls.md +314 -0
  228. package/docusaurus/docs/openapi/overview.md +212 -0
  229. package/docusaurus/docs/openapi/response-validation.md +344 -0
  230. package/docusaurus/docs/scheduling/cron.md +305 -0
  231. package/docusaurus/docs/scheduling/daemon-mode.md +317 -0
  232. package/docusaurus/docs/scheduling/intervals.md +289 -0
  233. package/docusaurus/docs/scheduling/overview.md +231 -0
  234. package/docusaurus/docs/stores/custom-adapters.md +376 -0
  235. package/docusaurus/docs/stores/file.md +236 -0
  236. package/docusaurus/docs/stores/memory.md +193 -0
  237. package/docusaurus/docs/stores/overview.md +274 -0
  238. package/docusaurus/docs/stores/postgrest.md +316 -0
  239. package/docusaurus/docusaurus.config.ts +148 -0
  240. package/docusaurus/package-lock.json +18029 -0
  241. package/docusaurus/package.json +47 -0
  242. package/docusaurus/sidebars.ts +155 -0
  243. package/docusaurus/src/components/HomepageFeatures/index.tsx +105 -0
  244. package/docusaurus/src/components/HomepageFeatures/styles.module.css +12 -0
  245. package/docusaurus/src/css/custom.css +169 -0
  246. package/docusaurus/src/pages/index.module.css +48 -0
  247. package/docusaurus/src/pages/index.tsx +110 -0
  248. package/docusaurus/src/pages/markdown-page.md +7 -0
  249. package/docusaurus/static/.nojekyll +0 -0
  250. package/docusaurus/static/img/docusaurus-social-card.jpg +0 -0
  251. package/docusaurus/static/img/docusaurus.png +0 -0
  252. package/docusaurus/static/img/favicon.ico +0 -0
  253. package/docusaurus/static/img/logo.svg +10 -0
  254. package/docusaurus/static/img/undraw_docusaurus_mountain.svg +171 -0
  255. package/docusaurus/static/img/undraw_docusaurus_react.svg +170 -0
  256. package/docusaurus/static/img/undraw_docusaurus_tree.svg +40 -0
  257. package/docusaurus/tsconfig.json +8 -0
  258. package/examples/README.md +112 -0
  259. package/examples/error-handling/README.md +150 -0
  260. package/examples/error-handling/payment-processor.vague +287 -0
  261. package/examples/github-sync/README.md +74 -0
  262. package/examples/github-sync/fetch-issues.vague +47 -0
  263. package/examples/github-sync/fetch-prs.vague +40 -0
  264. package/examples/github-sync/mission.vague +101 -0
  265. package/examples/github-sync/normalize.vague +70 -0
  266. package/examples/jsonplaceholder/README.md +28 -0
  267. package/examples/jsonplaceholder/posts.vague +48 -0
  268. package/examples/petstore/README.md +35 -0
  269. package/examples/petstore/openapi.yaml +97 -0
  270. package/examples/petstore/sync.vague +52 -0
  271. package/examples/temporal-comparison/README.md +297 -0
  272. package/examples/temporal-comparison/reconciliation.vague +355 -0
  273. package/examples/temporal-comparison/temporal/activities/index.ts +8 -0
  274. package/examples/temporal-comparison/temporal/activities/shipstation.ts +225 -0
  275. package/examples/temporal-comparison/temporal/activities/shopify.ts +257 -0
  276. package/examples/temporal-comparison/temporal/activities/storage.ts +198 -0
  277. package/examples/temporal-comparison/temporal/activities/stripe.ts +169 -0
  278. package/examples/temporal-comparison/temporal/activities/validation.ts +205 -0
  279. package/examples/temporal-comparison/temporal/client/schedule.ts +218 -0
  280. package/examples/temporal-comparison/temporal/config/retry.ts +63 -0
  281. package/examples/temporal-comparison/temporal/types/index.ts +129 -0
  282. package/examples/temporal-comparison/temporal/workers/main.ts +130 -0
  283. package/examples/temporal-comparison/temporal/workflows/orderReconciliation.ts +262 -0
  284. package/examples/xero/README.md +88 -0
  285. package/examples/xero/invoices.vague +189 -0
  286. package/package.json +40 -0
  287. package/src/api-integration.test.ts +954 -0
  288. package/src/ast/index.ts +1 -0
  289. package/src/ast/nodes.ts +310 -0
  290. package/src/auth/auth.test.ts +326 -0
  291. package/src/auth/circuit-breaker.test.ts +390 -0
  292. package/src/auth/circuit-breaker.ts +379 -0
  293. package/src/auth/credentials.test.ts +273 -0
  294. package/src/auth/credentials.ts +246 -0
  295. package/src/auth/index.ts +40 -0
  296. package/src/auth/oauth2-provider.ts +177 -0
  297. package/src/auth/rate-limiter.ts +459 -0
  298. package/src/auth/token-store.ts +177 -0
  299. package/src/auth/types.ts +159 -0
  300. package/src/benchmark/e2e.bench.ts +288 -0
  301. package/src/benchmark/evaluator.bench.ts +331 -0
  302. package/src/benchmark/fixtures.ts +295 -0
  303. package/src/benchmark/index.ts +108 -0
  304. package/src/benchmark/lexer.bench.ts +69 -0
  305. package/src/benchmark/parser.bench.ts +103 -0
  306. package/src/benchmark/resilience.bench.ts +193 -0
  307. package/src/benchmark/store.bench.ts +147 -0
  308. package/src/benchmark/utils.ts +230 -0
  309. package/src/cli.ts +313 -0
  310. package/src/errors/errors.test.ts +234 -0
  311. package/src/errors/index.ts +223 -0
  312. package/src/execution/execution.test.ts +307 -0
  313. package/src/execution/index.ts +21 -0
  314. package/src/execution/state.ts +207 -0
  315. package/src/execution/store.ts +188 -0
  316. package/src/index.ts +169 -0
  317. package/src/integration.test.ts +192 -0
  318. package/src/interpreter/context.ts +57 -0
  319. package/src/interpreter/evaluator.test.ts +796 -0
  320. package/src/interpreter/evaluator.ts +245 -0
  321. package/src/interpreter/executor.ts +946 -0
  322. package/src/interpreter/fetch-handler.ts +302 -0
  323. package/src/interpreter/http.test.ts +423 -0
  324. package/src/interpreter/http.ts +308 -0
  325. package/src/interpreter/index.ts +32 -0
  326. package/src/interpreter/pagination.ts +207 -0
  327. package/src/interpreter/progress.test.ts +276 -0
  328. package/src/interpreter/schema-matcher.test.ts +160 -0
  329. package/src/interpreter/schema-matcher.ts +168 -0
  330. package/src/interpreter/signals.ts +73 -0
  331. package/src/interpreter/step-handlers/for-handler.ts +65 -0
  332. package/src/interpreter/step-handlers/index.ts +17 -0
  333. package/src/interpreter/step-handlers/map-handler.ts +24 -0
  334. package/src/interpreter/step-handlers/match-handler.ts +101 -0
  335. package/src/interpreter/step-handlers/store-handler.ts +78 -0
  336. package/src/interpreter/step-handlers/types.ts +17 -0
  337. package/src/interpreter/step-handlers/validate-handler.ts +30 -0
  338. package/src/interpreter/step-handlers/webhook-handler.ts +142 -0
  339. package/src/lexer/index.ts +18 -0
  340. package/src/lexer/lexer.test.ts +316 -0
  341. package/src/lexer/tokens.ts +179 -0
  342. package/src/loader/index.ts +288 -0
  343. package/src/loader/loader.test.ts +360 -0
  344. package/src/oas/index.ts +4 -0
  345. package/src/oas/loader.ts +126 -0
  346. package/src/oas/oas.test.ts +254 -0
  347. package/src/oas/validator.ts +299 -0
  348. package/src/parser/base.ts +124 -0
  349. package/src/parser/expressions.test.ts +525 -0
  350. package/src/parser/expressions.ts +314 -0
  351. package/src/parser/index.ts +3 -0
  352. package/src/parser/match.test.ts +296 -0
  353. package/src/parser/parser.test.ts +739 -0
  354. package/src/parser/parser.ts +1469 -0
  355. package/src/parser/schedule.test.ts +287 -0
  356. package/src/parser/webhook.test.ts +248 -0
  357. package/src/plugin.ts +83 -0
  358. package/src/scheduler/cron-parser.test.ts +236 -0
  359. package/src/scheduler/cron-parser.ts +236 -0
  360. package/src/scheduler/index.ts +10 -0
  361. package/src/scheduler/scheduler.ts +443 -0
  362. package/src/scheduler/types.ts +71 -0
  363. package/src/stores/factory.ts +104 -0
  364. package/src/stores/file.test.ts +276 -0
  365. package/src/stores/file.ts +211 -0
  366. package/src/stores/index.ts +6 -0
  367. package/src/stores/memory.test.ts +238 -0
  368. package/src/stores/memory.ts +63 -0
  369. package/src/stores/postgrest.test.ts +488 -0
  370. package/src/stores/postgrest.ts +263 -0
  371. package/src/stores/stores.test.ts +197 -0
  372. package/src/stores/types.ts +58 -0
  373. package/src/sync/index.ts +16 -0
  374. package/src/sync/state.ts +126 -0
  375. package/src/sync/store.ts +139 -0
  376. package/src/sync/sync.test.ts +271 -0
  377. package/src/utils/async.ts +10 -0
  378. package/src/utils/file.ts +106 -0
  379. package/src/utils/index.ts +14 -0
  380. package/src/utils/logger.ts +53 -0
  381. package/src/utils/path.ts +47 -0
  382. package/src/webhook/index.ts +15 -0
  383. package/src/webhook/server.test.ts +253 -0
  384. package/src/webhook/server.ts +389 -0
  385. package/src/webhook/store.ts +239 -0
  386. package/src/webhook/types.ts +93 -0
  387. package/tsconfig.json +17 -0
  388. package/vitest.config.ts +39 -0
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Execution State - Durable state for resumable missions
3
+ *
4
+ * Tracks progress through pipeline stages, enabling:
5
+ * - Resume from last successful step after failures
6
+ * - Idempotent re-execution
7
+ * - Progress visibility
8
+ */
9
+ export type ExecutionStatus = 'pending' | 'running' | 'completed' | 'failed' | 'paused';
10
+ export type StageStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
11
+ /**
12
+ * State for a single pipeline stage (action execution)
13
+ */
14
+ export interface StageState {
15
+ /** Action name */
16
+ action: string;
17
+ /** Current status */
18
+ status: StageStatus;
19
+ /** When this stage started */
20
+ startedAt?: Date;
21
+ /** When this stage completed/failed */
22
+ completedAt?: Date;
23
+ /** Error message if failed */
24
+ error?: string;
25
+ /** Number of items processed (for actions with loops) */
26
+ itemsProcessed?: number;
27
+ /** Total items to process */
28
+ itemsTotal?: number;
29
+ /** Retry attempt number (0 = first attempt) */
30
+ attempt: number;
31
+ }
32
+ /**
33
+ * Checkpoint within an action (for resuming mid-action)
34
+ */
35
+ export interface Checkpoint {
36
+ /** Stage index in pipeline */
37
+ stageIndex: number;
38
+ /** Step index within action */
39
+ stepIndex: number;
40
+ /** For loops: current item index */
41
+ itemIndex?: number;
42
+ /** Saved context variables */
43
+ variables?: Record<string, unknown>;
44
+ /** Timestamp */
45
+ createdAt: Date;
46
+ /** Webhook wait state (for resuming webhook waits) */
47
+ webhookWait?: WebhookWaitState;
48
+ }
49
+ /**
50
+ * State for waiting on webhook callbacks
51
+ */
52
+ export interface WebhookWaitState {
53
+ /** Webhook registration ID */
54
+ registrationId: string;
55
+ /** Path for the webhook endpoint */
56
+ path: string;
57
+ /** Full webhook URL */
58
+ webhookUrl: string;
59
+ /** Number of expected events */
60
+ expectedEvents: number;
61
+ /** Number of events received so far */
62
+ receivedEvents: number;
63
+ /** When the wait started */
64
+ waitStartedAt: Date;
65
+ /** When the wait expires */
66
+ expiresAt: Date;
67
+ }
68
+ /**
69
+ * Complete execution state for a mission run
70
+ */
71
+ export interface ExecutionState {
72
+ /** Unique execution ID */
73
+ id: string;
74
+ /** Mission name */
75
+ mission: string;
76
+ /** Overall status */
77
+ status: ExecutionStatus;
78
+ /** When execution started */
79
+ startedAt: Date;
80
+ /** When execution completed/failed */
81
+ completedAt?: Date;
82
+ /** Total duration in ms */
83
+ duration?: number;
84
+ /** State of each pipeline stage */
85
+ stages: StageState[];
86
+ /** Latest checkpoint for resume */
87
+ checkpoint?: Checkpoint;
88
+ /** Execution errors */
89
+ errors: ExecutionStateError[];
90
+ /** Metadata (user-provided context) */
91
+ metadata?: Record<string, unknown>;
92
+ }
93
+ export interface ExecutionStateError {
94
+ stageIndex: number;
95
+ action: string;
96
+ step: string;
97
+ message: string;
98
+ timestamp: Date;
99
+ attempt: number;
100
+ }
101
+ /**
102
+ * Options for creating a new execution
103
+ */
104
+ export interface CreateExecutionOptions {
105
+ /** Mission name */
106
+ mission: string;
107
+ /** Pipeline stage names (actions) */
108
+ stages: string[];
109
+ /** Optional metadata */
110
+ metadata?: Record<string, unknown>;
111
+ }
112
+ /**
113
+ * Generate a unique execution ID
114
+ */
115
+ export declare function generateExecutionId(): string;
116
+ /**
117
+ * Create initial execution state
118
+ */
119
+ export declare function createExecutionState(options: CreateExecutionOptions): ExecutionState;
120
+ /**
121
+ * Find the stage to resume from
122
+ * Returns the index of the first non-completed stage, or -1 if all complete
123
+ */
124
+ export declare function findResumePoint(state: ExecutionState): number;
125
+ /**
126
+ * Check if execution can be resumed
127
+ */
128
+ export declare function canResume(state: ExecutionState): boolean;
129
+ /**
130
+ * Calculate execution progress as percentage
131
+ */
132
+ export declare function getProgress(state: ExecutionState): number;
133
+ /**
134
+ * Get a summary of the execution state
135
+ */
136
+ export declare function getExecutionSummary(state: ExecutionState): string;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Execution State - Durable state for resumable missions
3
+ *
4
+ * Tracks progress through pipeline stages, enabling:
5
+ * - Resume from last successful step after failures
6
+ * - Idempotent re-execution
7
+ * - Progress visibility
8
+ */
9
+ /**
10
+ * Generate a unique execution ID
11
+ */
12
+ export function generateExecutionId() {
13
+ const timestamp = Date.now().toString(36);
14
+ const random = Math.random().toString(36).substring(2, 8);
15
+ return `exec_${timestamp}_${random}`;
16
+ }
17
+ /**
18
+ * Create initial execution state
19
+ */
20
+ export function createExecutionState(options) {
21
+ return {
22
+ id: generateExecutionId(),
23
+ mission: options.mission,
24
+ status: 'pending',
25
+ startedAt: new Date(),
26
+ stages: options.stages.map((action) => ({
27
+ action,
28
+ status: 'pending',
29
+ attempt: 0,
30
+ })),
31
+ errors: [],
32
+ metadata: options.metadata,
33
+ };
34
+ }
35
+ /**
36
+ * Find the stage to resume from
37
+ * Returns the index of the first non-completed stage, or -1 if all complete
38
+ */
39
+ export function findResumePoint(state) {
40
+ // If there's a checkpoint, use it
41
+ if (state.checkpoint) {
42
+ return state.checkpoint.stageIndex;
43
+ }
44
+ // Otherwise, find first non-completed stage
45
+ for (let i = 0; i < state.stages.length; i++) {
46
+ const stage = state.stages[i];
47
+ if (stage.status !== 'completed' && stage.status !== 'skipped') {
48
+ return i;
49
+ }
50
+ }
51
+ return -1; // All stages complete
52
+ }
53
+ /**
54
+ * Check if execution can be resumed
55
+ */
56
+ export function canResume(state) {
57
+ return state.status === 'failed' || state.status === 'paused';
58
+ }
59
+ /**
60
+ * Calculate execution progress as percentage
61
+ */
62
+ export function getProgress(state) {
63
+ if (state.stages.length === 0)
64
+ return 100;
65
+ const completed = state.stages.filter((s) => s.status === 'completed' || s.status === 'skipped').length;
66
+ return Math.round((completed / state.stages.length) * 100);
67
+ }
68
+ /**
69
+ * Get a summary of the execution state
70
+ */
71
+ export function getExecutionSummary(state) {
72
+ const progress = getProgress(state);
73
+ const completed = state.stages.filter((s) => s.status === 'completed').length;
74
+ const failed = state.stages.filter((s) => s.status === 'failed').length;
75
+ const pending = state.stages.filter((s) => s.status === 'pending').length;
76
+ let summary = `${state.mission} [${state.id}]: ${state.status} (${progress}%)`;
77
+ summary += ` - ${completed} completed, ${failed} failed, ${pending} pending`;
78
+ if (state.duration) {
79
+ summary += ` - ${Math.round(state.duration / 1000)}s`;
80
+ }
81
+ return summary;
82
+ }
@@ -0,0 +1,52 @@
1
+ import type { ExecutionState } from './state.js';
2
+ /**
3
+ * Execution state store interface
4
+ */
5
+ export interface ExecutionStore {
6
+ /** Save execution state */
7
+ save(state: ExecutionState): Promise<void>;
8
+ /** Load execution state by ID */
9
+ load(id: string): Promise<ExecutionState | null>;
10
+ /** List all executions for a mission */
11
+ listByMission(mission: string): Promise<ExecutionState[]>;
12
+ /** List recent executions */
13
+ listRecent(limit?: number): Promise<ExecutionState[]>;
14
+ /** Delete execution state */
15
+ delete(id: string): Promise<void>;
16
+ /** Find the latest execution for a mission */
17
+ findLatest(mission: string): Promise<ExecutionState | null>;
18
+ /** Find resumable executions (failed/paused) */
19
+ findResumable(mission: string): Promise<ExecutionState[]>;
20
+ }
21
+ /**
22
+ * File-based execution state store
23
+ * Stores each execution as a JSON file in .reqon-data/executions/
24
+ */
25
+ export declare class FileExecutionStore implements ExecutionStore {
26
+ private baseDir;
27
+ private initialized;
28
+ constructor(baseDir?: string);
29
+ private getFilePath;
30
+ private deserialize;
31
+ save(state: ExecutionState): Promise<void>;
32
+ load(id: string): Promise<ExecutionState | null>;
33
+ listByMission(mission: string): Promise<ExecutionState[]>;
34
+ listRecent(limit?: number): Promise<ExecutionState[]>;
35
+ delete(id: string): Promise<void>;
36
+ findLatest(mission: string): Promise<ExecutionState | null>;
37
+ findResumable(mission: string): Promise<ExecutionState[]>;
38
+ }
39
+ /**
40
+ * In-memory execution store (for testing)
41
+ */
42
+ export declare class MemoryExecutionStore implements ExecutionStore {
43
+ private states;
44
+ save(state: ExecutionState): Promise<void>;
45
+ load(id: string): Promise<ExecutionState | null>;
46
+ listByMission(mission: string): Promise<ExecutionState[]>;
47
+ listRecent(limit?: number): Promise<ExecutionState[]>;
48
+ delete(id: string): Promise<void>;
49
+ findLatest(mission: string): Promise<ExecutionState | null>;
50
+ findResumable(mission: string): Promise<ExecutionState[]>;
51
+ clear(): void;
52
+ }
@@ -0,0 +1,120 @@
1
+ import { join } from 'node:path';
2
+ import { ensureDirectory, writeJsonFile, readJsonFile, listFiles, deleteFile, restoreDates, restoreDatesInArray, } from '../utils/file.js';
3
+ /**
4
+ * File-based execution state store
5
+ * Stores each execution as a JSON file in .reqon-data/executions/
6
+ */
7
+ export class FileExecutionStore {
8
+ baseDir;
9
+ initialized;
10
+ constructor(baseDir = '.reqon-data/executions') {
11
+ this.baseDir = baseDir;
12
+ this.initialized = ensureDirectory(this.baseDir);
13
+ }
14
+ getFilePath(id) {
15
+ return join(this.baseDir, `${id}.json`);
16
+ }
17
+ deserialize(parsed) {
18
+ // Restore Date objects
19
+ restoreDates(parsed, ['startedAt', 'completedAt']);
20
+ if (parsed.checkpoint && typeof parsed.checkpoint === 'object') {
21
+ restoreDates(parsed.checkpoint, ['createdAt']);
22
+ }
23
+ restoreDatesInArray(parsed.stages, ['startedAt', 'completedAt']);
24
+ restoreDatesInArray(parsed.errors, ['timestamp']);
25
+ return parsed;
26
+ }
27
+ async save(state) {
28
+ await this.initialized;
29
+ const filePath = this.getFilePath(state.id);
30
+ await writeJsonFile(filePath, state);
31
+ }
32
+ async load(id) {
33
+ await this.initialized;
34
+ const filePath = this.getFilePath(id);
35
+ const parsed = await readJsonFile(filePath);
36
+ return parsed ? this.deserialize(parsed) : null;
37
+ }
38
+ async listByMission(mission) {
39
+ const all = await this.listRecent();
40
+ return all.filter((s) => s.mission === mission);
41
+ }
42
+ async listRecent(limit = 50) {
43
+ await this.initialized;
44
+ const files = await listFiles(this.baseDir, '.json');
45
+ const states = [];
46
+ for (const file of files) {
47
+ const parsed = await readJsonFile(file);
48
+ if (parsed) {
49
+ states.push(this.deserialize(parsed));
50
+ }
51
+ }
52
+ // Sort by start time, newest first
53
+ states.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
54
+ return states.slice(0, limit);
55
+ }
56
+ async delete(id) {
57
+ await this.initialized;
58
+ await deleteFile(this.getFilePath(id));
59
+ }
60
+ async findLatest(mission) {
61
+ const executions = await this.listByMission(mission);
62
+ return executions[0] ?? null;
63
+ }
64
+ async findResumable(mission) {
65
+ const executions = await this.listByMission(mission);
66
+ return executions.filter((e) => e.status === 'failed' || e.status === 'paused');
67
+ }
68
+ }
69
+ /**
70
+ * In-memory execution store (for testing)
71
+ */
72
+ export class MemoryExecutionStore {
73
+ states = new Map();
74
+ async save(state) {
75
+ // Deep clone to avoid reference issues
76
+ this.states.set(state.id, JSON.parse(JSON.stringify(state)));
77
+ }
78
+ async load(id) {
79
+ const state = this.states.get(id);
80
+ if (!state)
81
+ return null;
82
+ // Restore Date objects
83
+ const restored = JSON.parse(JSON.stringify(state));
84
+ restored.startedAt = new Date(restored.startedAt);
85
+ if (restored.completedAt) {
86
+ restored.completedAt = new Date(restored.completedAt);
87
+ }
88
+ return restored;
89
+ }
90
+ async listByMission(mission) {
91
+ const all = await this.listRecent();
92
+ return all.filter((s) => s.mission === mission);
93
+ }
94
+ async listRecent(limit = 50) {
95
+ const states = Array.from(this.states.values()).map((s) => {
96
+ const restored = JSON.parse(JSON.stringify(s));
97
+ restored.startedAt = new Date(restored.startedAt);
98
+ if (restored.completedAt) {
99
+ restored.completedAt = new Date(restored.completedAt);
100
+ }
101
+ return restored;
102
+ });
103
+ states.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
104
+ return states.slice(0, limit);
105
+ }
106
+ async delete(id) {
107
+ this.states.delete(id);
108
+ }
109
+ async findLatest(mission) {
110
+ const executions = await this.listByMission(mission);
111
+ return executions[0] ?? null;
112
+ }
113
+ async findResumable(mission) {
114
+ const executions = await this.listByMission(mission);
115
+ return executions.filter((e) => e.status === 'failed' || e.status === 'paused');
116
+ }
117
+ clear() {
118
+ this.states.clear();
119
+ }
120
+ }
@@ -0,0 +1,27 @@
1
+ export { ReqonLexer, ReqonTokenType, type ReqonToken } from './lexer/index.js';
2
+ export { reqonPlugin, registerReqonPlugin } from './plugin.js';
3
+ export { ReqonParser } from './parser/index.js';
4
+ export * from './ast/index.js';
5
+ export { MissionExecutor, HttpClient, BearerAuthProvider, OAuth2AuthProvider, createContext, evaluate, type ExecutionResult, type ExecutionError, type ExecutorConfig, type ExecutionContext, type ProgressCallbacks, type ExecutionStartEvent, type ExecutionCompleteEvent, type StageStartEvent, type StageCompleteEvent, } from './interpreter/index.js';
6
+ export { MemoryStore, FileStore, createStore, type StoreAdapter, type StoreFilter, type StoreConfig, } from './stores/index.js';
7
+ export { createExecutionState, findResumePoint, canResume, getProgress, getExecutionSummary, FileExecutionStore, MemoryExecutionStore, type ExecutionState, type ExecutionStore, type StageState, } from './execution/index.js';
8
+ export { Scheduler, parseCronExpression, getNextRunTime, intervalToMs, shouldRunNow, type ScheduledJob, type SchedulerState, type ScheduleEvent, type SchedulerCallbacks, type SchedulerConfig, type ScheduledMission, } from './scheduler/index.js';
9
+ export { generateCheckpointKey, formatSinceDate, parseSinceDate, EPOCH, FileSyncStore, MemorySyncStore, type SyncCheckpoint, type SyncStore, } from './sync/index.js';
10
+ export { ReqonError, ParseError, LexerError, RuntimeError, ValidationError, formatErrors, getSourceLine, getSourceContext, type SourceLocation, type ErrorContext, } from './errors/index.js';
11
+ export { loadMission, isMissionFolder, getMissionName, type LoadResult, type LoadOptions, } from './loader/index.js';
12
+ export { loadEnv, loadCredentials, resolveCredentials, resolveEnvString, hasEnvReference, credentialsFromEnv, type CredentialsConfig, type LoadEnvResult, type AuthCredentials, type SourceCredentials, } from './auth/credentials.js';
13
+ export { WebhookServer, MemoryWebhookStore, FileWebhookStore, type WebhookStore, type WebhookServerConfig, type WebhookServerCallbacks, type WebhookRegistration, type WebhookEvent, type WaitResult, } from './webhook/index.js';
14
+ import { type ExecutorConfig } from './interpreter/index.js';
15
+ import type { ReqonProgram } from './ast/index.js';
16
+ export declare function parse(source: string, filePath?: string): ReqonProgram;
17
+ export declare function execute(source: string, config?: ExecutorConfig): Promise<import('./interpreter/index.js').ExecutionResult>;
18
+ export declare function fromFile(filePath: string, config?: ExecutorConfig): Promise<import('./interpreter/index.js').ExecutionResult>;
19
+ /**
20
+ * Load and execute a mission from a file or folder.
21
+ *
22
+ * Supports both:
23
+ * - Single file: ./sync-invoices.reqon
24
+ * - Folder: ./sync-invoices/ (with mission.reqon + action files)
25
+ */
26
+ export declare function fromPath(path: string, config?: ExecutorConfig): Promise<import('./interpreter/index.js').ExecutionResult>;
27
+ export declare function reqon(strings: TemplateStringsArray, ...values: unknown[]): ReqonProgram;
package/dist/index.js ADDED
@@ -0,0 +1,57 @@
1
+ export { ReqonLexer, ReqonTokenType } from './lexer/index.js';
2
+ export { reqonPlugin, registerReqonPlugin } from './plugin.js';
3
+ export { ReqonParser } from './parser/index.js';
4
+ export * from './ast/index.js';
5
+ export { MissionExecutor, HttpClient, BearerAuthProvider, OAuth2AuthProvider, createContext, evaluate, } from './interpreter/index.js';
6
+ export { MemoryStore, FileStore, createStore, } from './stores/index.js';
7
+ export { createExecutionState, findResumePoint, canResume, getProgress, getExecutionSummary, FileExecutionStore, MemoryExecutionStore, } from './execution/index.js';
8
+ export { Scheduler, parseCronExpression, getNextRunTime, intervalToMs, shouldRunNow, } from './scheduler/index.js';
9
+ export { generateCheckpointKey, formatSinceDate, parseSinceDate, EPOCH, FileSyncStore, MemorySyncStore, } from './sync/index.js';
10
+ export { ReqonError, ParseError, LexerError, RuntimeError, ValidationError, formatErrors, getSourceLine, getSourceContext, } from './errors/index.js';
11
+ export { loadMission, isMissionFolder, getMissionName, } from './loader/index.js';
12
+ export { loadEnv, loadCredentials, resolveCredentials, resolveEnvString, hasEnvReference, credentialsFromEnv, } from './auth/credentials.js';
13
+ export { WebhookServer, MemoryWebhookStore, FileWebhookStore, } from './webhook/index.js';
14
+ import { readFile } from 'node:fs/promises';
15
+ import { resolve } from 'node:path';
16
+ import { ReqonLexer } from './lexer/index.js';
17
+ import { ReqonParser } from './parser/index.js';
18
+ import { MissionExecutor } from './interpreter/index.js';
19
+ import { loadMission } from './loader/index.js';
20
+ export function parse(source, filePath) {
21
+ const lexer = new ReqonLexer(source);
22
+ const tokens = lexer.tokenize();
23
+ const parser = new ReqonParser(tokens, source, filePath);
24
+ return parser.parse();
25
+ }
26
+ export async function execute(source, config = {}) {
27
+ const program = parse(source);
28
+ const executor = new MissionExecutor(config);
29
+ return executor.execute(program);
30
+ }
31
+ export async function fromFile(filePath, config = {}) {
32
+ const absolutePath = resolve(filePath);
33
+ const source = await readFile(absolutePath, 'utf-8');
34
+ const program = parse(source, absolutePath);
35
+ const executor = new MissionExecutor(config);
36
+ return executor.execute(program);
37
+ }
38
+ /**
39
+ * Load and execute a mission from a file or folder.
40
+ *
41
+ * Supports both:
42
+ * - Single file: ./sync-invoices.reqon
43
+ * - Folder: ./sync-invoices/ (with mission.reqon + action files)
44
+ */
45
+ export async function fromPath(path, config = {}) {
46
+ const { program } = await loadMission(path);
47
+ const executor = new MissionExecutor(config);
48
+ return executor.execute(program);
49
+ }
50
+ // Tagged template literal for inline missions
51
+ export function reqon(strings, ...values) {
52
+ let source = strings[0];
53
+ for (let i = 0; i < values.length; i++) {
54
+ source += String(values[i]) + strings[i + 1];
55
+ }
56
+ return parse(source);
57
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,168 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parse, execute } from './index.js';
3
+ import { MemoryStore } from './stores/index.js';
4
+ describe('Reqon Integration', () => {
5
+ it('parses and executes a simple mission with dry run', async () => {
6
+ const source = `
7
+ mission TestMission {
8
+ source API {
9
+ auth: bearer,
10
+ base: "https://api.example.com"
11
+ }
12
+
13
+ store items: memory("items")
14
+
15
+ action FetchItems {
16
+ fetch GET "/items"
17
+
18
+ store response -> items {
19
+ key: .id
20
+ }
21
+ }
22
+
23
+ run FetchItems
24
+ }
25
+ `;
26
+ const program = parse(source);
27
+ expect(program.type).toBe('ReqonProgram');
28
+ const result = await execute(source, { dryRun: true, verbose: false });
29
+ expect(result.success).toBe(true);
30
+ expect(result.actionsRun).toContain('FetchItems');
31
+ });
32
+ it('executes a mission with mock data flow', async () => {
33
+ const source = `
34
+ mission MockDataFlow {
35
+ source API {
36
+ auth: bearer,
37
+ base: "https://api.example.com"
38
+ }
39
+
40
+ store raw: memory("raw")
41
+ store processed: memory("processed")
42
+
43
+ action Process {
44
+ for item in raw {
45
+ map item -> Processed {
46
+ id: .id,
47
+ name: .title,
48
+ value: .amount
49
+ }
50
+
51
+ store response -> processed {
52
+ key: .id
53
+ }
54
+ }
55
+ }
56
+
57
+ run Process
58
+ }
59
+ `;
60
+ // Pre-populate the raw store
61
+ const rawStore = new MemoryStore('raw');
62
+ await rawStore.set('1', { id: '1', title: 'Item 1', amount: 100 });
63
+ await rawStore.set('2', { id: '2', title: 'Item 2', amount: 200 });
64
+ const result = await execute(source, {
65
+ dryRun: false,
66
+ verbose: false,
67
+ stores: { raw: rawStore },
68
+ });
69
+ expect(result.success).toBe(true);
70
+ expect(result.actionsRun).toContain('Process');
71
+ // Check processed store
72
+ const processedStore = result.stores.get('processed');
73
+ expect(processedStore).toBeDefined();
74
+ const items = await processedStore.list();
75
+ expect(items).toHaveLength(2);
76
+ expect(items[0]).toMatchObject({ id: '1', name: 'Item 1', value: 100 });
77
+ });
78
+ it('parses the Xero example file', async () => {
79
+ const fs = await import('node:fs/promises');
80
+ const source = await fs.readFile('./examples/xero/invoices.reqon', 'utf-8');
81
+ const program = parse(source);
82
+ expect(program.type).toBe('ReqonProgram');
83
+ expect(program.statements).toHaveLength(1);
84
+ const mission = program.statements[0];
85
+ if (mission.type === 'MissionDefinition') {
86
+ expect(mission.name).toBe('SyncXeroInvoices');
87
+ expect(mission.sources).toHaveLength(1);
88
+ expect(mission.stores).toHaveLength(2);
89
+ expect(mission.actions).toHaveLength(3);
90
+ expect(mission.pipeline.stages).toHaveLength(3);
91
+ }
92
+ });
93
+ it('handles validation failures gracefully', async () => {
94
+ const source = `
95
+ mission ValidationTest {
96
+ source API {
97
+ auth: bearer,
98
+ base: "https://api.example.com"
99
+ }
100
+
101
+ store items: memory("items")
102
+
103
+ action Validate {
104
+ for item in items {
105
+ validate item {
106
+ assume .value > 0
107
+ }
108
+ }
109
+ }
110
+
111
+ run Validate
112
+ }
113
+ `;
114
+ const itemsStore = new MemoryStore('items');
115
+ await itemsStore.set('1', { id: '1', value: -5 }); // Invalid: negative value
116
+ const result = await execute(source, {
117
+ stores: { items: itemsStore },
118
+ });
119
+ // Validation should fail
120
+ expect(result.success).toBe(false);
121
+ expect(result.errors.length).toBeGreaterThan(0);
122
+ });
123
+ it('processes match expressions in map steps', async () => {
124
+ const source = `
125
+ mission MatchTest {
126
+ source API {
127
+ auth: bearer,
128
+ base: "https://api.example.com"
129
+ }
130
+
131
+ store input: memory("input")
132
+ store output: memory("output")
133
+
134
+ action Transform {
135
+ for item in input {
136
+ map item -> Output {
137
+ id: .id,
138
+ status: match .state {
139
+ "A" => "active",
140
+ "I" => "inactive",
141
+ _ => "unknown"
142
+ }
143
+ }
144
+
145
+ store response -> output {
146
+ key: .id
147
+ }
148
+ }
149
+ }
150
+
151
+ run Transform
152
+ }
153
+ `;
154
+ const inputStore = new MemoryStore('input');
155
+ await inputStore.set('1', { id: '1', state: 'A' });
156
+ await inputStore.set('2', { id: '2', state: 'I' });
157
+ await inputStore.set('3', { id: '3', state: 'X' });
158
+ const result = await execute(source, {
159
+ stores: { input: inputStore },
160
+ });
161
+ expect(result.success).toBe(true);
162
+ const outputStore = result.stores.get('output');
163
+ const items = await outputStore.list();
164
+ expect(items).toContainEqual(expect.objectContaining({ id: '1', status: 'active' }));
165
+ expect(items).toContainEqual(expect.objectContaining({ id: '2', status: 'inactive' }));
166
+ expect(items).toContainEqual(expect.objectContaining({ id: '3', status: 'unknown' }));
167
+ });
168
+ });
@@ -0,0 +1,15 @@
1
+ import type { SchemaDefinition } from 'vague-lang';
2
+ import type { StoreAdapter } from '../stores/types.js';
3
+ import type { HttpClient } from './http.js';
4
+ export interface ExecutionContext {
5
+ stores: Map<string, StoreAdapter>;
6
+ sources: Map<string, HttpClient>;
7
+ schemas: Map<string, SchemaDefinition>;
8
+ variables: Map<string, unknown>;
9
+ response?: unknown;
10
+ parent?: ExecutionContext;
11
+ }
12
+ export declare function createContext(): ExecutionContext;
13
+ export declare function childContext(parent: ExecutionContext): ExecutionContext;
14
+ export declare function getVariable(ctx: ExecutionContext, name: string): unknown;
15
+ export declare function setVariable(ctx: ExecutionContext, name: string, value: unknown): void;