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,148 @@
1
+ /**
2
+ * In-memory token store - useful for development and single-process deployments
3
+ */
4
+ export class InMemoryTokenStore {
5
+ tokens = new Map();
6
+ async get(connectionId) {
7
+ const stored = this.tokens.get(connectionId);
8
+ if (!stored)
9
+ return null;
10
+ return {
11
+ accessToken: stored.accessToken,
12
+ refreshToken: stored.refreshToken,
13
+ expiresAt: stored.expiresAt,
14
+ refreshExpiresAt: stored.refreshExpiresAt,
15
+ tokenType: stored.tokenType,
16
+ scope: stored.scope,
17
+ };
18
+ }
19
+ async set(connectionId, tokens) {
20
+ const existing = this.tokens.get(connectionId);
21
+ this.tokens.set(connectionId, {
22
+ ...tokens,
23
+ lastUsedAt: existing?.lastUsedAt ?? new Date(),
24
+ });
25
+ }
26
+ async delete(connectionId) {
27
+ this.tokens.delete(connectionId);
28
+ }
29
+ async touch(connectionId) {
30
+ const stored = this.tokens.get(connectionId);
31
+ if (stored) {
32
+ stored.lastUsedAt = new Date();
33
+ }
34
+ }
35
+ async list() {
36
+ return Array.from(this.tokens.keys());
37
+ }
38
+ /** Get tokens that need proactive refresh (approaching expiry or non-use expiry) */
39
+ async getTokensNeedingRefresh(bufferSeconds = 300) {
40
+ const now = new Date();
41
+ const buffer = bufferSeconds * 1000;
42
+ const needsRefresh = [];
43
+ for (const [connectionId, stored] of this.tokens) {
44
+ // Check access token expiry
45
+ if (stored.expiresAt) {
46
+ const expiresIn = stored.expiresAt.getTime() - now.getTime();
47
+ if (expiresIn < buffer) {
48
+ needsRefresh.push(connectionId);
49
+ continue;
50
+ }
51
+ }
52
+ // Check refresh token expiry from non-use
53
+ if (stored.refreshExpiresAt && stored.lastUsedAt) {
54
+ const refreshExpiresIn = stored.refreshExpiresAt.getTime() - now.getTime();
55
+ if (refreshExpiresIn < buffer) {
56
+ needsRefresh.push(connectionId);
57
+ }
58
+ }
59
+ }
60
+ return needsRefresh;
61
+ }
62
+ }
63
+ /**
64
+ * File-based token store - persists tokens to a JSON file
65
+ * Suitable for CLI tools and single-user scenarios
66
+ */
67
+ export class FileTokenStore {
68
+ filePath;
69
+ cache = null;
70
+ constructor(filePath) {
71
+ this.filePath = filePath;
72
+ }
73
+ async load() {
74
+ if (this.cache)
75
+ return this.cache;
76
+ try {
77
+ const fs = await import('node:fs/promises');
78
+ const content = await fs.readFile(this.filePath, 'utf-8');
79
+ const data = JSON.parse(content);
80
+ // Revive dates
81
+ const map = new Map();
82
+ for (const [key, value] of Object.entries(data)) {
83
+ map.set(key, {
84
+ ...value,
85
+ expiresAt: value.expiresAt ? new Date(value.expiresAt) : undefined,
86
+ refreshExpiresAt: value.refreshExpiresAt ? new Date(value.refreshExpiresAt) : undefined,
87
+ lastUsedAt: value.lastUsedAt ? new Date(value.lastUsedAt) : undefined,
88
+ });
89
+ }
90
+ this.cache = map;
91
+ return map;
92
+ }
93
+ catch {
94
+ this.cache = new Map();
95
+ return this.cache;
96
+ }
97
+ }
98
+ async save() {
99
+ if (!this.cache)
100
+ return;
101
+ const fs = await import('node:fs/promises');
102
+ const data = {};
103
+ for (const [key, value] of this.cache) {
104
+ data[key] = value;
105
+ }
106
+ await fs.writeFile(this.filePath, JSON.stringify(data, null, 2), 'utf-8');
107
+ }
108
+ async get(connectionId) {
109
+ const map = await this.load();
110
+ const stored = map.get(connectionId);
111
+ if (!stored)
112
+ return null;
113
+ return {
114
+ accessToken: stored.accessToken,
115
+ refreshToken: stored.refreshToken,
116
+ expiresAt: stored.expiresAt,
117
+ refreshExpiresAt: stored.refreshExpiresAt,
118
+ tokenType: stored.tokenType,
119
+ scope: stored.scope,
120
+ };
121
+ }
122
+ async set(connectionId, tokens) {
123
+ const map = await this.load();
124
+ const existing = map.get(connectionId);
125
+ map.set(connectionId, {
126
+ ...tokens,
127
+ lastUsedAt: existing?.lastUsedAt ?? new Date(),
128
+ });
129
+ await this.save();
130
+ }
131
+ async delete(connectionId) {
132
+ const map = await this.load();
133
+ map.delete(connectionId);
134
+ await this.save();
135
+ }
136
+ async touch(connectionId) {
137
+ const map = await this.load();
138
+ const stored = map.get(connectionId);
139
+ if (stored) {
140
+ stored.lastUsedAt = new Date();
141
+ await this.save();
142
+ }
143
+ }
144
+ async list() {
145
+ const map = await this.load();
146
+ return Array.from(map.keys());
147
+ }
148
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Auth provider interface - abstracts token retrieval and refresh
3
+ */
4
+ export interface AuthProvider {
5
+ /** Get a valid access token (refreshing if needed) */
6
+ getToken(): Promise<string>;
7
+ /** Force a token refresh */
8
+ refreshToken?(): Promise<string>;
9
+ /** Get token metadata (for monitoring) */
10
+ getTokenInfo?(): TokenInfo;
11
+ }
12
+ export interface TokenInfo {
13
+ /** When the access token expires */
14
+ expiresAt?: Date;
15
+ /** When the refresh token expires (for non-use expiry tracking) */
16
+ refreshExpiresAt?: Date;
17
+ /** Last time the token was successfully used */
18
+ lastUsedAt?: Date;
19
+ /** Connection/tenant identifier */
20
+ connectionId?: string;
21
+ }
22
+ /**
23
+ * OAuth2 token set - what gets stored
24
+ */
25
+ export interface OAuth2Tokens {
26
+ accessToken: string;
27
+ refreshToken?: string;
28
+ expiresAt?: Date;
29
+ refreshExpiresAt?: Date;
30
+ tokenType?: string;
31
+ scope?: string;
32
+ }
33
+ /**
34
+ * Token store interface - pluggable storage backend
35
+ */
36
+ export interface TokenStore {
37
+ /** Get tokens for a connection */
38
+ get(connectionId: string): Promise<OAuth2Tokens | null>;
39
+ /** Store tokens for a connection */
40
+ set(connectionId: string, tokens: OAuth2Tokens): Promise<void>;
41
+ /** Delete tokens for a connection */
42
+ delete(connectionId: string): Promise<void>;
43
+ /** Update last used timestamp */
44
+ touch(connectionId: string): Promise<void>;
45
+ /** List all connections (for proactive refresh) */
46
+ list(): Promise<string[]>;
47
+ }
48
+ /**
49
+ * OAuth2 configuration for token refresh
50
+ */
51
+ export interface OAuth2Config {
52
+ tokenEndpoint: string;
53
+ clientId: string;
54
+ clientSecret?: string;
55
+ /** Seconds before expiry to trigger refresh (default: 300 = 5 min) */
56
+ refreshBuffer?: number;
57
+ }
58
+ /**
59
+ * Rate limit information extracted from response headers
60
+ */
61
+ export interface RateLimitInfo {
62
+ /** Requests remaining in current window */
63
+ remaining?: number;
64
+ /** Total requests allowed in window */
65
+ limit?: number;
66
+ /** When the rate limit resets (Unix timestamp or Date) */
67
+ resetAt?: Date;
68
+ /** Retry after N seconds (from 429 response) */
69
+ retryAfter?: number;
70
+ }
71
+ /**
72
+ * Rate limit strategy
73
+ */
74
+ export type RateLimitStrategy = 'pause' | 'throttle' | 'fail';
75
+ /**
76
+ * Rate limit configuration
77
+ */
78
+ export interface RateLimitConfig {
79
+ /** Strategy when rate limited (default: 'pause') */
80
+ strategy?: RateLimitStrategy;
81
+ /** Max seconds to wait before failing (default: 300) */
82
+ maxWait?: number;
83
+ /** Log warning after waiting this many seconds (default: 10) */
84
+ notifyAt?: number;
85
+ /** Fallback rate limit if no headers (requests per minute) */
86
+ fallbackRpm?: number;
87
+ }
88
+ /**
89
+ * Rate limit event - emitted when rate limiting occurs
90
+ */
91
+ export interface RateLimitEvent {
92
+ source: string;
93
+ endpoint?: string;
94
+ waitSeconds: number;
95
+ remaining?: number;
96
+ resetAt?: Date;
97
+ strategy: RateLimitStrategy;
98
+ }
99
+ /**
100
+ * Rate limit event handlers
101
+ */
102
+ export interface RateLimitCallbacks {
103
+ /** Called when rate limited and waiting */
104
+ onRateLimited?: (event: RateLimitEvent) => void;
105
+ /** Called when rate limit wait is complete */
106
+ onResumed?: (event: {
107
+ source: string;
108
+ endpoint?: string;
109
+ waitedSeconds: number;
110
+ }) => void;
111
+ /** Called periodically during long waits */
112
+ onWaiting?: (event: RateLimitEvent & {
113
+ elapsedSeconds: number;
114
+ }) => void;
115
+ }
116
+ /**
117
+ * Rate limiter interface - tracks and enforces rate limits
118
+ */
119
+ export interface RateLimiter {
120
+ /** Check if we should proceed with a request */
121
+ canProceed(source: string, endpoint?: string): Promise<boolean>;
122
+ /** Wait until we can proceed (blocks if rate limited) */
123
+ waitForCapacity(source: string, endpoint?: string): Promise<void>;
124
+ /** Record rate limit info from a response */
125
+ recordResponse(source: string, info: RateLimitInfo, endpoint?: string): void;
126
+ /** Get current rate limit status */
127
+ getStatus(source: string, endpoint?: string): RateLimitStatus;
128
+ /** Configure rate limiting for a source */
129
+ configure(source: string, config: RateLimitConfig): void;
130
+ /** Set event callbacks */
131
+ setCallbacks(callbacks: RateLimitCallbacks): void;
132
+ /** Get delay for throttle mode (returns 0 if no throttling needed) */
133
+ getThrottleDelay(source: string, endpoint?: string): number;
134
+ }
135
+ export interface RateLimitStatus {
136
+ remaining?: number;
137
+ limit?: number;
138
+ resetAt?: Date;
139
+ isLimited: boolean;
140
+ /** Seconds until reset (if limited) */
141
+ resetInSeconds?: number;
142
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,270 @@
1
+ #!/usr/bin/env node
2
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
3
+ import { resolve, dirname } from 'node:path';
4
+ import { fromPath, Scheduler, loadMission } from './index.js';
5
+ import { ReqonError } from './errors/index.js';
6
+ import { loadEnv, loadCredentials } from './auth/credentials.js';
7
+ import { WebhookServer } from './webhook/index.js';
8
+ async function main() {
9
+ const args = process.argv.slice(2);
10
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
11
+ console.log(`
12
+ Reqon - A DSL for fetch, map, validate pipelines
13
+
14
+ Usage:
15
+ reqon <file.reqon|folder> [options]
16
+
17
+ Options:
18
+ --dry-run Run without making actual HTTP requests
19
+ --verbose Enable verbose logging
20
+ --auth <file> JSON file with auth credentials (supports env var interpolation)
21
+ --env <file> Path to .env file (default: .env in current directory)
22
+ --output <path> Export stores to JSON (file or directory)
23
+ --daemon Run as daemon, executing scheduled missions
24
+ --once Run scheduled missions once immediately, then exit
25
+ --webhook Enable webhook server for 'wait' steps
26
+ --webhook-port <n> Port for webhook server (default: 3000)
27
+ --webhook-url <url> Base URL for webhook endpoints (default: http://localhost:3000)
28
+ --help, -h Show this help message
29
+
30
+ Environment Variables:
31
+ Credentials in --auth files support env var interpolation:
32
+ $VAR_NAME, \${VAR_NAME}, \${VAR_NAME:-default}
33
+
34
+ Auto-discovery from env vars (no --auth file needed):
35
+ REQON_{SOURCE}_TOKEN Bearer token for source
36
+ REQON_{SOURCE}_TYPE Auth type (bearer, oauth2, api_key, basic)
37
+ REQON_{SOURCE}_API_KEY API key for source
38
+
39
+ Examples:
40
+ reqon sync-invoices.reqon --verbose
41
+ reqon ./sync-invoices/ --verbose # folder with mission.reqon + action files
42
+ reqon sync-invoices.reqon --auth ./credentials.json
43
+ reqon sync-invoices.reqon --env .env.production --auth ./credentials.json
44
+ reqon sync-invoices.reqon --output ./output.json
45
+ reqon sync-invoices.reqon --daemon --verbose
46
+ reqon sync-invoices.reqon --webhook --webhook-port 8080 --verbose
47
+ `);
48
+ process.exit(0);
49
+ }
50
+ const filePath = args[0];
51
+ const dryRun = args.includes('--dry-run');
52
+ const verbose = args.includes('--verbose');
53
+ const daemon = args.includes('--daemon');
54
+ const once = args.includes('--once');
55
+ const webhookEnabled = args.includes('--webhook');
56
+ // Parse webhook options
57
+ let webhookPort = 3000;
58
+ const webhookPortIndex = args.indexOf('--webhook-port');
59
+ if (webhookPortIndex !== -1 && args[webhookPortIndex + 1]) {
60
+ webhookPort = parseInt(args[webhookPortIndex + 1], 10);
61
+ }
62
+ let webhookUrl;
63
+ const webhookUrlIndex = args.indexOf('--webhook-url');
64
+ if (webhookUrlIndex !== -1 && args[webhookUrlIndex + 1]) {
65
+ webhookUrl = args[webhookUrlIndex + 1];
66
+ }
67
+ // Load .env file(s)
68
+ let envFile;
69
+ const envIndex = args.indexOf('--env');
70
+ if (envIndex !== -1 && args[envIndex + 1]) {
71
+ envFile = args[envIndex + 1];
72
+ }
73
+ const envResult = loadEnv({ envFile });
74
+ if (verbose && envResult.loaded) {
75
+ console.log(`Loaded ${envResult.count} env vars from: ${envResult.files.join(', ')}`);
76
+ }
77
+ // Load and resolve auth credentials
78
+ let auth;
79
+ const authIndex = args.indexOf('--auth');
80
+ if (authIndex !== -1 && args[authIndex + 1]) {
81
+ const authPath = resolve(args[authIndex + 1]);
82
+ const authContent = await readFile(authPath, 'utf-8');
83
+ const rawAuth = JSON.parse(authContent);
84
+ // Resolve env var references in the auth config
85
+ auth = loadCredentials(rawAuth);
86
+ }
87
+ let outputPath;
88
+ const outputIndex = args.indexOf('--output');
89
+ if (outputIndex !== -1 && args[outputIndex + 1]) {
90
+ outputPath = resolve(args[outputIndex + 1]);
91
+ }
92
+ // Daemon mode: run scheduled missions
93
+ if (daemon || once) {
94
+ await runDaemon(filePath, {
95
+ verbose,
96
+ dryRun,
97
+ auth: auth,
98
+ once,
99
+ });
100
+ return;
101
+ }
102
+ // Single run mode (default)
103
+ console.log(`Running: ${filePath}`);
104
+ // Start webhook server if enabled
105
+ let webhookServer;
106
+ if (webhookEnabled) {
107
+ webhookServer = new WebhookServer({
108
+ port: webhookPort,
109
+ baseUrl: webhookUrl ?? `http://localhost:${webhookPort}`,
110
+ verbose,
111
+ });
112
+ await webhookServer.start();
113
+ console.log(`Webhook server started on port ${webhookPort}`);
114
+ }
115
+ try {
116
+ const result = await fromPath(filePath, {
117
+ dryRun,
118
+ verbose,
119
+ auth: auth,
120
+ webhookServer,
121
+ });
122
+ if (result.success) {
123
+ console.log(`\n✓ Mission completed successfully`);
124
+ console.log(` Duration: ${result.duration}ms`);
125
+ console.log(` Actions run: ${result.actionsRun.join(' → ')}`);
126
+ // Print store stats and optionally export
127
+ const storeData = {};
128
+ for (const [name, store] of result.stores) {
129
+ const items = await store.list();
130
+ console.log(` Store "${name}": ${items.length} items`);
131
+ storeData[name] = items;
132
+ }
133
+ // Export to JSON if requested
134
+ if (outputPath) {
135
+ await mkdir(dirname(outputPath), { recursive: true });
136
+ await writeFile(outputPath, JSON.stringify(storeData, null, 2));
137
+ console.log(` Output written to: ${outputPath}`);
138
+ }
139
+ }
140
+ else {
141
+ console.log(`\n✗ Mission failed`);
142
+ for (const error of result.errors) {
143
+ console.error(` [${error.action}/${error.step}] ${error.message}`);
144
+ }
145
+ process.exit(1);
146
+ }
147
+ }
148
+ catch (error) {
149
+ if (error instanceof ReqonError) {
150
+ console.error(error.format());
151
+ }
152
+ else {
153
+ console.error(`Error: ${error.message}`);
154
+ }
155
+ process.exit(1);
156
+ }
157
+ finally {
158
+ // Stop webhook server if it was started
159
+ if (webhookServer) {
160
+ await webhookServer.stop();
161
+ }
162
+ }
163
+ }
164
+ async function runDaemon(filePath, options) {
165
+ const absolutePath = resolve(filePath);
166
+ let program;
167
+ let baseDir;
168
+ try {
169
+ const result = await loadMission(absolutePath);
170
+ program = result.program;
171
+ baseDir = result.baseDir;
172
+ }
173
+ catch (error) {
174
+ if (error instanceof ReqonError) {
175
+ console.error(error.format());
176
+ }
177
+ else {
178
+ console.error(`Error loading mission: ${error.message}`);
179
+ }
180
+ process.exit(1);
181
+ }
182
+ // Find scheduled missions
183
+ const scheduledMissions = program.statements.filter((s) => s.type === 'MissionDefinition' && s.schedule);
184
+ if (scheduledMissions.length === 0) {
185
+ console.error('No scheduled missions found in the file');
186
+ console.error('Add a schedule to your mission, e.g.:');
187
+ console.error(' schedule: every 6 hours');
188
+ console.error(' schedule: cron "0 */6 * * *"');
189
+ process.exit(1);
190
+ }
191
+ console.log(`Found ${scheduledMissions.length} scheduled mission(s)`);
192
+ // Create scheduler with callbacks
193
+ const scheduler = new Scheduler({
194
+ verbose: options.verbose,
195
+ callbacks: {
196
+ onJobStarted: (event) => {
197
+ console.log(`[${formatTime(event.timestamp)}] Starting: ${event.missionName}`);
198
+ },
199
+ onJobCompleted: (event) => {
200
+ console.log(`[${formatTime(event.timestamp)}] Completed: ${event.missionName} (${event.duration}ms)`);
201
+ },
202
+ onJobFailed: (event) => {
203
+ console.error(`[${formatTime(event.timestamp)}] Failed: ${event.missionName} - ${event.error}`);
204
+ },
205
+ onJobSkipped: (event) => {
206
+ console.log(`[${formatTime(event.timestamp)}] Skipped: ${event.missionName} - ${event.reason}`);
207
+ },
208
+ },
209
+ }, {
210
+ dryRun: options.dryRun,
211
+ verbose: options.verbose,
212
+ auth: options.auth,
213
+ });
214
+ // Register scheduled missions
215
+ scheduler.registerProgram(program, absolutePath);
216
+ // Print job info
217
+ const jobs = scheduler.getJobs();
218
+ console.log('\nScheduled jobs:');
219
+ for (const job of jobs) {
220
+ const schedule = job.schedule;
221
+ let scheduleStr;
222
+ if (schedule.scheduleType === 'interval') {
223
+ scheduleStr = `every ${schedule.interval.value} ${schedule.interval.unit}`;
224
+ }
225
+ else if (schedule.scheduleType === 'cron') {
226
+ scheduleStr = `cron "${schedule.cronExpression}"`;
227
+ }
228
+ else {
229
+ scheduleStr = `at "${schedule.runAt}"`;
230
+ }
231
+ console.log(` - ${job.missionName}: ${scheduleStr}`);
232
+ if (job.nextRun) {
233
+ console.log(` Next run: ${job.nextRun.toISOString()}`);
234
+ }
235
+ }
236
+ console.log('');
237
+ // Handle --once mode
238
+ if (options.once) {
239
+ console.log('Running all scheduled missions once...\n');
240
+ for (const job of jobs) {
241
+ await scheduler.trigger(job.missionName);
242
+ }
243
+ console.log('\nAll missions completed.');
244
+ return;
245
+ }
246
+ // Start daemon mode
247
+ console.log('Starting scheduler daemon (Ctrl+C to stop)...\n');
248
+ // Handle graceful shutdown
249
+ let shuttingDown = false;
250
+ const shutdown = async () => {
251
+ if (shuttingDown)
252
+ return;
253
+ shuttingDown = true;
254
+ console.log('\nShutting down scheduler...');
255
+ await scheduler.stop();
256
+ process.exit(0);
257
+ };
258
+ process.on('SIGINT', shutdown);
259
+ process.on('SIGTERM', shutdown);
260
+ // Start the scheduler
261
+ await scheduler.start();
262
+ // Keep the process running
263
+ await new Promise(() => {
264
+ // This promise never resolves - we wait for SIGINT/SIGTERM
265
+ });
266
+ }
267
+ function formatTime(date) {
268
+ return date.toISOString().replace('T', ' ').substring(0, 19);
269
+ }
270
+ main();
@@ -0,0 +1 @@
1
+ export {};