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,145 @@
1
+ import { Context, Data, Effect, HashMap, Layer, Ref } from "effect"
2
+ import type { ImposterConfig } from "../domain/imposter"
3
+ import { ImposterNotFoundError } from "../domain/imposter"
4
+ import type { Stub } from "../schemas/StubSchema"
5
+
6
+ export class StubNotFoundError extends Data.TaggedError("StubNotFoundError")<{
7
+ readonly imposterId: string
8
+ readonly stubId: string
9
+ }> {}
10
+
11
+ export interface ImposterRecord {
12
+ readonly config: ImposterConfig
13
+ readonly stubs: ReadonlyArray<Stub>
14
+ }
15
+
16
+ export interface ImposterRepositoryShape {
17
+ readonly create: (config: ImposterConfig) => Effect.Effect<ImposterRecord>
18
+ readonly get: (id: string) => Effect.Effect<ImposterRecord, ImposterNotFoundError>
19
+ readonly getAll: Effect.Effect<ReadonlyArray<ImposterRecord>>
20
+ readonly update: (
21
+ id: string,
22
+ fn: (r: ImposterRecord) => ImposterRecord
23
+ ) => Effect.Effect<ImposterRecord, ImposterNotFoundError>
24
+ readonly remove: (id: string) => Effect.Effect<ImposterRecord, ImposterNotFoundError>
25
+ readonly addStub: (imposterId: string, stub: Stub) => Effect.Effect<Stub, ImposterNotFoundError>
26
+ readonly getStubs: (imposterId: string) => Effect.Effect<ReadonlyArray<Stub>, ImposterNotFoundError>
27
+ readonly updateStub: (
28
+ imposterId: string,
29
+ stubId: string,
30
+ fn: (s: Stub) => Stub
31
+ ) => Effect.Effect<Stub, ImposterNotFoundError | StubNotFoundError>
32
+ readonly removeStub: (
33
+ imposterId: string,
34
+ stubId: string
35
+ ) => Effect.Effect<Stub, ImposterNotFoundError | StubNotFoundError>
36
+ }
37
+
38
+ export class ImposterRepository extends Context.Tag("ImposterRepository")<
39
+ ImposterRepository,
40
+ ImposterRepositoryShape
41
+ >() {}
42
+
43
+ export const ImposterRepositoryLive = Layer.effect(
44
+ ImposterRepository,
45
+ Effect.gen(function*() {
46
+ const storeRef = yield* Ref.make(HashMap.empty<string, ImposterRecord>())
47
+
48
+ const getRecord = (id: string): Effect.Effect<ImposterRecord, ImposterNotFoundError> =>
49
+ Ref.get(storeRef).pipe(
50
+ Effect.flatMap((store) => {
51
+ const record = HashMap.get(store, id)
52
+ return record._tag === "Some"
53
+ ? Effect.succeed(record.value)
54
+ : Effect.fail(new ImposterNotFoundError({ id }))
55
+ })
56
+ )
57
+
58
+ type Store = HashMap.HashMap<string, ImposterRecord>
59
+ type ModifyRecord<A, E> = readonly [Effect.Effect<A, E>, Store]
60
+ type RecordResult = ModifyRecord<ImposterRecord, ImposterNotFoundError>
61
+ type StubResult = ModifyRecord<Stub, ImposterNotFoundError>
62
+ type StubOrNotFound = ModifyRecord<Stub, ImposterNotFoundError | StubNotFoundError>
63
+
64
+ const create = (config: ImposterConfig): Effect.Effect<ImposterRecord> => {
65
+ const record: ImposterRecord = { config, stubs: [] }
66
+ return Ref.modify(
67
+ storeRef,
68
+ (store): ModifyRecord<ImposterRecord, never> => [Effect.succeed(record), HashMap.set(store, config.id, record)]
69
+ ).pipe(Effect.flatten)
70
+ }
71
+
72
+ const get = (id: string) => getRecord(id)
73
+
74
+ const getAll: Effect.Effect<ReadonlyArray<ImposterRecord>> = Ref.get(storeRef).pipe(
75
+ Effect.map((store) => Array.from(HashMap.values(store)))
76
+ )
77
+
78
+ const update = (id: string, fn: (r: ImposterRecord) => ImposterRecord) =>
79
+ Ref.modify(storeRef, (store): RecordResult => {
80
+ const existing = HashMap.get(store, id)
81
+ if (existing._tag === "None") {
82
+ return [Effect.fail(new ImposterNotFoundError({ id })), store]
83
+ }
84
+ const updated = fn(existing.value)
85
+ return [Effect.succeed(updated), HashMap.set(store, id, updated)]
86
+ }).pipe(Effect.flatten)
87
+
88
+ const remove = (id: string) =>
89
+ Ref.modify(storeRef, (store): RecordResult => {
90
+ const existing = HashMap.get(store, id)
91
+ if (existing._tag === "None") {
92
+ return [Effect.fail(new ImposterNotFoundError({ id })), store]
93
+ }
94
+ return [Effect.succeed(existing.value), HashMap.remove(store, id)]
95
+ }).pipe(Effect.flatten)
96
+
97
+ const addStub = (imposterId: string, stub: Stub) =>
98
+ Ref.modify(storeRef, (store): StubResult => {
99
+ const existing = HashMap.get(store, imposterId)
100
+ if (existing._tag === "None") {
101
+ return [Effect.fail(new ImposterNotFoundError({ id: imposterId })), store]
102
+ }
103
+ const updated: ImposterRecord = { ...existing.value, stubs: [...existing.value.stubs, stub] }
104
+ return [Effect.succeed(stub), HashMap.set(store, imposterId, updated)]
105
+ }).pipe(Effect.flatten)
106
+
107
+ const getStubs = (imposterId: string) => getRecord(imposterId).pipe(Effect.map((r) => r.stubs))
108
+
109
+ const updateStub = (imposterId: string, stubId: string, fn: (s: Stub) => Stub) =>
110
+ Ref.modify(storeRef, (store): StubOrNotFound => {
111
+ const existing = HashMap.get(store, imposterId)
112
+ if (existing._tag === "None") {
113
+ return [Effect.fail(new ImposterNotFoundError({ id: imposterId })), store]
114
+ }
115
+ const stubIndex = existing.value.stubs.findIndex((s) => s.id === stubId)
116
+ if (stubIndex === -1) {
117
+ return [Effect.fail(new StubNotFoundError({ imposterId, stubId })), store]
118
+ }
119
+ const updatedStub = fn(existing.value.stubs[stubIndex]!)
120
+ const newStubs = [...existing.value.stubs]
121
+ newStubs[stubIndex] = updatedStub
122
+ const updated: ImposterRecord = { ...existing.value, stubs: newStubs }
123
+ return [Effect.succeed(updatedStub), HashMap.set(store, imposterId, updated)]
124
+ }).pipe(Effect.flatten)
125
+
126
+ const removeStub = (imposterId: string, stubId: string) =>
127
+ Ref.modify(storeRef, (store): StubOrNotFound => {
128
+ const existing = HashMap.get(store, imposterId)
129
+ if (existing._tag === "None") {
130
+ return [Effect.fail(new ImposterNotFoundError({ id: imposterId })), store]
131
+ }
132
+ const stub = existing.value.stubs.find((s) => s.id === stubId)
133
+ if (!stub) {
134
+ return [Effect.fail(new StubNotFoundError({ imposterId, stubId })), store]
135
+ }
136
+ const updated: ImposterRecord = {
137
+ ...existing.value,
138
+ stubs: existing.value.stubs.filter((s) => s.id !== stubId)
139
+ }
140
+ return [Effect.succeed(stub), HashMap.set(store, imposterId, updated)]
141
+ }).pipe(Effect.flatten)
142
+
143
+ return { create, get, getAll, update, remove, addStub, getStubs, updateStub, removeStub }
144
+ })
145
+ )
@@ -0,0 +1,32 @@
1
+ import * as Schema from "effect/Schema"
2
+ import { NonEmptyString, PortNumber } from "./common"
3
+ import { CreateStubRequest, ProxyConfig } from "./StubSchema"
4
+
5
+ export const ImposterConfig = Schema.Struct({
6
+ name: Schema.optional(NonEmptyString),
7
+ port: PortNumber,
8
+ stubs: Schema.optionalWith(Schema.Array(CreateStubRequest), { default: () => [] }),
9
+ proxy: Schema.optional(ProxyConfig)
10
+ })
11
+ export type ImposterConfig = Schema.Schema.Type<typeof ImposterConfig>
12
+
13
+ export const AdminConfig = Schema.Struct({
14
+ port: Schema.optionalWith(PortNumber, { default: () => 2525 as Schema.Schema.Type<typeof PortNumber> }),
15
+ portRangeMin: Schema.optionalWith(PortNumber, { default: () => 3000 as Schema.Schema.Type<typeof PortNumber> }),
16
+ portRangeMax: Schema.optionalWith(PortNumber, { default: () => 4000 as Schema.Schema.Type<typeof PortNumber> }),
17
+ maxImposters: Schema.optionalWith(
18
+ Schema.Number.pipe(Schema.int(), Schema.positive()),
19
+ { default: () => 100 }
20
+ ),
21
+ logLevel: Schema.optionalWith(
22
+ Schema.Literal("debug", "info", "warn", "error"),
23
+ { default: () => "info" as const }
24
+ )
25
+ })
26
+ export type AdminConfig = Schema.Schema.Type<typeof AdminConfig>
27
+
28
+ export const ConfigFile = Schema.Struct({
29
+ admin: Schema.optionalWith(AdminConfig, { default: () => Schema.decodeSync(AdminConfig)({}) }),
30
+ imposters: Schema.optionalWith(Schema.Array(ImposterConfig), { default: () => [] })
31
+ })
32
+ export type ConfigFile = Schema.Schema.Type<typeof ConfigFile>
@@ -0,0 +1,232 @@
1
+ import * as Schema from "effect/Schema"
2
+ import {
3
+ ImposterStatus,
4
+ NonEmptyString,
5
+ PaginationMeta,
6
+ PaginationQuery,
7
+ PortNumber,
8
+ Protocol,
9
+ ProtocolFilter,
10
+ StatusFilter
11
+ } from "./common"
12
+ import { ProxyConfig } from "./StubSchema"
13
+
14
+ // Create Imposter Request Schema - POST /imposters
15
+ export const CreateImposterRequest = Schema.Struct({
16
+ name: Schema.optional(NonEmptyString),
17
+ port: Schema.optional(PortNumber),
18
+ protocol: Schema.optionalWith(Protocol, { default: () => "HTTP" as const }),
19
+ adminPath: Schema.optionalWith(
20
+ Schema.String.pipe(Schema.startsWith("/")),
21
+ { default: () => "/_admin" }
22
+ ),
23
+ proxy: Schema.optional(ProxyConfig)
24
+ })
25
+ export type CreateImposterRequest = Schema.Schema.Type<typeof CreateImposterRequest>
26
+
27
+ // Update Imposter Request Schema - PATCH /imposters/{id}
28
+ export const UpdateImposterRequest = Schema.Struct({
29
+ name: Schema.optional(NonEmptyString),
30
+ status: Schema.optional(ImposterStatus),
31
+ port: Schema.optional(PortNumber),
32
+ adminPath: Schema.optional(Schema.String.pipe(Schema.startsWith("/"))),
33
+ proxy: Schema.optional(Schema.NullOr(ProxyConfig))
34
+ })
35
+ export type UpdateImposterRequest = Schema.Schema.Type<typeof UpdateImposterRequest>
36
+
37
+ // List Imposters Query Schema - GET /imposters query params
38
+ export const ListImpostersQuery = Schema.Struct({
39
+ ...PaginationQuery.fields,
40
+ status: StatusFilter,
41
+ protocol: ProtocolFilter
42
+ })
43
+ export type ListImpostersQuery = Schema.Schema.Type<typeof ListImpostersQuery>
44
+
45
+ // Route API Schemas
46
+ export const CreateRouteRequest = Schema.Struct({
47
+ path: Schema.String.pipe(Schema.startsWith("/")),
48
+ method: Schema.optionalWith(
49
+ Schema.Literal("GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"),
50
+ { default: () => "GET" as const }
51
+ ),
52
+ response: Schema.Struct({
53
+ status: Schema.optionalWith(
54
+ Schema.Number.pipe(Schema.int(), Schema.between(100, 599)),
55
+ { default: () => 200 }
56
+ ),
57
+ headers: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.String })),
58
+ body: Schema.optional(Schema.Unknown)
59
+ }),
60
+ delay: Schema.optional(Schema.Number.pipe(Schema.int(), Schema.between(0, 60000)))
61
+ })
62
+ export type CreateRouteRequest = Schema.Schema.Type<typeof CreateRouteRequest>
63
+
64
+ export const RouteResponse = Schema.Struct({
65
+ id: NonEmptyString,
66
+ path: NonEmptyString,
67
+ method: Schema.Literal("GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"),
68
+ response: Schema.Struct({
69
+ status: Schema.Number.pipe(Schema.int(), Schema.between(100, 599)),
70
+ headers: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.String })),
71
+ body: Schema.optional(Schema.Unknown)
72
+ }),
73
+ delay: Schema.optional(Schema.Number.pipe(Schema.int(), Schema.between(0, 60000))),
74
+ createdAt: Schema.DateTimeUtc
75
+ })
76
+ export type RouteResponse = Schema.Schema.Type<typeof RouteResponse>
77
+
78
+ export const ListRoutesResponse = Schema.Struct({
79
+ routes: Schema.Array(RouteResponse),
80
+ pagination: PaginationMeta
81
+ })
82
+ export type ListRoutesResponse = Schema.Schema.Type<typeof ListRoutesResponse>
83
+
84
+ // Endpoint Summary Schema (for imposter responses)
85
+ export const EndpointSummary = Schema.Struct({
86
+ id: NonEmptyString,
87
+ path: NonEmptyString,
88
+ method: NonEmptyString,
89
+ status: Schema.Number.pipe(Schema.int(), Schema.between(100, 599)),
90
+ hasDelay: Schema.Boolean,
91
+ delayMs: Schema.optional(Schema.Number.pipe(Schema.int(), Schema.nonNegative()))
92
+ })
93
+ export type EndpointSummary = Schema.Schema.Type<typeof EndpointSummary>
94
+
95
+ // Statistics Schema
96
+ export const Statistics = Schema.Struct({
97
+ totalRequests: Schema.Number.pipe(Schema.int(), Schema.nonNegative()),
98
+ requestsPerMinute: Schema.Number.pipe(Schema.nonNegative()),
99
+ averageResponseTime: Schema.Number.pipe(Schema.nonNegative()),
100
+ errorRate: Schema.Number.pipe(Schema.between(0, 1)),
101
+ requestsByMethod: Schema.optionalWith(
102
+ Schema.Record({ key: Schema.String, value: Schema.Number }),
103
+ { default: () => ({}) }
104
+ ),
105
+ requestsByStatusCode: Schema.optionalWith(
106
+ Schema.Record({ key: Schema.String, value: Schema.Number }),
107
+ { default: () => ({}) }
108
+ ),
109
+ lastRequestAt: Schema.optional(Schema.DateTimeUtc),
110
+ p50ResponseTime: Schema.optional(Schema.Number),
111
+ p95ResponseTime: Schema.optional(Schema.Number),
112
+ p99ResponseTime: Schema.optional(Schema.Number)
113
+ })
114
+ export type Statistics = Schema.Schema.Type<typeof Statistics>
115
+
116
+ // Core Imposter Response Schema
117
+ export const ImposterResponse = Schema.Struct({
118
+ id: NonEmptyString,
119
+ name: NonEmptyString,
120
+ port: PortNumber,
121
+ protocol: Protocol,
122
+ status: ImposterStatus,
123
+ endpointCount: Schema.Number.pipe(Schema.int(), Schema.nonNegative()),
124
+ createdAt: Schema.DateTimeUtc,
125
+ adminUrl: NonEmptyString,
126
+ adminPath: NonEmptyString,
127
+ uptime: Schema.optional(Schema.String), // Formatted duration string
128
+ endpoints: Schema.optional(Schema.Array(EndpointSummary)),
129
+ statistics: Schema.optional(Statistics),
130
+ proxy: Schema.optional(ProxyConfig)
131
+ })
132
+ export type ImposterResponse = Schema.Schema.Type<typeof ImposterResponse>
133
+
134
+ // List Imposters Response Schema - GET /imposters
135
+ export const ListImpostersResponse = Schema.Struct({
136
+ imposters: Schema.Array(ImposterResponse),
137
+ pagination: PaginationMeta
138
+ })
139
+ export type ListImpostersResponse = Schema.Schema.Type<typeof ListImpostersResponse>
140
+
141
+ // Delete Imposter Query Schema - DELETE /imposters/{id}
142
+ export const DeleteImposterQuery = Schema.Struct({
143
+ force: Schema.optionalWith(Schema.Boolean, { default: () => false })
144
+ })
145
+ export type DeleteImposterQuery = Schema.Schema.Type<typeof DeleteImposterQuery>
146
+
147
+ // Delete Imposter Response Schema
148
+ export const DeleteImposterResponse = Schema.Struct({
149
+ message: NonEmptyString,
150
+ id: NonEmptyString,
151
+ deletedAt: Schema.DateTimeUtc
152
+ })
153
+ export type DeleteImposterResponse = Schema.Schema.Type<typeof DeleteImposterResponse>
154
+
155
+ // System Memory Info Schema
156
+ export const MemoryInfo = Schema.Struct({
157
+ used: NonEmptyString,
158
+ free: NonEmptyString
159
+ })
160
+ export type MemoryInfo = Schema.Schema.Type<typeof MemoryInfo>
161
+
162
+ // System Imposters Summary Schema
163
+ export const ImpostersSummary = Schema.Struct({
164
+ total: Schema.Number.pipe(Schema.int(), Schema.nonNegative()),
165
+ running: Schema.Number.pipe(Schema.int(), Schema.nonNegative()),
166
+ stopped: Schema.Number.pipe(Schema.int(), Schema.nonNegative())
167
+ })
168
+ export type ImpostersSummary = Schema.Schema.Type<typeof ImpostersSummary>
169
+
170
+ // System Ports Summary Schema
171
+ export const PortsSummary = Schema.Struct({
172
+ available: Schema.Number.pipe(Schema.int(), Schema.nonNegative()),
173
+ allocated: Schema.Number.pipe(Schema.int(), Schema.nonNegative())
174
+ })
175
+ export type PortsSummary = Schema.Schema.Type<typeof PortsSummary>
176
+
177
+ // System Info Schema (for health endpoint)
178
+ export const SystemInfo = Schema.Struct({
179
+ memory: MemoryInfo,
180
+ imposters: ImpostersSummary,
181
+ ports: PortsSummary
182
+ })
183
+ export type SystemInfo = Schema.Schema.Type<typeof SystemInfo>
184
+
185
+ // Health Response Schema - GET /health
186
+ export const HealthResponse = Schema.Struct({
187
+ status: Schema.Literal("healthy", "unhealthy"),
188
+ timestamp: Schema.DateTimeUtc,
189
+ version: NonEmptyString,
190
+ uptime: Schema.String, // Formatted duration
191
+ system: SystemInfo
192
+ })
193
+ export type HealthResponse = Schema.Schema.Type<typeof HealthResponse>
194
+
195
+ // Server Configuration Schema
196
+ export const ServerConfiguration = Schema.Struct({
197
+ maxImposters: Schema.Number.pipe(Schema.int(), Schema.positive()),
198
+ portRange: Schema.Struct({
199
+ min: PortNumber,
200
+ max: PortNumber
201
+ }),
202
+ defaultTimeout: Schema.Number.pipe(Schema.int(), Schema.positive()),
203
+ logLevel: Schema.Literal("debug", "info", "warn", "error")
204
+ })
205
+ export type ServerConfiguration = Schema.Schema.Type<typeof ServerConfiguration>
206
+
207
+ // Server Features Schema
208
+ export const ServerFeatures = Schema.Struct({
209
+ openApiGeneration: Schema.Boolean,
210
+ clientGeneration: Schema.Boolean,
211
+ authentication: Schema.Boolean,
212
+ clustering: Schema.Boolean
213
+ })
214
+ export type ServerFeatures = Schema.Schema.Type<typeof ServerFeatures>
215
+
216
+ // Server Info Schema
217
+ export const ServerInfo = Schema.Struct({
218
+ name: NonEmptyString,
219
+ version: NonEmptyString,
220
+ buildTime: Schema.DateTimeUtc,
221
+ platform: NonEmptyString,
222
+ protocols: Schema.Array(Protocol)
223
+ })
224
+ export type ServerInfo = Schema.Schema.Type<typeof ServerInfo>
225
+
226
+ // Server Info Response Schema - GET /info
227
+ export const ServerInfoResponse = Schema.Struct({
228
+ server: ServerInfo,
229
+ configuration: ServerConfiguration,
230
+ features: ServerFeatures
231
+ })
232
+ export type ServerInfoResponse = Schema.Schema.Type<typeof ServerInfoResponse>
@@ -0,0 +1,38 @@
1
+ import * as Schema from "effect/Schema"
2
+ import { NonEmptyString } from "./common"
3
+
4
+ export const RequestLogEntry = Schema.Struct({
5
+ id: NonEmptyString,
6
+ imposterId: NonEmptyString,
7
+ timestamp: Schema.DateTimeUtc,
8
+ request: Schema.Struct({
9
+ method: Schema.String,
10
+ path: Schema.String,
11
+ headers: Schema.Record({ key: Schema.String, value: Schema.String }),
12
+ query: Schema.Record({ key: Schema.String, value: Schema.String }),
13
+ body: Schema.optional(Schema.Unknown)
14
+ }),
15
+ response: Schema.Struct({
16
+ status: Schema.Number,
17
+ headers: Schema.optionalWith(
18
+ Schema.Record({ key: Schema.String, value: Schema.String }),
19
+ { default: () => ({}) }
20
+ ),
21
+ body: Schema.optional(Schema.String),
22
+ matchedStubId: Schema.optional(NonEmptyString),
23
+ proxied: Schema.optionalWith(Schema.Boolean, { default: () => false })
24
+ }),
25
+ duration: Schema.Number
26
+ })
27
+ export type RequestLogEntry = Schema.Schema.Type<typeof RequestLogEntry>
28
+
29
+ export const ListRequestsUrlParams = Schema.Struct({
30
+ limit: Schema.optionalWith(
31
+ Schema.NumberFromString.pipe(Schema.int(), Schema.positive()),
32
+ { default: () => 50 }
33
+ ),
34
+ method: Schema.optional(Schema.String),
35
+ path: Schema.optional(Schema.String),
36
+ status: Schema.optional(Schema.NumberFromString)
37
+ })
38
+ export type ListRequestsUrlParams = Schema.Schema.Type<typeof ListRequestsUrlParams>
@@ -0,0 +1,90 @@
1
+ import * as Schema from "effect/Schema"
2
+ import { NonEmptyString } from "./common"
3
+
4
+ // Proxy Mode
5
+ export const ProxyMode = Schema.Literal("passthrough", "record")
6
+ export type ProxyMode = Schema.Schema.Type<typeof ProxyMode>
7
+
8
+ // Proxy Configuration
9
+ export const ProxyConfig = Schema.Struct({
10
+ targetUrl: Schema.String.pipe(Schema.pattern(/^https?:\/\//)),
11
+ mode: Schema.optionalWith(ProxyMode, { default: () => "passthrough" as const }),
12
+ addHeaders: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.String })),
13
+ removeHeaders: Schema.optionalWith(Schema.Array(Schema.String), { default: () => [] as const }),
14
+ followRedirects: Schema.optionalWith(Schema.Boolean, { default: () => true }),
15
+ timeout: Schema.optionalWith(
16
+ Schema.Number.pipe(Schema.int(), Schema.between(100, 60000)),
17
+ { default: () => 10000 }
18
+ )
19
+ })
20
+ export type ProxyConfig = Schema.Schema.Type<typeof ProxyConfig>
21
+
22
+ // Predicate operators for matching incoming requests
23
+ export const PredicateOperator = Schema.Literal(
24
+ "equals",
25
+ "contains",
26
+ "startsWith",
27
+ "matches",
28
+ "exists"
29
+ )
30
+ export type PredicateOperator = Schema.Schema.Type<typeof PredicateOperator>
31
+
32
+ // Which part of the request to match against
33
+ export const PredicateField = Schema.Literal(
34
+ "method",
35
+ "path",
36
+ "headers",
37
+ "query",
38
+ "body"
39
+ )
40
+ export type PredicateField = Schema.Schema.Type<typeof PredicateField>
41
+
42
+ // A single predicate matcher
43
+ export const Predicate = Schema.Struct({
44
+ field: PredicateField,
45
+ operator: PredicateOperator,
46
+ value: Schema.Unknown,
47
+ caseSensitive: Schema.optionalWith(Schema.Boolean, { default: () => true })
48
+ })
49
+ export type Predicate = Schema.Schema.Type<typeof Predicate>
50
+
51
+ // How to cycle through responses
52
+ export const ResponseMode = Schema.Literal("sequential", "random", "repeat")
53
+ export type ResponseMode = Schema.Schema.Type<typeof ResponseMode>
54
+
55
+ // A single response configuration
56
+ export const ResponseConfig = Schema.Struct({
57
+ status: Schema.optionalWith(
58
+ Schema.Number.pipe(Schema.int(), Schema.between(100, 599)),
59
+ { default: () => 200 }
60
+ ),
61
+ headers: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.String })),
62
+ body: Schema.optional(Schema.Unknown),
63
+ delay: Schema.optional(Schema.Number.pipe(Schema.int(), Schema.between(0, 60000)))
64
+ })
65
+ export type ResponseConfig = Schema.Schema.Type<typeof ResponseConfig>
66
+
67
+ // A stub: predicates (AND-combined) + responses (cycled)
68
+ export const Stub = Schema.Struct({
69
+ id: NonEmptyString,
70
+ predicates: Schema.Array(Predicate),
71
+ responses: Schema.NonEmptyArray(ResponseConfig),
72
+ responseMode: Schema.optionalWith(ResponseMode, { default: () => "sequential" as const })
73
+ })
74
+ export type Stub = Schema.Schema.Type<typeof Stub>
75
+
76
+ // API request to create a stub (id is auto-generated)
77
+ export const CreateStubRequest = Schema.Struct({
78
+ predicates: Schema.optionalWith(Schema.Array(Predicate), { default: () => [] as const }),
79
+ responses: Schema.NonEmptyArray(ResponseConfig),
80
+ responseMode: Schema.optionalWith(ResponseMode, { default: () => "sequential" as const })
81
+ })
82
+ export type CreateStubRequest = Schema.Schema.Type<typeof CreateStubRequest>
83
+
84
+ // API request to update a stub
85
+ export const UpdateStubRequest = Schema.Struct({
86
+ predicates: Schema.optional(Schema.Array(Predicate)),
87
+ responses: Schema.optional(Schema.NonEmptyArray(ResponseConfig)),
88
+ responseMode: Schema.optional(ResponseMode)
89
+ })
90
+ export type UpdateStubRequest = Schema.Schema.Type<typeof UpdateStubRequest>
@@ -0,0 +1,95 @@
1
+ import { Clock, DateTime, Effect } from "effect"
2
+ import * as Duration from "effect/Duration"
3
+ import * as Schema from "effect/Schema"
4
+
5
+ // Common enums
6
+ export const ImposterStatus = Schema.Literal("running", "stopped", "starting", "stopping")
7
+ export type ImposterStatus = Schema.Schema.Type<typeof ImposterStatus>
8
+
9
+ export const Protocol = Schema.Literal("HTTP")
10
+ export type Protocol = Schema.Schema.Type<typeof Protocol>
11
+
12
+ // Utility schemas for validation
13
+ export const PositiveInteger = Schema.Number.pipe(
14
+ Schema.int(),
15
+ Schema.positive(),
16
+ Schema.brand("PositiveInteger")
17
+ )
18
+ export type PositiveInteger = Schema.Schema.Type<typeof PositiveInteger>
19
+
20
+ export const NonEmptyString = Schema.String.pipe(
21
+ Schema.minLength(1),
22
+ Schema.brand("NonEmptyString")
23
+ )
24
+ export type NonEmptyString = Schema.Schema.Type<typeof NonEmptyString>
25
+
26
+ export const PortNumber = Schema.Number.pipe(
27
+ Schema.int(),
28
+ Schema.between(1024, 65535),
29
+ Schema.brand("PortNumber")
30
+ )
31
+ export type PortNumber = Schema.Schema.Type<typeof PortNumber>
32
+
33
+ export const PaginationQuery = Schema.Struct({
34
+ // PositiveInteger.make() is safe here — 50 is a compile-time constant that always passes validation
35
+ limit: Schema.optionalWith(PositiveInteger, { default: () => PositiveInteger.make(50) }),
36
+ offset: Schema.optionalWith(
37
+ Schema.Number.pipe(Schema.int(), Schema.nonNegative()),
38
+ { default: () => 0 }
39
+ )
40
+ })
41
+ export type PaginationQuery = Schema.Schema.Type<typeof PaginationQuery>
42
+
43
+ export const PaginationMeta = Schema.Struct({
44
+ total: Schema.Number.pipe(Schema.int(), Schema.nonNegative()),
45
+ limit: PositiveInteger,
46
+ offset: Schema.Number.pipe(Schema.int(), Schema.nonNegative()),
47
+ hasMore: Schema.Boolean
48
+ })
49
+ export type PaginationMeta = Schema.Schema.Type<typeof PaginationMeta>
50
+
51
+ export const ErrorCode = Schema.Union(
52
+ // Validation errors
53
+ Schema.Literal("VALIDATION_ERROR"),
54
+ Schema.Literal("INVALID_ENDPOINT"),
55
+ // Resource errors
56
+ Schema.Literal("IMPOSTER_NOT_FOUND"),
57
+ Schema.Literal("PORT_IN_USE"),
58
+ Schema.Literal("IMPOSTER_BUSY"),
59
+ // System errors
60
+ Schema.Literal("SYSTEM_ERROR"),
61
+ Schema.Literal("CONFIRMATION_REQUIRED"),
62
+ // Conflict errors
63
+ Schema.Literal("ENDPOINT_CONFLICT")
64
+ )
65
+ export type ErrorCode = Schema.Schema.Type<typeof ErrorCode>
66
+
67
+ export const ErrorDetails = Schema.Struct({
68
+ code: ErrorCode,
69
+ message: NonEmptyString,
70
+ field: Schema.optional(Schema.String),
71
+ value: Schema.optional(Schema.Unknown),
72
+ details: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Unknown }))
73
+ })
74
+ export type ErrorDetails = Schema.Schema.Type<typeof ErrorDetails>
75
+
76
+ export const ErrorResponse = Schema.Struct({
77
+ error: ErrorDetails
78
+ })
79
+ export type ErrorResponse = Schema.Schema.Type<typeof ErrorResponse>
80
+
81
+ // Common query filters
82
+ export const StatusFilter = Schema.optional(ImposterStatus)
83
+ export const ProtocolFilter = Schema.optional(Protocol)
84
+
85
+ // DateTime schemas using Effect's DateTime
86
+ export const DateTimeSchema = Schema.DateTimeUtc
87
+ export type DateTimeSchema = Schema.Schema.Type<typeof DateTimeSchema>
88
+
89
+ // Helper to create current DateTime
90
+ export const currentDateTime = Effect.map(Clock.currentTimeMillis, (ms) => DateTime.unsafeMake(ms))
91
+
92
+ // Helper to format duration as uptime string (HH:MM:SS)
93
+ export const formatDurationAsUptime = (duration: Duration.Duration): string => {
94
+ return Duration.format(duration)
95
+ }
@@ -0,0 +1,22 @@
1
+ import { HttpApiBuilder } from "@effect/platform"
2
+ import * as Layer from "effect/Layer"
3
+ import { ApiLayer } from "../layers/ApiLayer"
4
+ import { MainLayer } from "../layers/MainLayer"
5
+ import { makeAdminUiRouter } from "../ui/admin/AdminUiRouter"
6
+
7
+ export const FullLayer = ApiLayer.pipe(Layer.provide(MainLayer))
8
+
9
+ export const makeWebHandler = () => HttpApiBuilder.toWebHandler(FullLayer)
10
+
11
+ export const makeCompositeHandler = (adminPort: number) => {
12
+ const { dispose, handler: apiHandler } = HttpApiBuilder.toWebHandler(FullLayer)
13
+ const adminUiRouter = makeAdminUiRouter({ apiHandler, adminPort })
14
+
15
+ const handler = async (request: Request): Promise<Response> => {
16
+ const uiResponse = await adminUiRouter(request)
17
+ if (uiResponse !== null) return uiResponse
18
+ return apiHandler(request)
19
+ }
20
+
21
+ return { handler, dispose }
22
+ }
@@ -0,0 +1,19 @@
1
+ import { Context, Layer } from "effect"
2
+
3
+ export interface ServerInstance {
4
+ readonly port: number
5
+ readonly stop: (closeActive: boolean) => void
6
+ }
7
+
8
+ export interface ServerFactoryShape {
9
+ readonly create: (options: {
10
+ readonly port: number
11
+ readonly fetch: (request: Request) => Promise<Response>
12
+ }) => ServerInstance
13
+ }
14
+
15
+ export class ServerFactory extends Context.Tag("ServerFactory")<ServerFactory, ServerFactoryShape>() {}
16
+
17
+ export const BunServerFactoryLive = Layer.succeed(ServerFactory, {
18
+ create: (options) => Bun.serve(options)
19
+ })