imposters 0.1.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 (426) hide show
  1. package/LICENSE +21 -0
  2. package/Program/package.json +6 -0
  3. package/README.md +365 -0
  4. package/api/AdminApi/package.json +6 -0
  5. package/api/ApiErrors/package.json +6 -0
  6. package/api/ApiSchemas/package.json +6 -0
  7. package/api/Conversions/package.json +6 -0
  8. package/api/ImpostersGroup/package.json +6 -0
  9. package/api/ImpostersHandlers/package.json +6 -0
  10. package/api/SystemGroup/package.json +6 -0
  11. package/api/SystemHandlers/package.json +6 -0
  12. package/bin/imposters +47 -0
  13. package/cli/Commands/package.json +6 -0
  14. package/cli/ConfigLoader/package.json +6 -0
  15. package/client/HandlerHttpClient/package.json +6 -0
  16. package/client/ImpostersClient/package.json +6 -0
  17. package/client/testing/package.json +6 -0
  18. package/dist/cjs/Program.js +4 -0
  19. package/dist/cjs/Program.js.map +1 -0
  20. package/dist/cjs/api/AdminApi.js +11 -0
  21. package/dist/cjs/api/AdminApi.js.map +1 -0
  22. package/dist/cjs/api/ApiErrors.js +30 -0
  23. package/dist/cjs/api/ApiErrors.js.map +1 -0
  24. package/dist/cjs/api/ApiSchemas.js +36 -0
  25. package/dist/cjs/api/ApiSchemas.js.map +1 -0
  26. package/dist/cjs/api/Conversions.js +41 -0
  27. package/dist/cjs/api/Conversions.js.map +1 -0
  28. package/dist/cjs/api/ImpostersGroup.js +37 -0
  29. package/dist/cjs/api/ImpostersGroup.js.map +1 -0
  30. package/dist/cjs/api/ImpostersHandlers.js +361 -0
  31. package/dist/cjs/api/ImpostersHandlers.js.map +1 -0
  32. package/dist/cjs/api/SystemGroup.js +12 -0
  33. package/dist/cjs/api/SystemGroup.js.map +1 -0
  34. package/dist/cjs/api/SystemHandlers.js +74 -0
  35. package/dist/cjs/api/SystemHandlers.js.map +1 -0
  36. package/dist/cjs/cli/Commands.js +104 -0
  37. package/dist/cjs/cli/Commands.js.map +1 -0
  38. package/dist/cjs/cli/ConfigLoader.js +34 -0
  39. package/dist/cjs/cli/ConfigLoader.js.map +1 -0
  40. package/dist/cjs/client/HandlerHttpClient.js +50 -0
  41. package/dist/cjs/client/HandlerHttpClient.js.map +1 -0
  42. package/dist/cjs/client/ImpostersClient.js +20 -0
  43. package/dist/cjs/client/ImpostersClient.js.map +1 -0
  44. package/dist/cjs/client/index.js +57 -0
  45. package/dist/cjs/client/index.js.map +1 -0
  46. package/dist/cjs/client/testing.js +94 -0
  47. package/dist/cjs/client/testing.js.map +1 -0
  48. package/dist/cjs/domain/imposter.js +125 -0
  49. package/dist/cjs/domain/imposter.js.map +1 -0
  50. package/dist/cjs/domain/route.js +185 -0
  51. package/dist/cjs/domain/route.js.map +1 -0
  52. package/dist/cjs/index.js +106 -0
  53. package/dist/cjs/index.js.map +1 -0
  54. package/dist/cjs/layers/ApiLayer.js +18 -0
  55. package/dist/cjs/layers/ApiLayer.js.map +1 -0
  56. package/dist/cjs/layers/MainLayer.js +27 -0
  57. package/dist/cjs/layers/MainLayer.js.map +1 -0
  58. package/dist/cjs/matching/ExpressionEvaluator.js +103 -0
  59. package/dist/cjs/matching/ExpressionEvaluator.js.map +1 -0
  60. package/dist/cjs/matching/RequestMatcher.js +145 -0
  61. package/dist/cjs/matching/RequestMatcher.js.map +1 -0
  62. package/dist/cjs/matching/ResponseGenerator.js +80 -0
  63. package/dist/cjs/matching/ResponseGenerator.js.map +1 -0
  64. package/dist/cjs/matching/TemplateEngine.js +55 -0
  65. package/dist/cjs/matching/TemplateEngine.js.map +1 -0
  66. package/dist/cjs/repositories/ImposterRepository.js +118 -0
  67. package/dist/cjs/repositories/ImposterRepository.js.map +1 -0
  68. package/dist/cjs/schemas/ConfigFileSchema.js +44 -0
  69. package/dist/cjs/schemas/ConfigFileSchema.js.map +1 -0
  70. package/dist/cjs/schemas/ImposterSchema.js +202 -0
  71. package/dist/cjs/schemas/ImposterSchema.js.map +1 -0
  72. package/dist/cjs/schemas/RequestLogSchema.js +51 -0
  73. package/dist/cjs/schemas/RequestLogSchema.js.map +1 -0
  74. package/dist/cjs/schemas/StubSchema.js +84 -0
  75. package/dist/cjs/schemas/StubSchema.js.map +1 -0
  76. package/dist/cjs/schemas/common.js +67 -0
  77. package/dist/cjs/schemas/common.js.map +1 -0
  78. package/dist/cjs/server/AdminServer.js +36 -0
  79. package/dist/cjs/server/AdminServer.js.map +1 -0
  80. package/dist/cjs/server/BunServer.js +13 -0
  81. package/dist/cjs/server/BunServer.js.map +1 -0
  82. package/dist/cjs/server/FiberManager.js +21 -0
  83. package/dist/cjs/server/FiberManager.js.map +1 -0
  84. package/dist/cjs/server/ImposterServer.js +234 -0
  85. package/dist/cjs/server/ImposterServer.js.map +1 -0
  86. package/dist/cjs/services/AppConfig.js +18 -0
  87. package/dist/cjs/services/AppConfig.js.map +1 -0
  88. package/dist/cjs/services/MetricsService.js +113 -0
  89. package/dist/cjs/services/MetricsService.js.map +1 -0
  90. package/dist/cjs/services/PortAllocator.js +50 -0
  91. package/dist/cjs/services/PortAllocator.js.map +1 -0
  92. package/dist/cjs/services/ProxyService.js +109 -0
  93. package/dist/cjs/services/ProxyService.js.map +1 -0
  94. package/dist/cjs/services/RequestLogger.js +60 -0
  95. package/dist/cjs/services/RequestLogger.js.map +1 -0
  96. package/dist/cjs/services/Uuid.js +10 -0
  97. package/dist/cjs/services/Uuid.js.map +1 -0
  98. package/dist/cjs/services/UuidLive.js +16 -0
  99. package/dist/cjs/services/UuidLive.js.map +1 -0
  100. package/dist/cjs/ui/UiRouter.js +242 -0
  101. package/dist/cjs/ui/UiRouter.js.map +1 -0
  102. package/dist/cjs/ui/admin/AdminLayout.js +36 -0
  103. package/dist/cjs/ui/admin/AdminLayout.js.map +1 -0
  104. package/dist/cjs/ui/admin/AdminUiRouter.js +155 -0
  105. package/dist/cjs/ui/admin/AdminUiRouter.js.map +1 -0
  106. package/dist/cjs/ui/admin/pages/AdminDashboard.js +55 -0
  107. package/dist/cjs/ui/admin/pages/AdminDashboard.js.map +1 -0
  108. package/dist/cjs/ui/admin/partials.js +64 -0
  109. package/dist/cjs/ui/admin/partials.js.map +1 -0
  110. package/dist/cjs/ui/html.js +42 -0
  111. package/dist/cjs/ui/html.js.map +1 -0
  112. package/dist/cjs/ui/layout.js +39 -0
  113. package/dist/cjs/ui/layout.js.map +1 -0
  114. package/dist/cjs/ui/pages/dashboard.js +51 -0
  115. package/dist/cjs/ui/pages/dashboard.js.map +1 -0
  116. package/dist/cjs/ui/pages/request-detail.js +119 -0
  117. package/dist/cjs/ui/pages/request-detail.js.map +1 -0
  118. package/dist/cjs/ui/pages/requests.js +120 -0
  119. package/dist/cjs/ui/pages/requests.js.map +1 -0
  120. package/dist/cjs/ui/pages/stubs.js +46 -0
  121. package/dist/cjs/ui/pages/stubs.js.map +1 -0
  122. package/dist/cjs/ui/partials.js +104 -0
  123. package/dist/cjs/ui/partials.js.map +1 -0
  124. package/dist/dts/Program.d.ts +2 -0
  125. package/dist/dts/Program.d.ts.map +1 -0
  126. package/dist/dts/api/AdminApi.d.ts +490 -0
  127. package/dist/dts/api/AdminApi.d.ts.map +1 -0
  128. package/dist/dts/api/ApiErrors.d.ts +26 -0
  129. package/dist/dts/api/ApiErrors.d.ts.map +1 -0
  130. package/dist/dts/api/ApiSchemas.d.ts +36 -0
  131. package/dist/dts/api/ApiSchemas.d.ts.map +1 -0
  132. package/dist/dts/api/Conversions.d.ts +7 -0
  133. package/dist/dts/api/Conversions.d.ts.map +1 -0
  134. package/dist/dts/api/ImpostersGroup.d.ts +448 -0
  135. package/dist/dts/api/ImpostersGroup.d.ts.map +1 -0
  136. package/dist/dts/api/ImpostersHandlers.d.ts +9 -0
  137. package/dist/dts/api/ImpostersHandlers.d.ts.map +1 -0
  138. package/dist/dts/api/SystemGroup.d.ts +46 -0
  139. package/dist/dts/api/SystemGroup.d.ts.map +1 -0
  140. package/dist/dts/api/SystemHandlers.d.ts +4 -0
  141. package/dist/dts/api/SystemHandlers.d.ts.map +1 -0
  142. package/dist/dts/cli/Commands.d.ts +4 -0
  143. package/dist/dts/cli/Commands.d.ts.map +1 -0
  144. package/dist/dts/cli/ConfigLoader.d.ts +13 -0
  145. package/dist/dts/cli/ConfigLoader.d.ts.map +1 -0
  146. package/dist/dts/client/HandlerHttpClient.d.ts +5 -0
  147. package/dist/dts/client/HandlerHttpClient.d.ts.map +1 -0
  148. package/dist/dts/client/ImpostersClient.d.ts +1868 -0
  149. package/dist/dts/client/ImpostersClient.d.ts.map +1 -0
  150. package/dist/dts/client/index.d.ts +6 -0
  151. package/dist/dts/client/index.d.ts.map +1 -0
  152. package/dist/dts/client/testing.d.ts +35 -0
  153. package/dist/dts/client/testing.d.ts.map +1 -0
  154. package/dist/dts/domain/imposter.d.ts +123 -0
  155. package/dist/dts/domain/imposter.d.ts.map +1 -0
  156. package/dist/dts/domain/route.d.ts +128 -0
  157. package/dist/dts/domain/route.d.ts.map +1 -0
  158. package/dist/dts/index.d.ts +60 -0
  159. package/dist/dts/index.d.ts.map +1 -0
  160. package/dist/dts/layers/ApiLayer.d.ts +3 -0
  161. package/dist/dts/layers/ApiLayer.d.ts.map +1 -0
  162. package/dist/dts/layers/MainLayer.d.ts +3 -0
  163. package/dist/dts/layers/MainLayer.d.ts.map +1 -0
  164. package/dist/dts/matching/ExpressionEvaluator.d.ts +11 -0
  165. package/dist/dts/matching/ExpressionEvaluator.d.ts.map +1 -0
  166. package/dist/dts/matching/RequestMatcher.d.ts +13 -0
  167. package/dist/dts/matching/RequestMatcher.d.ts.map +1 -0
  168. package/dist/dts/matching/ResponseGenerator.d.ts +9 -0
  169. package/dist/dts/matching/ResponseGenerator.d.ts.map +1 -0
  170. package/dist/dts/matching/TemplateEngine.d.ts +4 -0
  171. package/dist/dts/matching/TemplateEngine.d.ts.map +1 -0
  172. package/dist/dts/repositories/ImposterRepository.d.ts +33 -0
  173. package/dist/dts/repositories/ImposterRepository.d.ts.map +1 -0
  174. package/dist/dts/schemas/ConfigFileSchema.d.ts +142 -0
  175. package/dist/dts/schemas/ConfigFileSchema.d.ts.map +1 -0
  176. package/dist/dts/schemas/ImposterSchema.d.ts +368 -0
  177. package/dist/dts/schemas/ImposterSchema.d.ts.map +1 -0
  178. package/dist/dts/schemas/RequestLogSchema.d.ts +36 -0
  179. package/dist/dts/schemas/RequestLogSchema.d.ts.map +1 -0
  180. package/dist/dts/schemas/StubSchema.d.ts +112 -0
  181. package/dist/dts/schemas/StubSchema.d.ts.map +1 -0
  182. package/dist/dts/schemas/common.d.ts +56 -0
  183. package/dist/dts/schemas/common.d.ts.map +1 -0
  184. package/dist/dts/server/AdminServer.d.ts +11 -0
  185. package/dist/dts/server/AdminServer.d.ts.map +1 -0
  186. package/dist/dts/server/BunServer.d.ts +17 -0
  187. package/dist/dts/server/BunServer.d.ts.map +1 -0
  188. package/dist/dts/server/FiberManager.d.ts +12 -0
  189. package/dist/dts/server/FiberManager.d.ts.map +1 -0
  190. package/dist/dts/server/ImposterServer.d.ts +29 -0
  191. package/dist/dts/server/ImposterServer.d.ts.map +1 -0
  192. package/dist/dts/services/AppConfig.d.ts +14 -0
  193. package/dist/dts/services/AppConfig.d.ts.map +1 -0
  194. package/dist/dts/services/MetricsService.d.ts +26 -0
  195. package/dist/dts/services/MetricsService.d.ts.map +1 -0
  196. package/dist/dts/services/PortAllocator.d.ts +29 -0
  197. package/dist/dts/services/PortAllocator.d.ts.map +1 -0
  198. package/dist/dts/services/ProxyService.d.ts +24 -0
  199. package/dist/dts/services/ProxyService.d.ts.map +1 -0
  200. package/dist/dts/services/RequestLogger.d.ts +23 -0
  201. package/dist/dts/services/RequestLogger.d.ts.map +1 -0
  202. package/dist/dts/services/Uuid.d.ts +9 -0
  203. package/dist/dts/services/Uuid.d.ts.map +1 -0
  204. package/dist/dts/services/UuidLive.d.ts +4 -0
  205. package/dist/dts/services/UuidLive.d.ts.map +1 -0
  206. package/dist/dts/ui/UiRouter.d.ts +15 -0
  207. package/dist/dts/ui/UiRouter.d.ts.map +1 -0
  208. package/dist/dts/ui/admin/AdminLayout.d.ts +7 -0
  209. package/dist/dts/ui/admin/AdminLayout.d.ts.map +1 -0
  210. package/dist/dts/ui/admin/AdminUiRouter.d.ts +6 -0
  211. package/dist/dts/ui/admin/AdminUiRouter.d.ts.map +1 -0
  212. package/dist/dts/ui/admin/pages/AdminDashboard.d.ts +7 -0
  213. package/dist/dts/ui/admin/pages/AdminDashboard.d.ts.map +1 -0
  214. package/dist/dts/ui/admin/partials.d.ts +14 -0
  215. package/dist/dts/ui/admin/partials.d.ts.map +1 -0
  216. package/dist/dts/ui/html.d.ts +12 -0
  217. package/dist/dts/ui/html.d.ts.map +1 -0
  218. package/dist/dts/ui/layout.d.ts +9 -0
  219. package/dist/dts/ui/layout.d.ts.map +1 -0
  220. package/dist/dts/ui/pages/dashboard.d.ts +10 -0
  221. package/dist/dts/ui/pages/dashboard.d.ts.map +1 -0
  222. package/dist/dts/ui/pages/request-detail.d.ts +11 -0
  223. package/dist/dts/ui/pages/request-detail.d.ts.map +1 -0
  224. package/dist/dts/ui/pages/requests.d.ts +15 -0
  225. package/dist/dts/ui/pages/requests.d.ts.map +1 -0
  226. package/dist/dts/ui/pages/stubs.d.ts +8 -0
  227. package/dist/dts/ui/pages/stubs.d.ts.map +1 -0
  228. package/dist/dts/ui/partials.d.ts +13 -0
  229. package/dist/dts/ui/partials.d.ts.map +1 -0
  230. package/dist/esm/Program.js +2 -0
  231. package/dist/esm/Program.js.map +1 -0
  232. package/dist/esm/api/AdminApi.js +5 -0
  233. package/dist/esm/api/AdminApi.js.map +1 -0
  234. package/dist/esm/api/ApiErrors.js +20 -0
  235. package/dist/esm/api/ApiErrors.js.map +1 -0
  236. package/dist/esm/api/ApiSchemas.js +29 -0
  237. package/dist/esm/api/ApiSchemas.js.map +1 -0
  238. package/dist/esm/api/Conversions.js +32 -0
  239. package/dist/esm/api/Conversions.js.map +1 -0
  240. package/dist/esm/api/ImpostersGroup.js +30 -0
  241. package/dist/esm/api/ImpostersGroup.js.map +1 -0
  242. package/dist/esm/api/ImpostersHandlers.js +354 -0
  243. package/dist/esm/api/ImpostersHandlers.js.map +1 -0
  244. package/dist/esm/api/SystemGroup.js +6 -0
  245. package/dist/esm/api/SystemGroup.js.map +1 -0
  246. package/dist/esm/api/SystemHandlers.js +67 -0
  247. package/dist/esm/api/SystemHandlers.js.map +1 -0
  248. package/dist/esm/cli/Commands.js +98 -0
  249. package/dist/esm/cli/Commands.js.map +1 -0
  250. package/dist/esm/cli/ConfigLoader.js +25 -0
  251. package/dist/esm/cli/ConfigLoader.js.map +1 -0
  252. package/dist/esm/client/HandlerHttpClient.js +42 -0
  253. package/dist/esm/client/HandlerHttpClient.js.map +1 -0
  254. package/dist/esm/client/ImpostersClient.js +10 -0
  255. package/dist/esm/client/ImpostersClient.js.map +1 -0
  256. package/dist/esm/client/index.js +4 -0
  257. package/dist/esm/client/index.js.map +1 -0
  258. package/dist/esm/client/testing.js +86 -0
  259. package/dist/esm/client/testing.js.map +1 -0
  260. package/dist/esm/domain/imposter.js +103 -0
  261. package/dist/esm/domain/imposter.js.map +1 -0
  262. package/dist/esm/domain/route.js +164 -0
  263. package/dist/esm/domain/route.js.map +1 -0
  264. package/dist/esm/index.js +60 -0
  265. package/dist/esm/index.js.map +1 -0
  266. package/dist/esm/layers/ApiLayer.js +11 -0
  267. package/dist/esm/layers/ApiLayer.js.map +1 -0
  268. package/dist/esm/layers/MainLayer.js +20 -0
  269. package/dist/esm/layers/MainLayer.js.map +1 -0
  270. package/dist/esm/matching/ExpressionEvaluator.js +94 -0
  271. package/dist/esm/matching/ExpressionEvaluator.js.map +1 -0
  272. package/dist/esm/matching/RequestMatcher.js +135 -0
  273. package/dist/esm/matching/RequestMatcher.js.map +1 -0
  274. package/dist/esm/matching/ResponseGenerator.js +71 -0
  275. package/dist/esm/matching/ResponseGenerator.js.map +1 -0
  276. package/dist/esm/matching/TemplateEngine.js +47 -0
  277. package/dist/esm/matching/TemplateEngine.js.map +1 -0
  278. package/dist/esm/package.json +4 -0
  279. package/dist/esm/repositories/ImposterRepository.js +110 -0
  280. package/dist/esm/repositories/ImposterRepository.js.map +1 -0
  281. package/dist/esm/schemas/ConfigFileSchema.js +37 -0
  282. package/dist/esm/schemas/ConfigFileSchema.js.map +1 -0
  283. package/dist/esm/schemas/ImposterSchema.js +195 -0
  284. package/dist/esm/schemas/ImposterSchema.js.map +1 -0
  285. package/dist/esm/schemas/RequestLogSchema.js +44 -0
  286. package/dist/esm/schemas/RequestLogSchema.js.map +1 -0
  287. package/dist/esm/schemas/StubSchema.js +77 -0
  288. package/dist/esm/schemas/StubSchema.js.map +1 -0
  289. package/dist/esm/schemas/common.js +59 -0
  290. package/dist/esm/schemas/common.js.map +1 -0
  291. package/dist/esm/server/AdminServer.js +27 -0
  292. package/dist/esm/server/AdminServer.js.map +1 -0
  293. package/dist/esm/server/BunServer.js +6 -0
  294. package/dist/esm/server/BunServer.js.map +1 -0
  295. package/dist/esm/server/FiberManager.js +14 -0
  296. package/dist/esm/server/FiberManager.js.map +1 -0
  297. package/dist/esm/server/ImposterServer.js +225 -0
  298. package/dist/esm/server/ImposterServer.js.map +1 -0
  299. package/dist/esm/services/AppConfig.js +11 -0
  300. package/dist/esm/services/AppConfig.js.map +1 -0
  301. package/dist/esm/services/MetricsService.js +105 -0
  302. package/dist/esm/services/MetricsService.js.map +1 -0
  303. package/dist/esm/services/PortAllocator.js +41 -0
  304. package/dist/esm/services/PortAllocator.js.map +1 -0
  305. package/dist/esm/services/ProxyService.js +101 -0
  306. package/dist/esm/services/ProxyService.js.map +1 -0
  307. package/dist/esm/services/RequestLogger.js +53 -0
  308. package/dist/esm/services/RequestLogger.js.map +1 -0
  309. package/dist/esm/services/Uuid.js +3 -0
  310. package/dist/esm/services/Uuid.js.map +1 -0
  311. package/dist/esm/services/UuidLive.js +9 -0
  312. package/dist/esm/services/UuidLive.js.map +1 -0
  313. package/dist/esm/ui/UiRouter.js +235 -0
  314. package/dist/esm/ui/UiRouter.js.map +1 -0
  315. package/dist/esm/ui/admin/AdminLayout.js +29 -0
  316. package/dist/esm/ui/admin/AdminLayout.js.map +1 -0
  317. package/dist/esm/ui/admin/AdminUiRouter.js +148 -0
  318. package/dist/esm/ui/admin/AdminUiRouter.js.map +1 -0
  319. package/dist/esm/ui/admin/pages/AdminDashboard.js +48 -0
  320. package/dist/esm/ui/admin/pages/AdminDashboard.js.map +1 -0
  321. package/dist/esm/ui/admin/partials.js +54 -0
  322. package/dist/esm/ui/admin/partials.js.map +1 -0
  323. package/dist/esm/ui/html.js +32 -0
  324. package/dist/esm/ui/html.js.map +1 -0
  325. package/dist/esm/ui/layout.js +32 -0
  326. package/dist/esm/ui/layout.js.map +1 -0
  327. package/dist/esm/ui/pages/dashboard.js +44 -0
  328. package/dist/esm/ui/pages/dashboard.js.map +1 -0
  329. package/dist/esm/ui/pages/request-detail.js +112 -0
  330. package/dist/esm/ui/pages/request-detail.js.map +1 -0
  331. package/dist/esm/ui/pages/requests.js +112 -0
  332. package/dist/esm/ui/pages/requests.js.map +1 -0
  333. package/dist/esm/ui/pages/stubs.js +39 -0
  334. package/dist/esm/ui/pages/stubs.js.map +1 -0
  335. package/dist/esm/ui/partials.js +91 -0
  336. package/dist/esm/ui/partials.js.map +1 -0
  337. package/domain/imposter/package.json +6 -0
  338. package/domain/route/package.json +6 -0
  339. package/layers/ApiLayer/package.json +6 -0
  340. package/layers/MainLayer/package.json +6 -0
  341. package/matching/ExpressionEvaluator/package.json +6 -0
  342. package/matching/RequestMatcher/package.json +6 -0
  343. package/matching/ResponseGenerator/package.json +6 -0
  344. package/matching/TemplateEngine/package.json +6 -0
  345. package/package.json +435 -0
  346. package/repositories/ImposterRepository/package.json +6 -0
  347. package/schemas/ConfigFileSchema/package.json +6 -0
  348. package/schemas/ImposterSchema/package.json +6 -0
  349. package/schemas/RequestLogSchema/package.json +6 -0
  350. package/schemas/StubSchema/package.json +6 -0
  351. package/schemas/common/package.json +6 -0
  352. package/server/AdminServer/package.json +6 -0
  353. package/server/BunServer/package.json +6 -0
  354. package/server/FiberManager/package.json +6 -0
  355. package/server/ImposterServer/package.json +6 -0
  356. package/services/AppConfig/package.json +6 -0
  357. package/services/MetricsService/package.json +6 -0
  358. package/services/PortAllocator/package.json +6 -0
  359. package/services/ProxyService/package.json +6 -0
  360. package/services/RequestLogger/package.json +6 -0
  361. package/services/Uuid/package.json +6 -0
  362. package/services/UuidLive/package.json +6 -0
  363. package/src/Program.ts +1 -0
  364. package/src/api/AdminApi.ts +7 -0
  365. package/src/api/ApiErrors.ts +20 -0
  366. package/src/api/ApiSchemas.ts +36 -0
  367. package/src/api/Conversions.ts +34 -0
  368. package/src/api/ImpostersGroup.ts +103 -0
  369. package/src/api/ImpostersHandlers.ts +387 -0
  370. package/src/api/SystemGroup.ts +12 -0
  371. package/src/api/SystemHandlers.ts +76 -0
  372. package/src/cli/Commands.ts +119 -0
  373. package/src/cli/ConfigLoader.ts +41 -0
  374. package/src/client/HandlerHttpClient.ts +50 -0
  375. package/src/client/ImpostersClient.ts +21 -0
  376. package/src/client/index.ts +9 -0
  377. package/src/client/testing.ts +105 -0
  378. package/src/domain/imposter.ts +186 -0
  379. package/src/domain/route.ts +255 -0
  380. package/src/index.ts +153 -0
  381. package/src/layers/ApiLayer.ts +21 -0
  382. package/src/layers/MainLayer.ts +43 -0
  383. package/src/matching/ExpressionEvaluator.ts +102 -0
  384. package/src/matching/RequestMatcher.ts +162 -0
  385. package/src/matching/ResponseGenerator.ts +86 -0
  386. package/src/matching/TemplateEngine.ts +54 -0
  387. package/src/repositories/ImposterRepository.ts +145 -0
  388. package/src/schemas/ConfigFileSchema.ts +32 -0
  389. package/src/schemas/ImposterSchema.ts +232 -0
  390. package/src/schemas/RequestLogSchema.ts +38 -0
  391. package/src/schemas/StubSchema.ts +90 -0
  392. package/src/schemas/common.ts +95 -0
  393. package/src/server/AdminServer.ts +22 -0
  394. package/src/server/BunServer.ts +19 -0
  395. package/src/server/FiberManager.ts +25 -0
  396. package/src/server/ImposterServer.ts +244 -0
  397. package/src/services/AppConfig.ts +22 -0
  398. package/src/services/MetricsService.ts +157 -0
  399. package/src/services/PortAllocator.ts +68 -0
  400. package/src/services/ProxyService.ts +139 -0
  401. package/src/services/RequestLogger.ts +87 -0
  402. package/src/services/Uuid.ts +9 -0
  403. package/src/services/UuidLive.ts +9 -0
  404. package/src/types/bun.d.ts +6 -0
  405. package/src/ui/UiRouter.ts +278 -0
  406. package/src/ui/admin/AdminLayout.ts +36 -0
  407. package/src/ui/admin/AdminUiRouter.ts +170 -0
  408. package/src/ui/admin/pages/AdminDashboard.ts +54 -0
  409. package/src/ui/admin/partials.ts +83 -0
  410. package/src/ui/html.ts +37 -0
  411. package/src/ui/layout.ts +44 -0
  412. package/src/ui/pages/dashboard.ts +64 -0
  413. package/src/ui/pages/request-detail.ts +142 -0
  414. package/src/ui/pages/requests.ts +141 -0
  415. package/src/ui/pages/stubs.ts +52 -0
  416. package/src/ui/partials.ts +133 -0
  417. package/ui/UiRouter/package.json +6 -0
  418. package/ui/admin/AdminLayout/package.json +6 -0
  419. package/ui/admin/AdminUiRouter/package.json +6 -0
  420. package/ui/admin/pages/AdminDashboard/package.json +6 -0
  421. package/ui/admin/partials/package.json +6 -0
  422. package/ui/html/package.json +6 -0
  423. package/ui/layout/package.json +6 -0
  424. package/ui/pages/dashboard/package.json +6 -0
  425. package/ui/pages/requests/package.json +6 -0
  426. package/ui/pages/stubs/package.json +6 -0
