reqon-dsl 0.2.0 → 0.4.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 (450) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +45 -3
  3. package/dist/ast/nodes.d.ts +91 -4
  4. package/dist/ast/nodes.js +14 -0
  5. package/dist/auth/circuit-breaker.d.ts +11 -0
  6. package/dist/auth/circuit-breaker.js +90 -18
  7. package/dist/auth/credentials.d.ts +6 -1
  8. package/dist/auth/credentials.js +12 -4
  9. package/dist/auth/oauth2-provider.js +13 -3
  10. package/dist/auth/rate-limiter.d.ts +12 -1
  11. package/dist/auth/rate-limiter.js +39 -26
  12. package/dist/auth/token-store.js +8 -1
  13. package/dist/cli.d.ts +24 -1
  14. package/dist/cli.js +149 -10
  15. package/dist/config/constants.d.ts +152 -0
  16. package/dist/config/constants.js +139 -0
  17. package/dist/config/index.d.ts +4 -0
  18. package/dist/config/index.js +4 -0
  19. package/dist/control/index.d.ts +2 -0
  20. package/dist/control/index.js +1 -0
  21. package/dist/control/server.d.ts +105 -0
  22. package/dist/control/server.js +315 -0
  23. package/dist/control/types.d.ts +61 -0
  24. package/dist/control/types.js +7 -0
  25. package/dist/debug/cli-debugger.d.ts +17 -0
  26. package/dist/debug/cli-debugger.js +185 -0
  27. package/dist/debug/controller.d.ts +94 -0
  28. package/dist/debug/controller.js +45 -0
  29. package/dist/debug/index.d.ts +6 -0
  30. package/dist/debug/index.js +5 -0
  31. package/dist/errors/index.d.ts +67 -0
  32. package/dist/errors/index.js +89 -1
  33. package/dist/execution/index.d.ts +1 -1
  34. package/dist/execution/state.d.ts +24 -0
  35. package/dist/execution/store.js +2 -2
  36. package/dist/execution-log/events.d.ts +125 -0
  37. package/dist/execution-log/events.js +17 -0
  38. package/dist/execution-log/fold.d.ts +38 -0
  39. package/dist/execution-log/fold.js +54 -0
  40. package/dist/execution-log/index.d.ts +18 -0
  41. package/dist/execution-log/index.js +6 -0
  42. package/dist/execution-log/postgres-store.d.ts +36 -0
  43. package/dist/execution-log/postgres-store.js +108 -0
  44. package/dist/execution-log/resume.d.ts +11 -0
  45. package/dist/execution-log/resume.js +5 -0
  46. package/dist/execution-log/sqlite-store.d.ts +16 -0
  47. package/dist/execution-log/sqlite-store.js +101 -0
  48. package/dist/execution-log/store.d.ts +72 -0
  49. package/dist/execution-log/store.js +182 -0
  50. package/dist/index.d.ts +23 -2
  51. package/dist/index.js +35 -3
  52. package/dist/interpreter/context.d.ts +29 -0
  53. package/dist/interpreter/context.js +18 -0
  54. package/dist/interpreter/evaluator.d.ts +63 -1
  55. package/dist/interpreter/evaluator.js +219 -42
  56. package/dist/interpreter/executor.d.ts +132 -14
  57. package/dist/interpreter/executor.js +883 -178
  58. package/dist/interpreter/fetch-handler.d.ts +48 -1
  59. package/dist/interpreter/fetch-handler.js +216 -38
  60. package/dist/interpreter/http.d.ts +34 -0
  61. package/dist/interpreter/http.js +203 -28
  62. package/dist/interpreter/index.d.ts +5 -3
  63. package/dist/interpreter/index.js +4 -2
  64. package/dist/interpreter/pagination.d.ts +12 -3
  65. package/dist/interpreter/pagination.js +102 -32
  66. package/dist/interpreter/signals.d.ts +8 -0
  67. package/dist/interpreter/signals.js +12 -0
  68. package/dist/interpreter/source-manager.d.ts +75 -0
  69. package/dist/interpreter/source-manager.js +157 -0
  70. package/dist/interpreter/step-handlers/apply-handler.d.ts +29 -0
  71. package/dist/interpreter/step-handlers/apply-handler.js +79 -0
  72. package/dist/interpreter/step-handlers/for-handler.d.ts +16 -0
  73. package/dist/interpreter/step-handlers/for-handler.js +89 -7
  74. package/dist/interpreter/step-handlers/index.d.ts +4 -2
  75. package/dist/interpreter/step-handlers/index.js +4 -2
  76. package/dist/interpreter/step-handlers/match-handler.d.ts +9 -0
  77. package/dist/interpreter/step-handlers/match-handler.js +47 -17
  78. package/dist/interpreter/step-handlers/pause-handler.d.ts +52 -0
  79. package/dist/interpreter/step-handlers/pause-handler.js +87 -0
  80. package/dist/interpreter/step-handlers/store-handler.d.ts +17 -1
  81. package/dist/interpreter/step-handlers/store-handler.js +61 -20
  82. package/dist/interpreter/step-handlers/types.d.ts +3 -0
  83. package/dist/interpreter/step-handlers/validate-handler.d.ts +2 -1
  84. package/dist/interpreter/step-handlers/validate-handler.js +7 -2
  85. package/dist/interpreter/step-handlers/webhook-handler.d.ts +4 -0
  86. package/dist/interpreter/step-handlers/webhook-handler.js +31 -5
  87. package/dist/interpreter/store-manager.d.ts +46 -0
  88. package/dist/interpreter/store-manager.js +70 -0
  89. package/dist/lexer/index.d.ts +11 -4
  90. package/dist/lexer/index.js +11 -4
  91. package/dist/lexer/tokens.d.ts +17 -1
  92. package/dist/lexer/tokens.js +36 -0
  93. package/dist/loader/index.js +5 -8
  94. package/dist/mcp/index.d.ts +11 -0
  95. package/dist/mcp/index.js +11 -0
  96. package/dist/mcp/sandbox.d.ts +41 -0
  97. package/dist/mcp/sandbox.js +76 -0
  98. package/dist/mcp/server.d.ts +17 -0
  99. package/dist/mcp/server.js +504 -0
  100. package/dist/oas/index.d.ts +2 -0
  101. package/dist/oas/index.js +1 -0
  102. package/dist/oas/loader.d.ts +13 -1
  103. package/dist/oas/loader.js +25 -3
  104. package/dist/oas/mock-generator.d.ts +12 -0
  105. package/dist/oas/mock-generator.js +196 -0
  106. package/dist/oas/validator.js +45 -5
  107. package/dist/observability/events.d.ts +248 -0
  108. package/dist/observability/events.js +85 -0
  109. package/dist/observability/index.d.ts +15 -0
  110. package/dist/observability/index.js +12 -0
  111. package/dist/observability/logger.d.ts +106 -0
  112. package/dist/observability/logger.js +266 -0
  113. package/dist/observability/otel.d.ts +143 -0
  114. package/dist/observability/otel.js +421 -0
  115. package/dist/parser/action-parser.d.ts +105 -0
  116. package/dist/parser/action-parser.js +645 -0
  117. package/dist/parser/base.d.ts +7 -0
  118. package/dist/parser/base.js +11 -0
  119. package/dist/parser/expressions.d.ts +14 -0
  120. package/dist/parser/expressions.js +89 -6
  121. package/dist/parser/fetch-parser.d.ts +27 -0
  122. package/dist/parser/fetch-parser.js +280 -0
  123. package/dist/parser/index.d.ts +17 -0
  124. package/dist/parser/index.js +17 -0
  125. package/dist/parser/parser.d.ts +44 -46
  126. package/dist/parser/parser.js +122 -1070
  127. package/dist/parser/pipeline-parser.d.ts +12 -0
  128. package/dist/parser/pipeline-parser.js +52 -0
  129. package/dist/parser/schedule-parser.d.ts +7 -0
  130. package/dist/parser/schedule-parser.js +137 -0
  131. package/dist/parser/source-parser.d.ts +9 -0
  132. package/dist/parser/source-parser.js +151 -0
  133. package/dist/pause/index.d.ts +15 -0
  134. package/dist/pause/index.js +12 -0
  135. package/dist/pause/log-store.d.ts +33 -0
  136. package/dist/pause/log-store.js +98 -0
  137. package/dist/pause/manager.d.ts +130 -0
  138. package/dist/pause/manager.js +294 -0
  139. package/dist/pause/state.d.ts +93 -0
  140. package/dist/pause/state.js +103 -0
  141. package/dist/pause/store.d.ts +61 -0
  142. package/dist/pause/store.js +158 -0
  143. package/dist/plugin.d.ts +9 -12
  144. package/dist/plugin.js +10 -13
  145. package/dist/scheduler/cron-parser.d.ts +10 -3
  146. package/dist/scheduler/cron-parser.js +227 -48
  147. package/dist/scheduler/scheduler.js +56 -22
  148. package/dist/stores/factory.d.ts +7 -1
  149. package/dist/stores/factory.js +14 -3
  150. package/dist/stores/file.d.ts +26 -0
  151. package/dist/stores/file.js +67 -21
  152. package/dist/stores/index.d.ts +16 -1
  153. package/dist/stores/index.js +16 -1
  154. package/dist/stores/memory.d.ts +4 -0
  155. package/dist/stores/memory.js +8 -6
  156. package/dist/stores/postgrest.d.ts +28 -0
  157. package/dist/stores/postgrest.js +84 -37
  158. package/dist/stores/types.d.ts +17 -0
  159. package/dist/stores/types.js +12 -0
  160. package/dist/sync/index.d.ts +3 -2
  161. package/dist/sync/index.js +2 -1
  162. package/dist/sync/log-store.d.ts +30 -0
  163. package/dist/sync/log-store.js +45 -0
  164. package/dist/sync/store.js +1 -1
  165. package/dist/trace/index.d.ts +18 -0
  166. package/dist/trace/index.js +13 -0
  167. package/dist/trace/log-view.d.ts +57 -0
  168. package/dist/trace/log-view.js +76 -0
  169. package/dist/trace/recorder.d.ts +75 -0
  170. package/dist/trace/recorder.js +157 -0
  171. package/dist/trace/replay.d.ts +132 -0
  172. package/dist/trace/replay.js +264 -0
  173. package/dist/trace/state.d.ts +102 -0
  174. package/dist/trace/state.js +86 -0
  175. package/dist/trace/store.d.ts +75 -0
  176. package/dist/trace/store.js +250 -0
  177. package/dist/utils/deep-merge.d.ts +10 -0
  178. package/dist/utils/deep-merge.js +23 -0
  179. package/dist/utils/file.d.ts +13 -4
  180. package/dist/utils/file.js +70 -12
  181. package/dist/utils/index.d.ts +2 -1
  182. package/dist/utils/index.js +2 -1
  183. package/dist/utils/long-timeout.d.ts +19 -0
  184. package/dist/utils/long-timeout.js +33 -0
  185. package/dist/utils/path.d.ts +22 -1
  186. package/dist/utils/path.js +46 -1
  187. package/dist/utils/redact.d.ts +22 -0
  188. package/dist/utils/redact.js +42 -0
  189. package/dist/utils/type-guards.d.ts +58 -0
  190. package/dist/utils/type-guards.js +92 -0
  191. package/dist/webhook/server.d.ts +9 -0
  192. package/dist/webhook/server.js +122 -36
  193. package/dist/webhook/types.d.ts +9 -1
  194. package/package.json +76 -9
  195. package/.claude/settings.local.json +0 -31
  196. package/.claude/skills/api-integration.md +0 -125
  197. package/.claude/skills/database-schema.md +0 -51
  198. package/.claude/skills/dsl-design.md +0 -80
  199. package/.claude/skills/property-testing.md +0 -143
  200. package/.claude/skills/reqon/SKILL.md +0 -44
  201. package/.claude/skills/reqon/references/examples.md +0 -206
  202. package/.claude/skills/reqon/references/syntax.md +0 -263
  203. package/.claude/skills/vscode-extension.md +0 -113
  204. package/.github/dependabot.yml +0 -32
  205. package/.github/pull_request_template.md +0 -21
  206. package/.github/workflows/ci.yml +0 -174
  207. package/.github/workflows/release.yml +0 -73
  208. package/CLAUDE.md +0 -72
  209. package/CONTRIBUTING.md +0 -161
  210. package/TODO.md +0 -51
  211. package/dist/auth/auth.test.d.ts +0 -1
  212. package/dist/auth/auth.test.js +0 -255
  213. package/dist/errors/errors.test.d.ts +0 -1
  214. package/dist/errors/errors.test.js +0 -165
  215. package/dist/execution/execution.test.d.ts +0 -1
  216. package/dist/execution/execution.test.js +0 -246
  217. package/dist/integration.test.d.ts +0 -1
  218. package/dist/integration.test.js +0 -168
  219. package/dist/interpreter/evaluator.test.d.ts +0 -1
  220. package/dist/interpreter/evaluator.test.js +0 -512
  221. package/dist/interpreter/http.test.d.ts +0 -1
  222. package/dist/interpreter/http.test.js +0 -299
  223. package/dist/interpreter/progress.test.d.ts +0 -1
  224. package/dist/interpreter/progress.test.js +0 -216
  225. package/dist/interpreter/schema-matcher.test.d.ts +0 -1
  226. package/dist/interpreter/schema-matcher.test.js +0 -122
  227. package/dist/lexer/lexer.d.ts +0 -24
  228. package/dist/lexer/lexer.js +0 -264
  229. package/dist/lexer/lexer.test.d.ts +0 -1
  230. package/dist/lexer/lexer.test.js +0 -259
  231. package/dist/loader/loader.test.d.ts +0 -1
  232. package/dist/loader/loader.test.js +0 -287
  233. package/dist/oas/oas.test.d.ts +0 -1
  234. package/dist/oas/oas.test.js +0 -218
  235. package/dist/parser/expressions.test.d.ts +0 -1
  236. package/dist/parser/expressions.test.js +0 -378
  237. package/dist/parser/match.test.d.ts +0 -1
  238. package/dist/parser/match.test.js +0 -254
  239. package/dist/parser/parser.test.d.ts +0 -1
  240. package/dist/parser/parser.test.js +0 -333
  241. package/dist/parser/schedule.test.d.ts +0 -1
  242. package/dist/parser/schedule.test.js +0 -241
  243. package/dist/scheduler/cron-parser.test.d.ts +0 -1
  244. package/dist/scheduler/cron-parser.test.js +0 -188
  245. package/dist/stores/file.test.d.ts +0 -1
  246. package/dist/stores/file.test.js +0 -165
  247. package/dist/stores/memory.test.d.ts +0 -1
  248. package/dist/stores/memory.test.js +0 -157
  249. package/dist/stores/stores.test.d.ts +0 -1
  250. package/dist/stores/stores.test.js +0 -158
  251. package/dist/sync/sync.test.d.ts +0 -1
  252. package/dist/sync/sync.test.js +0 -221
  253. package/docusaurus/README.md +0 -41
  254. package/docusaurus/docs/advanced/execution-state.md +0 -283
  255. package/docusaurus/docs/advanced/extending-reqon.md +0 -388
  256. package/docusaurus/docs/advanced/multi-file-missions.md +0 -250
  257. package/docusaurus/docs/advanced/parallel-execution.md +0 -353
  258. package/docusaurus/docs/api-reference.md +0 -443
  259. package/docusaurus/docs/authentication/api-key.md +0 -339
  260. package/docusaurus/docs/authentication/basic.md +0 -276
  261. package/docusaurus/docs/authentication/bearer.md +0 -282
  262. package/docusaurus/docs/authentication/oauth2.md +0 -317
  263. package/docusaurus/docs/authentication/overview.md +0 -251
  264. package/docusaurus/docs/cli.md +0 -229
  265. package/docusaurus/docs/core-concepts/actions.md +0 -286
  266. package/docusaurus/docs/core-concepts/missions.md +0 -264
  267. package/docusaurus/docs/core-concepts/schemas.md +0 -353
  268. package/docusaurus/docs/core-concepts/sources.md +0 -339
  269. package/docusaurus/docs/core-concepts/stores.md +0 -332
  270. package/docusaurus/docs/dsl-syntax/expressions.md +0 -361
  271. package/docusaurus/docs/dsl-syntax/fetch.md +0 -293
  272. package/docusaurus/docs/dsl-syntax/for-loops.md +0 -324
  273. package/docusaurus/docs/dsl-syntax/map.md +0 -345
  274. package/docusaurus/docs/dsl-syntax/match.md +0 -387
  275. package/docusaurus/docs/dsl-syntax/pipelines.md +0 -397
  276. package/docusaurus/docs/dsl-syntax/validate.md +0 -401
  277. package/docusaurus/docs/error-handling/dead-letter-queues.md +0 -399
  278. package/docusaurus/docs/error-handling/flow-control.md +0 -337
  279. package/docusaurus/docs/error-handling/retry-strategies.md +0 -368
  280. package/docusaurus/docs/examples.md +0 -488
  281. package/docusaurus/docs/getting-started.md +0 -256
  282. package/docusaurus/docs/http/circuit-breaker.md +0 -401
  283. package/docusaurus/docs/http/incremental-sync.md +0 -394
  284. package/docusaurus/docs/http/pagination.md +0 -361
  285. package/docusaurus/docs/http/rate-limiting.md +0 -383
  286. package/docusaurus/docs/http/requests.md +0 -328
  287. package/docusaurus/docs/http/retry.md +0 -402
  288. package/docusaurus/docs/intro.md +0 -90
  289. package/docusaurus/docs/openapi/loading-specs.md +0 -305
  290. package/docusaurus/docs/openapi/operation-calls.md +0 -314
  291. package/docusaurus/docs/openapi/overview.md +0 -212
  292. package/docusaurus/docs/openapi/response-validation.md +0 -344
  293. package/docusaurus/docs/scheduling/cron.md +0 -305
  294. package/docusaurus/docs/scheduling/daemon-mode.md +0 -317
  295. package/docusaurus/docs/scheduling/intervals.md +0 -289
  296. package/docusaurus/docs/scheduling/overview.md +0 -231
  297. package/docusaurus/docs/stores/custom-adapters.md +0 -376
  298. package/docusaurus/docs/stores/file.md +0 -236
  299. package/docusaurus/docs/stores/memory.md +0 -193
  300. package/docusaurus/docs/stores/overview.md +0 -274
  301. package/docusaurus/docs/stores/postgrest.md +0 -316
  302. package/docusaurus/docusaurus.config.ts +0 -148
  303. package/docusaurus/package-lock.json +0 -18029
  304. package/docusaurus/package.json +0 -47
  305. package/docusaurus/sidebars.ts +0 -155
  306. package/docusaurus/src/components/HomepageFeatures/index.tsx +0 -105
  307. package/docusaurus/src/components/HomepageFeatures/styles.module.css +0 -12
  308. package/docusaurus/src/css/custom.css +0 -169
  309. package/docusaurus/src/pages/index.module.css +0 -48
  310. package/docusaurus/src/pages/index.tsx +0 -110
  311. package/docusaurus/src/pages/markdown-page.md +0 -7
  312. package/docusaurus/static/.nojekyll +0 -0
  313. package/docusaurus/static/img/docusaurus-social-card.jpg +0 -0
  314. package/docusaurus/static/img/docusaurus.png +0 -0
  315. package/docusaurus/static/img/favicon.ico +0 -0
  316. package/docusaurus/static/img/logo.svg +0 -10
  317. package/docusaurus/static/img/undraw_docusaurus_mountain.svg +0 -171
  318. package/docusaurus/static/img/undraw_docusaurus_react.svg +0 -170
  319. package/docusaurus/static/img/undraw_docusaurus_tree.svg +0 -40
  320. package/docusaurus/tsconfig.json +0 -8
  321. package/examples/README.md +0 -112
  322. package/examples/error-handling/README.md +0 -150
  323. package/examples/error-handling/payment-processor.vague +0 -287
  324. package/examples/github-sync/README.md +0 -74
  325. package/examples/github-sync/fetch-issues.vague +0 -47
  326. package/examples/github-sync/fetch-prs.vague +0 -40
  327. package/examples/github-sync/mission.vague +0 -101
  328. package/examples/github-sync/normalize.vague +0 -70
  329. package/examples/jsonplaceholder/README.md +0 -28
  330. package/examples/jsonplaceholder/posts.vague +0 -48
  331. package/examples/petstore/README.md +0 -35
  332. package/examples/petstore/openapi.yaml +0 -97
  333. package/examples/petstore/sync.vague +0 -52
  334. package/examples/temporal-comparison/README.md +0 -297
  335. package/examples/temporal-comparison/reconciliation.vague +0 -355
  336. package/examples/temporal-comparison/temporal/activities/index.ts +0 -8
  337. package/examples/temporal-comparison/temporal/activities/shipstation.ts +0 -225
  338. package/examples/temporal-comparison/temporal/activities/shopify.ts +0 -257
  339. package/examples/temporal-comparison/temporal/activities/storage.ts +0 -198
  340. package/examples/temporal-comparison/temporal/activities/stripe.ts +0 -169
  341. package/examples/temporal-comparison/temporal/activities/validation.ts +0 -205
  342. package/examples/temporal-comparison/temporal/client/schedule.ts +0 -218
  343. package/examples/temporal-comparison/temporal/config/retry.ts +0 -63
  344. package/examples/temporal-comparison/temporal/types/index.ts +0 -129
  345. package/examples/temporal-comparison/temporal/workers/main.ts +0 -130
  346. package/examples/temporal-comparison/temporal/workflows/orderReconciliation.ts +0 -262
  347. package/examples/xero/README.md +0 -88
  348. package/examples/xero/invoices.vague +0 -189
  349. package/src/api-integration.test.ts +0 -954
  350. package/src/ast/index.ts +0 -1
  351. package/src/ast/nodes.ts +0 -310
  352. package/src/auth/auth.test.ts +0 -326
  353. package/src/auth/circuit-breaker.test.ts +0 -390
  354. package/src/auth/circuit-breaker.ts +0 -379
  355. package/src/auth/credentials.test.ts +0 -273
  356. package/src/auth/credentials.ts +0 -246
  357. package/src/auth/index.ts +0 -40
  358. package/src/auth/oauth2-provider.ts +0 -177
  359. package/src/auth/rate-limiter.ts +0 -459
  360. package/src/auth/token-store.ts +0 -177
  361. package/src/auth/types.ts +0 -159
  362. package/src/benchmark/e2e.bench.ts +0 -288
  363. package/src/benchmark/evaluator.bench.ts +0 -331
  364. package/src/benchmark/fixtures.ts +0 -295
  365. package/src/benchmark/index.ts +0 -108
  366. package/src/benchmark/lexer.bench.ts +0 -69
  367. package/src/benchmark/parser.bench.ts +0 -103
  368. package/src/benchmark/resilience.bench.ts +0 -193
  369. package/src/benchmark/store.bench.ts +0 -147
  370. package/src/benchmark/utils.ts +0 -230
  371. package/src/cli.ts +0 -313
  372. package/src/errors/errors.test.ts +0 -234
  373. package/src/errors/index.ts +0 -223
  374. package/src/execution/execution.test.ts +0 -307
  375. package/src/execution/index.ts +0 -21
  376. package/src/execution/state.ts +0 -207
  377. package/src/execution/store.ts +0 -188
  378. package/src/index.ts +0 -169
  379. package/src/integration.test.ts +0 -192
  380. package/src/interpreter/context.ts +0 -57
  381. package/src/interpreter/evaluator.test.ts +0 -796
  382. package/src/interpreter/evaluator.ts +0 -245
  383. package/src/interpreter/executor.ts +0 -946
  384. package/src/interpreter/fetch-handler.ts +0 -302
  385. package/src/interpreter/http.test.ts +0 -423
  386. package/src/interpreter/http.ts +0 -308
  387. package/src/interpreter/index.ts +0 -32
  388. package/src/interpreter/pagination.ts +0 -207
  389. package/src/interpreter/progress.test.ts +0 -276
  390. package/src/interpreter/schema-matcher.test.ts +0 -160
  391. package/src/interpreter/schema-matcher.ts +0 -168
  392. package/src/interpreter/signals.ts +0 -73
  393. package/src/interpreter/step-handlers/for-handler.ts +0 -65
  394. package/src/interpreter/step-handlers/index.ts +0 -17
  395. package/src/interpreter/step-handlers/map-handler.ts +0 -24
  396. package/src/interpreter/step-handlers/match-handler.ts +0 -101
  397. package/src/interpreter/step-handlers/store-handler.ts +0 -78
  398. package/src/interpreter/step-handlers/types.ts +0 -17
  399. package/src/interpreter/step-handlers/validate-handler.ts +0 -30
  400. package/src/interpreter/step-handlers/webhook-handler.ts +0 -142
  401. package/src/lexer/index.ts +0 -18
  402. package/src/lexer/lexer.test.ts +0 -316
  403. package/src/lexer/tokens.ts +0 -179
  404. package/src/loader/index.ts +0 -288
  405. package/src/loader/loader.test.ts +0 -360
  406. package/src/oas/index.ts +0 -4
  407. package/src/oas/loader.ts +0 -126
  408. package/src/oas/oas.test.ts +0 -254
  409. package/src/oas/validator.ts +0 -299
  410. package/src/parser/base.ts +0 -124
  411. package/src/parser/expressions.test.ts +0 -525
  412. package/src/parser/expressions.ts +0 -314
  413. package/src/parser/index.ts +0 -3
  414. package/src/parser/match.test.ts +0 -296
  415. package/src/parser/parser.test.ts +0 -739
  416. package/src/parser/parser.ts +0 -1469
  417. package/src/parser/schedule.test.ts +0 -287
  418. package/src/parser/webhook.test.ts +0 -248
  419. package/src/plugin.ts +0 -83
  420. package/src/scheduler/cron-parser.test.ts +0 -236
  421. package/src/scheduler/cron-parser.ts +0 -236
  422. package/src/scheduler/index.ts +0 -10
  423. package/src/scheduler/scheduler.ts +0 -443
  424. package/src/scheduler/types.ts +0 -71
  425. package/src/stores/factory.ts +0 -104
  426. package/src/stores/file.test.ts +0 -276
  427. package/src/stores/file.ts +0 -211
  428. package/src/stores/index.ts +0 -6
  429. package/src/stores/memory.test.ts +0 -238
  430. package/src/stores/memory.ts +0 -63
  431. package/src/stores/postgrest.test.ts +0 -488
  432. package/src/stores/postgrest.ts +0 -263
  433. package/src/stores/stores.test.ts +0 -197
  434. package/src/stores/types.ts +0 -58
  435. package/src/sync/index.ts +0 -16
  436. package/src/sync/state.ts +0 -126
  437. package/src/sync/store.ts +0 -139
  438. package/src/sync/sync.test.ts +0 -271
  439. package/src/utils/async.ts +0 -10
  440. package/src/utils/file.ts +0 -106
  441. package/src/utils/index.ts +0 -14
  442. package/src/utils/logger.ts +0 -53
  443. package/src/utils/path.ts +0 -47
  444. package/src/webhook/index.ts +0 -15
  445. package/src/webhook/server.test.ts +0 -253
  446. package/src/webhook/server.ts +0 -389
  447. package/src/webhook/store.ts +0 -239
  448. package/src/webhook/types.ts +0 -93
  449. package/tsconfig.json +0 -17
  450. package/vitest.config.ts +0 -39
