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.
- package/LICENSE +21 -0
- package/Program/package.json +6 -0
- package/README.md +365 -0
- package/api/AdminApi/package.json +6 -0
- package/api/ApiErrors/package.json +6 -0
- package/api/ApiSchemas/package.json +6 -0
- package/api/Conversions/package.json +6 -0
- package/api/ImpostersGroup/package.json +6 -0
- package/api/ImpostersHandlers/package.json +6 -0
- package/api/SystemGroup/package.json +6 -0
- package/api/SystemHandlers/package.json +6 -0
- package/bin/imposters +47 -0
- package/cli/Commands/package.json +6 -0
- package/cli/ConfigLoader/package.json +6 -0
- package/client/HandlerHttpClient/package.json +6 -0
- package/client/ImpostersClient/package.json +6 -0
- package/client/testing/package.json +6 -0
- package/dist/cjs/Program.js +4 -0
- package/dist/cjs/Program.js.map +1 -0
- package/dist/cjs/api/AdminApi.js +11 -0
- package/dist/cjs/api/AdminApi.js.map +1 -0
- package/dist/cjs/api/ApiErrors.js +30 -0
- package/dist/cjs/api/ApiErrors.js.map +1 -0
- package/dist/cjs/api/ApiSchemas.js +36 -0
- package/dist/cjs/api/ApiSchemas.js.map +1 -0
- package/dist/cjs/api/Conversions.js +41 -0
- package/dist/cjs/api/Conversions.js.map +1 -0
- package/dist/cjs/api/ImpostersGroup.js +37 -0
- package/dist/cjs/api/ImpostersGroup.js.map +1 -0
- package/dist/cjs/api/ImpostersHandlers.js +361 -0
- package/dist/cjs/api/ImpostersHandlers.js.map +1 -0
- package/dist/cjs/api/SystemGroup.js +12 -0
- package/dist/cjs/api/SystemGroup.js.map +1 -0
- package/dist/cjs/api/SystemHandlers.js +74 -0
- package/dist/cjs/api/SystemHandlers.js.map +1 -0
- package/dist/cjs/cli/Commands.js +104 -0
- package/dist/cjs/cli/Commands.js.map +1 -0
- package/dist/cjs/cli/ConfigLoader.js +34 -0
- package/dist/cjs/cli/ConfigLoader.js.map +1 -0
- package/dist/cjs/client/HandlerHttpClient.js +50 -0
- package/dist/cjs/client/HandlerHttpClient.js.map +1 -0
- package/dist/cjs/client/ImpostersClient.js +20 -0
- package/dist/cjs/client/ImpostersClient.js.map +1 -0
- package/dist/cjs/client/index.js +57 -0
- package/dist/cjs/client/index.js.map +1 -0
- package/dist/cjs/client/testing.js +94 -0
- package/dist/cjs/client/testing.js.map +1 -0
- package/dist/cjs/domain/imposter.js +125 -0
- package/dist/cjs/domain/imposter.js.map +1 -0
- package/dist/cjs/domain/route.js +185 -0
- package/dist/cjs/domain/route.js.map +1 -0
- package/dist/cjs/index.js +106 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/layers/ApiLayer.js +18 -0
- package/dist/cjs/layers/ApiLayer.js.map +1 -0
- package/dist/cjs/layers/MainLayer.js +27 -0
- package/dist/cjs/layers/MainLayer.js.map +1 -0
- package/dist/cjs/matching/ExpressionEvaluator.js +103 -0
- package/dist/cjs/matching/ExpressionEvaluator.js.map +1 -0
- package/dist/cjs/matching/RequestMatcher.js +145 -0
- package/dist/cjs/matching/RequestMatcher.js.map +1 -0
- package/dist/cjs/matching/ResponseGenerator.js +80 -0
- package/dist/cjs/matching/ResponseGenerator.js.map +1 -0
- package/dist/cjs/matching/TemplateEngine.js +55 -0
- package/dist/cjs/matching/TemplateEngine.js.map +1 -0
- package/dist/cjs/repositories/ImposterRepository.js +118 -0
- package/dist/cjs/repositories/ImposterRepository.js.map +1 -0
- package/dist/cjs/schemas/ConfigFileSchema.js +44 -0
- package/dist/cjs/schemas/ConfigFileSchema.js.map +1 -0
- package/dist/cjs/schemas/ImposterSchema.js +202 -0
- package/dist/cjs/schemas/ImposterSchema.js.map +1 -0
- package/dist/cjs/schemas/RequestLogSchema.js +51 -0
- package/dist/cjs/schemas/RequestLogSchema.js.map +1 -0
- package/dist/cjs/schemas/StubSchema.js +84 -0
- package/dist/cjs/schemas/StubSchema.js.map +1 -0
- package/dist/cjs/schemas/common.js +67 -0
- package/dist/cjs/schemas/common.js.map +1 -0
- package/dist/cjs/server/AdminServer.js +36 -0
- package/dist/cjs/server/AdminServer.js.map +1 -0
- package/dist/cjs/server/BunServer.js +13 -0
- package/dist/cjs/server/BunServer.js.map +1 -0
- package/dist/cjs/server/FiberManager.js +21 -0
- package/dist/cjs/server/FiberManager.js.map +1 -0
- package/dist/cjs/server/ImposterServer.js +234 -0
- package/dist/cjs/server/ImposterServer.js.map +1 -0
- package/dist/cjs/services/AppConfig.js +18 -0
- package/dist/cjs/services/AppConfig.js.map +1 -0
- package/dist/cjs/services/MetricsService.js +113 -0
- package/dist/cjs/services/MetricsService.js.map +1 -0
- package/dist/cjs/services/PortAllocator.js +50 -0
- package/dist/cjs/services/PortAllocator.js.map +1 -0
- package/dist/cjs/services/ProxyService.js +109 -0
- package/dist/cjs/services/ProxyService.js.map +1 -0
- package/dist/cjs/services/RequestLogger.js +60 -0
- package/dist/cjs/services/RequestLogger.js.map +1 -0
- package/dist/cjs/services/Uuid.js +10 -0
- package/dist/cjs/services/Uuid.js.map +1 -0
- package/dist/cjs/services/UuidLive.js +16 -0
- package/dist/cjs/services/UuidLive.js.map +1 -0
- package/dist/cjs/ui/UiRouter.js +242 -0
- package/dist/cjs/ui/UiRouter.js.map +1 -0
- package/dist/cjs/ui/admin/AdminLayout.js +36 -0
- package/dist/cjs/ui/admin/AdminLayout.js.map +1 -0
- package/dist/cjs/ui/admin/AdminUiRouter.js +155 -0
- package/dist/cjs/ui/admin/AdminUiRouter.js.map +1 -0
- package/dist/cjs/ui/admin/pages/AdminDashboard.js +55 -0
- package/dist/cjs/ui/admin/pages/AdminDashboard.js.map +1 -0
- package/dist/cjs/ui/admin/partials.js +64 -0
- package/dist/cjs/ui/admin/partials.js.map +1 -0
- package/dist/cjs/ui/html.js +42 -0
- package/dist/cjs/ui/html.js.map +1 -0
- package/dist/cjs/ui/layout.js +39 -0
- package/dist/cjs/ui/layout.js.map +1 -0
- package/dist/cjs/ui/pages/dashboard.js +51 -0
- package/dist/cjs/ui/pages/dashboard.js.map +1 -0
- package/dist/cjs/ui/pages/request-detail.js +119 -0
- package/dist/cjs/ui/pages/request-detail.js.map +1 -0
- package/dist/cjs/ui/pages/requests.js +120 -0
- package/dist/cjs/ui/pages/requests.js.map +1 -0
- package/dist/cjs/ui/pages/stubs.js +46 -0
- package/dist/cjs/ui/pages/stubs.js.map +1 -0
- package/dist/cjs/ui/partials.js +104 -0
- package/dist/cjs/ui/partials.js.map +1 -0
- package/dist/dts/Program.d.ts +2 -0
- package/dist/dts/Program.d.ts.map +1 -0
- package/dist/dts/api/AdminApi.d.ts +490 -0
- package/dist/dts/api/AdminApi.d.ts.map +1 -0
- package/dist/dts/api/ApiErrors.d.ts +26 -0
- package/dist/dts/api/ApiErrors.d.ts.map +1 -0
- package/dist/dts/api/ApiSchemas.d.ts +36 -0
- package/dist/dts/api/ApiSchemas.d.ts.map +1 -0
- package/dist/dts/api/Conversions.d.ts +7 -0
- package/dist/dts/api/Conversions.d.ts.map +1 -0
- package/dist/dts/api/ImpostersGroup.d.ts +448 -0
- package/dist/dts/api/ImpostersGroup.d.ts.map +1 -0
- package/dist/dts/api/ImpostersHandlers.d.ts +9 -0
- package/dist/dts/api/ImpostersHandlers.d.ts.map +1 -0
- package/dist/dts/api/SystemGroup.d.ts +46 -0
- package/dist/dts/api/SystemGroup.d.ts.map +1 -0
- package/dist/dts/api/SystemHandlers.d.ts +4 -0
- package/dist/dts/api/SystemHandlers.d.ts.map +1 -0
- package/dist/dts/cli/Commands.d.ts +4 -0
- package/dist/dts/cli/Commands.d.ts.map +1 -0
- package/dist/dts/cli/ConfigLoader.d.ts +13 -0
- package/dist/dts/cli/ConfigLoader.d.ts.map +1 -0
- package/dist/dts/client/HandlerHttpClient.d.ts +5 -0
- package/dist/dts/client/HandlerHttpClient.d.ts.map +1 -0
- package/dist/dts/client/ImpostersClient.d.ts +1868 -0
- package/dist/dts/client/ImpostersClient.d.ts.map +1 -0
- package/dist/dts/client/index.d.ts +6 -0
- package/dist/dts/client/index.d.ts.map +1 -0
- package/dist/dts/client/testing.d.ts +35 -0
- package/dist/dts/client/testing.d.ts.map +1 -0
- package/dist/dts/domain/imposter.d.ts +123 -0
- package/dist/dts/domain/imposter.d.ts.map +1 -0
- package/dist/dts/domain/route.d.ts +128 -0
- package/dist/dts/domain/route.d.ts.map +1 -0
- package/dist/dts/index.d.ts +60 -0
- package/dist/dts/index.d.ts.map +1 -0
- package/dist/dts/layers/ApiLayer.d.ts +3 -0
- package/dist/dts/layers/ApiLayer.d.ts.map +1 -0
- package/dist/dts/layers/MainLayer.d.ts +3 -0
- package/dist/dts/layers/MainLayer.d.ts.map +1 -0
- package/dist/dts/matching/ExpressionEvaluator.d.ts +11 -0
- package/dist/dts/matching/ExpressionEvaluator.d.ts.map +1 -0
- package/dist/dts/matching/RequestMatcher.d.ts +13 -0
- package/dist/dts/matching/RequestMatcher.d.ts.map +1 -0
- package/dist/dts/matching/ResponseGenerator.d.ts +9 -0
- package/dist/dts/matching/ResponseGenerator.d.ts.map +1 -0
- package/dist/dts/matching/TemplateEngine.d.ts +4 -0
- package/dist/dts/matching/TemplateEngine.d.ts.map +1 -0
- package/dist/dts/repositories/ImposterRepository.d.ts +33 -0
- package/dist/dts/repositories/ImposterRepository.d.ts.map +1 -0
- package/dist/dts/schemas/ConfigFileSchema.d.ts +142 -0
- package/dist/dts/schemas/ConfigFileSchema.d.ts.map +1 -0
- package/dist/dts/schemas/ImposterSchema.d.ts +368 -0
- package/dist/dts/schemas/ImposterSchema.d.ts.map +1 -0
- package/dist/dts/schemas/RequestLogSchema.d.ts +36 -0
- package/dist/dts/schemas/RequestLogSchema.d.ts.map +1 -0
- package/dist/dts/schemas/StubSchema.d.ts +112 -0
- package/dist/dts/schemas/StubSchema.d.ts.map +1 -0
- package/dist/dts/schemas/common.d.ts +56 -0
- package/dist/dts/schemas/common.d.ts.map +1 -0
- package/dist/dts/server/AdminServer.d.ts +11 -0
- package/dist/dts/server/AdminServer.d.ts.map +1 -0
- package/dist/dts/server/BunServer.d.ts +17 -0
- package/dist/dts/server/BunServer.d.ts.map +1 -0
- package/dist/dts/server/FiberManager.d.ts +12 -0
- package/dist/dts/server/FiberManager.d.ts.map +1 -0
- package/dist/dts/server/ImposterServer.d.ts +29 -0
- package/dist/dts/server/ImposterServer.d.ts.map +1 -0
- package/dist/dts/services/AppConfig.d.ts +14 -0
- package/dist/dts/services/AppConfig.d.ts.map +1 -0
- package/dist/dts/services/MetricsService.d.ts +26 -0
- package/dist/dts/services/MetricsService.d.ts.map +1 -0
- package/dist/dts/services/PortAllocator.d.ts +29 -0
- package/dist/dts/services/PortAllocator.d.ts.map +1 -0
- package/dist/dts/services/ProxyService.d.ts +24 -0
- package/dist/dts/services/ProxyService.d.ts.map +1 -0
- package/dist/dts/services/RequestLogger.d.ts +23 -0
- package/dist/dts/services/RequestLogger.d.ts.map +1 -0
- package/dist/dts/services/Uuid.d.ts +9 -0
- package/dist/dts/services/Uuid.d.ts.map +1 -0
- package/dist/dts/services/UuidLive.d.ts +4 -0
- package/dist/dts/services/UuidLive.d.ts.map +1 -0
- package/dist/dts/ui/UiRouter.d.ts +15 -0
- package/dist/dts/ui/UiRouter.d.ts.map +1 -0
- package/dist/dts/ui/admin/AdminLayout.d.ts +7 -0
- package/dist/dts/ui/admin/AdminLayout.d.ts.map +1 -0
- package/dist/dts/ui/admin/AdminUiRouter.d.ts +6 -0
- package/dist/dts/ui/admin/AdminUiRouter.d.ts.map +1 -0
- package/dist/dts/ui/admin/pages/AdminDashboard.d.ts +7 -0
- package/dist/dts/ui/admin/pages/AdminDashboard.d.ts.map +1 -0
- package/dist/dts/ui/admin/partials.d.ts +14 -0
- package/dist/dts/ui/admin/partials.d.ts.map +1 -0
- package/dist/dts/ui/html.d.ts +12 -0
- package/dist/dts/ui/html.d.ts.map +1 -0
- package/dist/dts/ui/layout.d.ts +9 -0
- package/dist/dts/ui/layout.d.ts.map +1 -0
- package/dist/dts/ui/pages/dashboard.d.ts +10 -0
- package/dist/dts/ui/pages/dashboard.d.ts.map +1 -0
- package/dist/dts/ui/pages/request-detail.d.ts +11 -0
- package/dist/dts/ui/pages/request-detail.d.ts.map +1 -0
- package/dist/dts/ui/pages/requests.d.ts +15 -0
- package/dist/dts/ui/pages/requests.d.ts.map +1 -0
- package/dist/dts/ui/pages/stubs.d.ts +8 -0
- package/dist/dts/ui/pages/stubs.d.ts.map +1 -0
- package/dist/dts/ui/partials.d.ts +13 -0
- package/dist/dts/ui/partials.d.ts.map +1 -0
- package/dist/esm/Program.js +2 -0
- package/dist/esm/Program.js.map +1 -0
- package/dist/esm/api/AdminApi.js +5 -0
- package/dist/esm/api/AdminApi.js.map +1 -0
- package/dist/esm/api/ApiErrors.js +20 -0
- package/dist/esm/api/ApiErrors.js.map +1 -0
- package/dist/esm/api/ApiSchemas.js +29 -0
- package/dist/esm/api/ApiSchemas.js.map +1 -0
- package/dist/esm/api/Conversions.js +32 -0
- package/dist/esm/api/Conversions.js.map +1 -0
- package/dist/esm/api/ImpostersGroup.js +30 -0
- package/dist/esm/api/ImpostersGroup.js.map +1 -0
- package/dist/esm/api/ImpostersHandlers.js +354 -0
- package/dist/esm/api/ImpostersHandlers.js.map +1 -0
- package/dist/esm/api/SystemGroup.js +6 -0
- package/dist/esm/api/SystemGroup.js.map +1 -0
- package/dist/esm/api/SystemHandlers.js +67 -0
- package/dist/esm/api/SystemHandlers.js.map +1 -0
- package/dist/esm/cli/Commands.js +98 -0
- package/dist/esm/cli/Commands.js.map +1 -0
- package/dist/esm/cli/ConfigLoader.js +25 -0
- package/dist/esm/cli/ConfigLoader.js.map +1 -0
- package/dist/esm/client/HandlerHttpClient.js +42 -0
- package/dist/esm/client/HandlerHttpClient.js.map +1 -0
- package/dist/esm/client/ImpostersClient.js +10 -0
- package/dist/esm/client/ImpostersClient.js.map +1 -0
- package/dist/esm/client/index.js +4 -0
- package/dist/esm/client/index.js.map +1 -0
- package/dist/esm/client/testing.js +86 -0
- package/dist/esm/client/testing.js.map +1 -0
- package/dist/esm/domain/imposter.js +103 -0
- package/dist/esm/domain/imposter.js.map +1 -0
- package/dist/esm/domain/route.js +164 -0
- package/dist/esm/domain/route.js.map +1 -0
- package/dist/esm/index.js +60 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/layers/ApiLayer.js +11 -0
- package/dist/esm/layers/ApiLayer.js.map +1 -0
- package/dist/esm/layers/MainLayer.js +20 -0
- package/dist/esm/layers/MainLayer.js.map +1 -0
- package/dist/esm/matching/ExpressionEvaluator.js +94 -0
- package/dist/esm/matching/ExpressionEvaluator.js.map +1 -0
- package/dist/esm/matching/RequestMatcher.js +135 -0
- package/dist/esm/matching/RequestMatcher.js.map +1 -0
- package/dist/esm/matching/ResponseGenerator.js +71 -0
- package/dist/esm/matching/ResponseGenerator.js.map +1 -0
- package/dist/esm/matching/TemplateEngine.js +47 -0
- package/dist/esm/matching/TemplateEngine.js.map +1 -0
- package/dist/esm/package.json +4 -0
- package/dist/esm/repositories/ImposterRepository.js +110 -0
- package/dist/esm/repositories/ImposterRepository.js.map +1 -0
- package/dist/esm/schemas/ConfigFileSchema.js +37 -0
- package/dist/esm/schemas/ConfigFileSchema.js.map +1 -0
- package/dist/esm/schemas/ImposterSchema.js +195 -0
- package/dist/esm/schemas/ImposterSchema.js.map +1 -0
- package/dist/esm/schemas/RequestLogSchema.js +44 -0
- package/dist/esm/schemas/RequestLogSchema.js.map +1 -0
- package/dist/esm/schemas/StubSchema.js +77 -0
- package/dist/esm/schemas/StubSchema.js.map +1 -0
- package/dist/esm/schemas/common.js +59 -0
- package/dist/esm/schemas/common.js.map +1 -0
- package/dist/esm/server/AdminServer.js +27 -0
- package/dist/esm/server/AdminServer.js.map +1 -0
- package/dist/esm/server/BunServer.js +6 -0
- package/dist/esm/server/BunServer.js.map +1 -0
- package/dist/esm/server/FiberManager.js +14 -0
- package/dist/esm/server/FiberManager.js.map +1 -0
- package/dist/esm/server/ImposterServer.js +225 -0
- package/dist/esm/server/ImposterServer.js.map +1 -0
- package/dist/esm/services/AppConfig.js +11 -0
- package/dist/esm/services/AppConfig.js.map +1 -0
- package/dist/esm/services/MetricsService.js +105 -0
- package/dist/esm/services/MetricsService.js.map +1 -0
- package/dist/esm/services/PortAllocator.js +41 -0
- package/dist/esm/services/PortAllocator.js.map +1 -0
- package/dist/esm/services/ProxyService.js +101 -0
- package/dist/esm/services/ProxyService.js.map +1 -0
- package/dist/esm/services/RequestLogger.js +53 -0
- package/dist/esm/services/RequestLogger.js.map +1 -0
- package/dist/esm/services/Uuid.js +3 -0
- package/dist/esm/services/Uuid.js.map +1 -0
- package/dist/esm/services/UuidLive.js +9 -0
- package/dist/esm/services/UuidLive.js.map +1 -0
- package/dist/esm/ui/UiRouter.js +235 -0
- package/dist/esm/ui/UiRouter.js.map +1 -0
- package/dist/esm/ui/admin/AdminLayout.js +29 -0
- package/dist/esm/ui/admin/AdminLayout.js.map +1 -0
- package/dist/esm/ui/admin/AdminUiRouter.js +148 -0
- package/dist/esm/ui/admin/AdminUiRouter.js.map +1 -0
- package/dist/esm/ui/admin/pages/AdminDashboard.js +48 -0
- package/dist/esm/ui/admin/pages/AdminDashboard.js.map +1 -0
- package/dist/esm/ui/admin/partials.js +54 -0
- package/dist/esm/ui/admin/partials.js.map +1 -0
- package/dist/esm/ui/html.js +32 -0
- package/dist/esm/ui/html.js.map +1 -0
- package/dist/esm/ui/layout.js +32 -0
- package/dist/esm/ui/layout.js.map +1 -0
- package/dist/esm/ui/pages/dashboard.js +44 -0
- package/dist/esm/ui/pages/dashboard.js.map +1 -0
- package/dist/esm/ui/pages/request-detail.js +112 -0
- package/dist/esm/ui/pages/request-detail.js.map +1 -0
- package/dist/esm/ui/pages/requests.js +112 -0
- package/dist/esm/ui/pages/requests.js.map +1 -0
- package/dist/esm/ui/pages/stubs.js +39 -0
- package/dist/esm/ui/pages/stubs.js.map +1 -0
- package/dist/esm/ui/partials.js +91 -0
- package/dist/esm/ui/partials.js.map +1 -0
- package/domain/imposter/package.json +6 -0
- package/domain/route/package.json +6 -0
- package/layers/ApiLayer/package.json +6 -0
- package/layers/MainLayer/package.json +6 -0
- package/matching/ExpressionEvaluator/package.json +6 -0
- package/matching/RequestMatcher/package.json +6 -0
- package/matching/ResponseGenerator/package.json +6 -0
- package/matching/TemplateEngine/package.json +6 -0
- package/package.json +435 -0
- package/repositories/ImposterRepository/package.json +6 -0
- package/schemas/ConfigFileSchema/package.json +6 -0
- package/schemas/ImposterSchema/package.json +6 -0
- package/schemas/RequestLogSchema/package.json +6 -0
- package/schemas/StubSchema/package.json +6 -0
- package/schemas/common/package.json +6 -0
- package/server/AdminServer/package.json +6 -0
- package/server/BunServer/package.json +6 -0
- package/server/FiberManager/package.json +6 -0
- package/server/ImposterServer/package.json +6 -0
- package/services/AppConfig/package.json +6 -0
- package/services/MetricsService/package.json +6 -0
- package/services/PortAllocator/package.json +6 -0
- package/services/ProxyService/package.json +6 -0
- package/services/RequestLogger/package.json +6 -0
- package/services/Uuid/package.json +6 -0
- package/services/UuidLive/package.json +6 -0
- package/src/Program.ts +1 -0
- package/src/api/AdminApi.ts +7 -0
- package/src/api/ApiErrors.ts +20 -0
- package/src/api/ApiSchemas.ts +36 -0
- package/src/api/Conversions.ts +34 -0
- package/src/api/ImpostersGroup.ts +103 -0
- package/src/api/ImpostersHandlers.ts +387 -0
- package/src/api/SystemGroup.ts +12 -0
- package/src/api/SystemHandlers.ts +76 -0
- package/src/cli/Commands.ts +119 -0
- package/src/cli/ConfigLoader.ts +41 -0
- package/src/client/HandlerHttpClient.ts +50 -0
- package/src/client/ImpostersClient.ts +21 -0
- package/src/client/index.ts +9 -0
- package/src/client/testing.ts +105 -0
- package/src/domain/imposter.ts +186 -0
- package/src/domain/route.ts +255 -0
- package/src/index.ts +153 -0
- package/src/layers/ApiLayer.ts +21 -0
- package/src/layers/MainLayer.ts +43 -0
- package/src/matching/ExpressionEvaluator.ts +102 -0
- package/src/matching/RequestMatcher.ts +162 -0
- package/src/matching/ResponseGenerator.ts +86 -0
- package/src/matching/TemplateEngine.ts +54 -0
- package/src/repositories/ImposterRepository.ts +145 -0
- package/src/schemas/ConfigFileSchema.ts +32 -0
- package/src/schemas/ImposterSchema.ts +232 -0
- package/src/schemas/RequestLogSchema.ts +38 -0
- package/src/schemas/StubSchema.ts +90 -0
- package/src/schemas/common.ts +95 -0
- package/src/server/AdminServer.ts +22 -0
- package/src/server/BunServer.ts +19 -0
- package/src/server/FiberManager.ts +25 -0
- package/src/server/ImposterServer.ts +244 -0
- package/src/services/AppConfig.ts +22 -0
- package/src/services/MetricsService.ts +157 -0
- package/src/services/PortAllocator.ts +68 -0
- package/src/services/ProxyService.ts +139 -0
- package/src/services/RequestLogger.ts +87 -0
- package/src/services/Uuid.ts +9 -0
- package/src/services/UuidLive.ts +9 -0
- package/src/types/bun.d.ts +6 -0
- package/src/ui/UiRouter.ts +278 -0
- package/src/ui/admin/AdminLayout.ts +36 -0
- package/src/ui/admin/AdminUiRouter.ts +170 -0
- package/src/ui/admin/pages/AdminDashboard.ts +54 -0
- package/src/ui/admin/partials.ts +83 -0
- package/src/ui/html.ts +37 -0
- package/src/ui/layout.ts +44 -0
- package/src/ui/pages/dashboard.ts +64 -0
- package/src/ui/pages/request-detail.ts +142 -0
- package/src/ui/pages/requests.ts +141 -0
- package/src/ui/pages/stubs.ts +52 -0
- package/src/ui/partials.ts +133 -0
- package/ui/UiRouter/package.json +6 -0
- package/ui/admin/AdminLayout/package.json +6 -0
- package/ui/admin/AdminUiRouter/package.json +6 -0
- package/ui/admin/pages/AdminDashboard/package.json +6 -0
- package/ui/admin/partials/package.json +6 -0
- package/ui/html/package.json +6 -0
- package/ui/layout/package.json +6 -0
- package/ui/pages/dashboard/package.json +6 -0
- package/ui/pages/requests/package.json +6 -0
- package/ui/pages/stubs/package.json +6 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Context, Data, Effect, Layer } from "effect";
|
|
2
|
+
import { NonEmptyString } from "../schemas/common";
|
|
3
|
+
import { Uuid } from "./Uuid";
|
|
4
|
+
export class ProxyError extends Data.TaggedError("ProxyError") {}
|
|
5
|
+
const HOP_BY_HOP_HEADERS = /*#__PURE__*/new Set(["host", "connection", "keep-alive", "transfer-encoding", "upgrade", "proxy-authenticate", "proxy-authorization", "te", "trailers"]);
|
|
6
|
+
export class ProxyService extends Context.Tag("ProxyService")() {}
|
|
7
|
+
export const ProxyServiceLive = /*#__PURE__*/Layer.effect(ProxyService, /*#__PURE__*/Effect.gen(function* () {
|
|
8
|
+
const uuid = yield* Uuid;
|
|
9
|
+
const forward = (ctx, config, originalUrl) => Effect.gen(function* () {
|
|
10
|
+
// Build target URL preserving path and query
|
|
11
|
+
const targetBase = config.targetUrl.replace(/\/$/, "");
|
|
12
|
+
const targetUrl = `${targetBase}${originalUrl.pathname}${originalUrl.search}`;
|
|
13
|
+
// Build headers
|
|
14
|
+
const headers = new Headers();
|
|
15
|
+
for (const [key, val] of Object.entries(ctx.headers)) {
|
|
16
|
+
if (!HOP_BY_HOP_HEADERS.has(key.toLowerCase())) {
|
|
17
|
+
headers.set(key, val);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// Remove headers specified in config
|
|
21
|
+
for (const h of config.removeHeaders) {
|
|
22
|
+
headers.delete(h);
|
|
23
|
+
}
|
|
24
|
+
// Add headers specified in config
|
|
25
|
+
if (config.addHeaders) {
|
|
26
|
+
for (const [key, val] of Object.entries(config.addHeaders)) {
|
|
27
|
+
headers.set(key, val);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Build body
|
|
31
|
+
let body;
|
|
32
|
+
if (ctx.body !== undefined && ctx.body !== null) {
|
|
33
|
+
body = typeof ctx.body === "string" ? ctx.body : JSON.stringify(ctx.body);
|
|
34
|
+
}
|
|
35
|
+
const response = yield* Effect.tryPromise({
|
|
36
|
+
try: signal => fetch(targetUrl, {
|
|
37
|
+
method: ctx.method,
|
|
38
|
+
headers,
|
|
39
|
+
...(body !== undefined && ctx.method !== "GET" && ctx.method !== "HEAD" ? {
|
|
40
|
+
body
|
|
41
|
+
} : {}),
|
|
42
|
+
redirect: config.followRedirects ? "follow" : "manual",
|
|
43
|
+
signal
|
|
44
|
+
}),
|
|
45
|
+
catch: err => new ProxyError({
|
|
46
|
+
targetUrl,
|
|
47
|
+
reason: `Failed to reach target: ${err}`,
|
|
48
|
+
cause: err
|
|
49
|
+
})
|
|
50
|
+
}).pipe(Effect.timeoutFail({
|
|
51
|
+
duration: `${config.timeout} millis`,
|
|
52
|
+
onTimeout: () => new ProxyError({
|
|
53
|
+
targetUrl,
|
|
54
|
+
reason: `Request timed out after ${config.timeout}ms`
|
|
55
|
+
})
|
|
56
|
+
}));
|
|
57
|
+
return response;
|
|
58
|
+
});
|
|
59
|
+
const recordAsStub = (request, response) => Effect.gen(function* () {
|
|
60
|
+
const id = yield* uuid.generateShort;
|
|
61
|
+
const respHeaders = {};
|
|
62
|
+
response.headers.forEach((val, key) => {
|
|
63
|
+
respHeaders[key] = val;
|
|
64
|
+
});
|
|
65
|
+
const respText = yield* Effect.promise(() => response.text());
|
|
66
|
+
let respBody = respText;
|
|
67
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
68
|
+
if (contentType.includes("application/json") && respText) {
|
|
69
|
+
try {
|
|
70
|
+
respBody = JSON.parse(respText);
|
|
71
|
+
} catch {
|
|
72
|
+
// keep as string
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
id: NonEmptyString.make(id),
|
|
77
|
+
predicates: [{
|
|
78
|
+
field: "method",
|
|
79
|
+
operator: "equals",
|
|
80
|
+
value: request.method,
|
|
81
|
+
caseSensitive: true
|
|
82
|
+
}, {
|
|
83
|
+
field: "path",
|
|
84
|
+
operator: "equals",
|
|
85
|
+
value: request.path,
|
|
86
|
+
caseSensitive: true
|
|
87
|
+
}],
|
|
88
|
+
responses: [{
|
|
89
|
+
status: response.status,
|
|
90
|
+
headers: respHeaders,
|
|
91
|
+
body: respBody
|
|
92
|
+
}],
|
|
93
|
+
responseMode: "sequential"
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
forward,
|
|
98
|
+
recordAsStub
|
|
99
|
+
};
|
|
100
|
+
}));
|
|
101
|
+
//# sourceMappingURL=ProxyService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProxyService.js","names":["Context","Data","Effect","Layer","NonEmptyString","Uuid","ProxyError","TaggedError","HOP_BY_HOP_HEADERS","Set","ProxyService","Tag","ProxyServiceLive","effect","gen","uuid","forward","ctx","config","originalUrl","targetBase","targetUrl","replace","pathname","search","headers","Headers","key","val","Object","entries","has","toLowerCase","set","h","removeHeaders","delete","addHeaders","body","undefined","JSON","stringify","response","tryPromise","try","signal","fetch","method","redirect","followRedirects","catch","err","reason","cause","pipe","timeoutFail","duration","timeout","onTimeout","recordAsStub","request","id","generateShort","respHeaders","forEach","respText","promise","text","respBody","contentType","get","includes","parse","make","predicates","field","operator","value","caseSensitive","path","responses","status","responseMode"],"sources":["../../../src/services/ProxyService.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Context, Effect, HashMap, Layer, PubSub, Ref } from "effect";
|
|
2
|
+
const MAX_ENTRIES = 100;
|
|
3
|
+
export class RequestLogger extends Context.Tag("RequestLogger")() {}
|
|
4
|
+
export const RequestLoggerLive = /*#__PURE__*/Layer.scoped(RequestLogger, /*#__PURE__*/Effect.gen(function* () {
|
|
5
|
+
const storeRef = yield* Ref.make(HashMap.empty());
|
|
6
|
+
const pubsub = yield* PubSub.sliding(256);
|
|
7
|
+
const log = entry => Effect.gen(function* () {
|
|
8
|
+
yield* Ref.update(storeRef, store => {
|
|
9
|
+
const existing = HashMap.get(store, entry.imposterId);
|
|
10
|
+
const entries = existing._tag === "Some" ? existing.value : [];
|
|
11
|
+
const updated = [...entries, entry].slice(-MAX_ENTRIES);
|
|
12
|
+
return HashMap.set(store, entry.imposterId, updated);
|
|
13
|
+
});
|
|
14
|
+
yield* PubSub.publish(pubsub, entry);
|
|
15
|
+
});
|
|
16
|
+
const getEntries = (imposterId, opts) => Ref.get(storeRef).pipe(Effect.map(store => {
|
|
17
|
+
const existing = HashMap.get(store, imposterId);
|
|
18
|
+
let entries = existing._tag === "Some" ? existing.value : [];
|
|
19
|
+
if (opts?.method !== undefined) {
|
|
20
|
+
entries = entries.filter(e => e.request.method.toUpperCase() === opts.method.toUpperCase());
|
|
21
|
+
}
|
|
22
|
+
if (opts?.path !== undefined) {
|
|
23
|
+
entries = entries.filter(e => e.request.path === opts.path);
|
|
24
|
+
}
|
|
25
|
+
if (opts?.status !== undefined) {
|
|
26
|
+
entries = entries.filter(e => e.response.status === opts.status);
|
|
27
|
+
}
|
|
28
|
+
const limit = opts?.limit ?? 50;
|
|
29
|
+
return entries.slice(-limit);
|
|
30
|
+
}));
|
|
31
|
+
const getCount = imposterId => Ref.get(storeRef).pipe(Effect.map(store => {
|
|
32
|
+
const existing = HashMap.get(store, imposterId);
|
|
33
|
+
return existing._tag === "Some" ? existing.value.length : 0;
|
|
34
|
+
}));
|
|
35
|
+
const clear = imposterId => Ref.update(storeRef, store => HashMap.set(store, imposterId, []));
|
|
36
|
+
const subscribe = PubSub.subscribe(pubsub);
|
|
37
|
+
const getEntryById = (imposterId, entryId) => Ref.get(storeRef).pipe(Effect.map(store => {
|
|
38
|
+
const existing = HashMap.get(store, imposterId);
|
|
39
|
+
if (existing._tag === "None") return null;
|
|
40
|
+
return existing.value.find(e => e.id === entryId) ?? null;
|
|
41
|
+
}));
|
|
42
|
+
const removeImposter = imposterId => Ref.update(storeRef, HashMap.remove(imposterId));
|
|
43
|
+
return {
|
|
44
|
+
log,
|
|
45
|
+
getEntries,
|
|
46
|
+
getCount,
|
|
47
|
+
clear,
|
|
48
|
+
subscribe,
|
|
49
|
+
getEntryById,
|
|
50
|
+
removeImposter
|
|
51
|
+
};
|
|
52
|
+
}));
|
|
53
|
+
//# sourceMappingURL=RequestLogger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestLogger.js","names":["Context","Effect","HashMap","Layer","PubSub","Ref","MAX_ENTRIES","RequestLogger","Tag","RequestLoggerLive","scoped","gen","storeRef","make","empty","pubsub","sliding","log","entry","update","store","existing","get","imposterId","entries","_tag","value","updated","slice","set","publish","getEntries","opts","pipe","map","method","undefined","filter","e","request","toUpperCase","path","status","response","limit","getCount","length","clear","subscribe","getEntryById","entryId","find","id","removeImposter","remove"],"sources":["../../../src/services/RequestLogger.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Uuid.js","names":["Context","Uuid","Tag"],"sources":["../../../src/services/Uuid.ts"],"sourcesContent":[null],"mappings":"AAAA,SAASA,OAAO,QAAqB,QAAQ;AAE7C,OAAM,MAAOC,IAAK,SAAQD,OAAO,CAACE,GAAG,CAAC,aAAa,CAAC,EAMjD","ignoreList":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import { v4 } from "uuid";
|
|
4
|
+
import { Uuid } from "./Uuid";
|
|
5
|
+
export const UuidLive = /*#__PURE__*/Layer.succeed(Uuid, {
|
|
6
|
+
generate: /*#__PURE__*/Effect.sync(() => v4()),
|
|
7
|
+
generateShort: /*#__PURE__*/Effect.sync(() => v4().substring(0, 8))
|
|
8
|
+
});
|
|
9
|
+
//# sourceMappingURL=UuidLive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UuidLive.js","names":["Effect","Layer","v4","Uuid","UuidLive","succeed","generate","sync","generateShort","substring"],"sources":["../../../src/services/UuidLive.ts"],"sourcesContent":[null],"mappings":"AAAA,OAAO,KAAKA,MAAM,MAAM,eAAe;AACvC,OAAO,KAAKC,KAAK,MAAM,cAAc;AACrC,SAASC,EAAE,QAAQ,MAAM;AACzB,SAASC,IAAI,QAAQ,QAAQ;AAE7B,OAAO,MAAMC,QAAQ,gBAAGH,KAAK,CAACI,OAAO,CAACF,IAAI,EAAE;EAC1CG,QAAQ,eAAEN,MAAM,CAACO,IAAI,CAAC,MAAML,EAAE,EAAE,CAAC;EACjCM,aAAa,eAAER,MAAM,CAACO,IAAI,CAAC,MAAML,EAAE,EAAE,CAACO,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC;CACtD,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { Effect, Ref } from "effect";
|
|
2
|
+
import { NonEmptyString } from "../schemas/common";
|
|
3
|
+
import { dashboardPage } from "./pages/dashboard";
|
|
4
|
+
import { requestDetailPage } from "./pages/request-detail";
|
|
5
|
+
import { requestsPage, testResultPartial } from "./pages/requests";
|
|
6
|
+
import { stubsPage } from "./pages/stubs";
|
|
7
|
+
import { errorPartial, requestTablePartial, stubListPartial } from "./partials";
|
|
8
|
+
const htmlResponse = (body, status = 200) => new Response(body, {
|
|
9
|
+
status,
|
|
10
|
+
headers: {
|
|
11
|
+
"content-type": "text/html; charset=utf-8"
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
const parseStubIdFromPath = path => {
|
|
15
|
+
const match = path.match(/^\/stubs\/(.+)$/);
|
|
16
|
+
return match ? match[1] : null;
|
|
17
|
+
};
|
|
18
|
+
export const makeUiRouter = deps => async request => {
|
|
19
|
+
const url = new URL(request.url);
|
|
20
|
+
if (!url.pathname.startsWith("/_admin")) return null;
|
|
21
|
+
const path = url.pathname.slice("/_admin".length) || "/";
|
|
22
|
+
const method = request.method.toUpperCase();
|
|
23
|
+
// GET / — dashboard
|
|
24
|
+
if (method === "GET" && (path === "/" || path === "")) {
|
|
25
|
+
return deps.runPromise(Effect.gen(function* () {
|
|
26
|
+
const stubs = yield* Ref.get(deps.stubsRef);
|
|
27
|
+
const requestCount = yield* deps.requestLogger.getCount(deps.id);
|
|
28
|
+
const recentRequests = yield* deps.requestLogger.getEntries(deps.id, {
|
|
29
|
+
limit: 10
|
|
30
|
+
});
|
|
31
|
+
return htmlResponse(dashboardPage({
|
|
32
|
+
config: deps.config,
|
|
33
|
+
stubCount: stubs.length,
|
|
34
|
+
requestCount,
|
|
35
|
+
recentRequests: recentRequests.slice().reverse()
|
|
36
|
+
}).value);
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
// GET /stubs — stubs page
|
|
40
|
+
if (method === "GET" && path === "/stubs") {
|
|
41
|
+
return deps.runPromise(Effect.gen(function* () {
|
|
42
|
+
const stubs = yield* Ref.get(deps.stubsRef);
|
|
43
|
+
return htmlResponse(stubsPage({
|
|
44
|
+
config: deps.config,
|
|
45
|
+
stubs
|
|
46
|
+
}).value);
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
// POST /stubs — add stub
|
|
50
|
+
if (method === "POST" && path === "/stubs") {
|
|
51
|
+
try {
|
|
52
|
+
const formData = await request.formData();
|
|
53
|
+
const predicatesRaw = formData.get("predicates");
|
|
54
|
+
const responsesRaw = formData.get("responses");
|
|
55
|
+
const responseMode = formData.get("responseMode") || "sequential";
|
|
56
|
+
if (!responsesRaw) {
|
|
57
|
+
return htmlResponse(errorPartial("Responses field is required.").value, 200);
|
|
58
|
+
}
|
|
59
|
+
let predicates;
|
|
60
|
+
let responses;
|
|
61
|
+
try {
|
|
62
|
+
predicates = JSON.parse(predicatesRaw || "[]");
|
|
63
|
+
responses = JSON.parse(responsesRaw);
|
|
64
|
+
} catch {
|
|
65
|
+
return htmlResponse(errorPartial("Invalid JSON in predicates or responses.").value, 200);
|
|
66
|
+
}
|
|
67
|
+
if (!Array.isArray(responses) || responses.length === 0) {
|
|
68
|
+
return htmlResponse(errorPartial("Responses must be a non-empty array.").value, 200);
|
|
69
|
+
}
|
|
70
|
+
const stubId = NonEmptyString.make(crypto.randomUUID().slice(0, 8));
|
|
71
|
+
const stub = {
|
|
72
|
+
id: stubId,
|
|
73
|
+
predicates: predicates,
|
|
74
|
+
responses: responses,
|
|
75
|
+
responseMode: responseMode
|
|
76
|
+
};
|
|
77
|
+
return await deps.runPromise(Effect.gen(function* () {
|
|
78
|
+
yield* deps.repo.addStub(deps.id, stub).pipe(Effect.catchAll(() => Effect.void));
|
|
79
|
+
const updated = yield* deps.repo.getStubs(deps.id).pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
80
|
+
yield* Ref.set(deps.stubsRef, updated);
|
|
81
|
+
return htmlResponse(stubListPartial(updated).value);
|
|
82
|
+
}));
|
|
83
|
+
} catch {
|
|
84
|
+
return htmlResponse(errorPartial("Failed to parse form data.").value, 200);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// DELETE /stubs/:id — delete stub
|
|
88
|
+
const deleteStubId = method === "DELETE" ? parseStubIdFromPath(path) : null;
|
|
89
|
+
if (deleteStubId !== null) {
|
|
90
|
+
return deps.runPromise(Effect.gen(function* () {
|
|
91
|
+
yield* deps.repo.removeStub(deps.id, deleteStubId).pipe(Effect.catchAll(() => Effect.void));
|
|
92
|
+
const updated = yield* deps.repo.getStubs(deps.id).pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
93
|
+
yield* Ref.set(deps.stubsRef, updated);
|
|
94
|
+
return htmlResponse(stubListPartial(updated).value);
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
// PUT /stubs/:id — update stub
|
|
98
|
+
const putStubId = method === "PUT" ? parseStubIdFromPath(path) : null;
|
|
99
|
+
if (putStubId !== null) {
|
|
100
|
+
try {
|
|
101
|
+
const formData = await request.formData();
|
|
102
|
+
const predicatesRaw = formData.get("predicates");
|
|
103
|
+
const responsesRaw = formData.get("responses");
|
|
104
|
+
const responseMode = formData.get("responseMode");
|
|
105
|
+
return await deps.runPromise(Effect.gen(function* () {
|
|
106
|
+
yield* deps.repo.updateStub(deps.id, putStubId, existing => ({
|
|
107
|
+
...existing,
|
|
108
|
+
...(predicatesRaw ? {
|
|
109
|
+
predicates: JSON.parse(predicatesRaw)
|
|
110
|
+
} : {}),
|
|
111
|
+
...(responsesRaw ? {
|
|
112
|
+
responses: JSON.parse(responsesRaw)
|
|
113
|
+
} : {}),
|
|
114
|
+
...(responseMode ? {
|
|
115
|
+
responseMode: responseMode
|
|
116
|
+
} : {})
|
|
117
|
+
})).pipe(Effect.catchAll(() => Effect.void));
|
|
118
|
+
const updated = yield* deps.repo.getStubs(deps.id).pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
119
|
+
yield* Ref.set(deps.stubsRef, updated);
|
|
120
|
+
return htmlResponse(stubListPartial(updated).value);
|
|
121
|
+
}));
|
|
122
|
+
} catch {
|
|
123
|
+
return htmlResponse(errorPartial("Failed to parse form data.").value, 200);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// GET /requests — full page
|
|
127
|
+
if (method === "GET" && path === "/requests") {
|
|
128
|
+
return deps.runPromise(Effect.gen(function* () {
|
|
129
|
+
const entries = yield* deps.requestLogger.getEntries(deps.id, {
|
|
130
|
+
limit: 100
|
|
131
|
+
});
|
|
132
|
+
return htmlResponse(requestsPage({
|
|
133
|
+
config: deps.config,
|
|
134
|
+
entries
|
|
135
|
+
}).value);
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
// GET /requests/list — HTMX partial (filtered table body)
|
|
139
|
+
if (method === "GET" && path === "/requests/list") {
|
|
140
|
+
const params = url.searchParams;
|
|
141
|
+
return deps.runPromise(Effect.gen(function* () {
|
|
142
|
+
const opts = {
|
|
143
|
+
limit: 100
|
|
144
|
+
};
|
|
145
|
+
const methodFilter = params.get("method");
|
|
146
|
+
if (methodFilter) opts.method = methodFilter;
|
|
147
|
+
const pathFilter = params.get("path");
|
|
148
|
+
if (pathFilter) opts.path = pathFilter;
|
|
149
|
+
const statusFilter = params.get("status");
|
|
150
|
+
if (statusFilter) opts.status = Number(statusFilter);
|
|
151
|
+
const entries = yield* deps.requestLogger.getEntries(deps.id, opts);
|
|
152
|
+
return htmlResponse(requestTablePartial(entries.slice().reverse()).value);
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
// POST /requests/test — send test request to own port
|
|
156
|
+
if (method === "POST" && path === "/requests/test") {
|
|
157
|
+
try {
|
|
158
|
+
const formData = await request.formData();
|
|
159
|
+
const testMethod = formData.get("method") || "GET";
|
|
160
|
+
const testPath = formData.get("path") || "/";
|
|
161
|
+
const testBody = formData.get("body");
|
|
162
|
+
const testContentType = formData.get("contentType") || "application/json";
|
|
163
|
+
const testHeadersRaw = formData.get("headers") || "";
|
|
164
|
+
const headers = {};
|
|
165
|
+
if (testContentType && testBody) {
|
|
166
|
+
headers["content-type"] = testContentType;
|
|
167
|
+
}
|
|
168
|
+
for (const line of testHeadersRaw.split("\n")) {
|
|
169
|
+
const colonIdx = line.indexOf(":");
|
|
170
|
+
if (colonIdx > 0) {
|
|
171
|
+
headers[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const startTime = Date.now();
|
|
175
|
+
const testResp = await fetch(`http://localhost:${deps.config.port}${testPath}`, {
|
|
176
|
+
method: testMethod,
|
|
177
|
+
headers,
|
|
178
|
+
...(testBody && testMethod !== "GET" && testMethod !== "HEAD" ? {
|
|
179
|
+
body: testBody
|
|
180
|
+
} : {})
|
|
181
|
+
});
|
|
182
|
+
const duration = Date.now() - startTime;
|
|
183
|
+
const respBody = await testResp.text();
|
|
184
|
+
const respHeaders = {};
|
|
185
|
+
testResp.headers.forEach((val, key) => {
|
|
186
|
+
respHeaders[key] = val;
|
|
187
|
+
});
|
|
188
|
+
return htmlResponse(testResultPartial({
|
|
189
|
+
status: testResp.status,
|
|
190
|
+
headers: respHeaders,
|
|
191
|
+
body: respBody,
|
|
192
|
+
duration
|
|
193
|
+
}).value);
|
|
194
|
+
} catch (err) {
|
|
195
|
+
return htmlResponse(`<div class="bg-red-50 border border-red-200 text-red-700 rounded p-3">Request failed: ${String(err)}</div>`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// DELETE /requests — clear log
|
|
199
|
+
if (method === "DELETE" && path === "/requests") {
|
|
200
|
+
return deps.runPromise(Effect.gen(function* () {
|
|
201
|
+
yield* deps.requestLogger.clear(deps.id);
|
|
202
|
+
return htmlResponse(requestTablePartial([]).value);
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
// GET /requests/:id — detail page
|
|
206
|
+
if (method === "GET" && path.startsWith("/requests/")) {
|
|
207
|
+
const entryId = path.slice("/requests/".length);
|
|
208
|
+
if (entryId && !entryId.includes("/")) {
|
|
209
|
+
return deps.runPromise(Effect.gen(function* () {
|
|
210
|
+
const entry = yield* deps.requestLogger.getEntryById(deps.id, entryId);
|
|
211
|
+
if (entry === null) {
|
|
212
|
+
return htmlResponse("<h1>Request not found</h1>", 404);
|
|
213
|
+
}
|
|
214
|
+
// Try to find matched stub
|
|
215
|
+
let matchedStub = null;
|
|
216
|
+
if (entry.response.matchedStubId) {
|
|
217
|
+
try {
|
|
218
|
+
const stubs = yield* deps.repo.getStubs(deps.id).pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
219
|
+
matchedStub = stubs.find(s => s.id === entry.response.matchedStubId) ?? null;
|
|
220
|
+
} catch {
|
|
221
|
+
// ignore
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return htmlResponse(requestDetailPage({
|
|
225
|
+
config: deps.config,
|
|
226
|
+
entry,
|
|
227
|
+
matchedStub
|
|
228
|
+
}).value);
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// Fallback: 404 within /_admin
|
|
233
|
+
return htmlResponse("<h1>Not Found</h1>", 404);
|
|
234
|
+
};
|
|
235
|
+
//# sourceMappingURL=UiRouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UiRouter.js","names":["Effect","Ref","NonEmptyString","dashboardPage","requestDetailPage","requestsPage","testResultPartial","stubsPage","errorPartial","requestTablePartial","stubListPartial","htmlResponse","body","status","Response","headers","parseStubIdFromPath","path","match","makeUiRouter","deps","request","url","URL","pathname","startsWith","slice","length","method","toUpperCase","runPromise","gen","stubs","get","stubsRef","requestCount","requestLogger","getCount","id","recentRequests","getEntries","limit","config","stubCount","reverse","value","formData","predicatesRaw","responsesRaw","responseMode","predicates","responses","JSON","parse","Array","isArray","stubId","make","crypto","randomUUID","stub","repo","addStub","pipe","catchAll","void","updated","getStubs","succeed","set","deleteStubId","removeStub","putStubId","updateStub","existing","entries","params","searchParams","opts","methodFilter","pathFilter","statusFilter","Number","testMethod","testPath","testBody","testContentType","testHeadersRaw","line","split","colonIdx","indexOf","trim","startTime","Date","now","testResp","fetch","port","duration","respBody","text","respHeaders","forEach","val","key","err","String","clear","entryId","includes","entry","getEntryById","matchedStub","response","matchedStubId","find","s"],"sources":["../../../src/ui/UiRouter.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { html, raw } from "../html";
|
|
2
|
+
export const adminLayout = (opts, content) => html`<!DOCTYPE html>
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
|
+
<title>${opts.title}</title>
|
|
8
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
9
|
+
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
|
10
|
+
</head>
|
|
11
|
+
<body class="bg-gray-50 min-h-screen">
|
|
12
|
+
<nav class="bg-gray-800 text-white shadow-md">
|
|
13
|
+
<div class="max-w-6xl mx-auto px-4 py-3 flex items-center justify-between">
|
|
14
|
+
<div class="flex items-center gap-3">
|
|
15
|
+
<span class="text-lg font-bold">Imposters</span>
|
|
16
|
+
<span class="text-gray-400 text-sm">Admin</span>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="flex gap-3 items-center">
|
|
19
|
+
<a href="/_ui" class="px-3 py-1.5 bg-gray-700 text-white rounded text-sm font-medium hover:bg-gray-600">Dashboard</a>
|
|
20
|
+
<a href="/docs" class="text-gray-400 hover:text-white text-sm">API Docs</a>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</nav>
|
|
24
|
+
<main class="max-w-6xl mx-auto px-4 py-6">
|
|
25
|
+
${raw(content.value)}
|
|
26
|
+
</main>
|
|
27
|
+
</body>
|
|
28
|
+
</html>`;
|
|
29
|
+
//# sourceMappingURL=AdminLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AdminLayout.js","names":["html","raw","adminLayout","opts","content","title","value"],"sources":["../../../../src/ui/admin/AdminLayout.ts"],"sourcesContent":[null],"mappings":"AAAA,SAASA,IAAI,EAAEC,GAAG,QAAQ,SAAS;AAQnC,OAAO,MAAMC,WAAW,GAAGA,CAACC,IAAqB,EAAEC,OAAiB,KAClEJ,IAAI;;;;;WAKKG,IAAI,CAACE,KAAK;;;;;;;;;;;;;;;;;;MAkBfJ,GAAG,CAACG,OAAO,CAACE,KAAK,CAAC;;;QAGhB","ignoreList":[]}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { adminDashboardPage } from "./pages/AdminDashboard";
|
|
2
|
+
import { adminErrorPartial, imposterListPartial, imposterRowPartial } from "./partials";
|
|
3
|
+
const htmlResponse = (body, status = 200) => new Response(body, {
|
|
4
|
+
status,
|
|
5
|
+
headers: {
|
|
6
|
+
"content-type": "text/html; charset=utf-8"
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
const toAdminData = imp => ({
|
|
10
|
+
id: imp.id,
|
|
11
|
+
name: imp.name ?? `imposter-${imp.id}`,
|
|
12
|
+
port: imp.port,
|
|
13
|
+
status: imp.status,
|
|
14
|
+
protocol: imp.protocol ?? "HTTP",
|
|
15
|
+
stubCount: imp.stubs?.length ?? imp.endpointCount ?? imp.stubCount ?? 0
|
|
16
|
+
});
|
|
17
|
+
const fetchImposters = async apiHandler => {
|
|
18
|
+
const resp = await apiHandler(new Request("http://localhost/imposters?limit=50", {
|
|
19
|
+
method: "GET"
|
|
20
|
+
}));
|
|
21
|
+
if (!resp.ok) return [];
|
|
22
|
+
const data = await resp.json();
|
|
23
|
+
const items = Array.isArray(data) ? data : data.imposters ?? data.items ?? [];
|
|
24
|
+
return items.map(toAdminData);
|
|
25
|
+
};
|
|
26
|
+
const fetchImposter = async (apiHandler, id) => {
|
|
27
|
+
const resp = await apiHandler(new Request(`http://localhost/imposters/${id}`, {
|
|
28
|
+
method: "GET"
|
|
29
|
+
}));
|
|
30
|
+
if (!resp.ok) return null;
|
|
31
|
+
const imp = await resp.json();
|
|
32
|
+
return toAdminData(imp);
|
|
33
|
+
};
|
|
34
|
+
export const makeAdminUiRouter = deps => async request => {
|
|
35
|
+
const url = new URL(request.url);
|
|
36
|
+
if (!url.pathname.startsWith("/_ui")) return null;
|
|
37
|
+
const path = url.pathname.slice("/_ui".length) || "/";
|
|
38
|
+
const method = request.method.toUpperCase();
|
|
39
|
+
// GET / — dashboard
|
|
40
|
+
if (method === "GET" && (path === "/" || path === "")) {
|
|
41
|
+
const imposters = await fetchImposters(deps.apiHandler);
|
|
42
|
+
return htmlResponse(adminDashboardPage({
|
|
43
|
+
imposters
|
|
44
|
+
}).value);
|
|
45
|
+
}
|
|
46
|
+
// GET /imposters — HTMX partial (imposter list)
|
|
47
|
+
if (method === "GET" && path === "/imposters") {
|
|
48
|
+
const imposters = await fetchImposters(deps.apiHandler);
|
|
49
|
+
return htmlResponse(imposterListPartial(imposters).value);
|
|
50
|
+
}
|
|
51
|
+
// POST /imposters — create imposter from form
|
|
52
|
+
if (method === "POST" && path === "/imposters") {
|
|
53
|
+
try {
|
|
54
|
+
const formData = await request.formData();
|
|
55
|
+
const name = formData.get("name");
|
|
56
|
+
const portStr = formData.get("port");
|
|
57
|
+
const autoStart = formData.get("autoStart") === "on";
|
|
58
|
+
const payload = {
|
|
59
|
+
protocol: "HTTP"
|
|
60
|
+
};
|
|
61
|
+
if (name && name.trim()) payload.name = name.trim();
|
|
62
|
+
if (portStr && portStr.trim()) payload.port = Number(portStr.trim());
|
|
63
|
+
const createResp = await deps.apiHandler(new Request("http://localhost/imposters", {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: {
|
|
66
|
+
"content-type": "application/json"
|
|
67
|
+
},
|
|
68
|
+
body: JSON.stringify(payload)
|
|
69
|
+
}));
|
|
70
|
+
if (!createResp.ok) {
|
|
71
|
+
const errBody = await createResp.text();
|
|
72
|
+
const imposters = await fetchImposters(deps.apiHandler);
|
|
73
|
+
return htmlResponse(adminErrorPartial(`Failed to create imposter: ${errBody}`).value + imposterListPartial(imposters).value);
|
|
74
|
+
}
|
|
75
|
+
const created = await createResp.json();
|
|
76
|
+
// Auto-start if requested
|
|
77
|
+
if (autoStart) {
|
|
78
|
+
await deps.apiHandler(new Request(`http://localhost/imposters/${created.id}`, {
|
|
79
|
+
method: "PATCH",
|
|
80
|
+
headers: {
|
|
81
|
+
"content-type": "application/json"
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
status: "running"
|
|
85
|
+
})
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
const imposters = await fetchImposters(deps.apiHandler);
|
|
89
|
+
return htmlResponse(imposterListPartial(imposters).value);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
const imposters = await fetchImposters(deps.apiHandler);
|
|
92
|
+
return htmlResponse(adminErrorPartial(`Error: ${String(err)}`).value + imposterListPartial(imposters).value);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// POST /imposters/:id/start
|
|
96
|
+
const startMatch = path.match(/^\/imposters\/([^/]+)\/start$/);
|
|
97
|
+
if (method === "POST" && startMatch) {
|
|
98
|
+
const id = startMatch[1];
|
|
99
|
+
await deps.apiHandler(new Request(`http://localhost/imposters/${id}`, {
|
|
100
|
+
method: "PATCH",
|
|
101
|
+
headers: {
|
|
102
|
+
"content-type": "application/json"
|
|
103
|
+
},
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
status: "running"
|
|
106
|
+
})
|
|
107
|
+
}));
|
|
108
|
+
// Small delay for the server to start
|
|
109
|
+
await new Promise(r => setTimeout(r, 100));
|
|
110
|
+
const imp = await fetchImposter(deps.apiHandler, id);
|
|
111
|
+
if (!imp) {
|
|
112
|
+
return htmlResponse(adminErrorPartial("Imposter not found").value, 404);
|
|
113
|
+
}
|
|
114
|
+
return htmlResponse(imposterRowPartial(imp).value);
|
|
115
|
+
}
|
|
116
|
+
// POST /imposters/:id/stop
|
|
117
|
+
const stopMatch = path.match(/^\/imposters\/([^/]+)\/stop$/);
|
|
118
|
+
if (method === "POST" && stopMatch) {
|
|
119
|
+
const id = stopMatch[1];
|
|
120
|
+
await deps.apiHandler(new Request(`http://localhost/imposters/${id}`, {
|
|
121
|
+
method: "PATCH",
|
|
122
|
+
headers: {
|
|
123
|
+
"content-type": "application/json"
|
|
124
|
+
},
|
|
125
|
+
body: JSON.stringify({
|
|
126
|
+
status: "stopped"
|
|
127
|
+
})
|
|
128
|
+
}));
|
|
129
|
+
await new Promise(r => setTimeout(r, 100));
|
|
130
|
+
const imp = await fetchImposter(deps.apiHandler, id);
|
|
131
|
+
if (!imp) {
|
|
132
|
+
return htmlResponse(adminErrorPartial("Imposter not found").value, 404);
|
|
133
|
+
}
|
|
134
|
+
return htmlResponse(imposterRowPartial(imp).value);
|
|
135
|
+
}
|
|
136
|
+
// DELETE /imposters/:id
|
|
137
|
+
const deleteMatch = path.match(/^\/imposters\/([^/]+)$/);
|
|
138
|
+
if (method === "DELETE" && deleteMatch) {
|
|
139
|
+
const id = deleteMatch[1];
|
|
140
|
+
await deps.apiHandler(new Request(`http://localhost/imposters/${id}?force=true`, {
|
|
141
|
+
method: "DELETE"
|
|
142
|
+
}));
|
|
143
|
+
const imposters = await fetchImposters(deps.apiHandler);
|
|
144
|
+
return htmlResponse(imposterListPartial(imposters).value);
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=AdminUiRouter.js.map
|