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,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
|
+
})
|