@@ -7,6 +7,27 @@
7
7
  */
8
8
  export declare function extractNestedValue(data: Record<string, unknown>, path: string): unknown;
9
9
  /**
10
- * Traverse an object path and return the value, with fallback lookup
10
+ * Error thrown when a path segment would escape its base directory.
11
11
  */
12
+ export declare class PathTraversalError extends Error {
13
+ readonly segment: string;
14
+ readonly baseDir: string;
15
+ constructor(segment: string, baseDir: string);
16
+ }
17
+ /**
18
+ * Safely join an untrusted path segment under a base directory, asserting the
19
+ * result stays confined inside that base. Guards against `../`, absolute paths,
20
+ * and embedded separators in store names / IDs.
21
+ *
22
+ * @throws {PathTraversalError} if the resolved path escapes baseDir
23
+ */
24
+ export declare function safeJoin(baseDir: string, ...segments: string[]): string;
25
+ /**
26
+ * Sanitize a single untrusted path segment (e.g. a store name or record ID) so
27
+ * it cannot traverse directories. Rejects empty, separator-bearing, or
28
+ * traversal segments rather than silently mangling them.
29
+ *
30
+ * @throws {PathTraversalError} if the segment is unsafe
31
+ */
32
+ export declare function sanitizeSegment(segment: string, baseDir?: string): string;
12
33
  export declare function traversePath(parts: string[], current: unknown, fallbackLookup?: (key: string) => unknown): unknown;
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Utility functions for path traversal and value extraction
3
3
  */
