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 @@
1
+ export {};
@@ -0,0 +1,333 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ReqonLexer } from '../lexer/index.js';
3
+ import { ReqonParser } from './parser.js';
4
+ describe('ReqonParser', () => {
5
+ function parse(source) {
6
+ const lexer = new ReqonLexer(source);
7
+ const tokens = lexer.tokenize();
8
+ const parser = new ReqonParser(tokens);
9
+ return parser.parse();
10
+ }
11
+ it('parses a simple mission', () => {
12
+ const source = `
13
+ mission TestMission {
14
+ source API {
15
+ auth: bearer,
16
+ base: "https://api.example.com"
17
+ }
18
+
19
+ store items: memory("items")
20
+
21
+ action FetchItems {
22
+ fetch GET "/items"
23
+
24
+ store response -> items {
25
+ key: .id
26
+ }
27
+ }
28
+
29
+ run FetchItems
30
+ }
31
+ `;
32
+ const program = parse(source);
33
+ expect(program.type).toBe('ReqonProgram');
34
+ expect(program.statements).toHaveLength(1);
35
+ const mission = program.statements[0];
36
+ expect(mission.type).toBe('MissionDefinition');
37
+ if (mission.type === 'MissionDefinition') {
38
+ expect(mission.name).toBe('TestMission');
39
+ expect(mission.sources).toHaveLength(1);
40
+ expect(mission.stores).toHaveLength(1);
41
+ expect(mission.actions).toHaveLength(1);
42
+ expect(mission.pipeline.stages).toHaveLength(1);
43
+ }
44
+ });
45
+ it('parses fetch with pagination', () => {
46
+ const source = `
47
+ mission PaginatedFetch {
48
+ source API {
49
+ auth: oauth2,
50
+ base: "https://api.example.com"
51
+ }
52
+
53
+ store items: memory("items")
54
+
55
+ action FetchAll {
56
+ fetch GET "/items" {
57
+ paginate: offset(page, 50),
58
+ until: response.items.length == 0
59
+ }
60
+
61
+ store response.items -> items {
62
+ key: .id
63
+ }
64
+ }
65
+
66
+ run FetchAll
67
+ }
68
+ `;
69
+ const program = parse(source);
70
+ const mission = program.statements[0];
71
+ if (mission.type === 'MissionDefinition') {
72
+ const action = mission.actions[0];
73
+ const fetchStep = action.steps[0];
74
+ if (fetchStep.type === 'FetchStep') {
75
+ expect(fetchStep.paginate).toBeDefined();
76
+ expect(fetchStep.paginate?.type).toBe('offset');
77
+ expect(fetchStep.paginate?.pageSize).toBe(50);
78
+ expect(fetchStep.until).toBeDefined();
79
+ }
80
+ }
81
+ });
82
+ it('parses for loops with conditions', () => {
83
+ const source = `
84
+ mission IterateItems {
85
+ source API {
86
+ auth: bearer,
87
+ base: "https://api.example.com"
88
+ }
89
+
90
+ store items: memory("items")
91
+
92
+ action ProcessItems {
93
+ for item in items where .status == "pending" {
94
+ fetch GET "/items/{item.id}"
95
+
96
+ store response -> items {
97
+ key: .id,
98
+ upsert: true
99
+ }
100
+ }
101
+ }
102
+
103
+ run ProcessItems
104
+ }
105
+ `;
106
+ const program = parse(source);
107
+ const mission = program.statements[0];
108
+ if (mission.type === 'MissionDefinition') {
109
+ const action = mission.actions[0];
110
+ const forStep = action.steps[0];
111
+ if (forStep.type === 'ForStep') {
112
+ expect(forStep.variable).toBe('item');
113
+ expect(forStep.condition).toBeDefined();
114
+ expect(forStep.steps).toHaveLength(2);
115
+ }
116
+ }
117
+ });
118
+ it('parses map steps with match expressions', () => {
119
+ const source = `
120
+ mission MapItems {
121
+ source API {
122
+ auth: bearer,
123
+ base: "https://api.example.com"
124
+ }
125
+
126
+ store raw: memory("raw")
127
+ store normalized: memory("normalized")
128
+
129
+ action Normalize {
130
+ for item in raw {
131
+ map item -> StandardItem {
132
+ id: .external_id,
133
+ status: match .state {
134
+ "active" => "enabled",
135
+ "inactive" => "disabled",
136
+ _ => "unknown"
137
+ }
138
+ }
139
+
140
+ store response -> normalized {
141
+ key: .id
142
+ }
143
+ }
144
+ }
145
+
146
+ run Normalize
147
+ }
148
+ `;
149
+ const program = parse(source);
150
+ const mission = program.statements[0];
151
+ if (mission.type === 'MissionDefinition') {
152
+ const action = mission.actions[0];
153
+ const forStep = action.steps[0];
154
+ if (forStep.type === 'ForStep') {
155
+ const mapStep = forStep.steps[0];
156
+ if (mapStep.type === 'MapStep') {
157
+ expect(mapStep.targetSchema).toBe('StandardItem');
158
+ expect(mapStep.mappings).toHaveLength(2);
159
+ }
160
+ }
161
+ }
162
+ });
163
+ it('parses validation steps', () => {
164
+ const source = `
165
+ mission ValidateData {
166
+ source API {
167
+ auth: bearer,
168
+ base: "https://api.example.com"
169
+ }
170
+
171
+ store data: memory("data")
172
+
173
+ action Fetch {
174
+ fetch GET "/data"
175
+
176
+ validate response {
177
+ assume length(.items) > 0,
178
+ assume .count > 0
179
+ }
180
+
181
+ store response.items -> data {
182
+ key: .id
183
+ }
184
+ }
185
+
186
+ run Fetch
187
+ }
188
+ `;
189
+ const program = parse(source);
190
+ const mission = program.statements[0];
191
+ if (mission.type === 'MissionDefinition') {
192
+ const action = mission.actions[0];
193
+ const validateStep = action.steps[1];
194
+ if (validateStep.type === 'ValidateStep') {
195
+ expect(validateStep.constraints).toHaveLength(2);
196
+ }
197
+ }
198
+ });
199
+ it('parses pipeline with multiple stages', () => {
200
+ const source = `
201
+ mission MultiStage {
202
+ source API {
203
+ auth: bearer,
204
+ base: "https://api.example.com"
205
+ }
206
+
207
+ store data: memory("data")
208
+
209
+ action Step1 {
210
+ fetch GET "/step1"
211
+ store response -> data { key: .id }
212
+ }
213
+
214
+ action Step2 {
215
+ fetch GET "/step2"
216
+ store response -> data { key: .id }
217
+ }
218
+
219
+ action Step3 {
220
+ fetch GET "/step3"
221
+ store response -> data { key: .id }
222
+ }
223
+
224
+ run Step1 then Step2 then Step3
225
+ }
226
+ `;
227
+ const program = parse(source);
228
+ const mission = program.statements[0];
229
+ if (mission.type === 'MissionDefinition') {
230
+ expect(mission.pipeline.stages).toHaveLength(3);
231
+ expect(mission.pipeline.stages[0].action).toBe('Step1');
232
+ expect(mission.pipeline.stages[1].action).toBe('Step2');
233
+ expect(mission.pipeline.stages[2].action).toBe('Step3');
234
+ }
235
+ });
236
+ it('parses parallel stages with bracket syntax', () => {
237
+ const source = `
238
+ mission ParallelSync {
239
+ source API {
240
+ auth: bearer,
241
+ base: "https://api.example.com"
242
+ }
243
+
244
+ store orders: memory("orders")
245
+ store payments: memory("payments")
246
+ store reconciled: memory("reconciled")
247
+
248
+ action FetchOrders {
249
+ fetch GET "/orders"
250
+ store response -> orders { key: .id }
251
+ }
252
+
253
+ action FetchPayments {
254
+ fetch GET "/payments"
255
+ store response -> payments { key: .id }
256
+ }
257
+
258
+ action Reconcile {
259
+ fetch GET "/reconcile"
260
+ store response -> reconciled { key: .id }
261
+ }
262
+
263
+ run [FetchOrders, FetchPayments] then Reconcile
264
+ }
265
+ `;
266
+ const program = parse(source);
267
+ const mission = program.statements[0];
268
+ if (mission.type === 'MissionDefinition') {
269
+ expect(mission.pipeline.stages).toHaveLength(2);
270
+ // First stage is parallel
271
+ const parallelStage = mission.pipeline.stages[0];
272
+ expect(parallelStage.actions).toBeDefined();
273
+ expect(parallelStage.actions).toEqual(['FetchOrders', 'FetchPayments']);
274
+ expect(parallelStage.action).toBeUndefined();
275
+ // Second stage is sequential
276
+ const sequentialStage = mission.pipeline.stages[1];
277
+ expect(sequentialStage.action).toBe('Reconcile');
278
+ expect(sequentialStage.actions).toBeUndefined();
279
+ }
280
+ });
281
+ it('parses multiple parallel stages', () => {
282
+ const source = `
283
+ mission ComplexPipeline {
284
+ source API {
285
+ auth: bearer,
286
+ base: "https://api.example.com"
287
+ }
288
+
289
+ store data: memory("data")
290
+
291
+ action A { fetch GET "/a" }
292
+ action B { fetch GET "/b" }
293
+ action C { fetch GET "/c" }
294
+ action D { fetch GET "/d" }
295
+ action E { fetch GET "/e" }
296
+
297
+ run A then [B, C, D] then E
298
+ }
299
+ `;
300
+ const program = parse(source);
301
+ const mission = program.statements[0];
302
+ if (mission.type === 'MissionDefinition') {
303
+ expect(mission.pipeline.stages).toHaveLength(3);
304
+ expect(mission.pipeline.stages[0].action).toBe('A');
305
+ expect(mission.pipeline.stages[1].actions).toEqual(['B', 'C', 'D']);
306
+ expect(mission.pipeline.stages[2].action).toBe('E');
307
+ }
308
+ });
309
+ it('parses parallel stage with three actions', () => {
310
+ const source = `
311
+ mission TripleParallel {
312
+ source API {
313
+ auth: bearer,
314
+ base: "https://api.example.com"
315
+ }
316
+
317
+ store data: memory("data")
318
+
319
+ action X { fetch GET "/x" }
320
+ action Y { fetch GET "/y" }
321
+ action Z { fetch GET "/z" }
322
+
323
+ run [X, Y, Z]
324
+ }
325
+ `;
326
+ const program = parse(source);
327
+ const mission = program.statements[0];
328
+ if (mission.type === 'MissionDefinition') {
329
+ expect(mission.pipeline.stages).toHaveLength(1);
330
+ expect(mission.pipeline.stages[0].actions).toEqual(['X', 'Y', 'Z']);
331
+ }
332
+ });
333
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,241 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ReqonLexer } from '../lexer/index.js';
3
+ import { ReqonParser } from './parser.js';
4
+ function parseSchedule(source) {
5
+ const lexer = new ReqonLexer(source);
6
+ const tokens = lexer.tokenize();
7
+ const parser = new ReqonParser(tokens);
8
+ const program = parser.parse();
9
+ const mission = program.statements.find((s) => s.type === 'MissionDefinition');
10
+ return mission?.schedule;
11
+ }
12
+ describe('Schedule parsing', () => {
13
+ describe('interval schedules', () => {
14
+ it('should parse schedule: every N hours', () => {
15
+ const source = `
16
+ mission Test {
17
+ schedule: every 6 hours
18
+ source API { auth: none, base: "http://api.example.com" }
19
+ action Sync { fetch GET "/data" }
20
+ run Sync
21
+ }
22
+ `;
23
+ const schedule = parseSchedule(source);
24
+ expect(schedule).toBeDefined();
25
+ expect(schedule.scheduleType).toBe('interval');
26
+ expect(schedule.interval).toEqual({ value: 6, unit: 'hours' });
27
+ });
28
+ it('should parse schedule: every N minutes', () => {
29
+ const source = `
30
+ mission Test {
31
+ schedule: every 30 minutes
32
+ source API { auth: none, base: "http://api.example.com" }
33
+ action Sync { fetch GET "/data" }
34
+ run Sync
35
+ }
36
+ `;
37
+ const schedule = parseSchedule(source);
38
+ expect(schedule).toBeDefined();
39
+ expect(schedule.scheduleType).toBe('interval');
40
+ expect(schedule.interval).toEqual({ value: 30, unit: 'minutes' });
41
+ });
42
+ it('should parse schedule: every N seconds', () => {
43
+ const source = `
44
+ mission Test {
45
+ schedule: every 60 seconds
46
+ source API { auth: none, base: "http://api.example.com" }
47
+ action Sync { fetch GET "/data" }
48
+ run Sync
49
+ }
50
+ `;
51
+ const schedule = parseSchedule(source);
52
+ expect(schedule).toBeDefined();
53
+ expect(schedule.scheduleType).toBe('interval');
54
+ expect(schedule.interval).toEqual({ value: 60, unit: 'seconds' });
55
+ });
56
+ it('should parse schedule: every N days', () => {
57
+ const source = `
58
+ mission Test {
59
+ schedule: every 1 days
60
+ source API { auth: none, base: "http://api.example.com" }
61
+ action Sync { fetch GET "/data" }
62
+ run Sync
63
+ }
64
+ `;
65
+ const schedule = parseSchedule(source);
66
+ expect(schedule).toBeDefined();
67
+ expect(schedule.scheduleType).toBe('interval');
68
+ expect(schedule.interval).toEqual({ value: 1, unit: 'days' });
69
+ });
70
+ it('should parse schedule: every N weeks', () => {
71
+ const source = `
72
+ mission Test {
73
+ schedule: every 2 weeks
74
+ source API { auth: none, base: "http://api.example.com" }
75
+ action Sync { fetch GET "/data" }
76
+ run Sync
77
+ }
78
+ `;
79
+ const schedule = parseSchedule(source);
80
+ expect(schedule).toBeDefined();
81
+ expect(schedule.scheduleType).toBe('interval');
82
+ expect(schedule.interval).toEqual({ value: 2, unit: 'weeks' });
83
+ });
84
+ });
85
+ describe('cron schedules', () => {
86
+ it('should parse schedule: cron expression', () => {
87
+ const source = `
88
+ mission Test {
89
+ schedule: cron "0 */6 * * *"
90
+ source API { auth: none, base: "http://api.example.com" }
91
+ action Sync { fetch GET "/data" }
92
+ run Sync
93
+ }
94
+ `;
95
+ const schedule = parseSchedule(source);
96
+ expect(schedule).toBeDefined();
97
+ expect(schedule.scheduleType).toBe('cron');
98
+ expect(schedule.cronExpression).toBe('0 */6 * * *');
99
+ });
100
+ it('should parse complex cron expressions', () => {
101
+ const source = `
102
+ mission Test {
103
+ schedule: cron "30 9 15 * 1-5"
104
+ source API { auth: none, base: "http://api.example.com" }
105
+ action Sync { fetch GET "/data" }
106
+ run Sync
107
+ }
108
+ `;
109
+ const schedule = parseSchedule(source);
110
+ expect(schedule).toBeDefined();
111
+ expect(schedule.scheduleType).toBe('cron');
112
+ expect(schedule.cronExpression).toBe('30 9 15 * 1-5');
113
+ });
114
+ });
115
+ describe('one-time schedules', () => {
116
+ it('should parse schedule: at datetime', () => {
117
+ const source = `
118
+ mission Test {
119
+ schedule: at "2025-01-25T15:00:00Z"
120
+ source API { auth: none, base: "http://api.example.com" }
121
+ action Sync { fetch GET "/data" }
122
+ run Sync
123
+ }
124
+ `;
125
+ const schedule = parseSchedule(source);
126
+ expect(schedule).toBeDefined();
127
+ expect(schedule.scheduleType).toBe('once');
128
+ expect(schedule.runAt).toBe('2025-01-25T15:00:00Z');
129
+ });
130
+ });
131
+ describe('schedule options', () => {
132
+ it('should parse schedule with timezone option', () => {
133
+ const source = `
134
+ mission Test {
135
+ schedule: every 6 hours {
136
+ timezone: "America/New_York"
137
+ }
138
+ source API { auth: none, base: "http://api.example.com" }
139
+ action Sync { fetch GET "/data" }
140
+ run Sync
141
+ }
142
+ `;
143
+ const schedule = parseSchedule(source);
144
+ expect(schedule).toBeDefined();
145
+ expect(schedule.timezone).toBe('America/New_York');
146
+ });
147
+ it('should parse schedule with maxConcurrency option', () => {
148
+ const source = `
149
+ mission Test {
150
+ schedule: every 6 hours {
151
+ maxConcurrency: 2
152
+ }
153
+ source API { auth: none, base: "http://api.example.com" }
154
+ action Sync { fetch GET "/data" }
155
+ run Sync
156
+ }
157
+ `;
158
+ const schedule = parseSchedule(source);
159
+ expect(schedule).toBeDefined();
160
+ expect(schedule.maxConcurrency).toBe(2);
161
+ });
162
+ it('should parse schedule with skipIfRunning option', () => {
163
+ const source = `
164
+ mission Test {
165
+ schedule: every 6 hours {
166
+ skipIfRunning: false
167
+ }
168
+ source API { auth: none, base: "http://api.example.com" }
169
+ action Sync { fetch GET "/data" }
170
+ run Sync
171
+ }
172
+ `;
173
+ const schedule = parseSchedule(source);
174
+ expect(schedule).toBeDefined();
175
+ expect(schedule.skipIfRunning).toBe(false);
176
+ });
177
+ it('should parse schedule with retry config', () => {
178
+ const source = `
179
+ mission Test {
180
+ schedule: every 6 hours {
181
+ retry: {
182
+ maxRetries: 5,
183
+ delaySeconds: 120
184
+ }
185
+ }
186
+ source API { auth: none, base: "http://api.example.com" }
187
+ action Sync { fetch GET "/data" }
188
+ run Sync
189
+ }
190
+ `;
191
+ const schedule = parseSchedule(source);
192
+ expect(schedule).toBeDefined();
193
+ expect(schedule.retryOnFailure).toEqual({
194
+ maxRetries: 5,
195
+ delaySeconds: 120,
196
+ });
197
+ });
198
+ it('should parse schedule with multiple options', () => {
199
+ const source = `
200
+ mission Test {
201
+ schedule: cron "0 9 * * 1-5" {
202
+ timezone: "Europe/London",
203
+ maxConcurrency: 1,
204
+ skipIfRunning: true,
205
+ retry: {
206
+ maxRetries: 3,
207
+ delaySeconds: 60
208
+ }
209
+ }
210
+ source API { auth: none, base: "http://api.example.com" }
211
+ action Sync { fetch GET "/data" }
212
+ run Sync
213
+ }
214
+ `;
215
+ const schedule = parseSchedule(source);
216
+ expect(schedule).toBeDefined();
217
+ expect(schedule.scheduleType).toBe('cron');
218
+ expect(schedule.cronExpression).toBe('0 9 * * 1-5');
219
+ expect(schedule.timezone).toBe('Europe/London');
220
+ expect(schedule.maxConcurrency).toBe(1);
221
+ expect(schedule.skipIfRunning).toBe(true);
222
+ expect(schedule.retryOnFailure).toEqual({
223
+ maxRetries: 3,
224
+ delaySeconds: 60,
225
+ });
226
+ });
227
+ });
228
+ describe('mission without schedule', () => {
229
+ it('should parse mission without schedule', () => {
230
+ const source = `
231
+ mission Test {
232
+ source API { auth: none, base: "http://api.example.com" }
233
+ action Sync { fetch GET "/data" }
234
+ run Sync
235
+ }
236
+ `;
237
+ const schedule = parseSchedule(source);
238
+ expect(schedule).toBeUndefined();
239
+ });
240
+ });
241
+ });
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Reqon plugin for Vague.
3
+ *
4
+ * This plugin registers Reqon's keywords and statement parsers with Vague,
5
+ * allowing Vague to parse Reqon syntax when the plugin is registered.
6
+ *
7
+ * Usage:
8
+ * import { registerPlugin } from 'vague-lang';
9
+ * import { reqonPlugin } from 'reqon';
10
+ * registerPlugin(reqonPlugin);
11
+ *
12
+ * Or simply import the plugin module to auto-register:
13
+ * import 'reqon/plugin';
14
+ */
15
+ import { type VaguePlugin } from 'vague-lang';
16
+ /**
17
+ * The Reqon plugin for Vague.
18
+ *
19
+ * Registers all Reqon keywords with Vague's lexer, allowing Vague's
20
+ * lexer to tokenize Reqon source code.
21
+ */
22
+ export declare const reqonPlugin: VaguePlugin;
23
+ /**
24
+ * Register Reqon with Vague's plugin system.
25
+ * Safe to call multiple times - will only register once.
26
+ */
27
+ export declare function registerReqonPlugin(): void;
28
+ /**
29
+ * Unregister Reqon from Vague's plugin system.
30
+ */
31
+ export declare function unregisterReqonPlugin(): void;
32
+ /**
33
+ * Check if Reqon plugin is currently registered.
34
+ */
35
+ export declare function isReqonPluginRegistered(): boolean;
package/dist/plugin.js ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Reqon plugin for Vague.
3
+ *
4
+ * This plugin registers Reqon's keywords and statement parsers with Vague,
5
+ * allowing Vague to parse Reqon syntax when the plugin is registered.
6
+ *
7
+ * Usage:
8
+ * import { registerPlugin } from 'vague-lang';
9
+ * import { reqonPlugin } from 'reqon';
10
+ * registerPlugin(reqonPlugin);
11
+ *
12
+ * Or simply import the plugin module to auto-register:
13
+ * import 'reqon/plugin';
14
+ */
15
+ import { registerPlugin, unregisterPlugin, } from 'vague-lang';
16
+ import { REQON_KEYWORDS } from './lexer/tokens.js';
17
+ /**
18
+ * Convert REQON_KEYWORDS map to PluginKeyword array for Vague plugin system.
19
+ */
20
+ function buildKeywords() {
21
+ const keywords = [];
22
+ for (const [keyword, tokenType] of Object.entries(REQON_KEYWORDS)) {
23
+ keywords.push({
24
+ keyword,
25
+ tokenType: tokenType,
26
+ });
27
+ }
28
+ return keywords;
29
+ }
30
+ /**
31
+ * The Reqon plugin for Vague.
32
+ *
33
+ * Registers all Reqon keywords with Vague's lexer, allowing Vague's
34
+ * lexer to tokenize Reqon source code.
35
+ */
36
+ export const reqonPlugin = {
37
+ name: 'reqon',
38
+ keywords: buildKeywords(),
39
+ // Statement parsers will be added when we refactor ReqonParser
40
+ };
41
+ let isRegistered = false;
42
+ /**
43
+ * Register Reqon with Vague's plugin system.
44
+ * Safe to call multiple times - will only register once.
45
+ */
46
+ export function registerReqonPlugin() {
47
+ if (!isRegistered) {
48
+ registerPlugin(reqonPlugin);
49
+ isRegistered = true;
50
+ }
51
+ }
52
+ /**
53
+ * Unregister Reqon from Vague's plugin system.
54
+ */
55
+ export function unregisterReqonPlugin() {
56
+ if (isRegistered) {
57
+ unregisterPlugin('reqon');
58
+ isRegistered = false;
59
+ }
60
+ }
61
+ /**
62
+ * Check if Reqon plugin is currently registered.
63
+ */
64
+ export function isReqonPluginRegistered() {
65
+ return isRegistered;
66
+ }
67
+ // Auto-register on import
68
+ registerReqonPlugin();