package/src/index.ts ADDED
@@ -0,0 +1,153 @@
1
+
2
+ export * as Program from "./Program.js"
3
+
4
+
5
+ export * as AdminApi from "./api/AdminApi.js"
6
+
7
+
8
+ export * as ApiErrors from "./api/ApiErrors.js"
9
+
10
+
11
+ export * as ApiSchemas from "./api/ApiSchemas.js"
12
+
13
+
14
+ export * as Conversions from "./api/Conversions.js"
15
+
16
+
17
+ export * as ImpostersGroup from "./api/ImpostersGroup.js"
18
+
19
+
20
+ export * as ImpostersHandlers from "./api/ImpostersHandlers.js"
21
+
22
+
23
+ export * as SystemGroup from "./api/SystemGroup.js"
24
+
25
+
26
+ export * as SystemHandlers from "./api/SystemHandlers.js"
27
+
28
+
29
+ export * as Commands from "./cli/Commands.js"
30
+
31
+
32
+ export * as ConfigLoader from "./cli/ConfigLoader.js"
33
+
34
+
35
+ export * as HandlerHttpClient from "./client/HandlerHttpClient.js"
36
+
37
+
38
+ export * as ImpostersClient from "./client/ImpostersClient.js"
39
+
40
+
41
+ export * as testing from "./client/testing.js"
42
+
43
+ /**
44
+ * Parses and validates imposter creation request
45
+ */
46
+ export * as imposter from "./domain/imposter.js"
47
+
48
+ /**
49
+ * Parses and validates route creation request
50
+ */
51
+ export * as route from "./domain/route.js"
52
+
53
+
54
+ export * as ApiLayer from "./layers/ApiLayer.js"
55
+
56
+
57
+ export * as MainLayer from "./layers/MainLayer.js"
58
+
59
+ /**
60
+ * Extract expression content from a ${...} pattern using brace-depth counting.
61
+ * Returns [expressionContent, endIndex] or null if no valid expression found.
62
+ */
63
+ export * as ExpressionEvaluator from "./matching/ExpressionEvaluator.js"
64
+
65
+
66
+ export * as RequestMatcher from "./matching/RequestMatcher.js"
67
+
68
+
69
+ export * as ResponseGenerator from "./matching/ResponseGenerator.js"
70
+
71
+
72
+ export * as TemplateEngine from "./matching/TemplateEngine.js"
73
+
74
+
75
+ export * as ImposterRepository from "./repositories/ImposterRepository.js"
76
+
77
+
78
+ export * as ConfigFileSchema from "./schemas/ConfigFileSchema.js"
79
+
80
+
81
+ export * as ImposterSchema from "./schemas/ImposterSchema.js"
82
+
83
+
84
+ export * as RequestLogSchema from "./schemas/RequestLogSchema.js"
85
+
86
+
87
+ export * as StubSchema from "./schemas/StubSchema.js"
88
+
89
+
90
+ export * as common from "./schemas/common.js"
91
+
92
+
93
+ export * as AdminServer from "./server/AdminServer.js"
94
+
95
+
96
+ export * as BunServer from "./server/BunServer.js"
97
+
98
+
99
+ export * as FiberManager from "./server/FiberManager.js"
100
+
101
+
102
+ export * as ImposterServer from "./server/ImposterServer.js"
103
+
104
+
105
+ export * as AppConfig from "./services/AppConfig.js"
106
+
107
+
108
+ export * as MetricsService from "./services/MetricsService.js"
109
+
110
+
111
+ export * as PortAllocator from "./services/PortAllocator.js"
112
+
113
+
114
+ export * as ProxyService from "./services/ProxyService.js"
115
+
116
+
117
+ export * as RequestLogger from "./services/RequestLogger.js"
118
+
119
+
120
+ export * as Uuid from "./services/Uuid.js"
121
+
122
+
123
+ export * as UuidLive from "./services/UuidLive.js"
124
+
125
+
126
+ export * as UiRouter from "./ui/UiRouter.js"
127
+
128
+
129
+ export * as AdminLayout from "./ui/admin/AdminLayout.js"
130
+
131
+
132
+ export * as AdminUiRouter from "./ui/admin/AdminUiRouter.js"
133
+
134
+
135
+ export * as AdminDashboard from "./ui/admin/pages/AdminDashboard.js"
136
+
137
+
138
+ export * as partials from "./ui/admin/partials.js"
139
+
140
+
141
+ export * as html from "./ui/html.js"
142
+
143
+
144
+ export * as layout from "./ui/layout.js"
145
+
146
+
147
+ export * as dashboard from "./ui/pages/dashboard.js"
148
+
149
+
150
+ export * as requests from "./ui/pages/requests.js"
151
+
152
+
153
+ export * as stubs from "./ui/pages/stubs.js"
@@ -0,0 +1,21 @@
1
+ import { HttpApiBuilder, HttpApiSwagger, HttpServer } from "@effect/platform"
2
+ import * as Layer from "effect/Layer"
3
+ import { AdminApi } from "../api/AdminApi"
4
+ import { ImpostersHandlersLive } from "../api/ImpostersHandlers"
5
+ import { SystemHandlersLive } from "../api/SystemHandlers"
6
+
7
+ const HandlerLayers = Layer.mergeAll(ImpostersHandlersLive, SystemHandlersLive)
8
+
9
+ const ApiLive = HttpApiBuilder.api(AdminApi).pipe(Layer.provide(HandlerLayers))
10
+
11
+ // Middleware layers need Api — provide it from ApiLive
12
+ const MiddlewareLive = Layer.mergeAll(
13
+ HttpApiBuilder.middlewareOpenApi(),
14
+ HttpApiSwagger.layer()
15
+ ).pipe(Layer.provide(ApiLive))
16
+
17
+ export const ApiLayer = Layer.mergeAll(
18
+ ApiLive,
19
+ MiddlewareLive,
20
+ HttpServer.layerContext
21
+ )
@@ -0,0 +1,43 @@
1
+ import * as Layer from "effect/Layer"
2
+ import { ImposterRepositoryLive } from "../repositories/ImposterRepository"
3
+ import { BunServerFactoryLive } from "../server/BunServer"
4
+ import { FiberManagerLive } from "../server/FiberManager"
5
+ import { ImposterServerLive } from "../server/ImposterServer"
6
+ import { AppConfigLive } from "../services/AppConfig"
7
+ import { MetricsServiceLive } from "../services/MetricsService"
8
+ import { PortAllocatorLive } from "../services/PortAllocator"
9
+ import { ProxyServiceLive } from "../services/ProxyService"
10
+ import { RequestLoggerLive } from "../services/RequestLogger"
11
+ import { UuidLive } from "../services/UuidLive"
12
+
13
+ // PortAllocatorLive depends on AppConfig
14
+ const PortAllocatorWithDeps = PortAllocatorLive.pipe(Layer.provide(AppConfigLive))
15
+
16
+ // ProxyServiceLive depends on Uuid
17
+ const ProxyServiceWithDeps = ProxyServiceLive.pipe(Layer.provide(UuidLive))
18
+
19
+ // ImposterServerLive depends on FiberManager + ImposterRepository + ServerFactory + RequestLogger + Metrics + Proxy
20
+ const ImposterServerWithDeps = ImposterServerLive.pipe(
21
+ Layer.provide(
22
+ Layer.mergeAll(
23
+ FiberManagerLive,
24
+ ImposterRepositoryLive,
25
+ BunServerFactoryLive,
26
+ RequestLoggerLive,
27
+ MetricsServiceLive,
28
+ ProxyServiceWithDeps
29
+ )
30
+ )
31
+ )
32
+
33
+ // Compose all services
34
+ export const MainLayer = Layer.mergeAll(
35
+ UuidLive,
36
+ AppConfigLive,
37
+ PortAllocatorWithDeps,
38
+ ImposterRepositoryLive,
39
+ FiberManagerLive,
40
+ RequestLoggerLive,
41
+ MetricsServiceLive,
42
+ ImposterServerWithDeps
43
+ )
@@ -0,0 +1,102 @@
1
+ import jsonata from "jsonata"
2
+ import type { RequestContext } from "./RequestMatcher"
3
+
4
+ const MAX_OUTPUT_SIZE = 1_048_576 // 1MB
5
+
6
+ /**
7
+ * Extract expression content from a ${...} pattern using brace-depth counting.
8
+ * Returns [expressionContent, endIndex] or null if no valid expression found.
9
+ */
10
+ const extractExpression = (str: string, startIndex: number): [string, number] | null => {
11
+ // startIndex should point to the '$' of '${'
12
+ if (str[startIndex] !== "$" || str[startIndex + 1] !== "{") return null
13
+ let depth = 1
14
+ let i = startIndex + 2
15
+ while (i < str.length && depth > 0) {
16
+ if (str[i] === "{") depth++
17
+ else if (str[i] === "}") depth--
18
+ i++
19
+ }
20
+ if (depth !== 0) return null
21
+ const content = str.slice(startIndex + 2, i - 1)
22
+ return [content, i]
23
+ }
24
+
25
+ /**
26
+ * Evaluate a single JSONata expression against the request context.
27
+ * Returns the result or undefined on error.
28
+ */
29
+ export const evaluateExpression = async (expr: string, ctx: RequestContext): Promise<unknown> => {
30
+ try {
31
+ const expression = jsonata(expr)
32
+ const context = { request: ctx }
33
+ return await expression.evaluate(context)
34
+ } catch {
35
+ return undefined
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Process a string, replacing all ${...} patterns with evaluated JSONata results.
41
+ */
42
+ const processString = async (str: string, ctx: RequestContext): Promise<unknown> => {
43
+ // Quick check: if no ${, return as-is
44
+ if (!str.includes("${")) return str
45
+
46
+ // If the entire string is a single expression, return the raw result (preserving type)
47
+ const singleMatch = extractExpression(str, 0)
48
+ if (singleMatch && singleMatch[1] === str.length) {
49
+ const result = await evaluateExpression(singleMatch[0], ctx)
50
+ if (result === undefined) return str // Preserve raw on failure
51
+ return result
52
+ }
53
+
54
+ // Multiple expressions or mixed content: concatenate as string
55
+ let result = ""
56
+ let i = 0
57
+ while (i < str.length) {
58
+ if (str[i] === "$" && i + 1 < str.length && str[i + 1] === "{") {
59
+ const extracted = extractExpression(str, i)
60
+ if (extracted) {
61
+ const [exprContent, endIndex] = extracted
62
+ const evalResult = await evaluateExpression(exprContent, ctx)
63
+ if (evalResult === undefined) {
64
+ // Preserve raw expression on failure
65
+ result += str.slice(i, endIndex)
66
+ } else if (typeof evalResult === "object" && evalResult !== null) {
67
+ const jsonStr = JSON.stringify(evalResult)
68
+ result += jsonStr
69
+ } else {
70
+ result += String(evalResult)
71
+ }
72
+ i = endIndex
73
+ if (result.length > MAX_OUTPUT_SIZE) {
74
+ return result.slice(0, MAX_OUTPUT_SIZE)
75
+ }
76
+ continue
77
+ }
78
+ }
79
+ result += str[i]
80
+ i++
81
+ }
82
+ return result
83
+ }
84
+
85
+ /**
86
+ * Recursively walk data structures, processing ${...} expressions in strings.
87
+ */
88
+ export const processExpressions = async (ctx: RequestContext, data: unknown): Promise<unknown> => {
89
+ if (typeof data === "string") return processString(data, ctx)
90
+ if (Array.isArray(data)) {
91
+ const results = await Promise.all(data.map((item) => processExpressions(ctx, item)))
92
+ return results
93
+ }
94
+ if (data !== null && typeof data === "object") {
95
+ const entries = Object.entries(data as Record<string, unknown>)
96
+ const resolved = await Promise.all(
97
+ entries.map(async ([k, v]) => [k, await processExpressions(ctx, v)] as const)
98
+ )
99
+ return Object.fromEntries(resolved)
100
+ }
101
+ return data
102
+ }
@@ -0,0 +1,162 @@
1
+ import type { Predicate, Stub } from "../schemas/StubSchema"
2
+
3
+ export interface RequestContext {
4
+ readonly method: string
5
+ readonly path: string
6
+ readonly headers: Record<string, string>
7
+ readonly query: Record<string, string>
8
+ readonly body: unknown
9
+ }
10
+
11
+ export const extractRequestContext = async (request: Request): Promise<RequestContext> => {
12
+ const url = new URL(request.url)
13
+ const method = request.method.toUpperCase()
14
+ const path = url.pathname
15
+
16
+ const headers: Record<string, string> = {}
17
+ request.headers.forEach((value, key) => {
18
+ headers[key.toLowerCase()] = value
19
+ })
20
+
21
+ const query: Record<string, string> = {}
22
+ url.searchParams.forEach((value, key) => {
23
+ query[key] = value
24
+ })
25
+
26
+ let body: unknown
27
+ if (request.body) {
28
+ const contentType = request.headers.get("content-type") ?? ""
29
+ const text = await request.text()
30
+ if (contentType.includes("application/json")) {
31
+ try {
32
+ body = JSON.parse(text)
33
+ } catch {
34
+ body = text
35
+ }
36
+ } else {
37
+ body = text === "" ? undefined : text
38
+ }
39
+ }
40
+
41
+ return { method, path, headers, query, body }
42
+ }
43
+
44
+ const normalize = (s: string, caseSensitive: boolean): string => caseSensitive ? s : s.toLowerCase()
45
+
46
+ const matchString = (
47
+ actual: string,
48
+ expected: unknown,
49
+ operator: Predicate["operator"],
50
+ caseSensitive: boolean
51
+ ): boolean => {
52
+ if (operator === "exists") return true
53
+ if (typeof expected !== "string") return false
54
+ const a = normalize(actual, caseSensitive)
55
+ const e = normalize(expected, caseSensitive)
56
+ switch (operator) {
57
+ case "equals":
58
+ return a === e
59
+ case "contains":
60
+ return a.includes(e)
61
+ case "startsWith":
62
+ return a.startsWith(e)
63
+ case "matches": {
64
+ const flags = caseSensitive ? "" : "i"
65
+ return new RegExp(expected, flags).test(actual)
66
+ }
67
+ }
68
+ }
69
+
70
+ const matchObject = (
71
+ actual: Record<string, string>,
72
+ expected: unknown,
73
+ operator: Predicate["operator"],
74
+ caseSensitive: boolean
75
+ ): boolean => {
76
+ if (operator === "exists") {
77
+ if (typeof expected !== "object" || expected === null) return true
78
+ return Object.keys(expected as Record<string, unknown>).every((key) => key.toLowerCase() in actual || key in actual)
79
+ }
80
+ if (typeof expected !== "object" || expected === null) return false
81
+ const entries = Object.entries(expected as Record<string, unknown>)
82
+ return entries.every(([key, val]) => {
83
+ const actualKey = Object.keys(actual).find(
84
+ (k) => normalize(k, caseSensitive) === normalize(key, caseSensitive)
85
+ )
86
+ if (actualKey === undefined) return false
87
+ const actualVal = actual[actualKey]!
88
+ if (typeof val !== "string") return false
89
+ return matchString(actualVal, val, operator, caseSensitive)
90
+ })
91
+ }
92
+
93
+ const deepSubsetMatch = (actual: unknown, expected: unknown, caseSensitive: boolean): boolean => {
94
+ if (expected === null || expected === undefined) return actual === expected
95
+ if (typeof expected === "string" && typeof actual === "string") {
96
+ return caseSensitive ? actual === expected : actual.toLowerCase() === expected.toLowerCase()
97
+ }
98
+ if (typeof expected === "number" || typeof expected === "boolean") return actual === expected
99
+ if (Array.isArray(expected)) {
100
+ if (!Array.isArray(actual)) return false
101
+ return expected.every((e, i) => i < actual.length && deepSubsetMatch(actual[i], e, caseSensitive))
102
+ }
103
+ if (typeof expected === "object" && expected !== null) {
104
+ if (typeof actual !== "object" || actual === null) return false
105
+ return Object.entries(expected as Record<string, unknown>).every(([key, val]) =>
106
+ deepSubsetMatch((actual as Record<string, unknown>)[key], val, caseSensitive)
107
+ )
108
+ }
109
+ return false
110
+ }
111
+
112
+ const matchBody = (
113
+ actual: unknown,
114
+ expected: unknown,
115
+ operator: Predicate["operator"],
116
+ caseSensitive: boolean
117
+ ): boolean => {
118
+ switch (operator) {
119
+ case "exists":
120
+ return actual !== null && actual !== undefined
121
+ case "equals":
122
+ return deepSubsetMatch(actual, expected, caseSensitive)
123
+ case "contains": {
124
+ const a = normalize(typeof actual === "string" ? actual : JSON.stringify(actual), caseSensitive)
125
+ const e = normalize(typeof expected === "string" ? expected : JSON.stringify(expected), caseSensitive)
126
+ return a.includes(e)
127
+ }
128
+ case "startsWith": {
129
+ const a = normalize(typeof actual === "string" ? actual : JSON.stringify(actual), caseSensitive)
130
+ const e = normalize(typeof expected === "string" ? expected : JSON.stringify(expected), caseSensitive)
131
+ return a.startsWith(e)
132
+ }
133
+ case "matches": {
134
+ const a = typeof actual === "string" ? actual : JSON.stringify(actual)
135
+ const pattern = typeof expected === "string" ? expected : JSON.stringify(expected)
136
+ const flags = caseSensitive ? "" : "i"
137
+ return new RegExp(pattern, flags).test(a)
138
+ }
139
+ }
140
+ }
141
+
142
+ export const evaluatePredicate = (ctx: RequestContext, predicate: Predicate): boolean => {
143
+ const { caseSensitive, field, operator, value } = predicate
144
+ switch (field) {
145
+ case "method":
146
+ return matchString(ctx.method, value, operator, caseSensitive)
147
+ case "path":
148
+ return matchString(ctx.path, value, operator, caseSensitive)
149
+ case "headers":
150
+ return matchObject(ctx.headers, value, operator, caseSensitive)
151
+ case "query":
152
+ return matchObject(ctx.query, value, operator, caseSensitive)
153
+ case "body":
154
+ return matchBody(ctx.body, value, operator, caseSensitive)
155
+ }
156
+ }
157
+
158
+ export const evaluatePredicates = (ctx: RequestContext, predicates: ReadonlyArray<Predicate>): boolean =>
159
+ predicates.length === 0 || predicates.every((p) => evaluatePredicate(ctx, p))
160
+
161
+ export const findMatchingStub = (ctx: RequestContext, stubs: ReadonlyArray<Stub>): Stub | undefined =>
162
+ stubs.find((stub) => evaluatePredicates(ctx, stub.predicates))
@@ -0,0 +1,86 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as HashMap from "effect/HashMap"
3
+ import * as Ref from "effect/Ref"
4
+ import type { ResponseConfig, ResponseMode } from "../schemas/StubSchema"
5
+ import type { RequestContext } from "./RequestMatcher"
6
+ import { applyTemplates } from "./TemplateEngine"
7
+
8
+ type CounterMap = HashMap.HashMap<string, number>
9
+ type CounterResult = readonly [Effect.Effect<number, never>, CounterMap]
10
+
11
+ export const makeResponseState = () =>
12
+ Effect.gen(function*() {
13
+ const countersRef = yield* Ref.make<CounterMap>(HashMap.empty())
14
+
15
+ const getNextIndex = (
16
+ imposterId: string,
17
+ stubId: string,
18
+ count: number,
19
+ mode: ResponseMode
20
+ ): Effect.Effect<number> => {
21
+ const key = `${imposterId}:${stubId}`
22
+ return Ref.modify(countersRef, (counters): CounterResult => {
23
+ const current = HashMap.get(counters, key)
24
+ const index = current._tag === "Some" ? current.value : 0
25
+ let result: number
26
+ switch (mode) {
27
+ case "sequential":
28
+ result = index % count
29
+ break
30
+ case "random":
31
+ result = Math.floor(Math.random() * count)
32
+ break
33
+ case "repeat":
34
+ result = Math.min(index, count - 1)
35
+ break
36
+ }
37
+ const nextIndex = mode === "random" ? index : index + 1
38
+ return [Effect.succeed(result), HashMap.set(counters, key, nextIndex)]
39
+ }).pipe(Effect.flatten)
40
+ }
41
+
42
+ const reset = (imposterId: string): Effect.Effect<void> =>
43
+ Ref.update(countersRef, (counters) => {
44
+ let updated = counters
45
+ for (const key of HashMap.keys(counters)) {
46
+ if (key.startsWith(`${imposterId}:`)) {
47
+ updated = HashMap.remove(updated, key)
48
+ }
49
+ }
50
+ return updated
51
+ })
52
+
53
+ return { getNextIndex, reset }
54
+ })
55
+
56
+ export const buildResponse = async (config: ResponseConfig, ctx: RequestContext): Promise<Response> => {
57
+ const headers = new Headers()
58
+ const responseHeaders = config.headers
59
+ if (responseHeaders !== undefined) {
60
+ for (const [key, val] of Object.entries(responseHeaders)) {
61
+ const templated = await applyTemplates(ctx, val)
62
+ headers.set(key, typeof templated === "string" ? templated : String(templated))
63
+ }
64
+ }
65
+
66
+ let bodyStr: string | null = null
67
+ if (config.body !== undefined) {
68
+ const templated = await applyTemplates(ctx, config.body)
69
+ if (typeof templated === "string") {
70
+ bodyStr = templated
71
+ if (!headers.has("content-type")) {
72
+ headers.set("content-type", "text/plain")
73
+ }
74
+ } else {
75
+ bodyStr = JSON.stringify(templated)
76
+ if (!headers.has("content-type")) {
77
+ headers.set("content-type", "application/json")
78
+ }
79
+ }
80
+ }
81
+
82
+ return new Response(bodyStr, {
83
+ status: config.status,
84
+ headers
85
+ })
86
+ }
@@ -0,0 +1,54 @@
1
+ import { substituteParams } from "../domain/route"
2
+ import { processExpressions } from "./ExpressionEvaluator"
3
+ import type { RequestContext } from "./RequestMatcher"
4
+
5
+ const flattenObject = (obj: unknown, prefix: string, result: Record<string, string>): void => {
6
+ if (obj === null || obj === undefined) return
7
+ if (typeof obj === "string") {
8
+ result[prefix] = obj
9
+ return
10
+ }
11
+ if (typeof obj === "number" || typeof obj === "boolean") {
12
+ result[prefix] = String(obj)
13
+ return
14
+ }
15
+ if (Array.isArray(obj)) {
16
+ result[prefix] = JSON.stringify(obj)
17
+ obj.forEach((item, i) => flattenObject(item, `${prefix}.${i}`, result))
18
+ return
19
+ }
20
+ if (typeof obj === "object") {
21
+ result[prefix] = JSON.stringify(obj)
22
+ for (const [key, val] of Object.entries(obj as Record<string, unknown>)) {
23
+ flattenObject(val, `${prefix}.${key}`, result)
24
+ }
25
+ }
26
+ }
27
+
28
+ export const flattenRequestContext = (ctx: RequestContext): Record<string, string> => {
29
+ const result: Record<string, string> = {
30
+ "request.method": ctx.method,
31
+ "request.path": ctx.path
32
+ }
33
+
34
+ for (const [key, val] of Object.entries(ctx.headers)) {
35
+ result[`request.headers.${key}`] = val
36
+ }
37
+
38
+ for (const [key, val] of Object.entries(ctx.query)) {
39
+ result[`request.query.${key}`] = val
40
+ }
41
+
42
+ if (ctx.body !== undefined && ctx.body !== null) {
43
+ flattenObject(ctx.body, "request.body", result)
44
+ }
45
+
46
+ return result
47
+ }
48
+
49
+ export const applyTemplates = async (ctx: RequestContext, data: unknown): Promise<unknown> => {
50
+ // Step 1: Apply {{key}} substitution
51
+ const substituted = substituteParams(flattenRequestContext(ctx))(data)
52
+ // Step 2: Apply ${expr} JSONata evaluation
53
+ return processExpressions(ctx, substituted)
54
+ }