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,4 @@
1
+ export { loadOAS, resolveOperation, getResponseSchema, clearCache } from './loader.js';
2
+ export type { OASSource, OASOperation, OpenAPISpec } from './loader.js';
3
+ export { validateResponse } from './validator.js';
4
+ export type { ValidationResult, ValidationError } from './validator.js';
@@ -0,0 +1,2 @@
1
+ export { loadOAS, resolveOperation, getResponseSchema, clearCache } from './loader.js';
2
+ export { validateResponse } from './validator.js';
@@ -0,0 +1,21 @@
1
+ import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
2
+ export type OpenAPISpec = OpenAPIV3.Document | OpenAPIV3_1.Document;
3
+ export interface OASOperation {
4
+ operationId: string;
5
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
6
+ path: string;
7
+ parameters?: OpenAPIV3.ParameterObject[];
8
+ requestBody?: OpenAPIV3.RequestBodyObject;
9
+ responses?: OpenAPIV3.ResponsesObject;
10
+ security?: OpenAPIV3.SecurityRequirementObject[];
11
+ }
12
+ export interface OASSource {
13
+ spec: OpenAPISpec;
14
+ baseUrl: string;
15
+ operations: Map<string, OASOperation>;
16
+ schemas: Map<string, OpenAPIV3.SchemaObject>;
17
+ }
18
+ export declare function loadOAS(specPath: string, forceReload?: boolean): Promise<OASSource>;
19
+ export declare function resolveOperation(source: OASSource, operationId: string): OASOperation;
20
+ export declare function getResponseSchema(source: OASSource, operationId: string, statusCode?: string): OpenAPIV3.SchemaObject | undefined;
21
+ export declare function clearCache(): void;
@@ -0,0 +1,82 @@
1
+ import SwaggerParser from '@apidevtools/swagger-parser';
2
+ // Cache loaded specs to avoid re-parsing
3
+ const specCache = new Map();
4
+ export async function loadOAS(specPath, forceReload = false) {
5
+ if (!forceReload && specCache.has(specPath)) {
6
+ return specCache.get(specPath);
7
+ }
8
+ const api = await SwaggerParser.dereference(specPath);
9
+ const baseUrl = extractBaseUrl(api);
10
+ const operations = extractOperations(api);
11
+ const schemas = extractSchemas(api);
12
+ const source = {
13
+ spec: api,
14
+ baseUrl,
15
+ operations,
16
+ schemas,
17
+ };
18
+ specCache.set(specPath, source);
19
+ return source;
20
+ }
21
+ function extractBaseUrl(spec) {
22
+ if (spec.servers && spec.servers.length > 0) {
23
+ return spec.servers[0].url;
24
+ }
25
+ return '';
26
+ }
27
+ function extractOperations(spec) {
28
+ const operations = new Map();
29
+ if (!spec.paths)
30
+ return operations;
31
+ for (const [path, pathItem] of Object.entries(spec.paths)) {
32
+ if (!pathItem)
33
+ continue;
34
+ const methods = ['get', 'post', 'put', 'patch', 'delete'];
35
+ for (const method of methods) {
36
+ const operation = pathItem[method];
37
+ if (!operation?.operationId)
38
+ continue;
39
+ operations.set(operation.operationId, {
40
+ operationId: operation.operationId,
41
+ method: method.toUpperCase(),
42
+ path,
43
+ parameters: operation.parameters,
44
+ requestBody: operation.requestBody,
45
+ responses: operation.responses,
46
+ security: operation.security,
47
+ });
48
+ }
49
+ }
50
+ return operations;
51
+ }
52
+ function extractSchemas(spec) {
53
+ const schemas = new Map();
54
+ const components = spec.components;
55
+ if (!components?.schemas)
56
+ return schemas;
57
+ for (const [name, schema] of Object.entries(components.schemas)) {
58
+ schemas.set(name, schema);
59
+ }
60
+ return schemas;
61
+ }
62
+ export function resolveOperation(source, operationId) {
63
+ const operation = source.operations.get(operationId);
64
+ if (!operation) {
65
+ const available = Array.from(source.operations.keys()).slice(0, 5).join(', ');
66
+ throw new Error(`Operation '${operationId}' not found in OAS spec. Available: ${available}...`);
67
+ }
68
+ return operation;
69
+ }
70
+ export function getResponseSchema(source, operationId, statusCode = '200') {
71
+ const operation = resolveOperation(source, operationId);
72
+ const response = operation.responses?.[statusCode];
73
+ if (!response?.content)
74
+ return undefined;
75
+ const jsonContent = response.content['application/json'];
76
+ if (!jsonContent?.schema)
77
+ return undefined;
78
+ return jsonContent.schema;
79
+ }
80
+ export function clearCache() {
81
+ specCache.clear();
82
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,218 @@
1
+ import { describe, it, expect, beforeAll } from 'vitest';
2
+ import { loadOAS, resolveOperation, getResponseSchema, clearCache } from './loader.js';
3
+ import { validateResponse } from './validator.js';
4
+ import { ReqonLexer } from '../lexer/index.js';
5
+ import { ReqonParser } from '../parser/parser.js';
6
+ describe('OAS Loader', () => {
7
+ beforeAll(() => {
8
+ clearCache();
9
+ });
10
+ it('loads and parses an OpenAPI spec', async () => {
11
+ const source = await loadOAS('./examples/petstore/openapi.yaml');
12
+ expect(source.baseUrl).toBe('https://api.petstore.example.com/v1');
13
+ expect(source.operations.size).toBe(3);
14
+ expect(source.schemas.size).toBe(2);
15
+ });
16
+ it('resolves operations by operationId', async () => {
17
+ const source = await loadOAS('./examples/petstore/openapi.yaml');
18
+ const listPets = resolveOperation(source, 'listPets');
19
+ expect(listPets.method).toBe('GET');
20
+ expect(listPets.path).toBe('/pets');
21
+ const getPet = resolveOperation(source, 'getPet');
22
+ expect(getPet.method).toBe('GET');
23
+ expect(getPet.path).toBe('/pets/{petId}');
24
+ const createPet = resolveOperation(source, 'createPet');
25
+ expect(createPet.method).toBe('POST');
26
+ expect(createPet.path).toBe('/pets');
27
+ });
28
+ it('throws for unknown operationId', async () => {
29
+ const source = await loadOAS('./examples/petstore/openapi.yaml');
30
+ expect(() => resolveOperation(source, 'unknownOp')).toThrow(/not found/);
31
+ });
32
+ it('extracts response schemas', async () => {
33
+ const source = await loadOAS('./examples/petstore/openapi.yaml');
34
+ const schema = getResponseSchema(source, 'getPet');
35
+ expect(schema).toBeDefined();
36
+ expect(schema?.type).toBe('object');
37
+ expect(schema?.properties).toHaveProperty('id');
38
+ expect(schema?.properties).toHaveProperty('name');
39
+ });
40
+ });
41
+ describe('OAS Validator', () => {
42
+ it('validates a valid response', () => {
43
+ const schema = {
44
+ type: 'object',
45
+ required: ['id', 'name'],
46
+ properties: {
47
+ id: { type: 'string' },
48
+ name: { type: 'string' },
49
+ age: { type: 'integer', minimum: 0 },
50
+ },
51
+ };
52
+ const data = { id: '123', name: 'Fluffy', age: 3 };
53
+ const result = validateResponse(data, schema);
54
+ expect(result.valid).toBe(true);
55
+ expect(result.errors).toHaveLength(0);
56
+ });
57
+ it('catches missing required properties', () => {
58
+ const schema = {
59
+ type: 'object',
60
+ required: ['id', 'name'],
61
+ properties: {
62
+ id: { type: 'string' },
63
+ name: { type: 'string' },
64
+ },
65
+ };
66
+ const data = { id: '123' }; // missing 'name'
67
+ const result = validateResponse(data, schema);
68
+ expect(result.valid).toBe(false);
69
+ expect(result.errors.some(e => e.path === 'name')).toBe(true);
70
+ });
71
+ it('catches type mismatches', () => {
72
+ const schema = {
73
+ type: 'object',
74
+ properties: {
75
+ age: { type: 'integer' },
76
+ },
77
+ };
78
+ const data = { age: 'not a number' };
79
+ const result = validateResponse(data, schema);
80
+ expect(result.valid).toBe(false);
81
+ expect(result.errors[0].path).toBe('age');
82
+ });
83
+ it('validates arrays', () => {
84
+ const schema = {
85
+ type: 'array',
86
+ items: {
87
+ type: 'object',
88
+ required: ['id'],
89
+ properties: {
90
+ id: { type: 'string' },
91
+ },
92
+ },
93
+ };
94
+ const validData = [{ id: '1' }, { id: '2' }];
95
+ expect(validateResponse(validData, schema).valid).toBe(true);
96
+ const invalidData = [{ id: '1' }, { noId: true }];
97
+ const result = validateResponse(invalidData, schema);
98
+ expect(result.valid).toBe(false);
99
+ expect(result.errors[0].path).toBe('[1].id');
100
+ });
101
+ });
102
+ describe('OAS Parser Integration', () => {
103
+ function parse(source) {
104
+ const lexer = new ReqonLexer(source);
105
+ const tokens = lexer.tokenize();
106
+ const parser = new ReqonParser(tokens);
107
+ return parser.parse();
108
+ }
109
+ it('parses source with OAS spec path', () => {
110
+ const source = `
111
+ mission TestOAS {
112
+ source Petstore from "./examples/petstore.yaml" {
113
+ auth: bearer
114
+ }
115
+
116
+ store pets: memory("pets")
117
+
118
+ action FetchPets {
119
+ fetch Petstore.listPets
120
+
121
+ store response.pets -> pets {
122
+ key: .id
123
+ }
124
+ }
125
+
126
+ run FetchPets
127
+ }
128
+ `;
129
+ const program = parse(source);
130
+ expect(program.type).toBe('ReqonProgram');
131
+ const mission = program.statements[0];
132
+ if (mission.type === 'MissionDefinition') {
133
+ expect(mission.sources[0].specPath).toBe('./examples/petstore.yaml');
134
+ expect(mission.sources[0].config.base).toBeUndefined();
135
+ const action = mission.actions[0];
136
+ const fetchStep = action.steps[0];
137
+ if (fetchStep.type === 'FetchStep') {
138
+ expect(fetchStep.operationRef).toBeDefined();
139
+ expect(fetchStep.operationRef?.source).toBe('Petstore');
140
+ expect(fetchStep.operationRef?.operationId).toBe('listPets');
141
+ expect(fetchStep.method).toBeUndefined();
142
+ expect(fetchStep.path).toBeUndefined();
143
+ }
144
+ }
145
+ });
146
+ it('parses source with OAS and explicit base URL override', () => {
147
+ const source = `
148
+ mission TestOAS {
149
+ source Petstore from "./examples/petstore.yaml" {
150
+ auth: bearer,
151
+ base: "https://staging.petstore.com/v1"
152
+ }
153
+
154
+ store pets: memory("pets")
155
+
156
+ action Fetch {
157
+ fetch GET "/custom-path"
158
+ store response -> pets { key: .id }
159
+ }
160
+
161
+ run Fetch
162
+ }
163
+ `;
164
+ const program = parse(source);
165
+ const mission = program.statements[0];
166
+ if (mission.type === 'MissionDefinition') {
167
+ expect(mission.sources[0].specPath).toBe('./examples/petstore.yaml');
168
+ expect(mission.sources[0].config.base).toBe('https://staging.petstore.com/v1');
169
+ }
170
+ });
171
+ it('supports both OAS and traditional fetch in same mission', () => {
172
+ const source = `
173
+ mission MixedFetch {
174
+ source Petstore from "./examples/petstore.yaml" {
175
+ auth: bearer
176
+ }
177
+
178
+ source Legacy {
179
+ auth: bearer,
180
+ base: "https://legacy.api.com"
181
+ }
182
+
183
+ store pets: memory("pets")
184
+
185
+ action FetchFromOAS {
186
+ fetch Petstore.listPets
187
+ store response.pets -> pets { key: .id }
188
+ }
189
+
190
+ action FetchFromLegacy {
191
+ fetch GET "/old-endpoint" {
192
+ source: Legacy
193
+ }
194
+ store response -> pets { key: .id }
195
+ }
196
+
197
+ run FetchFromOAS then FetchFromLegacy
198
+ }
199
+ `;
200
+ const program = parse(source);
201
+ const mission = program.statements[0];
202
+ if (mission.type === 'MissionDefinition') {
203
+ expect(mission.sources).toHaveLength(2);
204
+ const oasAction = mission.actions[0];
205
+ const legacyAction = mission.actions[1];
206
+ const oasFetch = oasAction.steps[0];
207
+ const legacyFetch = legacyAction.steps[0];
208
+ if (oasFetch.type === 'FetchStep' && legacyFetch.type === 'FetchStep') {
209
+ // OAS fetch
210
+ expect(oasFetch.operationRef).toBeDefined();
211
+ expect(oasFetch.method).toBeUndefined();
212
+ // Traditional fetch
213
+ expect(legacyFetch.operationRef).toBeUndefined();
214
+ expect(legacyFetch.method).toBe('GET');
215
+ }
216
+ }
217
+ });
218
+ });
@@ -0,0 +1,12 @@
1
+ import type { OpenAPIV3 } from 'openapi-types';
2
+ export interface ValidationResult {
3
+ valid: boolean;
4
+ errors: ValidationError[];
5
+ }
6
+ export interface ValidationError {
7
+ path: string;
8
+ message: string;
9
+ expected?: string;
10
+ actual?: string;
11
+ }
12
+ export declare function validateResponse(data: unknown, schema: OpenAPIV3.SchemaObject, path?: string): ValidationResult;
@@ -0,0 +1,227 @@
1
+ export function validateResponse(data, schema, path = '') {
2
+ const errors = [];
3
+ validateValue(data, schema, path, errors);
4
+ return {
5
+ valid: errors.length === 0,
6
+ errors,
7
+ };
8
+ }
9
+ function validateValue(value, schema, path, errors) {
10
+ // Handle nullable
11
+ if (value === null) {
12
+ if (schema.nullable)
13
+ return;
14
+ errors.push({ path, message: 'Value is null but schema is not nullable' });
15
+ return;
16
+ }
17
+ // Handle undefined/missing
18
+ if (value === undefined) {
19
+ // Required check happens at object level
20
+ return;
21
+ }
22
+ // Check type
23
+ const schemaType = schema.type;
24
+ switch (schemaType) {
25
+ case 'string':
26
+ validateString(value, schema, path, errors);
27
+ break;
28
+ case 'number':
29
+ case 'integer':
30
+ validateNumber(value, schema, path, errors);
31
+ break;
32
+ case 'boolean':
33
+ validateBoolean(value, path, errors);
34
+ break;
35
+ case 'array':
36
+ validateArray(value, schema, path, errors);
37
+ break;
38
+ case 'object':
39
+ validateObject(value, schema, path, errors);
40
+ break;
41
+ default:
42
+ // No type specified, or unknown type - allow anything
43
+ break;
44
+ }
45
+ // Check enum
46
+ if (schema.enum && !schema.enum.includes(value)) {
47
+ errors.push({
48
+ path,
49
+ message: `Value not in enum`,
50
+ expected: schema.enum.join(' | '),
51
+ actual: String(value),
52
+ });
53
+ }
54
+ }
55
+ function validateString(value, schema, path, errors) {
56
+ if (typeof value !== 'string') {
57
+ errors.push({
58
+ path,
59
+ message: 'Expected string',
60
+ expected: 'string',
61
+ actual: typeof value,
62
+ });
63
+ return;
64
+ }
65
+ if (schema.minLength !== undefined && value.length < schema.minLength) {
66
+ errors.push({
67
+ path,
68
+ message: `String too short`,
69
+ expected: `>= ${schema.minLength} chars`,
70
+ actual: `${value.length} chars`,
71
+ });
72
+ }
73
+ if (schema.maxLength !== undefined && value.length > schema.maxLength) {
74
+ errors.push({
75
+ path,
76
+ message: `String too long`,
77
+ expected: `<= ${schema.maxLength} chars`,
78
+ actual: `${value.length} chars`,
79
+ });
80
+ }
81
+ if (schema.pattern) {
82
+ const regex = new RegExp(schema.pattern);
83
+ if (!regex.test(value)) {
84
+ errors.push({
85
+ path,
86
+ message: `String does not match pattern`,
87
+ expected: schema.pattern,
88
+ actual: value,
89
+ });
90
+ }
91
+ }
92
+ }
93
+ function validateNumber(value, schema, path, errors) {
94
+ if (typeof value !== 'number') {
95
+ errors.push({
96
+ path,
97
+ message: 'Expected number',
98
+ expected: schema.type ?? 'number',
99
+ actual: typeof value,
100
+ });
101
+ return;
102
+ }
103
+ if (schema.type === 'integer' && !Number.isInteger(value)) {
104
+ errors.push({
105
+ path,
106
+ message: 'Expected integer',
107
+ expected: 'integer',
108
+ actual: String(value),
109
+ });
110
+ }
111
+ if (schema.minimum !== undefined && value < schema.minimum) {
112
+ errors.push({
113
+ path,
114
+ message: `Number below minimum`,
115
+ expected: `>= ${schema.minimum}`,
116
+ actual: String(value),
117
+ });
118
+ }
119
+ if (schema.maximum !== undefined && value > schema.maximum) {
120
+ errors.push({
121
+ path,
122
+ message: `Number above maximum`,
123
+ expected: `<= ${schema.maximum}`,
124
+ actual: String(value),
125
+ });
126
+ }
127
+ }
128
+ function validateBoolean(value, path, errors) {
129
+ if (typeof value !== 'boolean') {
130
+ errors.push({
131
+ path,
132
+ message: 'Expected boolean',
133
+ expected: 'boolean',
134
+ actual: typeof value,
135
+ });
136
+ }
137
+ }
138
+ function validateArray(value, schema, path, errors) {
139
+ if (!Array.isArray(value)) {
140
+ errors.push({
141
+ path,
142
+ message: 'Expected array',
143
+ expected: 'array',
144
+ actual: typeof value,
145
+ });
146
+ return;
147
+ }
148
+ if (schema.minItems !== undefined && value.length < schema.minItems) {
149
+ errors.push({
150
+ path,
151
+ message: `Array too short`,
152
+ expected: `>= ${schema.minItems} items`,
153
+ actual: `${value.length} items`,
154
+ });
155
+ }
156
+ if (schema.maxItems !== undefined && value.length > schema.maxItems) {
157
+ errors.push({
158
+ path,
159
+ message: `Array too long`,
160
+ expected: `<= ${schema.maxItems} items`,
161
+ actual: `${value.length} items`,
162
+ });
163
+ }
164
+ // Validate items
165
+ const arraySchema = schema;
166
+ if (arraySchema.items) {
167
+ const itemSchema = arraySchema.items;
168
+ value.forEach((item, index) => {
169
+ validateValue(item, itemSchema, `${path}[${index}]`, errors);
170
+ });
171
+ }
172
+ }
173
+ function validateObject(value, schema, path, errors) {
174
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
175
+ errors.push({
176
+ path,
177
+ message: 'Expected object',
178
+ expected: 'object',
179
+ actual: Array.isArray(value) ? 'array' : typeof value,
180
+ });
181
+ return;
182
+ }
183
+ const obj = value;
184
+ // Check required properties
185
+ if (schema.required) {
186
+ for (const prop of schema.required) {
187
+ if (!(prop in obj)) {
188
+ errors.push({
189
+ path: path ? `${path}.${prop}` : prop,
190
+ message: `Missing required property`,
191
+ });
192
+ }
193
+ }
194
+ }
195
+ // Validate properties
196
+ const properties = schema.properties;
197
+ if (properties) {
198
+ for (const [key, propSchema] of Object.entries(properties)) {
199
+ if (key in obj) {
200
+ const propPath = path ? `${path}.${key}` : key;
201
+ validateValue(obj[key], propSchema, propPath, errors);
202
+ }
203
+ }
204
+ }
205
+ // Handle additionalProperties
206
+ if (schema.additionalProperties === false) {
207
+ const allowedKeys = new Set(Object.keys(properties ?? {}));
208
+ for (const key of Object.keys(obj)) {
209
+ if (!allowedKeys.has(key)) {
210
+ errors.push({
211
+ path: path ? `${path}.${key}` : key,
212
+ message: `Unexpected property`,
213
+ });
214
+ }
215
+ }
216
+ }
217
+ else if (typeof schema.additionalProperties === 'object') {
218
+ const additionalSchema = schema.additionalProperties;
219
+ const allowedKeys = new Set(Object.keys(properties ?? {}));
220
+ for (const [key, val] of Object.entries(obj)) {
221
+ if (!allowedKeys.has(key)) {
222
+ const propPath = path ? `${path}.${key}` : key;
223
+ validateValue(val, additionalSchema, propPath, errors);
224
+ }
225
+ }
226
+ }
227
+ }
@@ -0,0 +1,33 @@
1
+ import { TokenType, type Token } from 'vague-lang';
2
+ import { ReqonTokenType } from '../lexer/tokens.js';
3
+ import { ParseError } from '../errors/index.js';
4
+ type AnyTokenType = TokenType | ReqonTokenType | string;
5
+ export declare class ReqonParserBase {
6
+ protected tokens: Token[];
7
+ protected pos: number;
8
+ protected source?: string;
9
+ protected filePath?: string;
10
+ constructor(tokens: Token[], source?: string, filePath?: string);
11
+ protected peek(): Token;
12
+ protected peekNext(): Token | undefined;
13
+ protected check(type: AnyTokenType): boolean;
14
+ protected checkAny(...types: AnyTokenType[]): boolean;
15
+ protected match(type: AnyTokenType): boolean;
16
+ protected matchAny(...types: AnyTokenType[]): AnyTokenType | null;
17
+ protected advance(): Token;
18
+ protected consume(type: AnyTokenType, message: string): Token;
19
+ /**
20
+ * Consume an identifier, allowing HTTP method tokens to be used as identifiers.
21
+ * This is needed because 'get', 'post', etc. are valid variable/store names.
22
+ */
23
+ protected consumeIdentifier(message: string): Token;
24
+ /**
25
+ * Check if current token is an identifier (including HTTP methods as identifiers)
26
+ */
27
+ protected checkIdentifier(): boolean;
28
+ protected isAtEnd(): boolean;
29
+ protected error(message: string): ParseError;
30
+ protected savePosition(): number;
31
+ protected restorePosition(saved: number): void;
32
+ }
33
+ export {};