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,267 @@
1
+ /**
2
+ * Circuit Breaker implementation for HTTP requests.
3
+ *
4
+ * Prevents repeated failures from cascading by automatically detecting
5
+ * failure patterns and "opening" the circuit to fail fast.
6
+ *
7
+ * States:
8
+ * - CLOSED: Normal operation, requests pass through
9
+ * - OPEN: Circuit tripped, requests fail immediately
10
+ * - HALF_OPEN: Testing if service recovered, limited requests allowed
11
+ */
12
+ const DEFAULT_CONFIG = {
13
+ failureThreshold: 5,
14
+ resetTimeout: 30000,
15
+ successThreshold: 2,
16
+ failureWindow: 60000,
17
+ failureStatusCodes: [500, 501, 502, 503, 504],
18
+ countNetworkErrors: true,
19
+ };
20
+ /**
21
+ * Error thrown when circuit breaker is open
22
+ */
23
+ export class CircuitBreakerError extends Error {
24
+ source;
25
+ endpoint;
26
+ nextAttemptIn;
27
+ constructor(source, endpoint, nextAttemptIn) {
28
+ super(`Circuit breaker open for ${source}${endpoint ? `:${endpoint}` : ''}. ` +
29
+ `Next attempt in ${Math.ceil(nextAttemptIn / 1000)}s`);
30
+ this.source = source;
31
+ this.endpoint = endpoint;
32
+ this.nextAttemptIn = nextAttemptIn;
33
+ this.name = 'CircuitBreakerError';
34
+ }
35
+ }
36
+ /**
37
+ * Circuit breaker for managing failure detection and recovery
38
+ */
39
+ export class CircuitBreaker {
40
+ circuits = new Map();
41
+ callbacks = {};
42
+ defaultConfig;
43
+ constructor(defaultConfig) {
44
+ this.defaultConfig = { ...DEFAULT_CONFIG, ...defaultConfig };
45
+ }
46
+ /**
47
+ * Configure circuit breaker for a specific source
48
+ */
49
+ configure(source, config) {
50
+ const key = this.getKey(source);
51
+ const existing = this.circuits.get(key);
52
+ if (existing) {
53
+ existing.config = { ...this.defaultConfig, ...config };
54
+ }
55
+ else {
56
+ this.circuits.set(key, this.createEntry({ ...this.defaultConfig, ...config }));
57
+ }
58
+ }
59
+ /**
60
+ * Set event callbacks
61
+ */
62
+ setCallbacks(callbacks) {
63
+ this.callbacks = callbacks;
64
+ }
65
+ /**
66
+ * Check if a request can proceed (throws if circuit is open)
67
+ */
68
+ canProceed(source, endpoint) {
69
+ const entry = this.getOrCreateEntry(source, endpoint);
70
+ const now = Date.now();
71
+ if (entry.state === 'closed') {
72
+ return true;
73
+ }
74
+ if (entry.state === 'open') {
75
+ const timeSinceOpen = now - (entry.openedAt ?? now);
76
+ if (timeSinceOpen >= entry.config.resetTimeout) {
77
+ // Transition to half-open
78
+ this.transitionTo(entry, 'half_open', source, endpoint);
79
+ return true;
80
+ }
81
+ return false;
82
+ }
83
+ // Half-open: allow the request through for testing
84
+ return true;
85
+ }
86
+ /**
87
+ * Ensure request can proceed, throwing CircuitBreakerError if not
88
+ */
89
+ ensureCanProceed(source, endpoint) {
90
+ if (!this.canProceed(source, endpoint)) {
91
+ const entry = this.getOrCreateEntry(source, endpoint);
92
+ const now = Date.now();
93
+ const nextAttemptIn = entry.config.resetTimeout - (now - (entry.openedAt ?? now));
94
+ this.callbacks.onRejected?.({
95
+ source,
96
+ endpoint,
97
+ nextAttemptIn,
98
+ });
99
+ throw new CircuitBreakerError(source, endpoint, nextAttemptIn);
100
+ }
101
+ }
102
+ /**
103
+ * Record a successful request
104
+ */
105
+ recordSuccess(source, endpoint) {
106
+ const entry = this.getOrCreateEntry(source, endpoint);
107
+ if (entry.state === 'half_open') {
108
+ entry.successes++;
109
+ if (entry.successes >= entry.config.successThreshold) {
110
+ // Recovery successful, close the circuit
111
+ this.transitionTo(entry, 'closed', source, endpoint);
112
+ }
113
+ }
114
+ else if (entry.state === 'closed') {
115
+ // Clear old failures from window
116
+ this.pruneOldFailures(entry);
117
+ }
118
+ }
119
+ /**
120
+ * Record a failed request
121
+ */
122
+ recordFailure(source, endpoint, statusCode, isNetworkError = false) {
123
+ const entry = this.getOrCreateEntry(source, endpoint);
124
+ const config = entry.config;
125
+ // Check if this failure type should be counted
126
+ const isFailureStatus = statusCode !== undefined && config.failureStatusCodes.includes(statusCode);
127
+ const shouldCount = isFailureStatus || (isNetworkError && config.countNetworkErrors);
128
+ if (!shouldCount) {
129
+ return;
130
+ }
131
+ const now = Date.now();
132
+ if (entry.state === 'half_open') {
133
+ // Any failure in half-open immediately re-opens circuit
134
+ this.transitionTo(entry, 'open', source, endpoint, 'Failure during recovery attempt');
135
+ return;
136
+ }
137
+ if (entry.state === 'closed') {
138
+ // Prune old failures and add new one
139
+ this.pruneOldFailures(entry);
140
+ entry.failureTimestamps.push(now);
141
+ entry.failures = entry.failureTimestamps.length;
142
+ entry.lastFailureTime = now;
143
+ // Check if we should open the circuit
144
+ if (entry.failures >= config.failureThreshold) {
145
+ this.transitionTo(entry, 'open', source, endpoint, `${entry.failures} failures in ${config.failureWindow}ms window`);
146
+ }
147
+ }
148
+ }
149
+ /**
150
+ * Get current status for a source/endpoint
151
+ */
152
+ getStatus(source, endpoint) {
153
+ const entry = this.getOrCreateEntry(source, endpoint);
154
+ const now = Date.now();
155
+ let nextAttemptTime;
156
+ if (entry.state === 'open' && entry.openedAt) {
157
+ const nextAttemptMs = entry.openedAt + entry.config.resetTimeout;
158
+ nextAttemptTime = new Date(nextAttemptMs);
159
+ }
160
+ return {
161
+ state: entry.state,
162
+ failures: entry.failures,
163
+ successes: entry.successes,
164
+ lastFailureTime: entry.lastFailureTime ? new Date(entry.lastFailureTime) : undefined,
165
+ nextAttemptTime,
166
+ isOpen: entry.state === 'open',
167
+ };
168
+ }
169
+ /**
170
+ * Force reset a circuit to closed state
171
+ */
172
+ reset(source, endpoint) {
173
+ const key = this.getKey(source, endpoint);
174
+ const entry = this.circuits.get(key);
175
+ if (entry) {
176
+ const previousState = entry.state;
177
+ entry.state = 'closed';
178
+ entry.failures = 0;
179
+ entry.successes = 0;
180
+ entry.failureTimestamps = [];
181
+ entry.lastFailureTime = undefined;
182
+ entry.openedAt = undefined;
183
+ if (previousState !== 'closed') {
184
+ this.callbacks.onClose?.({
185
+ source,
186
+ endpoint,
187
+ state: 'closed',
188
+ previousState,
189
+ failures: 0,
190
+ reason: 'Manual reset',
191
+ });
192
+ }
193
+ }
194
+ }
195
+ /**
196
+ * Get all circuit statuses
197
+ */
198
+ getAllStatuses() {
199
+ const result = new Map();
200
+ for (const [key, entry] of this.circuits) {
201
+ const [source, endpoint] = key.split(':');
202
+ result.set(key, this.getStatus(source, endpoint === '' ? undefined : endpoint));
203
+ }
204
+ return result;
205
+ }
206
+ getKey(source, endpoint) {
207
+ return endpoint ? `${source}:${endpoint}` : source;
208
+ }
209
+ createEntry(config) {
210
+ return {
211
+ state: 'closed',
212
+ failures: 0,
213
+ successes: 0,
214
+ failureTimestamps: [],
215
+ config,
216
+ };
217
+ }
218
+ getOrCreateEntry(source, endpoint) {
219
+ const key = this.getKey(source, endpoint);
220
+ let entry = this.circuits.get(key);
221
+ if (!entry) {
222
+ // Check for source-level config
223
+ const sourceEntry = this.circuits.get(source);
224
+ const config = sourceEntry?.config ?? this.defaultConfig;
225
+ entry = this.createEntry(config);
226
+ this.circuits.set(key, entry);
227
+ }
228
+ return entry;
229
+ }
230
+ pruneOldFailures(entry) {
231
+ const now = Date.now();
232
+ const windowStart = now - entry.config.failureWindow;
233
+ entry.failureTimestamps = entry.failureTimestamps.filter((ts) => ts >= windowStart);
234
+ entry.failures = entry.failureTimestamps.length;
235
+ }
236
+ transitionTo(entry, newState, source, endpoint, reason) {
237
+ const previousState = entry.state;
238
+ entry.state = newState;
239
+ const event = {
240
+ source,
241
+ endpoint,
242
+ state: newState,
243
+ previousState,
244
+ failures: entry.failures,
245
+ reason,
246
+ };
247
+ switch (newState) {
248
+ case 'open':
249
+ entry.openedAt = Date.now();
250
+ entry.successes = 0;
251
+ this.callbacks.onOpen?.(event);
252
+ break;
253
+ case 'half_open':
254
+ entry.successes = 0;
255
+ this.callbacks.onHalfOpen?.(event);
256
+ break;
257
+ case 'closed':
258
+ entry.failures = 0;
259
+ entry.successes = 0;
260
+ entry.failureTimestamps = [];
261
+ entry.lastFailureTime = undefined;
262
+ entry.openedAt = undefined;
263
+ this.callbacks.onClose?.(event);
264
+ break;
265
+ }
266
+ }
267
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Credentials resolver with dotenv support
3
+ *
4
+ * Supports loading credentials from:
5
+ * - Environment variables (via .env files or process.env)
6
+ * - JSON config files with env var interpolation
7
+ *
8
+ * Env var reference patterns:
9
+ * - $VAR_NAME
10
+ * - ${VAR_NAME}
11
+ * - ${VAR_NAME:-default} (with default value)
12
+ */
13
+ export interface CredentialsConfig {
14
+ /** Path to .env file (default: .env in cwd) */
15
+ envFile?: string;
16
+ /** Additional .env files to load (loaded in order, later files override) */
17
+ envFiles?: string[];
18
+ /** Whether to load from process.env (default: true) */
19
+ useProcessEnv?: boolean;
20
+ /** Base directory for resolving relative paths */
21
+ baseDir?: string;
22
+ }
23
+ export interface LoadEnvResult {
24
+ /** Whether any .env files were loaded */
25
+ loaded: boolean;
26
+ /** Paths of loaded .env files */
27
+ files: string[];
28
+ /** Number of variables loaded */
29
+ count: number;
30
+ }
31
+ /**
32
+ * Load environment variables from .env files
33
+ */
34
+ export declare function loadEnv(options?: CredentialsConfig): LoadEnvResult;
35
+ /**
36
+ * Resolve environment variable references in a string
37
+ *
38
+ * Supports:
39
+ * - $VAR_NAME
40
+ * - ${VAR_NAME}
41
+ * - ${VAR_NAME:-default}
42
+ */
43
+ export declare function resolveEnvString(value: string): string;
44
+ /**
45
+ * Check if a string contains env var references
46
+ */
47
+ export declare function hasEnvReference(value: string): boolean;
48
+ /**
49
+ * Recursively resolve all env var references in an object
50
+ */
51
+ export declare function resolveCredentials<T>(config: T): T;
52
+ /**
53
+ * Auth configuration types that can be resolved from env vars
54
+ */
55
+ export interface AuthCredentials {
56
+ [sourceName: string]: SourceCredentials;
57
+ }
58
+ export interface SourceCredentials {
59
+ type: 'bearer' | 'oauth2' | 'api_key' | 'basic';
60
+ /** Bearer token (for type: bearer) */
61
+ token?: string;
62
+ /** Access token (for type: oauth2) */
63
+ accessToken?: string;
64
+ /** Refresh token (for type: oauth2) */
65
+ refreshToken?: string;
66
+ /** Token endpoint URL (for type: oauth2) */
67
+ tokenEndpoint?: string;
68
+ /** OAuth2 client ID */
69
+ clientId?: string;
70
+ /** OAuth2 client secret */
71
+ clientSecret?: string;
72
+ /** API key value (for type: api_key) */
73
+ apiKey?: string;
74
+ /** API key header name (default: X-API-Key) */
75
+ headerName?: string;
76
+ /** Username (for type: basic) */
77
+ username?: string;
78
+ /** Password (for type: basic) */
79
+ password?: string;
80
+ }
81
+ /**
82
+ * Load and resolve credentials from a JSON file with env var interpolation
83
+ */
84
+ export declare function loadCredentials(config: Record<string, unknown>, options?: CredentialsConfig): AuthCredentials;
85
+ /**
86
+ * Build credentials directly from environment variables using a naming convention
87
+ *
88
+ * Convention: REQON_{SOURCE}_{FIELD}
89
+ * Example: REQON_GITHUB_TOKEN, REQON_XERO_CLIENT_ID
90
+ */
91
+ export declare function credentialsFromEnv(sourceNames: string[]): AuthCredentials;
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Credentials resolver with dotenv support
3
+ *
4
+ * Supports loading credentials from:
5
+ * - Environment variables (via .env files or process.env)
6
+ * - JSON config files with env var interpolation
7
+ *
8
+ * Env var reference patterns:
9
+ * - $VAR_NAME
10
+ * - ${VAR_NAME}
11
+ * - ${VAR_NAME:-default} (with default value)
12
+ */
13
+ import { config as dotenvConfig } from 'dotenv';
14
+ import { resolve } from 'node:path';
15
+ import { existsSync } from 'node:fs';
16
+ /**
17
+ * Load environment variables from .env files
18
+ */
19
+ export function loadEnv(options = {}) {
20
+ const baseDir = options.baseDir || process.cwd();
21
+ const files = [];
22
+ let totalCount = 0;
23
+ // Determine which .env files to load
24
+ const envFilePaths = [];
25
+ if (options.envFiles) {
26
+ envFilePaths.push(...options.envFiles.map((f) => resolve(baseDir, f)));
27
+ }
28
+ else if (options.envFile) {
29
+ envFilePaths.push(resolve(baseDir, options.envFile));
30
+ }
31
+ else {
32
+ // Default: look for .env, .env.local in order
33
+ const defaultFiles = ['.env', '.env.local'];
34
+ for (const file of defaultFiles) {
35
+ const path = resolve(baseDir, file);
36
+ if (existsSync(path)) {
37
+ envFilePaths.push(path);
38
+ }
39
+ }
40
+ }
41
+ // Load each file (later files override earlier ones)
42
+ for (const envPath of envFilePaths) {
43
+ if (existsSync(envPath)) {
44
+ const result = dotenvConfig({ path: envPath, override: true });
45
+ if (!result.error && result.parsed) {
46
+ files.push(envPath);
47
+ totalCount += Object.keys(result.parsed).length;
48
+ }
49
+ }
50
+ }
51
+ return {
52
+ loaded: files.length > 0,
53
+ files,
54
+ count: totalCount,
55
+ };
56
+ }
57
+ /**
58
+ * Resolve environment variable references in a string
59
+ *
60
+ * Supports:
61
+ * - $VAR_NAME
62
+ * - ${VAR_NAME}
63
+ * - ${VAR_NAME:-default}
64
+ */
65
+ export function resolveEnvString(value) {
66
+ // Pattern for ${VAR:-default} or ${VAR}
67
+ const bracketPattern = /\$\{([^}:]+)(?::-([^}]*))?\}/g;
68
+ // Pattern for $VAR (word characters only, not followed by {)
69
+ const simplePattern = /\$([A-Za-z_][A-Za-z0-9_]*)/g;
70
+ let result = value;
71
+ // First resolve ${VAR} and ${VAR:-default} patterns
72
+ result = result.replace(bracketPattern, (_match, varName, defaultValue) => {
73
+ const envValue = process.env[varName];
74
+ if (envValue !== undefined) {
75
+ return envValue;
76
+ }
77
+ if (defaultValue !== undefined) {
78
+ return defaultValue;
79
+ }
80
+ // Return empty string if no value and no default
81
+ return '';
82
+ });
83
+ // Then resolve $VAR patterns (only if not already resolved)
84
+ result = result.replace(simplePattern, (_match, varName) => {
85
+ const envValue = process.env[varName];
86
+ return envValue !== undefined ? envValue : '';
87
+ });
88
+ return result;
89
+ }
90
+ /**
91
+ * Check if a string contains env var references
92
+ */
93
+ export function hasEnvReference(value) {
94
+ return /\$\{?[A-Za-z_][A-Za-z0-9_]*/.test(value);
95
+ }
96
+ /**
97
+ * Recursively resolve all env var references in an object
98
+ */
99
+ export function resolveCredentials(config) {
100
+ if (config === null || config === undefined) {
101
+ return config;
102
+ }
103
+ if (typeof config === 'string') {
104
+ return resolveEnvString(config);
105
+ }
106
+ if (Array.isArray(config)) {
107
+ return config.map((item) => resolveCredentials(item));
108
+ }
109
+ if (typeof config === 'object') {
110
+ const result = {};
111
+ for (const [key, value] of Object.entries(config)) {
112
+ result[key] = resolveCredentials(value);
113
+ }
114
+ return result;
115
+ }
116
+ return config;
117
+ }
118
+ /**
119
+ * Load and resolve credentials from a JSON file with env var interpolation
120
+ */
121
+ export function loadCredentials(config, options = {}) {
122
+ // Load .env files first
123
+ loadEnv(options);
124
+ // Resolve env var references in the config
125
+ return resolveCredentials(config);
126
+ }
127
+ /**
128
+ * Build credentials directly from environment variables using a naming convention
129
+ *
130
+ * Convention: REQON_{SOURCE}_{FIELD}
131
+ * Example: REQON_GITHUB_TOKEN, REQON_XERO_CLIENT_ID
132
+ */
133
+ export function credentialsFromEnv(sourceNames) {
134
+ const credentials = {};
135
+ for (const sourceName of sourceNames) {
136
+ const prefix = `REQON_${sourceName.toUpperCase()}_`;
137
+ const sourceCredentials = {};
138
+ // Map env var suffixes to credential fields
139
+ const fieldMappings = {
140
+ TYPE: 'type',
141
+ TOKEN: 'token',
142
+ ACCESS_TOKEN: 'accessToken',
143
+ REFRESH_TOKEN: 'refreshToken',
144
+ TOKEN_ENDPOINT: 'tokenEndpoint',
145
+ CLIENT_ID: 'clientId',
146
+ CLIENT_SECRET: 'clientSecret',
147
+ API_KEY: 'apiKey',
148
+ HEADER_NAME: 'headerName',
149
+ USERNAME: 'username',
150
+ PASSWORD: 'password',
151
+ };
152
+ for (const [suffix, field] of Object.entries(fieldMappings)) {
153
+ const envVar = `${prefix}${suffix}`;
154
+ const value = process.env[envVar];
155
+ if (value !== undefined) {
156
+ sourceCredentials[field] = value;
157
+ }
158
+ }
159
+ // Only add if we found at least a type or token
160
+ if (sourceCredentials.type || sourceCredentials.token || sourceCredentials.accessToken) {
161
+ // Default to bearer if we have a token but no type
162
+ if (!sourceCredentials.type && sourceCredentials.token) {
163
+ sourceCredentials.type = 'bearer';
164
+ }
165
+ credentials[sourceName] = sourceCredentials;
166
+ }
167
+ }
168
+ return credentials;
169
+ }
@@ -0,0 +1,5 @@
1
+ export type { AuthProvider, TokenInfo, OAuth2Tokens, TokenStore, OAuth2Config, RateLimitInfo, RateLimiter, RateLimitStatus, RateLimitConfig, RateLimitCallbacks, RateLimitEvent, RateLimitStrategy, } from './types.js';
2
+ export { AdaptiveRateLimiter, parseRateLimitHeaders, RateLimitError, RateLimitTimeoutError, } from './rate-limiter.js';
3
+ export { InMemoryTokenStore, FileTokenStore } from './token-store.js';
4
+ export { OAuth2AuthProvider, BearerTokenProvider, ApiKeyProvider } from './oauth2-provider.js';
5
+ export { CircuitBreaker, CircuitBreakerError, type CircuitBreakerConfig, type CircuitBreakerStatus, type CircuitBreakerEvent, type CircuitBreakerCallbacks, type CircuitState, } from './circuit-breaker.js';
@@ -0,0 +1,8 @@
1
+ // Rate limiting
2
+ export { AdaptiveRateLimiter, parseRateLimitHeaders, RateLimitError, RateLimitTimeoutError, } from './rate-limiter.js';
3
+ // Token stores
4
+ export { InMemoryTokenStore, FileTokenStore } from './token-store.js';
5
+ // Auth providers
6
+ export { OAuth2AuthProvider, BearerTokenProvider, ApiKeyProvider } from './oauth2-provider.js';
7
+ // Circuit breaker
8
+ export { CircuitBreaker, CircuitBreakerError, } from './circuit-breaker.js';
@@ -0,0 +1,41 @@
1
+ import type { AuthProvider, TokenInfo, TokenStore, OAuth2Config } from './types.js';
2
+ /**
3
+ * OAuth2 auth provider with automatic token refresh
4
+ */
5
+ export declare class OAuth2AuthProvider implements AuthProvider {
6
+ private connectionId;
7
+ private store;
8
+ private config;
9
+ private refreshBuffer;
10
+ private refreshPromise;
11
+ constructor(options: {
12
+ connectionId: string;
13
+ store: TokenStore;
14
+ config: OAuth2Config;
15
+ });
16
+ getToken(): Promise<string>;
17
+ refreshToken(): Promise<string>;
18
+ private doRefresh;
19
+ private shouldRefresh;
20
+ getTokenInfo(): TokenInfo;
21
+ }
22
+ /**
23
+ * Simple bearer token provider (no refresh)
24
+ */
25
+ export declare class BearerTokenProvider implements AuthProvider {
26
+ private token;
27
+ constructor(token: string);
28
+ getToken(): Promise<string>;
29
+ getTokenInfo(): TokenInfo;
30
+ }
31
+ /**
32
+ * API key provider
33
+ */
34
+ export declare class ApiKeyProvider implements AuthProvider {
35
+ private apiKey;
36
+ private headerName;
37
+ constructor(apiKey: string, headerName?: string);
38
+ getToken(): Promise<string>;
39
+ getHeaderName(): string;
40
+ getTokenInfo(): TokenInfo;
41
+ }