4
+ import { join, resolve, sep } from 'node:path';
4
5
  /**
5
6
  * Extract a nested value from an object using dot notation path
6
7
  * @example extractNestedValue({ a: { b: 1 } }, 'a.b') // => 1
@@ -19,8 +20,52 @@ export function extractNestedValue(data, path) {
19
20
  return value;
20
21
  }
21
22
  /**
22
- * Traverse an object path and return the value, with fallback lookup
23
+ * Error thrown when a path segment would escape its base directory.
23
24
  */
25
+ export class PathTraversalError extends Error {
26
+ segment;
27
+ baseDir;
28
+ constructor(segment, baseDir) {
29
+ super(`Path segment "${segment}" escapes base directory "${baseDir}"`);
30
+ this.segment = segment;
31
+ this.baseDir = baseDir;
32
+ this.name = 'PathTraversalError';
33
+ }
34
+ }
35
+ /**
36
+ * Safely join an untrusted path segment under a base directory, asserting the
37
+ * result stays confined inside that base. Guards against `../`, absolute paths,
38
+ * and embedded separators in store names / IDs.
39
+ *
40
+ * @throws {PathTraversalError} if the resolved path escapes baseDir
41
+ */
42
+ export function safeJoin(baseDir, ...segments) {
43
+ const base = resolve(baseDir);
44
+ const joined = resolve(base, ...segments);
45
+ // Confined if it equals the base or sits inside it (base + separator prefix).
46
+ if (joined !== base && !joined.startsWith(base + sep)) {
47
+ throw new PathTraversalError(join(...segments), baseDir);
48
+ }
49
+ return joined;
50
+ }
51
+ /**
52
+ * Sanitize a single untrusted path segment (e.g. a store name or record ID) so
53
+ * it cannot traverse directories. Rejects empty, separator-bearing, or
54
+ * traversal segments rather than silently mangling them.
55
+ *
56
+ * @throws {PathTraversalError} if the segment is unsafe
57
+ */
58
+ export function sanitizeSegment(segment, baseDir = '.') {
59
+ if (segment.length === 0 ||
60
+ segment === '.' ||
61
+ segment === '..' ||
62
+ segment.includes('/') ||
63
+ segment.includes('\\') ||
64
+ segment.includes('\0')) {
65
+ throw new PathTraversalError(segment, baseDir);
66
+ }
67
+ return segment;
68
+ }
24
69
  export function traversePath(parts, current, fallbackLookup) {
25
70
  let value = current;
26
71
  for (let i = 0; i < parts.length; i++) {
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Secret redaction for logs, error messages, and durable trace files.
3
+ *
4
+ * Uses a denylist of sensitive key-name patterns: any object property whose
5
+ * key looks like a credential is replaced with {@link REDACTED}. This is a
6
+ * best-effort guard — it cannot catch a raw secret stored under an innocuous
7
+ * key — so callers should also avoid persisting cleartext credentials.
8
+ */
9
+ export declare const REDACTED = "[REDACTED]";
10
+ /** True when a key name looks like it holds a credential. */
11
+ export declare function isSensitiveKey(key: string): boolean;
12
+ /**
13
+ * Return a deep copy of `value` with any credential-looking properties
14
+ * replaced by {@link REDACTED}. Non-objects are returned unchanged. Cycles are
15
+ * handled, and the input is never mutated.
16
+ */
17
+ export declare function redactSecrets<T>(value: T, seen?: WeakSet<object>): T;
18
+ /**
19
+ * Redact a named value for logging: if the name itself looks sensitive the
20
+ * whole value is hidden; otherwise nested credential properties are redacted.
21
+ */
22
+ export declare function redactNamedValue(name: string, value: unknown): unknown;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Secret redaction for logs, error messages, and durable trace files.
3
+ *
4
+ * Uses a denylist of sensitive key-name patterns: any object property whose
5
+ * key looks like a credential is replaced with {@link REDACTED}. This is a
6
+ * best-effort guard — it cannot catch a raw secret stored under an innocuous
7
+ * key — so callers should also avoid persisting cleartext credentials.
8
+ */
9
+ export const REDACTED = '[REDACTED]';
10
+ /** Key names that should never have their value logged or persisted in clear. */
11
+ const SENSITIVE_KEY = /(pass(word|wd)?|secret|token|auth(orization)?|api[-_]?key|access[-_]?key|client[-_]?secret|credential|cookie|session|bearer|private[-_]?key)/i;
12
+ /** True when a key name looks like it holds a credential. */
13
+ export function isSensitiveKey(key) {
14
+ return SENSITIVE_KEY.test(key);
15
+ }
16
+ /**
17
+ * Return a deep copy of `value` with any credential-looking properties
18
+ * replaced by {@link REDACTED}. Non-objects are returned unchanged. Cycles are
19
+ * handled, and the input is never mutated.
20
+ */
21
+ export function redactSecrets(value, seen = new WeakSet()) {
22
+ if (value === null || typeof value !== 'object')
23
+ return value;
24
+ if (seen.has(value))
25
+ return value;
26
+ seen.add(value);
27
+ if (Array.isArray(value)) {
28
+ return value.map((item) => redactSecrets(item, seen));
29
+ }
30
+ const out = {};
31
+ for (const [key, val] of Object.entries(value)) {
32
+ out[key] = isSensitiveKey(key) ? REDACTED : redactSecrets(val, seen);
33
+ }
34
+ return out;
35
+ }
36
+ /**
37
+ * Redact a named value for logging: if the name itself looks sensitive the
38
+ * whole value is hidden; otherwise nested credential properties are redacted.
39
+ */
40
+ export function redactNamedValue(name, value) {
41
+ return isSensitiveKey(name) ? REDACTED : redactSecrets(value);
42
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Type guard utilities to reduce type assertions and improve type safety.
3
+ * These guards provide runtime type checking with TypeScript type narrowing.
4
+ */
5
+ /**
6
+ * Type guard to check if a value is a non-null object (excluding arrays).
7
+ * Narrows the type to Record<string, unknown> for safe property access.
8
+ */
9
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
10
+ /**
11
+ * Type guard to check if a value is a non-null object (including arrays).
12
+ * Useful when you need to access properties on any object-like value.
13
+ */
14
+ export declare function isObject(value: unknown): value is object;
15
+ /**
16
+ * Type guard to check if a value is an array of a specific type.
17
+ * Performs runtime check on each element using the provided guard.
18
+ */
19
+ export declare function isArrayOf<T>(value: unknown, guard: (item: unknown) => item is T): value is T[];
20
+ /**
21
+ * Type guard to check if a value is a string.
22
+ */
23
+ export declare function isString(value: unknown): value is string;
24
+ /**
25
+ * Type guard to check if a value is a number.
26
+ */
27
+ export declare function isNumber(value: unknown): value is number;
28
+ /**
29
+ * Type guard to check if a value is a boolean.
30
+ */
31
+ export declare function isBoolean(value: unknown): value is boolean;
32
+ /**
33
+ * Type guard to check if a value is defined (not undefined).
34
+ */
35
+ export declare function isDefined<T>(value: T | undefined): value is T;
36
+ /**
37
+ * Type guard to check if a value is not null or undefined.
38
+ */
39
+ export declare function isPresent<T>(value: T | null | undefined): value is T;
40
+ /**
41
+ * Safely access a property on an unknown value.
42
+ * Returns undefined if the value is not an object or the property doesn't exist.
43
+ */
44
+ export declare function getProperty(value: unknown, key: string): unknown;
45
+ /**
46
+ * Safely access a nested property path on an unknown value.
47
+ * Returns undefined if any part of the path is not an object.
48
+ */
49
+ export declare function getNestedProperty(value: unknown, path: string[]): unknown;
50
+ /**
51
+ * Check if an object has a specific property.
52
+ * Narrows the type to include that property.
53
+ */
54
+ export declare function hasProperty<K extends string>(value: unknown, key: K): value is Record<K, unknown>;
55
+ /**
56
+ * Check if an object has a specific property with a specific type.
57
+ */
58
+ export declare function hasTypedProperty<K extends string, T>(value: unknown, key: K, guard: (v: unknown) => v is T): value is Record<K, T>;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Type guard utilities to reduce type assertions and improve type safety.
3
+ * These guards provide runtime type checking with TypeScript type narrowing.
4
+ */
5
+ /**
6
+ * Type guard to check if a value is a non-null object (excluding arrays).
7
+ * Narrows the type to Record<string, unknown> for safe property access.
8
+ */
9
+ export function isRecord(value) {
10
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
11
+ }
12
+ /**
13
+ * Type guard to check if a value is a non-null object (including arrays).
14
+ * Useful when you need to access properties on any object-like value.
15
+ */
16
+ export function isObject(value) {
17
+ return value !== null && typeof value === 'object';
18
+ }
19
+ /**
20
+ * Type guard to check if a value is an array of a specific type.
21
+ * Performs runtime check on each element using the provided guard.
22
+ */
23
+ export function isArrayOf(value, guard) {
24
+ return Array.isArray(value) && value.every(guard);
25
+ }
26
+ /**
27
+ * Type guard to check if a value is a string.
28
+ */
29
+ export function isString(value) {
30
+ return typeof value === 'string';
31
+ }
32
+ /**
33
+ * Type guard to check if a value is a number.
34
+ */
35
+ export function isNumber(value) {
36
+ return typeof value === 'number';
37
+ }
38
+ /**
39
+ * Type guard to check if a value is a boolean.
40
+ */
41
+ export function isBoolean(value) {
42
+ return typeof value === 'boolean';
43
+ }
44
+ /**
45
+ * Type guard to check if a value is defined (not undefined).
46
+ */
47
+ export function isDefined(value) {
48
+ return value !== undefined;
49
+ }
50
+ /**
51
+ * Type guard to check if a value is not null or undefined.
52
+ */
53
+ export function isPresent(value) {
54
+ return value !== null && value !== undefined;
55
+ }
56
+ /**
57
+ * Safely access a property on an unknown value.
58
+ * Returns undefined if the value is not an object or the property doesn't exist.
59
+ */
60
+ export function getProperty(value, key) {
61
+ if (isRecord(value)) {
62
+ return value[key];
63
+ }
64
+ return undefined;
65
+ }
66
+ /**
67
+ * Safely access a nested property path on an unknown value.
68
+ * Returns undefined if any part of the path is not an object.
69
+ */
70
+ export function getNestedProperty(value, path) {
71
+ let current = value;
72
+ for (const key of path) {
73
+ if (!isRecord(current)) {
74
+ return undefined;
75
+ }
76
+ current = current[key];
77
+ }
78
+ return current;
79
+ }
80
+ /**
81
+ * Check if an object has a specific property.
82
+ * Narrows the type to include that property.
83
+ */
84
+ export function hasProperty(value, key) {
85
+ return isRecord(value) && key in value;
86
+ }
87
+ /**
88
+ * Check if an object has a specific property with a specific type.
89
+ */
90
+ export function hasTypedProperty(value, key, guard) {
91
+ return hasProperty(value, key) && guard(value[key]);
92
+ }
@@ -45,6 +45,8 @@ export declare class WebhookServer {
45
45
  * Wait for webhook events
46
46
  */
47
47
  waitForEvents(registrationId: string, timeout?: number): Promise<WaitResult>;
48
+ /** Remove a single waiter, dropping the registration's set when empty. */
49
+ private removePendingWait;
48
50
  /**
49
51
  * Unregister a webhook endpoint
50
52
  */
@@ -69,6 +71,13 @@ export declare class WebhookServer {
69
71
  * Read request body
70
72
  */
71
73
  private readBody;
74
+ /** True if a host string is a loopback address. */
75
+ private isLoopback;
76
+ /**
77
+ * Validate the shared secret from Authorization bearer, X-Webhook-Token
78
+ * header, or a `token` query param.
79
+ */
80
+ private authorized;
72
81
  /**
73
82
  * Extract headers from request
74
83
  */
@@ -7,7 +7,9 @@
7
7
  import { createServer } from 'node:http';
8
8
  import { parse as parseUrl } from 'node:url';
9
9
  import { randomUUID } from 'node:crypto';
10
+ import { setLongTimeout } from '../utils/long-timeout.js';
10
11
  import { MemoryWebhookStore } from './store.js';
12
+ import { WEBHOOK_DEFAULTS } from '../config/index.js';
11
13
  /**
12
14
  * Webhook Server
13
15
  *
@@ -18,16 +20,20 @@ export class WebhookServer {
18
20
  store;
19
21
  callbacks;
20
22
  server;
23
+ // Multiple concurrent waiters may await the same registration; each gets its
24
+ // own entry so a second waiter can't clobber the first's timer/promise.
21
25
  pendingWaits = new Map();
22
26
  cleanupInterval;
23
27
  running = false;
24
28
  constructor(config = {}, store, callbacks = {}) {
25
29
  this.config = {
26
- port: config.port ?? 3000,
27
- host: config.host ?? '0.0.0.0',
28
- baseUrl: config.baseUrl ?? `http://localhost:${config.port ?? 3000}`,
29
- defaultTimeout: config.defaultTimeout ?? 300000, // 5 minutes
30
+ port: config.port ?? WEBHOOK_DEFAULTS.PORT,
31
+ host: config.host ?? WEBHOOK_DEFAULTS.HOST,
32
+ baseUrl: config.baseUrl ?? `http://localhost:${config.port ?? WEBHOOK_DEFAULTS.PORT}`,
33
+ defaultTimeout: config.defaultTimeout ?? WEBHOOK_DEFAULTS.DEFAULT_TIMEOUT_MS,
30
34
  verbose: config.verbose ?? false,
35
+ secret: config.secret ?? '',
36
+ maxBodyBytes: config.maxBodyBytes ?? WEBHOOK_DEFAULTS.MAX_BODY_BYTES,
31
37
  };
32
38
  this.store = store ?? new MemoryWebhookStore();
33
39
  this.callbacks = callbacks;
@@ -38,16 +44,22 @@ export class WebhookServer {
38
44
  async start() {
39
45
  if (this.running)
40
46
  return;
47
+ // Warn loudly if exposing an unauthenticated webhook server off-host.
48
+ if (!this.isLoopback(this.config.host) && !this.config.secret) {
49
+ console.warn(`[Webhook] WARNING: binding to ${this.config.host} with no secret — ` +
50
+ `anyone who can reach the port can inject events.`);
51
+ }
41
52
  return new Promise((resolve, reject) => {
42
53
  this.server = createServer((req, res) => this.handleRequest(req, res));
54
+ this.server.setTimeout(WEBHOOK_DEFAULTS.SOCKET_TIMEOUT_MS);
43
55
  this.server.on('error', (error) => {
44
56
  reject(error);
45
57
  });
46
58
  this.server.listen(this.config.port, this.config.host, () => {
47
59
  this.running = true;
48
60
  this.log(`Webhook server listening on ${this.config.host}:${this.config.port}`);
49
- // Start cleanup interval (every minute)
50
- this.cleanupInterval = setInterval(() => this.cleanup(), 60000);
61
+ // Start cleanup interval
62
+ this.cleanupInterval = setInterval(() => this.cleanup(), WEBHOOK_DEFAULTS.CLEANUP_INTERVAL_MS);
51
63
  resolve();
52
64
  });
53
65
  });
@@ -64,13 +76,15 @@ export class WebhookServer {
64
76
  this.cleanupInterval = undefined;
65
77
  }
66
78
  // Cancel all pending waits
67
- for (const [id, pending] of this.pendingWaits) {
68
- clearTimeout(pending.timeoutId);
69
- pending.resolve({
70
- success: false,
71
- events: [],
72
- error: 'Server shutting down',
73
- });
79
+ for (const [, waiters] of this.pendingWaits) {
80
+ for (const pending of waiters) {
81
+ pending.timer.clear();
82
+ pending.resolve({
83
+ success: false,
84
+ events: [],
85
+ error: 'Server shutting down',
86
+ });
87
+ }
74
88
  }
75
89
  this.pendingWaits.clear();
76
90
  // Close server
@@ -133,10 +147,11 @@ export class WebhookServer {
133
147
  return { success: true, events };
134
148
  }
135
149
  // Wait for more events
136
- const waitTimeout = timeout ?? (registration.expiresAt.getTime() - Date.now());
150
+ const waitTimeout = timeout ?? registration.expiresAt.getTime() - Date.now();
137
151
  return new Promise((resolve) => {
138
- const timeoutId = setTimeout(() => {
139
- this.pendingWaits.delete(registrationId);
152
+ const pending = { registrationId, resolve, timer: undefined };
153
+ pending.timer = setLongTimeout(() => {
154
+ this.removePendingWait(registrationId, pending);
140
155
  this.store.getEvents(registrationId).then((events) => {
141
156
  resolve({
142
157
  success: events.length >= registration.expectedEvents,
@@ -145,23 +160,36 @@ export class WebhookServer {
145
160
  });
146
161
  });
147
162
  }, waitTimeout);
148
- this.pendingWaits.set(registrationId, {
149
- registrationId,
150
- resolve,
151
- timeoutId,
152
- });
163
+ let waiters = this.pendingWaits.get(registrationId);
164
+ if (!waiters) {
165
+ waiters = new Set();
166
+ this.pendingWaits.set(registrationId, waiters);
167
+ }
168
+ waiters.add(pending);
153
169
  });
154
170
  }
171
+ /** Remove a single waiter, dropping the registration's set when empty. */
172
+ removePendingWait(registrationId, pending) {
173
+ const waiters = this.pendingWaits.get(registrationId);
174
+ if (!waiters)
175
+ return;
176
+ waiters.delete(pending);
177
+ if (waiters.size === 0) {
178
+ this.pendingWaits.delete(registrationId);
179
+ }
180
+ }
155
181
  /**
156
182
  * Unregister a webhook endpoint
157
183
  */
158
184
  async unregister(registrationId) {
159
185
  await this.store.deleteRegistration(registrationId);
160
186
  await this.store.deleteEvents(registrationId);
161
- // Cancel pending wait if any
162
- const pending = this.pendingWaits.get(registrationId);
163
- if (pending) {
164
- clearTimeout(pending.timeoutId);
187
+ // Cancel any pending waits for this registration.
188
+ const waiters = this.pendingWaits.get(registrationId);
189
+ if (waiters) {
190
+ for (const pending of waiters) {
191
+ pending.timer.clear();
192
+ }
165
193
  this.pendingWaits.delete(registrationId);
166
194
  }
167
195
  this.log(`Unregistered webhook: ${registrationId}`);
@@ -210,11 +238,31 @@ export class WebhookServer {
210
238
  await this.store.deleteRegistration(registration.id);
211
239
  return;
212
240
  }
213
- // Parse request body
241
+ // Require the shared secret if one is configured.
242
+ if (this.config.secret && !this.authorized(req, url.query)) {
243
+ res.writeHead(401, { 'Content-Type': 'application/json' });
244
+ res.end(JSON.stringify({ error: 'Unauthorized' }));
245
+ return;
246
+ }
247
+ // Read the request body with a hard size cap to prevent an OOM from a
248
+ // large or slow-drip POST.
214
249
  let rawBody = '';
215
- let body = null;
216
250
  try {
217
251
  rawBody = await this.readBody(req);
252
+ }
253
+ catch (error) {
254
+ if (error.code === 'BODY_TOO_LARGE') {
255
+ res.writeHead(413, { 'Content-Type': 'application/json' });
256
+ res.end(JSON.stringify({ error: 'Request body too large' }));
257
+ return;
258
+ }
259
+ res.writeHead(400, { 'Content-Type': 'application/json' });
260
+ res.end(JSON.stringify({ error: 'Failed to read request body' }));
261
+ return;
262
+ }
263
+ // Parse request body
264
+ let body = null;
265
+ try {
218
266
  if (rawBody) {
219
267
  const contentType = req.headers['content-type'] ?? '';
220
268
  if (contentType.includes('application/json')) {
@@ -228,7 +276,7 @@ export class WebhookServer {
228
276
  }
229
277
  }
230
278
  }
231
- catch (error) {
279
+ catch {
232
280
  body = rawBody;
233
281
  }
234
282
  // Create event
@@ -252,12 +300,14 @@ export class WebhookServer {
252
300
  if (registration.receivedEvents >= registration.expectedEvents) {
253
301
  const events = await this.store.getEvents(registration.id);
254
302
  this.callbacks.onRegistrationComplete?.(registration, events);
255
- // Resolve pending wait
256
- const pending = this.pendingWaits.get(registration.id);
257
- if (pending) {
258
- clearTimeout(pending.timeoutId);
303
+ // Resolve every pending waiter for this registration.
304
+ const waiters = this.pendingWaits.get(registration.id);
305
+ if (waiters) {
259
306
  this.pendingWaits.delete(registration.id);
260
- pending.resolve({ success: true, events });
307
+ for (const pending of waiters) {
308
+ pending.timer.clear();
309
+ pending.resolve({ success: true, events });
310
+ }
261
311
  }
262
312
  }
263
313
  // Send response
@@ -273,17 +323,53 @@ export class WebhookServer {
273
323
  * Read request body
274
324
  */
275
325
  readBody(req) {
326
+ const limit = this.config.maxBodyBytes;
276
327
  return new Promise((resolve, reject) => {
277
- let data = '';
328
+ const chunks = [];
329
+ let size = 0;
330
+ let aborted = false;
278
331
  req.on('data', (chunk) => {
279
- data += chunk;
332
+ if (aborted)
333
+ return;
334
+ size += chunk.length;
335
+ if (size > limit) {
336
+ // Stop accumulating (memory stays bounded) and reject; the handler
337
+ // responds 413. We don't destroy the socket here so the response
338
+ // can flush first.
339
+ aborted = true;
340
+ const err = new Error('Request body too large');
341
+ err.code = 'BODY_TOO_LARGE';
342
+ reject(err);
343
+ return;
344
+ }
345
+ chunks.push(chunk);
280
346
  });
281
347
  req.on('end', () => {
282
- resolve(data);
348
+ resolve(Buffer.concat(chunks).toString('utf-8'));
283
349
  });
284
350
  req.on('error', reject);
285
351
  });
286
352
  }
353
+ /** True if a host string is a loopback address. */
354
+ isLoopback(host) {
355
+ return host === 'localhost' || host === '127.0.0.1' || host === '::1';
356
+ }
357
+ /**
358
+ * Validate the shared secret from Authorization bearer, X-Webhook-Token
359
+ * header, or a `token` query param.
360
+ */
361
+ authorized(req, query) {
362
+ const secret = this.config.secret;
363
+ const auth = req.headers.authorization;
364
+ if (auth === `Bearer ${secret}`)
365
+ return true;
366
+ const tokenHeader = req.headers['x-webhook-token'];
367
+ if (tokenHeader === secret)
368
+ return true;
369
+ if (typeof query.token === 'string' && query.token === secret)
370
+ return true;
371
+ return false;
372
+ }
287
373
  /**
288
374
  * Extract headers from request
289
375
  */
@@ -51,7 +51,7 @@ export interface WebhookEvent {
51
51
  export interface WebhookServerConfig {
52
52
  /** Port to listen on (default: 3000) */
53
53
  port?: number;
54
- /** Host to bind to (default: '0.0.0.0') */
54
+ /** Host to bind to (default: '127.0.0.1' — loopback only) */
55
55
  host?: string;
56
56
  /** Base URL for webhook endpoints (e.g., 'https://example.com/webhooks') */
57
57
  baseUrl?: string;
@@ -59,6 +59,14 @@ export interface WebhookServerConfig {
59
59
  defaultTimeout?: number;
60
60
  /** Enable verbose logging */
61
61
  verbose?: boolean;
62
+ /**
63
+ * Shared secret required on inbound webhook requests. When set, a request
64
+ * must present it via `Authorization: Bearer <secret>`, `X-Webhook-Token`,
65
+ * or a `token` query param, or it is rejected with 401.
66
+ */
67
+ secret?: string;
68
+ /** Maximum accepted request body size in bytes (default 1 MiB) */
69
+ maxBodyBytes?: number;
62
70
  }
63
71
  /**
64
72
  * Callbacks for webhook server events