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
@@ -0,0 +1,21 @@
1
+ import type { HttpClient } from "@effect/platform"
2
+ import { FetchHttpClient, HttpApiClient } from "@effect/platform"
3
+ import type { Effect } from "effect"
4
+ import { Context, Layer } from "effect"
5
+ import { AdminApi } from "../api/AdminApi"
6
+
7
+ export const makeImpostersClient = (baseUrl?: string) =>
8
+ HttpApiClient.make(AdminApi, { baseUrl: baseUrl ?? "http://localhost:2525" })
9
+
10
+ export type ImpostersClientShape = Effect.Effect.Success<ReturnType<typeof makeImpostersClient>>
11
+
12
+ export class ImpostersClient extends Context.Tag("ImpostersClient")<
13
+ ImpostersClient,
14
+ ImpostersClientShape
15
+ >() {}
16
+
17
+ export const ImpostersClientLive = (baseUrl?: string): Layer.Layer<ImpostersClient, never, HttpClient.HttpClient> =>
18
+ Layer.effect(ImpostersClient, makeImpostersClient(baseUrl))
19
+
20
+ export const ImpostersClientFetchLive = (baseUrl?: string): Layer.Layer<ImpostersClient> =>
21
+ ImpostersClientLive(baseUrl).pipe(Layer.provide(FetchHttpClient.layer))
@@ -0,0 +1,9 @@
1
+ export { HandlerHttpClientLive, makeHandlerHttpClient } from "./HandlerHttpClient"
2
+
3
+ export { ImpostersClient, ImpostersClientFetchLive, ImpostersClientLive, makeImpostersClient } from "./ImpostersClient"
4
+
5
+ export type { ImpostersClientShape } from "./ImpostersClient"
6
+
7
+ export { makeTestServer, withImposter } from "./testing"
8
+
9
+ export type { ImposterTestContext, StubConfig, WithImposterConfig } from "./testing"
@@ -0,0 +1,105 @@
1
+ import { HttpApiBuilder } from "@effect/platform"
2
+ import { Effect, Layer } from "effect"
3
+ import type { NonEmptyString, PortNumber } from "../schemas/common"
4
+ import type { CreateStubRequest } from "../schemas/StubSchema"
5
+ import { HandlerHttpClientLive } from "./HandlerHttpClient"
6
+ import { ImpostersClient, ImpostersClientLive } from "./ImpostersClient"
7
+
8
+ export interface StubConfig {
9
+ readonly predicates?: ReadonlyArray<{
10
+ readonly field: "method" | "path" | "headers" | "query" | "body"
11
+ readonly operator: "equals" | "contains" | "startsWith" | "matches" | "exists"
12
+ readonly value: unknown
13
+ readonly caseSensitive?: boolean
14
+ }>
15
+ readonly responses: readonly [ResponseConfigInput, ...ReadonlyArray<ResponseConfigInput>]
16
+ readonly responseMode?: "sequential" | "random" | "repeat"
17
+ }
18
+
19
+ interface ResponseConfigInput {
20
+ readonly status?: number
21
+ readonly headers?: Record<string, string>
22
+ readonly body?: unknown
23
+ readonly delay?: number
24
+ }
25
+
26
+ export interface WithImposterConfig {
27
+ readonly port?: number
28
+ readonly name?: string
29
+ readonly stubs?: ReadonlyArray<StubConfig>
30
+ }
31
+
32
+ export interface ImposterTestContext {
33
+ readonly id: string
34
+ readonly port: number
35
+ }
36
+
37
+ const asPort = (n: number) => n as PortNumber
38
+ const asNes = (s: string) => s as NonEmptyString
39
+
40
+ const toStubPayload = (stub: StubConfig): CreateStubRequest => ({
41
+ predicates: (stub.predicates ?? []).map((p) => ({
42
+ field: p.field,
43
+ operator: p.operator,
44
+ value: p.value,
45
+ caseSensitive: p.caseSensitive ?? true
46
+ })),
47
+ responses: stub.responses.map((r) => ({
48
+ status: r.status ?? 200,
49
+ ...(r.headers !== undefined ? { headers: r.headers } : {}),
50
+ ...(r.body !== undefined ? { body: r.body } : {}),
51
+ ...(r.delay !== undefined ? { delay: r.delay } : {})
52
+ })) as unknown as CreateStubRequest["responses"],
53
+ responseMode: stub.responseMode ?? "sequential"
54
+ })
55
+
56
+ export const withImposter = <A, E>(
57
+ config: WithImposterConfig,
58
+ testFn: (ctx: ImposterTestContext) => Effect.Effect<A, E, ImpostersClient>
59
+ ): Effect.Effect<A, E | Error, ImpostersClient> =>
60
+ Effect.acquireUseRelease(
61
+ Effect.gen(function*() {
62
+ const client = yield* ImpostersClient
63
+ const imp = yield* client.imposters.createImposter({
64
+ payload: {
65
+ ...(config.port !== undefined ? { port: asPort(config.port) } : {}),
66
+ ...(config.name !== undefined ? { name: asNes(config.name) } : {}),
67
+ protocol: "HTTP" as const,
68
+ adminPath: "/_admin"
69
+ }
70
+ })
71
+
72
+ for (const stub of config.stubs ?? []) {
73
+ yield* client.imposters.addStub({
74
+ path: { imposterId: imp.id },
75
+ payload: toStubPayload(stub)
76
+ })
77
+ }
78
+
79
+ yield* client.imposters.updateImposter({
80
+ path: { id: imp.id },
81
+ payload: { status: "running" }
82
+ })
83
+
84
+ yield* Effect.sleep("150 millis")
85
+
86
+ return { id: imp.id as string, port: imp.port as number }
87
+ }),
88
+ (ctx) => testFn(ctx),
89
+ (ctx) =>
90
+ Effect.gen(function*() {
91
+ const client = yield* ImpostersClient
92
+ yield* client.imposters.deleteImposter({
93
+ path: { id: ctx.id as typeof ctx.id & NonEmptyString },
94
+ urlParams: { force: true }
95
+ }).pipe(Effect.catchAll(() => Effect.void))
96
+ }).pipe(Effect.catchAll(() => Effect.void))
97
+ )
98
+
99
+ export const makeTestServer = (fullLayer: Layer.Layer<any, any, never>) => {
100
+ const { dispose, handler } = HttpApiBuilder.toWebHandler(fullLayer as any)
101
+ const clientLayer = ImpostersClientLive().pipe(
102
+ Layer.provide(HandlerHttpClientLive(handler))
103
+ )
104
+ return { handler, dispose, clientLayer }
105
+ }
@@ -0,0 +1,186 @@
1
+ import * as Clock from "effect/Clock"
2
+ import * as Data from "effect/Data"
3
+ import * as DateTime from "effect/DateTime"
4
+ import * as Duration from "effect/Duration"
5
+ import * as Effect from "effect/Effect"
6
+ import type * as ParseResult from "effect/ParseResult"
7
+ import * as Schema from "effect/Schema"
8
+ import { Uuid } from "../services/Uuid"
9
+
10
+ // Schemas for validation
11
+ const ImposterNameSchema = Schema.String.pipe(
12
+ Schema.minLength(1),
13
+ Schema.maxLength(100),
14
+ Schema.pattern(/^[a-zA-Z0-9-_]+$/)
15
+ )
16
+
17
+ const PortSchema = Schema.Number.pipe(
18
+ Schema.int(),
19
+ Schema.between(1024, 65535)
20
+ )
21
+
22
+ type ImposterStatus = "running" | "stopped" | "starting" | "stopping"
23
+
24
+ const CreateImposterRequestSchema = Schema.Struct({
25
+ name: Schema.optional(ImposterNameSchema),
26
+ port: Schema.optional(PortSchema)
27
+ })
28
+
29
+ export interface ProxyConfigDomain {
30
+ readonly targetUrl: string
31
+ readonly mode: "passthrough" | "record"
32
+ readonly addHeaders?: Record<string, string> | undefined
33
+ readonly removeHeaders: ReadonlyArray<string>
34
+ readonly followRedirects: boolean
35
+ readonly timeout: number
36
+ }
37
+
38
+ // Domain types using tagged interfaces
39
+ export interface ImposterConfig {
40
+ readonly _tag: "ImposterConfig"
41
+ readonly id: string
42
+ readonly name: string
43
+ readonly port: number
44
+ readonly status: ImposterStatus
45
+ readonly createdAt: DateTime.Utc
46
+ readonly proxy?: ProxyConfigDomain | undefined
47
+ }
48
+
49
+ export const ImposterConfig = Data.tagged<ImposterConfig>("ImposterConfig")
50
+
51
+ export interface CreateImposterRequest {
52
+ readonly _tag: "CreateImposterRequest"
53
+ readonly name?: string
54
+ readonly port?: number
55
+ }
56
+
57
+ export const CreateImposterRequest = Data.tagged<CreateImposterRequest>("CreateImposterRequest")
58
+
59
+ export interface ImposterRef {
60
+ readonly _tag: "ImposterRef"
61
+ readonly config: ImposterConfig
62
+ readonly startTime: DateTime.Utc
63
+ readonly endpointCount: number
64
+ }
65
+
66
+ export const ImposterRef = Data.tagged<ImposterRef>("ImposterRef")
67
+
68
+ // Tagged errors
69
+ export class ImposterError extends Data.TaggedError("ImposterError")<{
70
+ readonly reason: string
71
+ readonly cause?: unknown
72
+ }> {}
73
+
74
+ export class PortInUseError extends Data.TaggedError("PortInUseError")<{
75
+ readonly port: number
76
+ }> {}
77
+
78
+ export class ImposterNotFoundError extends Data.TaggedError("ImposterNotFoundError")<{
79
+ readonly id: string
80
+ }> {}
81
+
82
+ /**
83
+ * Parses and validates imposter creation request
84
+ */
85
+ export const parseCreateImposterRequest = (
86
+ input: unknown
87
+ ): Effect.Effect<typeof CreateImposterRequestSchema.Type, ParseResult.ParseError> =>
88
+ Schema.decodeUnknown(CreateImposterRequestSchema)(input)
89
+
90
+ /**
91
+ * Creates a new imposter configuration from validated input
92
+ */
93
+ export const createImposterConfig = (
94
+ validatedInput: typeof CreateImposterRequestSchema.Type
95
+ ) =>
96
+ Effect.gen(function*() {
97
+ const uuid = yield* Uuid
98
+ const id = yield* uuid.generateShort
99
+ const name = validatedInput.name ?? id
100
+
101
+ return ImposterConfig({
102
+ id,
103
+ name,
104
+ port: validatedInput.port ?? 0, // Will be assigned by port allocator if 0
105
+ status: "starting",
106
+ createdAt: DateTime.unsafeNow()
107
+ })
108
+ })
109
+
110
+ /**
111
+ * Creates a new imposter from raw input (parse + create)
112
+ */
113
+ export const newImposterConfig = (input: unknown) =>
114
+ Effect.gen(function*() {
115
+ const validated = yield* parseCreateImposterRequest(input)
116
+ return yield* createImposterConfig(validated)
117
+ })
118
+
119
+ /**
120
+ * Updates imposter status
121
+ */
122
+ export const updateImposterStatus = (status: ImposterStatus) => (config: ImposterConfig): ImposterConfig =>
123
+ ImposterConfig({
124
+ ...config,
125
+ status
126
+ })
127
+
128
+ /**
129
+ * Updates imposter port
130
+ */
131
+ export const updateImposterPort = (port: number) => (config: ImposterConfig): ImposterConfig =>
132
+ ImposterConfig({
133
+ ...config,
134
+ port
135
+ })
136
+
137
+ /**
138
+ * Calculates imposter uptime using Effect's Duration
139
+ */
140
+ export const calculateUptime = (startTime: DateTime.Utc) =>
141
+ Effect.map(Clock.currentTimeMillis, (now) => Duration.millis(now - DateTime.toEpochMillis(startTime)))
142
+
143
+ /**
144
+ * Creates an ImposterRef from config and runtime info
145
+ */
146
+ export const createImposterRef =
147
+ (startTime: DateTime.Utc) => (endpointCount: number) => (config: ImposterConfig): ImposterRef =>
148
+ ImposterRef({
149
+ config,
150
+ startTime,
151
+ endpointCount
152
+ })
153
+
154
+ /**
155
+ * Extracts imposter summary for API responses
156
+ */
157
+ export const toImposterSummary = (ref: ImposterRef) =>
158
+ Effect.map(calculateUptime(ref.startTime), (uptime) => ({
159
+ id: ref.config.id,
160
+ name: ref.config.name,
161
+ port: ref.config.port,
162
+ status: ref.config.status,
163
+ endpointCount: ref.endpointCount,
164
+ createdAt: DateTime.formatIso(ref.config.createdAt),
165
+ uptime: Duration.format(uptime)
166
+ }))
167
+
168
+ /**
169
+ * Gets uptime in human readable format
170
+ */
171
+ export const getUptimeFormatted = (ref: ImposterRef) => Effect.map(calculateUptime(ref.startTime), Duration.format)
172
+
173
+ /**
174
+ * Checks if imposter is running
175
+ */
176
+ export const isRunning = (config: ImposterConfig): boolean => config.status === "running"
177
+
178
+ /**
179
+ * Checks if imposter can be started
180
+ */
181
+ export const canStart = (config: ImposterConfig): boolean => config.status === "stopped" || config.status === "starting"
182
+
183
+ /**
184
+ * Checks if imposter can be stopped
185
+ */
186
+ export const canStop = (config: ImposterConfig): boolean => config.status === "running" || config.status === "stopping"
@@ -0,0 +1,255 @@
1
+ import * as Data from "effect/Data"
2
+ import * as DateTime from "effect/DateTime"
3
+ import * as Duration from "effect/Duration"
4
+ import * as Effect from "effect/Effect"
5
+ import { pipe } from "effect/Function"
6
+ import * as Option from "effect/Option"
7
+ import type * as ParseResult from "effect/ParseResult"
8
+ import * as Schema from "effect/Schema"
9
+ import { Uuid } from "../services/Uuid"
10
+
11
+ const HttpMethodSchema = Schema.Literal("GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS")
12
+
13
+ const StatusCodeSchema = Schema.Number.pipe(Schema.int(), Schema.between(100, 599))
14
+
15
+ const DelaySchema = Schema.Number.pipe(Schema.int(), Schema.between(0, 60000))
16
+
17
+ const PathSchema = Schema.String.pipe(
18
+ Schema.startsWith("/"),
19
+ Schema.pattern(/^\/[a-zA-Z0-9\-._~!$&'()*+,;=:@/{}[\]]*$/)
20
+ )
21
+
22
+ const ResponseSchema = Schema.Struct({
23
+ status: Schema.optionalWith(StatusCodeSchema, { default: () => 200 }),
24
+ headers: Schema.optional(
25
+ Schema.Record({ key: Schema.String, value: Schema.String })
26
+ ),
27
+ body: Schema.Unknown
28
+ })
29
+
30
+ const CreateRouteRequestSchema = Schema.Struct({
31
+ id: Schema.optional(Schema.String),
32
+ path: PathSchema,
33
+ method: Schema.optionalWith(HttpMethodSchema, { default: () => "GET" }),
34
+ response: ResponseSchema,
35
+ delay: Schema.optional(DelaySchema)
36
+ })
37
+
38
+ export interface Route {
39
+ readonly _tag: "Route"
40
+ readonly id: string
41
+ readonly path: string
42
+ readonly method: typeof HttpMethodSchema.Type
43
+ readonly response: Response
44
+ readonly delay: Option.Option<Duration.Duration>
45
+ readonly createdAt: DateTime.Utc
46
+ }
47
+
48
+ export const Route = Data.tagged<Route>("Route")
49
+
50
+ export interface Response {
51
+ readonly _tag: "Response"
52
+ readonly status: number
53
+ readonly headers: Option.Option<Record<string, string>>
54
+ readonly body: unknown
55
+ }
56
+
57
+ export const Response = Data.tagged<Response>("Response")
58
+
59
+ export interface CreateRouteRequest {
60
+ readonly _tag: "CreateRouteRequest"
61
+ readonly id?: string
62
+ readonly path: string
63
+ readonly method?: string
64
+ readonly response: {
65
+ readonly status?: number
66
+ readonly headers?: Record<string, string>
67
+ readonly body: unknown
68
+ }
69
+ readonly delay?: number
70
+ }
71
+
72
+ export const CreateRouteRequest = Data.tagged<CreateRouteRequest>("CreateRouteRequest")
73
+
74
+ // Tagged errors
75
+ export class RouteError extends Data.TaggedError("RouteError")<{
76
+ readonly reason: string
77
+ readonly field?: string
78
+ readonly value?: unknown
79
+ }> {}
80
+
81
+ export class RouteNotFoundError extends Data.TaggedError("RouteNotFoundError")<{
82
+ readonly id: string
83
+ }> {}
84
+
85
+ // Pure functions following Effect patterns
86
+
87
+ /**
88
+ * Parses and validates route creation request
89
+ */
90
+ export const parseCreateRouteRequest = (
91
+ input: unknown
92
+ ): Effect.Effect<typeof CreateRouteRequestSchema.Type, ParseResult.ParseError> =>
93
+ Schema.decodeUnknown(CreateRouteRequestSchema)(input)
94
+
95
+ /**
96
+ * Creates a new route from validated input
97
+ */
98
+ export const createRoute = (
99
+ validatedInput: typeof CreateRouteRequestSchema.Type
100
+ ) =>
101
+ Effect.gen(function*() {
102
+ const uuid = yield* Uuid
103
+ const id = validatedInput.id ?? (yield* uuid.generate)
104
+
105
+ const response = Response({
106
+ status: validatedInput.response.status,
107
+ headers: Option.fromNullable(validatedInput.response.headers),
108
+ body: validatedInput.response.body
109
+ })
110
+
111
+ return Route({
112
+ id,
113
+ path: validatedInput.path,
114
+ method: validatedInput.method,
115
+ response,
116
+ delay: pipe(
117
+ validatedInput.delay,
118
+ Option.fromNullable,
119
+ Option.map(Duration.millis)
120
+ ),
121
+ createdAt: DateTime.unsafeNow()
122
+ })
123
+ })
124
+
125
+ /**
126
+ * Creates a new route from raw input (parse + create)
127
+ */
128
+ export const newRoute = (input: unknown) =>
129
+ Effect.gen(function*() {
130
+ const validated = yield* parseCreateRouteRequest(input)
131
+ return yield* createRoute(validated)
132
+ })
133
+
134
+ /**
135
+ * Updates a route preserving creation time and ID
136
+ */
137
+ export const updateRoute = (updates: Partial<typeof CreateRouteRequestSchema.Type>) => (existingRoute: Route) =>
138
+ Effect.gen(function*() {
139
+ const updateRequest = {
140
+ id: existingRoute.id,
141
+ path: updates.path ?? existingRoute.path,
142
+ method: updates.method ?? existingRoute.method,
143
+ response: updates.response ?? {
144
+ status: existingRoute.response.status,
145
+ headers: Option.getOrUndefined(existingRoute.response.headers),
146
+ body: existingRoute.response.body
147
+ },
148
+ delay: updates.delay ?? pipe(
149
+ existingRoute.delay,
150
+ Option.map(Duration.toMillis),
151
+ Option.getOrUndefined
152
+ )
153
+ }
154
+
155
+ const updated = yield* createRoute(updateRequest)
156
+
157
+ return Route({
158
+ ...updated,
159
+ createdAt: existingRoute.createdAt
160
+ })
161
+ })
162
+
163
+ /**
164
+ * Substitutes parameters in a string using Effect's String utilities
165
+ */
166
+ const substituteInString = (params: Record<string, string>) => (str: string): string =>
167
+ Object.entries(params).reduce((acc, [key, value]) => acc.replaceAll(`{{${key}}}`, value), str)
168
+
169
+ /**
170
+ * Recursively substitutes parameters in unknown data structure
171
+ */
172
+ export const substituteParams = (params: Record<string, string>) => (body: unknown): unknown => {
173
+ if (typeof body === "string") return substituteInString(params)(body)
174
+ if (Array.isArray(body)) return body.map(substituteParams(params))
175
+ // TypeScript narrows `typeof body === "object"` to `object | null`, but we've excluded null and Array above.
176
+ // The cast to Record<string, unknown> is necessary since TS cannot narrow `object` to an indexable type.
177
+ if (body !== null && typeof body === "object") {
178
+ return Object.fromEntries(
179
+ Object.entries(body as Record<string, unknown>).map(([k, v]) => [k, substituteParams(params)(v)])
180
+ )
181
+ }
182
+ return body
183
+ }
184
+
185
+ /**
186
+ * Creates a response with substituted parameters
187
+ */
188
+ export const createResponseWithParams = (params: Record<string, string>) => (response: Response): Response =>
189
+ Response({
190
+ ...response,
191
+ body: substituteParams(params)(response.body)
192
+ })
193
+
194
+ /**
195
+ * Extracts route summary for API responses
196
+ */
197
+ export const toRouteSummary = (route: Route) => ({
198
+ id: route.id,
199
+ path: route.path,
200
+ method: route.method,
201
+ status: route.response.status,
202
+ hasDelay: Option.isSome(route.delay),
203
+ delayMs: pipe(
204
+ route.delay,
205
+ Option.map(Duration.toMillis),
206
+ Option.getOrUndefined
207
+ ),
208
+ createdAt: DateTime.formatIso(route.createdAt)
209
+ })
210
+
211
+ /**
212
+ * Gets delay in milliseconds for compatibility
213
+ */
214
+ export const getDelayMillis = (route: Route): Option.Option<number> => Option.map(route.delay, Duration.toMillis)
215
+
216
+ /**
217
+ * Checks if route has custom headers
218
+ */
219
+ export const hasCustomHeaders = (route: Route): boolean => Option.isSome(route.response.headers)
220
+
221
+ /**
222
+ * Gets headers as record or empty object
223
+ */
224
+ export const getHeaders = (route: Route): Record<string, string> =>
225
+ pipe(
226
+ route.response.headers,
227
+ Option.getOrElse(() => ({}))
228
+ )
229
+
230
+ /**
231
+ * Checks if route has delay configured
232
+ */
233
+ export const hasDelay = (route: Route): boolean => Option.isSome(route.delay)
234
+
235
+ /**
236
+ * Creates a minimal route for testing
237
+ */
238
+ export const createMinimalRoute = (path: string) => (method: typeof HttpMethodSchema.Type = "GET") =>
239
+ Effect.gen(function*() {
240
+ const uuid = yield* Uuid
241
+ const id = yield* uuid.generateShort
242
+
243
+ return Route({
244
+ id,
245
+ path,
246
+ method,
247
+ response: Response({
248
+ status: 200,
249
+ headers: Option.none(),
250
+ body: { message: "OK" }
251
+ }),
252
+ delay: Option.none(),
253
+ createdAt: DateTime.unsafeNow()
254
+ })
255
+ })