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,32 @@
1
+ import type { ScheduleDefinition, IntervalSchedule } from '../ast/nodes.js';
2
+ /**
3
+ * Parse a cron expression and calculate the next run time
4
+ *
5
+ * Cron format: "minute hour day-of-month month day-of-week"
6
+ * Supports: numbers, ranges (1-5), steps (*​/5), lists (1,3,5), and wildcards (*)
7
+ */
8
+ export declare function parseCronExpression(expression: string): CronSchedule;
9
+ interface CronSchedule {
10
+ minute: number[];
11
+ hour: number[];
12
+ dayOfMonth: number[];
13
+ month: number[];
14
+ dayOfWeek: number[];
15
+ }
16
+ /**
17
+ * Calculate the next run time for a cron schedule
18
+ */
19
+ export declare function getNextCronRun(schedule: CronSchedule, after?: Date): Date;
20
+ /**
21
+ * Convert interval schedule to milliseconds
22
+ */
23
+ export declare function intervalToMs(interval: IntervalSchedule): number;
24
+ /**
25
+ * Calculate next run time based on schedule definition
26
+ */
27
+ export declare function getNextRunTime(schedule: ScheduleDefinition, after?: Date): Date | null;
28
+ /**
29
+ * Check if a schedule should run now (within the check interval)
30
+ */
31
+ export declare function shouldRunNow(schedule: ScheduleDefinition, lastRun: Date | undefined, checkIntervalMs?: number): boolean;
32
+ export {};
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Parse a cron expression and calculate the next run time
3
+ *
4
+ * Cron format: "minute hour day-of-month month day-of-week"
5
+ * Supports: numbers, ranges (1-5), steps (*​/5), lists (1,3,5), and wildcards (*)
6
+ */
7
+ export function parseCronExpression(expression) {
8
+ const parts = expression.trim().split(/\s+/);
9
+ if (parts.length !== 5) {
10
+ throw new Error(`Invalid cron expression: expected 5 fields, got ${parts.length}`);
11
+ }
12
+ return {
13
+ minute: parseField(parts[0], 0, 59),
14
+ hour: parseField(parts[1], 0, 23),
15
+ dayOfMonth: parseField(parts[2], 1, 31),
16
+ month: parseField(parts[3], 1, 12),
17
+ dayOfWeek: parseField(parts[4], 0, 6), // 0 = Sunday
18
+ };
19
+ }
20
+ function parseField(field, min, max) {
21
+ const values = new Set();
22
+ for (const part of field.split(',')) {
23
+ if (part === '*') {
24
+ // All values
25
+ for (let i = min; i <= max; i++) {
26
+ values.add(i);
27
+ }
28
+ }
29
+ else if (part.includes('/')) {
30
+ // Step values (e.g., */5 or 1-10/2)
31
+ const [range, stepStr] = part.split('/');
32
+ const step = parseInt(stepStr, 10);
33
+ let start = min;
34
+ let end = max;
35
+ if (range !== '*') {
36
+ if (range.includes('-')) {
37
+ const [rangeStart, rangeEnd] = range.split('-').map((n) => parseInt(n, 10));
38
+ start = rangeStart;
39
+ end = rangeEnd;
40
+ }
41
+ else {
42
+ start = parseInt(range, 10);
43
+ }
44
+ }
45
+ for (let i = start; i <= end; i += step) {
46
+ values.add(i);
47
+ }
48
+ }
49
+ else if (part.includes('-')) {
50
+ // Range (e.g., 1-5)
51
+ const [start, end] = part.split('-').map((n) => parseInt(n, 10));
52
+ for (let i = start; i <= end; i++) {
53
+ values.add(i);
54
+ }
55
+ }
56
+ else {
57
+ // Single value
58
+ values.add(parseInt(part, 10));
59
+ }
60
+ }
61
+ // Validate all values are in range
62
+ for (const value of values) {
63
+ if (value < min || value > max) {
64
+ throw new Error(`Cron field value ${value} out of range [${min}, ${max}]`);
65
+ }
66
+ }
67
+ return Array.from(values).sort((a, b) => a - b);
68
+ }
69
+ /**
70
+ * Calculate the next run time for a cron schedule
71
+ */
72
+ export function getNextCronRun(schedule, after = new Date()) {
73
+ const next = new Date(after);
74
+ next.setSeconds(0, 0);
75
+ next.setMinutes(next.getMinutes() + 1); // Start from next minute
76
+ // Try up to 4 years to find a match
77
+ const maxIterations = 4 * 366 * 24 * 60;
78
+ for (let i = 0; i < maxIterations; i++) {
79
+ // Check month
80
+ if (!schedule.month.includes(next.getMonth() + 1)) {
81
+ // Move to first day of next matching month
82
+ next.setMonth(next.getMonth() + 1);
83
+ next.setDate(1);
84
+ next.setHours(0, 0, 0, 0);
85
+ continue;
86
+ }
87
+ // Check day of month
88
+ if (!schedule.dayOfMonth.includes(next.getDate())) {
89
+ next.setDate(next.getDate() + 1);
90
+ next.setHours(0, 0, 0, 0);
91
+ continue;
92
+ }
93
+ // Check day of week
94
+ if (!schedule.dayOfWeek.includes(next.getDay())) {
95
+ next.setDate(next.getDate() + 1);
96
+ next.setHours(0, 0, 0, 0);
97
+ continue;
98
+ }
99
+ // Check hour
100
+ if (!schedule.hour.includes(next.getHours())) {
101
+ next.setHours(next.getHours() + 1);
102
+ next.setMinutes(0, 0, 0);
103
+ continue;
104
+ }
105
+ // Check minute
106
+ if (!schedule.minute.includes(next.getMinutes())) {
107
+ next.setMinutes(next.getMinutes() + 1);
108
+ continue;
109
+ }
110
+ // Found a match!
111
+ return next;
112
+ }
113
+ throw new Error('Could not find next cron run time within 4 years');
114
+ }
115
+ /**
116
+ * Convert interval schedule to milliseconds
117
+ */
118
+ export function intervalToMs(interval) {
119
+ const multipliers = {
120
+ seconds: 1000,
121
+ minutes: 60 * 1000,
122
+ hours: 60 * 60 * 1000,
123
+ days: 24 * 60 * 60 * 1000,
124
+ weeks: 7 * 24 * 60 * 60 * 1000,
125
+ };
126
+ return interval.value * multipliers[interval.unit];
127
+ }
128
+ /**
129
+ * Calculate next run time based on schedule definition
130
+ */
131
+ export function getNextRunTime(schedule, after = new Date()) {
132
+ switch (schedule.scheduleType) {
133
+ case 'interval': {
134
+ if (!schedule.interval) {
135
+ throw new Error('Interval schedule missing interval configuration');
136
+ }
137
+ const intervalMs = intervalToMs(schedule.interval);
138
+ return new Date(after.getTime() + intervalMs);
139
+ }
140
+ case 'cron': {
141
+ if (!schedule.cronExpression) {
142
+ throw new Error('Cron schedule missing cron expression');
143
+ }
144
+ const cronSchedule = parseCronExpression(schedule.cronExpression);
145
+ return getNextCronRun(cronSchedule, after);
146
+ }
147
+ case 'once': {
148
+ if (!schedule.runAt) {
149
+ throw new Error('One-time schedule missing runAt datetime');
150
+ }
151
+ const runAt = new Date(schedule.runAt);
152
+ // If the scheduled time is in the past, return null (job should not run)
153
+ if (runAt <= after) {
154
+ return null;
155
+ }
156
+ return runAt;
157
+ }
158
+ default:
159
+ throw new Error(`Unknown schedule type: ${schedule.scheduleType}`);
160
+ }
161
+ }
162
+ /**
163
+ * Check if a schedule should run now (within the check interval)
164
+ */
165
+ export function shouldRunNow(schedule, lastRun, checkIntervalMs = 1000) {
166
+ const now = new Date();
167
+ switch (schedule.scheduleType) {
168
+ case 'interval': {
169
+ if (!lastRun)
170
+ return true; // Never run before, run now
171
+ if (!schedule.interval)
172
+ return false;
173
+ const intervalMs = intervalToMs(schedule.interval);
174
+ const elapsed = now.getTime() - lastRun.getTime();
175
+ return elapsed >= intervalMs;
176
+ }
177
+ case 'cron': {
178
+ if (!schedule.cronExpression)
179
+ return false;
180
+ const cronSchedule = parseCronExpression(schedule.cronExpression);
181
+ const nextRun = getNextCronRun(cronSchedule, lastRun ?? new Date(0));
182
+ // Check if we're within the check interval of the next run time
183
+ const diff = Math.abs(now.getTime() - nextRun.getTime());
184
+ return diff <= checkIntervalMs;
185
+ }
186
+ case 'once': {
187
+ if (!schedule.runAt)
188
+ return false;
189
+ if (lastRun)
190
+ return false; // Already ran
191
+ const runAt = new Date(schedule.runAt);
192
+ const diff = Math.abs(now.getTime() - runAt.getTime());
193
+ return diff <= checkIntervalMs;
194
+ }
195
+ default:
196
+ return false;
197
+ }
198
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,188 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseCronExpression, getNextCronRun, intervalToMs, getNextRunTime, shouldRunNow, } from './cron-parser.js';
3
+ describe('parseCronExpression', () => {
4
+ it('should parse simple cron expressions', () => {
5
+ const schedule = parseCronExpression('0 * * * *');
6
+ expect(schedule.minute).toEqual([0]);
7
+ expect(schedule.hour).toHaveLength(24); // All hours
8
+ expect(schedule.dayOfMonth).toHaveLength(31);
9
+ expect(schedule.month).toHaveLength(12);
10
+ expect(schedule.dayOfWeek).toHaveLength(7);
11
+ });
12
+ it('should parse specific values', () => {
13
+ const schedule = parseCronExpression('30 9 15 6 1');
14
+ expect(schedule.minute).toEqual([30]);
15
+ expect(schedule.hour).toEqual([9]);
16
+ expect(schedule.dayOfMonth).toEqual([15]);
17
+ expect(schedule.month).toEqual([6]);
18
+ expect(schedule.dayOfWeek).toEqual([1]);
19
+ });
20
+ it('should parse ranges', () => {
21
+ const schedule = parseCronExpression('0-5 9-17 * * 1-5');
22
+ expect(schedule.minute).toEqual([0, 1, 2, 3, 4, 5]);
23
+ expect(schedule.hour).toEqual([9, 10, 11, 12, 13, 14, 15, 16, 17]);
24
+ expect(schedule.dayOfWeek).toEqual([1, 2, 3, 4, 5]);
25
+ });
26
+ it('should parse step values', () => {
27
+ const schedule = parseCronExpression('*/15 */6 * * *');
28
+ expect(schedule.minute).toEqual([0, 15, 30, 45]);
29
+ expect(schedule.hour).toEqual([0, 6, 12, 18]);
30
+ });
31
+ it('should parse lists', () => {
32
+ const schedule = parseCronExpression('0,30 9,12,18 * * *');
33
+ expect(schedule.minute).toEqual([0, 30]);
34
+ expect(schedule.hour).toEqual([9, 12, 18]);
35
+ });
36
+ it('should parse combined expressions', () => {
37
+ const schedule = parseCronExpression('0 9-17/2 * * 1-5');
38
+ expect(schedule.minute).toEqual([0]);
39
+ expect(schedule.hour).toEqual([9, 11, 13, 15, 17]);
40
+ expect(schedule.dayOfWeek).toEqual([1, 2, 3, 4, 5]);
41
+ });
42
+ it('should throw on invalid cron expression', () => {
43
+ expect(() => parseCronExpression('0 * *')).toThrow('expected 5 fields');
44
+ });
45
+ it('should throw on out of range values', () => {
46
+ expect(() => parseCronExpression('60 * * * *')).toThrow('out of range');
47
+ expect(() => parseCronExpression('* 25 * * *')).toThrow('out of range');
48
+ });
49
+ });
50
+ describe('getNextCronRun', () => {
51
+ it('should calculate next run for every minute', () => {
52
+ const schedule = parseCronExpression('* * * * *');
53
+ const now = new Date('2025-01-20T10:30:00Z');
54
+ const next = getNextCronRun(schedule, now);
55
+ expect(next.getMinutes()).toBe(31);
56
+ expect(next.getHours()).toBe(10);
57
+ });
58
+ it('should calculate next run for specific time', () => {
59
+ const schedule = parseCronExpression('0 9 * * *');
60
+ const now = new Date('2025-01-20T10:30:00Z');
61
+ const next = getNextCronRun(schedule, now);
62
+ // Should be 9:00 the next day
63
+ expect(next.getMinutes()).toBe(0);
64
+ expect(next.getHours()).toBe(9);
65
+ expect(next.getDate()).toBe(21);
66
+ });
67
+ it('should handle hourly schedules', () => {
68
+ const schedule = parseCronExpression('0 * * * *');
69
+ const now = new Date('2025-01-20T10:30:00Z');
70
+ const next = getNextCronRun(schedule, now);
71
+ expect(next.getMinutes()).toBe(0);
72
+ expect(next.getHours()).toBe(11);
73
+ });
74
+ it('should handle day of week constraints', () => {
75
+ const schedule = parseCronExpression('0 9 * * 1'); // Monday only
76
+ const now = new Date('2025-01-20T10:30:00Z'); // This is a Monday
77
+ const next = getNextCronRun(schedule, now);
78
+ // Should be next Monday at 9:00
79
+ expect(next.getDay()).toBe(1); // Monday
80
+ expect(next.getHours()).toBe(9);
81
+ expect(next.getMinutes()).toBe(0);
82
+ });
83
+ });
84
+ describe('intervalToMs', () => {
85
+ it('should convert seconds to ms', () => {
86
+ const interval = { value: 30, unit: 'seconds' };
87
+ expect(intervalToMs(interval)).toBe(30_000);
88
+ });
89
+ it('should convert minutes to ms', () => {
90
+ const interval = { value: 5, unit: 'minutes' };
91
+ expect(intervalToMs(interval)).toBe(5 * 60 * 1000);
92
+ });
93
+ it('should convert hours to ms', () => {
94
+ const interval = { value: 6, unit: 'hours' };
95
+ expect(intervalToMs(interval)).toBe(6 * 60 * 60 * 1000);
96
+ });
97
+ it('should convert days to ms', () => {
98
+ const interval = { value: 1, unit: 'days' };
99
+ expect(intervalToMs(interval)).toBe(24 * 60 * 60 * 1000);
100
+ });
101
+ it('should convert weeks to ms', () => {
102
+ const interval = { value: 1, unit: 'weeks' };
103
+ expect(intervalToMs(interval)).toBe(7 * 24 * 60 * 60 * 1000);
104
+ });
105
+ });
106
+ describe('getNextRunTime', () => {
107
+ it('should calculate next run for interval schedule', () => {
108
+ const schedule = {
109
+ type: 'ScheduleDefinition',
110
+ scheduleType: 'interval',
111
+ interval: { value: 6, unit: 'hours' },
112
+ };
113
+ const now = new Date('2025-01-20T10:00:00Z');
114
+ const next = getNextRunTime(schedule, now);
115
+ expect(next).not.toBeNull();
116
+ expect(next.getTime()).toBe(now.getTime() + 6 * 60 * 60 * 1000);
117
+ });
118
+ it('should calculate next run for cron schedule', () => {
119
+ const schedule = {
120
+ type: 'ScheduleDefinition',
121
+ scheduleType: 'cron',
122
+ cronExpression: '0 */6 * * *',
123
+ };
124
+ const now = new Date('2025-01-20T10:30:00Z');
125
+ const next = getNextRunTime(schedule, now);
126
+ expect(next).not.toBeNull();
127
+ expect(next.getMinutes()).toBe(0);
128
+ expect(next.getHours()).toBe(12); // Next 6-hour mark
129
+ });
130
+ it('should calculate next run for one-time schedule', () => {
131
+ const schedule = {
132
+ type: 'ScheduleDefinition',
133
+ scheduleType: 'once',
134
+ runAt: '2025-01-25T15:00:00Z',
135
+ };
136
+ const now = new Date('2025-01-20T10:00:00Z');
137
+ const next = getNextRunTime(schedule, now);
138
+ expect(next).not.toBeNull();
139
+ expect(next.toISOString()).toBe('2025-01-25T15:00:00.000Z');
140
+ });
141
+ it('should return null for past one-time schedule', () => {
142
+ const schedule = {
143
+ type: 'ScheduleDefinition',
144
+ scheduleType: 'once',
145
+ runAt: '2025-01-15T15:00:00Z',
146
+ };
147
+ const now = new Date('2025-01-20T10:00:00Z');
148
+ const next = getNextRunTime(schedule, now);
149
+ expect(next).toBeNull();
150
+ });
151
+ });
152
+ describe('shouldRunNow', () => {
153
+ it('should return true for interval schedule that is due', () => {
154
+ const schedule = {
155
+ type: 'ScheduleDefinition',
156
+ scheduleType: 'interval',
157
+ interval: { value: 1, unit: 'hours' },
158
+ };
159
+ const lastRun = new Date(Date.now() - 61 * 60 * 1000); // 61 minutes ago
160
+ expect(shouldRunNow(schedule, lastRun)).toBe(true);
161
+ });
162
+ it('should return false for interval schedule not yet due', () => {
163
+ const schedule = {
164
+ type: 'ScheduleDefinition',
165
+ scheduleType: 'interval',
166
+ interval: { value: 1, unit: 'hours' },
167
+ };
168
+ const lastRun = new Date(Date.now() - 30 * 60 * 1000); // 30 minutes ago
169
+ expect(shouldRunNow(schedule, lastRun)).toBe(false);
170
+ });
171
+ it('should return true for first run of interval schedule', () => {
172
+ const schedule = {
173
+ type: 'ScheduleDefinition',
174
+ scheduleType: 'interval',
175
+ interval: { value: 1, unit: 'hours' },
176
+ };
177
+ expect(shouldRunNow(schedule, undefined)).toBe(true);
178
+ });
179
+ it('should return false for one-time schedule that already ran', () => {
180
+ const schedule = {
181
+ type: 'ScheduleDefinition',
182
+ scheduleType: 'once',
183
+ runAt: '2025-01-15T15:00:00Z',
184
+ };
185
+ const lastRun = new Date('2025-01-15T15:00:00Z');
186
+ expect(shouldRunNow(schedule, lastRun)).toBe(false);
187
+ });
188
+ });
@@ -0,0 +1,3 @@
1
+ export { Scheduler } from './scheduler.js';
2
+ export { parseCronExpression, getNextRunTime, intervalToMs, shouldRunNow } from './cron-parser.js';
3
+ export type { ScheduledJob, SchedulerState, ScheduleEvent, SchedulerCallbacks, SchedulerConfig, ScheduledMission, } from './types.js';
@@ -0,0 +1,2 @@
1
+ export { Scheduler } from './scheduler.js';
2
+ export { parseCronExpression, getNextRunTime, intervalToMs, shouldRunNow } from './cron-parser.js';
@@ -0,0 +1,81 @@
1
+ import type { ReqonProgram, MissionDefinition } from '../ast/nodes.js';
2
+ import type { ExecutorConfig, ExecutionResult } from '../interpreter/executor.js';
3
+ import type { ScheduledJob, SchedulerConfig } from './types.js';
4
+ /**
5
+ * Scheduler for running missions on a schedule
6
+ */
7
+ export declare class Scheduler {
8
+ private config;
9
+ private missions;
10
+ private state;
11
+ private running;
12
+ private checkTimer;
13
+ private retryTimers;
14
+ private executorConfig;
15
+ constructor(config?: SchedulerConfig, executorConfig?: ExecutorConfig);
16
+ /**
17
+ * Register a mission for scheduling
18
+ */
19
+ register(mission: MissionDefinition, filePath: string): void;
20
+ /**
21
+ * Register all scheduled missions from a program
22
+ */
23
+ registerProgram(program: ReqonProgram, filePath: string): void;
24
+ /**
25
+ * Start the scheduler
26
+ */
27
+ start(): Promise<void>;
28
+ /**
29
+ * Stop the scheduler
30
+ */
31
+ stop(): Promise<void>;
32
+ /**
33
+ * Check all jobs and run any that are due
34
+ */
35
+ private checkAndRun;
36
+ /**
37
+ * Run a scheduled job
38
+ */
39
+ private runJob;
40
+ /**
41
+ * Handle job failure with optional retry
42
+ */
43
+ private handleFailure;
44
+ /**
45
+ * Enable a job
46
+ */
47
+ enable(missionName: string): void;
48
+ /**
49
+ * Disable a job
50
+ */
51
+ disable(missionName: string): void;
52
+ /**
53
+ * Get job status
54
+ */
55
+ getJob(missionName: string): ScheduledJob | undefined;
56
+ /**
57
+ * Get all jobs
58
+ */
59
+ getJobs(): ScheduledJob[];
60
+ /**
61
+ * Trigger a job to run immediately
62
+ */
63
+ trigger(missionName: string): Promise<ExecutionResult | null>;
64
+ /**
65
+ * Load state from disk
66
+ */
67
+ private loadState;
68
+ /**
69
+ * Save state to disk
70
+ */
71
+ private saveState;
72
+ /**
73
+ * Emit a schedule event
74
+ */
75
+ private emitEvent;
76
+ /**
77
+ * Format schedule for display
78
+ */
79
+ private formatSchedule;
80
+ private log;
81
+ }