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,234 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ ReqonError,
4
+ ParseError,
5
+ LexerError,
6
+ RuntimeError,
7
+ ValidationError,
8
+ formatErrors,
9
+ getSourceLine,
10
+ getSourceContext,
11
+ } from './index.js';
12
+ import { ReqonLexer } from '../lexer/index.js';
13
+ import { ReqonParser } from '../parser/index.js';
14
+
15
+ describe('Error classes', () => {
16
+ describe('ReqonError', () => {
17
+ it('should format error with location', () => {
18
+ const error = new ReqonError('Something went wrong', { line: 5, column: 10 });
19
+ const formatted = error.format();
20
+
21
+ expect(formatted).toContain('ReqonError: Something went wrong');
22
+ expect(formatted).toContain('5:10');
23
+ });
24
+
25
+ it('should format error with source context', () => {
26
+ const source = `mission Test {
27
+ source API {
28
+ auth: bearer,
29
+ base: "https://api.example.com"
30
+ }
31
+ }`;
32
+ const error = new ReqonError('Unexpected token', { line: 3, column: 5 }, { source });
33
+ const formatted = error.format();
34
+
35
+ expect(formatted).toContain('auth: bearer,');
36
+ expect(formatted).toContain('3 |');
37
+ expect(formatted).toContain('^');
38
+ });
39
+
40
+ it('should include file path when provided', () => {
41
+ const error = new ReqonError(
42
+ 'Test error',
43
+ { line: 1, column: 1 },
44
+ { source: 'test', filePath: '/path/to/file.reqon' }
45
+ );
46
+ const formatted = error.format();
47
+
48
+ expect(formatted).toContain('/path/to/file.reqon:');
49
+ });
50
+ });
51
+
52
+ describe('ParseError', () => {
53
+ it('should include token value in format', () => {
54
+ const error = new ParseError(
55
+ "Expected '{'",
56
+ { line: 2, column: 15 },
57
+ undefined,
58
+ 'identifier'
59
+ );
60
+ const formatted = error.format();
61
+
62
+ expect(formatted).toContain("found: 'identifier'");
63
+ });
64
+
65
+ it('should format with source context and pointer', () => {
66
+ const source = `mission Test {
67
+ source API auth: bearer
68
+ }`;
69
+ const error = new ParseError(
70
+ "Expected '{'",
71
+ { line: 2, column: 14 },
72
+ { source },
73
+ 'auth'
74
+ );
75
+ const formatted = error.format();
76
+
77
+ expect(formatted).toContain('ParseError');
78
+ expect(formatted).toContain('2:14');
79
+ expect(formatted).toContain('source API auth: bearer');
80
+ expect(formatted).toContain('^');
81
+ });
82
+ });
83
+
84
+ describe('LexerError', () => {
85
+ it('should format with source context', () => {
86
+ const source = 'mission Test { @ }';
87
+ const error = new LexerError(
88
+ "Unexpected character '@'",
89
+ { line: 1, column: 16 },
90
+ { source }
91
+ );
92
+ const formatted = error.format();
93
+
94
+ expect(formatted).toContain('LexerError');
95
+ expect(formatted).toContain("Unexpected character '@'");
96
+ });
97
+ });
98
+
99
+ describe('RuntimeError', () => {
100
+ it('should include action and step info', () => {
101
+ const error = new RuntimeError(
102
+ 'Request failed with status 404',
103
+ { line: 5, column: 3 },
104
+ undefined,
105
+ { action: 'FetchData', stepType: 'fetch' }
106
+ );
107
+ const formatted = error.format();
108
+
109
+ expect(formatted).toContain('in action: FetchData');
110
+ expect(formatted).toContain('at step: fetch');
111
+ });
112
+ });
113
+
114
+ describe('ValidationError', () => {
115
+ it('should include severity', () => {
116
+ const error = new ValidationError(
117
+ 'Value must be positive',
118
+ { line: 10, column: 5 },
119
+ undefined,
120
+ { severity: 'warning' }
121
+ );
122
+
123
+ expect(error.severity).toBe('warning');
124
+ });
125
+ });
126
+ });
127
+
128
+ describe('formatErrors', () => {
129
+ it('should format multiple errors', () => {
130
+ const errors = [
131
+ new ParseError('Error 1', { line: 1, column: 1 }),
132
+ new ParseError('Error 2', { line: 5, column: 10 }),
133
+ ];
134
+
135
+ const formatted = formatErrors(errors);
136
+
137
+ expect(formatted).toContain('Error 1');
138
+ expect(formatted).toContain('Error 2');
139
+ expect(formatted).toContain('1:1');
140
+ expect(formatted).toContain('5:10');
141
+ });
142
+ });
143
+
144
+ describe('getSourceLine', () => {
145
+ it('should return the correct line', () => {
146
+ const source = 'line1\nline2\nline3';
147
+
148
+ expect(getSourceLine(source, 1)).toBe('line1');
149
+ expect(getSourceLine(source, 2)).toBe('line2');
150
+ expect(getSourceLine(source, 3)).toBe('line3');
151
+ });
152
+
153
+ it('should return undefined for out of range', () => {
154
+ const source = 'line1\nline2';
155
+
156
+ expect(getSourceLine(source, 5)).toBeUndefined();
157
+ });
158
+ });
159
+
160
+ describe('getSourceContext', () => {
161
+ it('should return surrounding lines', () => {
162
+ const source = 'line1\nline2\nline3\nline4\nline5';
163
+ const context = getSourceContext(source, 3, 1);
164
+
165
+ expect(context.lines).toHaveLength(3);
166
+ expect(context.lines[0]).toEqual({ num: 2, text: 'line2' });
167
+ expect(context.lines[1]).toEqual({ num: 3, text: 'line3' });
168
+ expect(context.lines[2]).toEqual({ num: 4, text: 'line4' });
169
+ expect(context.errorLineIndex).toBe(1);
170
+ });
171
+ });
172
+
173
+ describe('Integration with parser', () => {
174
+ it('should produce ParseError with source context', () => {
175
+ const source = `mission Test {
176
+ source API {
177
+ auth: bearer
178
+ }
179
+
180
+ action Fetch {
181
+ get "/items
182
+ }
183
+
184
+ run Fetch
185
+ }`;
186
+
187
+ const lexer = new ReqonLexer(source);
188
+
189
+ expect(() => lexer.tokenize()).toThrow();
190
+
191
+ try {
192
+ lexer.tokenize();
193
+ } catch (e) {
194
+ expect(e).toBeInstanceOf(Error);
195
+ const error = e as Error;
196
+ expect(error.message).toContain('Unterminated string');
197
+ }
198
+ });
199
+
200
+ it('should produce ParseError for syntax errors', () => {
201
+ const source = `mission Test {
202
+ source API {
203
+ auth: bearer,
204
+ base: "https://api.example.com"
205
+ }
206
+
207
+ action Fetch
208
+ get "/items"
209
+ }
210
+
211
+ run Fetch
212
+ }`;
213
+
214
+ const lexer = new ReqonLexer(source);
215
+ const tokens = lexer.tokenize();
216
+ const parser = new ReqonParser(tokens, source, 'test.reqon');
217
+
218
+ expect(() => parser.parse()).toThrow();
219
+
220
+ try {
221
+ parser.parse();
222
+ } catch (e) {
223
+ expect(e).toBeInstanceOf(ParseError);
224
+ const error = e as ParseError;
225
+ expect(error.location.line).toBeGreaterThan(0);
226
+ expect(error.context?.source).toBe(source);
227
+ expect(error.context?.filePath).toBe('test.reqon');
228
+
229
+ const formatted = error.format();
230
+ expect(formatted).toContain('test.reqon:');
231
+ expect(formatted).toContain('ParseError');
232
+ }
233
+ });
234
+ });
@@ -0,0 +1,223 @@
1
+ export interface SourceLocation {
2
+ line: number;
3
+ column: number;
4
+ /** End column (optional, for ranges) */
5
+ endColumn?: number;
6
+ }
7
+
8
+ export interface ErrorContext {
9
+ /** The source code being parsed/executed */
10
+ source?: string;
11
+ /** File path if available */
12
+ filePath?: string;
13
+ }
14
+
15
+ /**
16
+ * Base class for Reqon errors with source location info
17
+ */
18
+ export class ReqonError extends Error {
19
+ readonly location: SourceLocation;
20
+ readonly context?: ErrorContext;
21
+
22
+ constructor(message: string, location: SourceLocation, context?: ErrorContext) {
23
+ super(message);
24
+ this.name = 'ReqonError';
25
+ this.location = location;
26
+ this.context = context;
27
+ }
28
+
29
+ /**
30
+ * Format the error with source context for display
31
+ */
32
+ format(): string {
33
+ const lines: string[] = [];
34
+
35
+ // Header with location
36
+ const fileInfo = this.context?.filePath ? `${this.context.filePath}:` : '';
37
+ lines.push(`${this.name}: ${this.message}`);
38
+ lines.push(` --> ${fileInfo}${this.location.line}:${this.location.column}`);
39
+
40
+ // Show source context if available
41
+ if (this.context?.source) {
42
+ const sourceLines = this.context.source.split('\n');
43
+ const errorLine = sourceLines[this.location.line - 1];
44
+
45
+ if (errorLine !== undefined) {
46
+ const lineNum = this.location.line.toString();
47
+ const padding = ' '.repeat(lineNum.length);
48
+
49
+ lines.push(`${padding} |`);
50
+ lines.push(`${lineNum} | ${errorLine}`);
51
+
52
+ // Underline the error position
53
+ const underlineStart = this.location.column - 1;
54
+ const underlineLength = this.location.endColumn
55
+ ? this.location.endColumn - this.location.column
56
+ : 1;
57
+ const underline = ' '.repeat(underlineStart) + '^'.repeat(Math.max(1, underlineLength));
58
+ lines.push(`${padding} | ${underline}`);
59
+ }
60
+ }
61
+
62
+ return lines.join('\n');
63
+ }
64
+
65
+ toString(): string {
66
+ return this.format();
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Error during lexical analysis
72
+ */
73
+ export class LexerError extends ReqonError {
74
+ constructor(message: string, location: SourceLocation, context?: ErrorContext) {
75
+ super(message, location, context);
76
+ this.name = 'LexerError';
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Error during parsing
82
+ */
83
+ export class ParseError extends ReqonError {
84
+ /** The token value that caused the error */
85
+ readonly tokenValue?: string;
86
+
87
+ constructor(
88
+ message: string,
89
+ location: SourceLocation,
90
+ context?: ErrorContext,
91
+ tokenValue?: string
92
+ ) {
93
+ super(message, location, context);
94
+ this.name = 'ParseError';
95
+ this.tokenValue = tokenValue;
96
+ }
97
+
98
+ format(): string {
99
+ const base = super.format();
100
+ if (this.tokenValue) {
101
+ return `${base}\n found: '${this.tokenValue}'`;
102
+ }
103
+ return base;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Error during runtime evaluation
109
+ */
110
+ export class RuntimeError extends ReqonError {
111
+ /** The action/step where the error occurred */
112
+ readonly action?: string;
113
+ /** The step type (fetch, map, validate, etc.) */
114
+ readonly stepType?: string;
115
+
116
+ constructor(
117
+ message: string,
118
+ location: SourceLocation,
119
+ context?: ErrorContext,
120
+ options?: { action?: string; stepType?: string }
121
+ ) {
122
+ super(message, location, context);
123
+ this.name = 'RuntimeError';
124
+ this.action = options?.action;
125
+ this.stepType = options?.stepType;
126
+ }
127
+
128
+ format(): string {
129
+ const lines: string[] = [];
130
+
131
+ // Header
132
+ lines.push(`${this.name}: ${this.message}`);
133
+
134
+ if (this.action) {
135
+ lines.push(` in action: ${this.action}`);
136
+ }
137
+ if (this.stepType) {
138
+ lines.push(` at step: ${this.stepType}`);
139
+ }
140
+
141
+ // Location
142
+ const fileInfo = this.context?.filePath ? `${this.context.filePath}:` : '';
143
+ lines.push(` --> ${fileInfo}${this.location.line}:${this.location.column}`);
144
+
145
+ // Source context
146
+ if (this.context?.source) {
147
+ const sourceLines = this.context.source.split('\n');
148
+ const errorLine = sourceLines[this.location.line - 1];
149
+
150
+ if (errorLine !== undefined) {
151
+ const lineNum = this.location.line.toString();
152
+ const padding = ' '.repeat(lineNum.length);
153
+
154
+ lines.push(`${padding} |`);
155
+ lines.push(`${lineNum} | ${errorLine}`);
156
+
157
+ const underlineStart = this.location.column - 1;
158
+ const underline = ' '.repeat(underlineStart) + '^';
159
+ lines.push(`${padding} | ${underline}`);
160
+ }
161
+ }
162
+
163
+ return lines.join('\n');
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Error during validation
169
+ */
170
+ export class ValidationError extends ReqonError {
171
+ readonly constraint?: string;
172
+ readonly severity: 'error' | 'warning';
173
+
174
+ constructor(
175
+ message: string,
176
+ location: SourceLocation,
177
+ context?: ErrorContext,
178
+ options?: { constraint?: string; severity?: 'error' | 'warning' }
179
+ ) {
180
+ super(message, location, context);
181
+ this.name = 'ValidationError';
182
+ this.constraint = options?.constraint;
183
+ this.severity = options?.severity ?? 'error';
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Format multiple errors for display
189
+ */
190
+ export function formatErrors(errors: ReqonError[]): string {
191
+ return errors.map(e => e.format()).join('\n\n');
192
+ }
193
+
194
+ /**
195
+ * Get the source line at a given line number
196
+ */
197
+ export function getSourceLine(source: string, lineNumber: number): string | undefined {
198
+ const lines = source.split('\n');
199
+ return lines[lineNumber - 1];
200
+ }
201
+
202
+ /**
203
+ * Get surrounding context lines
204
+ */
205
+ export function getSourceContext(
206
+ source: string,
207
+ lineNumber: number,
208
+ contextLines: number = 2
209
+ ): { lines: Array<{ num: number; text: string }>; errorLineIndex: number } {
210
+ const allLines = source.split('\n');
211
+ const start = Math.max(0, lineNumber - 1 - contextLines);
212
+ const end = Math.min(allLines.length, lineNumber + contextLines);
213
+
214
+ const lines: Array<{ num: number; text: string }> = [];
215
+ for (let i = start; i < end; i++) {
216
+ lines.push({ num: i + 1, text: allLines[i] });
217
+ }
218
+
219
+ return {
220
+ lines,
221
+ errorLineIndex: lineNumber - 1 - start,
222
+ };
223
+ }