@c9up/ream 0.1.3
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/README.md +70 -0
- package/dist/Application.d.ts +71 -0
- package/dist/Application.d.ts.map +1 -0
- package/dist/Application.js +151 -0
- package/dist/Application.js.map +1 -0
- package/dist/Codemods.d.ts +16 -0
- package/dist/Codemods.d.ts.map +1 -0
- package/dist/Codemods.js +277 -0
- package/dist/Codemods.js.map +1 -0
- package/dist/ConfigLoader.d.ts +29 -0
- package/dist/ConfigLoader.d.ts.map +1 -0
- package/dist/ConfigLoader.js +43 -0
- package/dist/ConfigLoader.js.map +1 -0
- package/dist/ErrorBoundary.d.ts +51 -0
- package/dist/ErrorBoundary.d.ts.map +1 -0
- package/dist/ErrorBoundary.js +122 -0
- package/dist/ErrorBoundary.js.map +1 -0
- package/dist/GracefulShutdown.d.ts +28 -0
- package/dist/GracefulShutdown.d.ts.map +1 -0
- package/dist/GracefulShutdown.js +55 -0
- package/dist/GracefulShutdown.js.map +1 -0
- package/dist/HealthCheck.d.ts +42 -0
- package/dist/HealthCheck.d.ts.map +1 -0
- package/dist/HealthCheck.js +65 -0
- package/dist/HealthCheck.js.map +1 -0
- package/dist/HotReload.d.ts +28 -0
- package/dist/HotReload.d.ts.map +1 -0
- package/dist/HotReload.js +60 -0
- package/dist/HotReload.js.map +1 -0
- package/dist/HttpKernel.d.ts +62 -0
- package/dist/HttpKernel.d.ts.map +1 -0
- package/dist/HttpKernel.js +210 -0
- package/dist/HttpKernel.js.map +1 -0
- package/dist/Ignitor.d.ts +195 -0
- package/dist/Ignitor.d.ts.map +1 -0
- package/dist/Ignitor.js +505 -0
- package/dist/Ignitor.js.map +1 -0
- package/dist/Provider.d.ts +54 -0
- package/dist/Provider.d.ts.map +1 -0
- package/dist/Provider.js +37 -0
- package/dist/Provider.js.map +1 -0
- package/dist/bodyparser/BodyParserMiddleware.d.ts +38 -0
- package/dist/bodyparser/BodyParserMiddleware.d.ts.map +1 -0
- package/dist/bodyparser/BodyParserMiddleware.js +143 -0
- package/dist/bodyparser/BodyParserMiddleware.js.map +1 -0
- package/dist/bodyparser/MultipartFile.d.ts +77 -0
- package/dist/bodyparser/MultipartFile.d.ts.map +1 -0
- package/dist/bodyparser/MultipartFile.js +138 -0
- package/dist/bodyparser/MultipartFile.js.map +1 -0
- package/dist/bodyparser/parseSize.d.ts +6 -0
- package/dist/bodyparser/parseSize.d.ts.map +1 -0
- package/dist/bodyparser/parseSize.js +21 -0
- package/dist/bodyparser/parseSize.js.map +1 -0
- package/dist/bootstrap/hyper-server.d.ts +27 -0
- package/dist/bootstrap/hyper-server.d.ts.map +1 -0
- package/dist/bootstrap/hyper-server.js +63 -0
- package/dist/bootstrap/hyper-server.js.map +1 -0
- package/dist/console/CommandRunner.d.ts +18 -0
- package/dist/console/CommandRunner.d.ts.map +1 -0
- package/dist/console/CommandRunner.js +65 -0
- package/dist/console/CommandRunner.js.map +1 -0
- package/dist/container/Container.d.ts +60 -0
- package/dist/container/Container.d.ts.map +1 -0
- package/dist/container/Container.js +329 -0
- package/dist/container/Container.js.map +1 -0
- package/dist/container/types.d.ts +25 -0
- package/dist/container/types.d.ts.map +1 -0
- package/dist/container/types.js +6 -0
- package/dist/container/types.js.map +1 -0
- package/dist/decorators/Lazy.d.ts +19 -0
- package/dist/decorators/Lazy.d.ts.map +1 -0
- package/dist/decorators/Lazy.js +53 -0
- package/dist/decorators/Lazy.js.map +1 -0
- package/dist/decorators/Service.d.ts +48 -0
- package/dist/decorators/Service.d.ts.map +1 -0
- package/dist/decorators/Service.js +70 -0
- package/dist/decorators/Service.js.map +1 -0
- package/dist/errors/FuzzyMatcher.d.ts +41 -0
- package/dist/errors/FuzzyMatcher.d.ts.map +1 -0
- package/dist/errors/FuzzyMatcher.js +81 -0
- package/dist/errors/FuzzyMatcher.js.map +1 -0
- package/dist/errors/ModuleErrors.d.ts +41 -0
- package/dist/errors/ModuleErrors.d.ts.map +1 -0
- package/dist/errors/ModuleErrors.js +65 -0
- package/dist/errors/ModuleErrors.js.map +1 -0
- package/dist/errors/PipelineStageError.d.ts +31 -0
- package/dist/errors/PipelineStageError.d.ts.map +1 -0
- package/dist/errors/PipelineStageError.js +77 -0
- package/dist/errors/PipelineStageError.js.map +1 -0
- package/dist/errors/ReamError.d.ts +50 -0
- package/dist/errors/ReamError.d.ts.map +1 -0
- package/dist/errors/ReamError.js +108 -0
- package/dist/errors/ReamError.js.map +1 -0
- package/dist/events/Emitter.d.ts +124 -0
- package/dist/events/Emitter.d.ts.map +1 -0
- package/dist/events/Emitter.js +271 -0
- package/dist/events/Emitter.js.map +1 -0
- package/dist/events/EventsProvider.d.ts +28 -0
- package/dist/events/EventsProvider.d.ts.map +1 -0
- package/dist/events/EventsProvider.js +36 -0
- package/dist/events/EventsProvider.js.map +1 -0
- package/dist/events/config.d.ts +8 -0
- package/dist/events/config.d.ts.map +1 -0
- package/dist/events/config.js +4 -0
- package/dist/events/config.js.map +1 -0
- package/dist/events/configure.d.ts +10 -0
- package/dist/events/configure.d.ts.map +1 -0
- package/dist/events/configure.js +11 -0
- package/dist/events/configure.js.map +1 -0
- package/dist/events/helix/helpers.d.ts +62 -0
- package/dist/events/helix/helpers.d.ts.map +1 -0
- package/dist/events/helix/helpers.js +187 -0
- package/dist/events/helix/helpers.js.map +1 -0
- package/dist/events/index.d.ts +17 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +15 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/native.d.ts +21 -0
- package/dist/events/native.d.ts.map +1 -0
- package/dist/events/native.js +17 -0
- package/dist/events/native.js.map +1 -0
- package/dist/events/services/main.d.ts +18 -0
- package/dist/events/services/main.d.ts.map +1 -0
- package/dist/events/services/main.js +31 -0
- package/dist/events/services/main.js.map +1 -0
- package/dist/events/testing/FakeBus.d.ts +66 -0
- package/dist/events/testing/FakeBus.d.ts.map +1 -0
- package/dist/events/testing/FakeBus.js +356 -0
- package/dist/events/testing/FakeBus.js.map +1 -0
- package/dist/graphql/GraphQLEngine.d.ts +69 -0
- package/dist/graphql/GraphQLEngine.d.ts.map +1 -0
- package/dist/graphql/GraphQLEngine.js +309 -0
- package/dist/graphql/GraphQLEngine.js.map +1 -0
- package/dist/graphql/GraphQLProvider.d.ts +32 -0
- package/dist/graphql/GraphQLProvider.d.ts.map +1 -0
- package/dist/graphql/GraphQLProvider.js +49 -0
- package/dist/graphql/GraphQLProvider.js.map +1 -0
- package/dist/helpers/base64.d.ts +40 -0
- package/dist/helpers/base64.d.ts.map +1 -0
- package/dist/helpers/base64.js +70 -0
- package/dist/helpers/base64.js.map +1 -0
- package/dist/helpers/index.d.ts +7 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +5 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/is.d.ts +101 -0
- package/dist/helpers/is.d.ts.map +1 -0
- package/dist/helpers/is.js +151 -0
- package/dist/helpers/is.js.map +1 -0
- package/dist/helpers/napi-loader.d.ts +46 -0
- package/dist/helpers/napi-loader.d.ts.map +1 -0
- package/dist/helpers/napi-loader.js +69 -0
- package/dist/helpers/napi-loader.js.map +1 -0
- package/dist/helpers/safeEqual.d.ts +9 -0
- package/dist/helpers/safeEqual.d.ts.map +1 -0
- package/dist/helpers/safeEqual.js +16 -0
- package/dist/helpers/safeEqual.js.map +1 -0
- package/dist/helpers/string.d.ts +161 -0
- package/dist/helpers/string.d.ts.map +1 -0
- package/dist/helpers/string.js +429 -0
- package/dist/helpers/string.js.map +1 -0
- package/dist/helpers/types.d.ts +83 -0
- package/dist/helpers/types.d.ts.map +1 -0
- package/dist/helpers/types.js +6 -0
- package/dist/helpers/types.js.map +1 -0
- package/dist/http/Exception.d.ts +103 -0
- package/dist/http/Exception.d.ts.map +1 -0
- package/dist/http/Exception.js +228 -0
- package/dist/http/Exception.js.map +1 -0
- package/dist/http/HttpContext.d.ts +69 -0
- package/dist/http/HttpContext.d.ts.map +1 -0
- package/dist/http/HttpContext.js +55 -0
- package/dist/http/HttpContext.js.map +1 -0
- package/dist/http/RedirectBuilder.d.ts +31 -0
- package/dist/http/RedirectBuilder.d.ts.map +1 -0
- package/dist/http/RedirectBuilder.js +75 -0
- package/dist/http/RedirectBuilder.js.map +1 -0
- package/dist/http/Request.d.ts +160 -0
- package/dist/http/Request.d.ts.map +1 -0
- package/dist/http/Request.js +484 -0
- package/dist/http/Request.js.map +1 -0
- package/dist/http/Response.d.ts +114 -0
- package/dist/http/Response.d.ts.map +1 -0
- package/dist/http/Response.js +303 -0
- package/dist/http/Response.js.map +1 -0
- package/dist/http/SseStream.d.ts +89 -0
- package/dist/http/SseStream.d.ts.map +1 -0
- package/dist/http/SseStream.js +228 -0
- package/dist/http/SseStream.js.map +1 -0
- package/dist/http/urlDecode.d.ts +8 -0
- package/dist/http/urlDecode.d.ts.map +1 -0
- package/dist/http/urlDecode.js +18 -0
- package/dist/http/urlDecode.js.map +1 -0
- package/dist/index.d.ts +92 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/Pipeline.d.ts +36 -0
- package/dist/middleware/Pipeline.d.ts.map +1 -0
- package/dist/middleware/Pipeline.js +104 -0
- package/dist/middleware/Pipeline.js.map +1 -0
- package/dist/openapi/OpenApiGenerator.d.ts +58 -0
- package/dist/openapi/OpenApiGenerator.d.ts.map +1 -0
- package/dist/openapi/OpenApiGenerator.js +192 -0
- package/dist/openapi/OpenApiGenerator.js.map +1 -0
- package/dist/openapi/OpenApiMiddleware.d.ts +24 -0
- package/dist/openapi/OpenApiMiddleware.d.ts.map +1 -0
- package/dist/openapi/OpenApiMiddleware.js +78 -0
- package/dist/openapi/OpenApiMiddleware.js.map +1 -0
- package/dist/openapi/OpenApiProvider.d.ts +36 -0
- package/dist/openapi/OpenApiProvider.d.ts.map +1 -0
- package/dist/openapi/OpenApiProvider.js +42 -0
- package/dist/openapi/OpenApiProvider.js.map +1 -0
- package/dist/resources/ApiResource.d.ts +45 -0
- package/dist/resources/ApiResource.d.ts.map +1 -0
- package/dist/resources/ApiResource.js +58 -0
- package/dist/resources/ApiResource.js.map +1 -0
- package/dist/router/Router.d.ts +205 -0
- package/dist/router/Router.d.ts.map +1 -0
- package/dist/router/Router.js +552 -0
- package/dist/router/Router.js.map +1 -0
- package/dist/rpc/RpcProvider.d.ts +34 -0
- package/dist/rpc/RpcProvider.d.ts.map +1 -0
- package/dist/rpc/RpcProvider.js +37 -0
- package/dist/rpc/RpcProvider.js.map +1 -0
- package/dist/rpc/RpcRouter.d.ts +61 -0
- package/dist/rpc/RpcRouter.d.ts.map +1 -0
- package/dist/rpc/RpcRouter.js +187 -0
- package/dist/rpc/RpcRouter.js.map +1 -0
- package/dist/scheduler/Schedule.d.ts +53 -0
- package/dist/scheduler/Schedule.d.ts.map +1 -0
- package/dist/scheduler/Schedule.js +76 -0
- package/dist/scheduler/Schedule.js.map +1 -0
- package/dist/scheduler/ScheduleProvider.d.ts +56 -0
- package/dist/scheduler/ScheduleProvider.d.ts.map +1 -0
- package/dist/scheduler/ScheduleProvider.js +145 -0
- package/dist/scheduler/ScheduleProvider.js.map +1 -0
- package/dist/scheduler/Scheduler.d.ts +209 -0
- package/dist/scheduler/Scheduler.d.ts.map +1 -0
- package/dist/scheduler/Scheduler.js +432 -0
- package/dist/scheduler/Scheduler.js.map +1 -0
- package/dist/scheduler/index.d.ts +14 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/index.js +6 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/locks/LockBackend.d.ts +37 -0
- package/dist/scheduler/locks/LockBackend.d.ts.map +1 -0
- package/dist/scheduler/locks/LockBackend.js +13 -0
- package/dist/scheduler/locks/LockBackend.js.map +1 -0
- package/dist/scheduler/locks/MemoryLockBackend.d.ts +39 -0
- package/dist/scheduler/locks/MemoryLockBackend.d.ts.map +1 -0
- package/dist/scheduler/locks/MemoryLockBackend.js +71 -0
- package/dist/scheduler/locks/MemoryLockBackend.js.map +1 -0
- package/dist/scheduler/observability/ErrorReporter.d.ts +15 -0
- package/dist/scheduler/observability/ErrorReporter.d.ts.map +1 -0
- package/dist/scheduler/observability/ErrorReporter.js +13 -0
- package/dist/scheduler/observability/ErrorReporter.js.map +1 -0
- package/dist/scheduler/observability/ScheduleEvent.d.ts +57 -0
- package/dist/scheduler/observability/ScheduleEvent.d.ts.map +1 -0
- package/dist/scheduler/observability/ScheduleEvent.js +12 -0
- package/dist/scheduler/observability/ScheduleEvent.js.map +1 -0
- package/dist/scheduler/observability/ScheduleEventSink.d.ts +20 -0
- package/dist/scheduler/observability/ScheduleEventSink.d.ts.map +1 -0
- package/dist/scheduler/observability/ScheduleEventSink.js +17 -0
- package/dist/scheduler/observability/ScheduleEventSink.js.map +1 -0
- package/dist/scheduler/observability/StatsTracker.d.ts +50 -0
- package/dist/scheduler/observability/StatsTracker.d.ts.map +1 -0
- package/dist/scheduler/observability/StatsTracker.js +96 -0
- package/dist/scheduler/observability/StatsTracker.js.map +1 -0
- package/dist/security/CookieSigner.d.ts +13 -0
- package/dist/security/CookieSigner.d.ts.map +1 -0
- package/dist/security/CookieSigner.js +51 -0
- package/dist/security/CookieSigner.js.map +1 -0
- package/dist/security/SignedUrl.d.ts +17 -0
- package/dist/security/SignedUrl.d.ts.map +1 -0
- package/dist/security/SignedUrl.js +63 -0
- package/dist/security/SignedUrl.js.map +1 -0
- package/dist/security/crypto.d.ts +25 -0
- package/dist/security/crypto.d.ts.map +1 -0
- package/dist/security/crypto.js +82 -0
- package/dist/security/crypto.js.map +1 -0
- package/dist/server/HyperServer.d.ts +64 -0
- package/dist/server/HyperServer.d.ts.map +1 -0
- package/dist/server/HyperServer.js +54 -0
- package/dist/server/HyperServer.js.map +1 -0
- package/dist/server/Server.d.ts +68 -0
- package/dist/server/Server.d.ts.map +1 -0
- package/dist/server/Server.js +113 -0
- package/dist/server/Server.js.map +1 -0
- package/dist/services/app.d.ts +14 -0
- package/dist/services/app.d.ts.map +1 -0
- package/dist/services/app.js +27 -0
- package/dist/services/app.js.map +1 -0
- package/dist/services/router.d.ts +21 -0
- package/dist/services/router.d.ts.map +1 -0
- package/dist/services/router.js +36 -0
- package/dist/services/router.js.map +1 -0
- package/dist/services/server.d.ts +16 -0
- package/dist/services/server.d.ts.map +1 -0
- package/dist/services/server.js +32 -0
- package/dist/services/server.js.map +1 -0
- package/dist/session/Session.d.ts +88 -0
- package/dist/session/Session.d.ts.map +1 -0
- package/dist/session/Session.js +186 -0
- package/dist/session/Session.js.map +1 -0
- package/dist/session/SessionMiddleware.d.ts +16 -0
- package/dist/session/SessionMiddleware.d.ts.map +1 -0
- package/dist/session/SessionMiddleware.js +132 -0
- package/dist/session/SessionMiddleware.js.map +1 -0
- package/dist/session/drivers/CookieDriver.d.ts +17 -0
- package/dist/session/drivers/CookieDriver.d.ts.map +1 -0
- package/dist/session/drivers/CookieDriver.js +42 -0
- package/dist/session/drivers/CookieDriver.js.map +1 -0
- package/dist/session/drivers/MemoryDriver.d.ts +9 -0
- package/dist/session/drivers/MemoryDriver.d.ts.map +1 -0
- package/dist/session/drivers/MemoryDriver.js +24 -0
- package/dist/session/drivers/MemoryDriver.js.map +1 -0
- package/dist/storage/S3Driver.d.ts +27 -0
- package/dist/storage/S3Driver.d.ts.map +1 -0
- package/dist/storage/S3Driver.js +135 -0
- package/dist/storage/S3Driver.js.map +1 -0
- package/dist/storage/StaticMiddleware.d.ts +27 -0
- package/dist/storage/StaticMiddleware.d.ts.map +1 -0
- package/dist/storage/StaticMiddleware.js +153 -0
- package/dist/storage/StaticMiddleware.js.map +1 -0
- package/dist/storage/StaticProvider.d.ts +26 -0
- package/dist/storage/StaticProvider.d.ts.map +1 -0
- package/dist/storage/StaticProvider.js +31 -0
- package/dist/storage/StaticProvider.js.map +1 -0
- package/dist/storage/StorageManager.d.ts +31 -0
- package/dist/storage/StorageManager.d.ts.map +1 -0
- package/dist/storage/StorageManager.js +120 -0
- package/dist/storage/StorageManager.js.map +1 -0
- package/dist/testing/RequestBuilder.d.ts +67 -0
- package/dist/testing/RequestBuilder.d.ts.map +1 -0
- package/dist/testing/RequestBuilder.js +202 -0
- package/dist/testing/RequestBuilder.js.map +1 -0
- package/dist/testing/TestClient.d.ts +96 -0
- package/dist/testing/TestClient.d.ts.map +1 -0
- package/dist/testing/TestClient.js +263 -0
- package/dist/testing/TestClient.js.map +1 -0
- package/dist/types/helpers.d.ts +47 -0
- package/dist/types/helpers.d.ts.map +1 -0
- package/dist/types/helpers.js +5 -0
- package/dist/types/helpers.js.map +1 -0
- package/events.darwin-arm64.node +0 -0
- package/events.darwin-x64.node +0 -0
- package/events.linux-arm64-gnu.node +0 -0
- package/events.linux-x64-gnu.node +0 -0
- package/events.win32-x64-msvc.node +0 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +106 -0
- package/scheduler.darwin-arm64.node +0 -0
- package/scheduler.darwin-x64.node +0 -0
- package/scheduler.linux-arm64-gnu.node +0 -0
- package/scheduler.linux-x64-gnu.node +0 -0
- package/scheduler.win32-x64-msvc.node +0 -0
- package/scripts/copy-napi.mjs +113 -0
- package/src/Application.ts +177 -0
- package/src/Codemods.ts +343 -0
- package/src/ConfigLoader.ts +49 -0
- package/src/ErrorBoundary.ts +157 -0
- package/src/GracefulShutdown.ts +80 -0
- package/src/HealthCheck.ts +96 -0
- package/src/HotReload.ts +78 -0
- package/src/HttpKernel.ts +305 -0
- package/src/Ignitor.ts +656 -0
- package/src/Provider.ts +74 -0
- package/src/bodyparser/BodyParserMiddleware.ts +183 -0
- package/src/bodyparser/MultipartFile.ts +181 -0
- package/src/bodyparser/parseSize.ts +19 -0
- package/src/bootstrap/hyper-server.ts +78 -0
- package/src/console/CommandRunner.ts +82 -0
- package/src/container/Container.ts +383 -0
- package/src/container/types.ts +30 -0
- package/src/decorators/Lazy.ts +57 -0
- package/src/decorators/Service.ts +85 -0
- package/src/errors/FuzzyMatcher.ts +93 -0
- package/src/errors/ModuleErrors.ts +73 -0
- package/src/errors/PipelineStageError.ts +97 -0
- package/src/errors/ReamError.ts +138 -0
- package/src/events/Emitter.ts +333 -0
- package/src/events/EventsProvider.ts +58 -0
- package/src/events/config.ts +10 -0
- package/src/events/configure.ts +19 -0
- package/src/events/helix/helpers.ts +260 -0
- package/src/events/index.ts +22 -0
- package/src/events/native.ts +37 -0
- package/src/events/services/main.ts +39 -0
- package/src/events/testing/FakeBus.ts +420 -0
- package/src/graphql/GraphQLEngine.ts +421 -0
- package/src/graphql/GraphQLProvider.ts +59 -0
- package/src/helpers/base64.ts +72 -0
- package/src/helpers/index.ts +6 -0
- package/src/helpers/is.ts +162 -0
- package/src/helpers/napi-loader.ts +106 -0
- package/src/helpers/safeEqual.ts +15 -0
- package/src/helpers/string.ts +469 -0
- package/src/helpers/types.ts +100 -0
- package/src/http/Exception.ts +272 -0
- package/src/http/HttpContext.ts +108 -0
- package/src/http/RedirectBuilder.ts +89 -0
- package/src/http/Request.ts +540 -0
- package/src/http/Response.ts +351 -0
- package/src/http/SseStream.ts +250 -0
- package/src/http/urlDecode.ts +18 -0
- package/src/index.ts +263 -0
- package/src/middleware/Pipeline.ts +136 -0
- package/src/openapi/OpenApiGenerator.ts +254 -0
- package/src/openapi/OpenApiMiddleware.ts +101 -0
- package/src/openapi/OpenApiProvider.ts +67 -0
- package/src/resources/ApiResource.ts +79 -0
- package/src/router/Router.ts +733 -0
- package/src/rpc/RpcProvider.ts +54 -0
- package/src/rpc/RpcRouter.ts +243 -0
- package/src/scheduler/Schedule.ts +96 -0
- package/src/scheduler/ScheduleProvider.ts +177 -0
- package/src/scheduler/Scheduler.ts +601 -0
- package/src/scheduler/index.ts +24 -0
- package/src/scheduler/locks/LockBackend.ts +38 -0
- package/src/scheduler/locks/MemoryLockBackend.ts +85 -0
- package/src/scheduler/observability/ErrorReporter.ts +13 -0
- package/src/scheduler/observability/ScheduleEvent.ts +61 -0
- package/src/scheduler/observability/ScheduleEventSink.ts +21 -0
- package/src/scheduler/observability/StatsTracker.ts +134 -0
- package/src/security/CookieSigner.ts +54 -0
- package/src/security/SignedUrl.ts +71 -0
- package/src/security/crypto.ts +98 -0
- package/src/server/HyperServer.ts +121 -0
- package/src/server/Server.ts +147 -0
- package/src/services/app.ts +34 -0
- package/src/services/router.ts +44 -0
- package/src/services/server.ts +40 -0
- package/src/session/Session.ts +231 -0
- package/src/session/SessionMiddleware.ts +153 -0
- package/src/session/drivers/CookieDriver.ts +48 -0
- package/src/session/drivers/MemoryDriver.ts +29 -0
- package/src/storage/S3Driver.ts +171 -0
- package/src/storage/StaticMiddleware.ts +182 -0
- package/src/storage/StaticProvider.ts +40 -0
- package/src/storage/StorageManager.ts +142 -0
- package/src/testing/RequestBuilder.ts +275 -0
- package/src/testing/TestClient.ts +344 -0
- package/src/types/helpers.ts +52 -0
- package/swcrc.app.json +17 -0
- package/tsconfig.app.json +21 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IoC Container — AdonisJS Fold-compatible dependency injection.
|
|
3
|
+
*
|
|
4
|
+
* - make(Class) auto-constructs any class with @inject(), no explicit binding needed
|
|
5
|
+
* - call(instance, 'method') for method-level injection
|
|
6
|
+
* - singleton() / bind() for explicit factory bindings
|
|
7
|
+
* - swap() / restore() for testing
|
|
8
|
+
*
|
|
9
|
+
* @implements FR11, FR12, FR14, FR16
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import 'reflect-metadata'
|
|
13
|
+
import { createLazyProxy, getLazyParams } from '../decorators/Lazy.js'
|
|
14
|
+
import { getInjectTokens, getServiceMetadata, getServiceRegistry } from '../decorators/Service.js'
|
|
15
|
+
import { didYouMean } from '../errors/FuzzyMatcher.js'
|
|
16
|
+
import { ReamError } from '../errors/ReamError.js'
|
|
17
|
+
import type { Binding, ServiceFactory, ServiceToken } from './types.js'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Symbol descriptions that would collide with object-prototype keys if used
|
|
21
|
+
* as raw map keys. We reject them on registration so tokens cannot be used
|
|
22
|
+
* as a prototype-pollution vector.
|
|
23
|
+
*/
|
|
24
|
+
const RESERVED_TOKEN_NAMES = new Set(['__proto__', 'constructor', 'prototype'])
|
|
25
|
+
|
|
26
|
+
export class Container {
|
|
27
|
+
#bindings: Map<string, Binding> = new Map()
|
|
28
|
+
#singletons: Map<string, unknown> = new Map()
|
|
29
|
+
#overrides: Map<string, ServiceFactory> = new Map()
|
|
30
|
+
/**
|
|
31
|
+
* Snapshot of singletons taken when `swap`/`override` shadows a previously
|
|
32
|
+
* resolved instance. `restore()` re-seats them without re-invoking the
|
|
33
|
+
* original factory, so callers that captured the live reference keep
|
|
34
|
+
* pointing at the same object after the test ends.
|
|
35
|
+
*/
|
|
36
|
+
#singletonBackup: Map<string, unknown> = new Map()
|
|
37
|
+
#resolutionStack: string[] = []
|
|
38
|
+
#resolutionSet: Set<string> = new Set()
|
|
39
|
+
|
|
40
|
+
// ─── Explicit bindings ────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
/** Register a singleton binding (factory called once, cached). */
|
|
43
|
+
singleton<_T>(token: ServiceToken, factory: ServiceFactory): void {
|
|
44
|
+
const key = this.#tokenToKey(token)
|
|
45
|
+
this.#bindings.set(key, { token, factory, scope: 'singleton', dependencies: [] })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Register a transient binding (new instance per resolve). */
|
|
49
|
+
bind<_T>(token: ServiceToken, factory: ServiceFactory): void {
|
|
50
|
+
const key = this.#tokenToKey(token)
|
|
51
|
+
this.#bindings.set(key, { token, factory, scope: 'transient', dependencies: [] })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Bind an existing value directly. */
|
|
55
|
+
bindValue<T>(token: ServiceToken, value: T): void {
|
|
56
|
+
const key = this.#tokenToKey(token)
|
|
57
|
+
this.#singletons.set(key, value)
|
|
58
|
+
this.#bindings.set(key, { token, factory: () => value, scope: 'singleton', dependencies: [] })
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ─── Testing ──────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Replace a binding's factory for testing (AdonisJS-compatible swap).
|
|
65
|
+
* Snapshots the existing singleton (if any) so `restore()` re-seats the
|
|
66
|
+
* original instance instead of re-invoking the underlying factory.
|
|
67
|
+
*/
|
|
68
|
+
swap(token: ServiceToken, factory: ServiceFactory): void {
|
|
69
|
+
const key = this.#tokenToKey(token)
|
|
70
|
+
if (!this.#overrides.has(key) && this.#singletons.has(key)) {
|
|
71
|
+
this.#singletonBackup.set(key, this.#singletons.get(key))
|
|
72
|
+
}
|
|
73
|
+
this.#overrides.set(key, factory)
|
|
74
|
+
this.#singletons.delete(key)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Replace a binding with a literal value for testing. The value is wrapped
|
|
79
|
+
* as a factory internally, so each `resolve()` call returns it unchanged.
|
|
80
|
+
* Pair with `restore()` to undo without losing the original singleton's
|
|
81
|
+
* identity.
|
|
82
|
+
*/
|
|
83
|
+
override<T>(token: ServiceToken, value: T): void {
|
|
84
|
+
this.swap(token, () => value)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Restore a specific swap, or all swaps if no token given. */
|
|
88
|
+
restore(token?: ServiceToken): void {
|
|
89
|
+
if (token) {
|
|
90
|
+
this.#restoreOne(this.#tokenToKey(token))
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
for (const key of [...this.#overrides.keys()]) {
|
|
94
|
+
this.#restoreOne(key)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#restoreOne(key: string): void {
|
|
99
|
+
this.#overrides.delete(key)
|
|
100
|
+
this.#singletons.delete(key)
|
|
101
|
+
if (this.#singletonBackup.has(key)) {
|
|
102
|
+
this.#singletons.set(key, this.#singletonBackup.get(key))
|
|
103
|
+
this.#singletonBackup.delete(key)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ─── Resolution ───────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Resolve/construct a class or binding.
|
|
111
|
+
* Like AdonisJS `container.make()`:
|
|
112
|
+
* 1. Check swaps (test overrides)
|
|
113
|
+
* 2. Check cached singletons
|
|
114
|
+
* 3. Check explicit bindings (singleton/bind)
|
|
115
|
+
* 4. Auto-construct if class has @inject() or @Service()
|
|
116
|
+
* 5. Auto-construct any class (plain `new Class()`) as fallback
|
|
117
|
+
*/
|
|
118
|
+
make<T>(token: ServiceToken): T {
|
|
119
|
+
return this.resolve<T>(token)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Alias for make() — backward compatible. */
|
|
123
|
+
resolve<T>(token: ServiceToken): T {
|
|
124
|
+
const key = this.#tokenToKey(token)
|
|
125
|
+
|
|
126
|
+
if (this.#resolutionSet.has(key)) {
|
|
127
|
+
const cycle = [...this.#resolutionStack, key].join(' → ')
|
|
128
|
+
throw new ReamError('CIRCULAR_DEPENDENCY', `Circular dependency detected: ${cycle}`, {
|
|
129
|
+
hint: 'Use @Lazy() on one of the constructor parameters to break the cycle.',
|
|
130
|
+
context: { chain: cycle },
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
this.#resolutionStack.push(key)
|
|
134
|
+
this.#resolutionSet.add(key)
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
return this.#resolveInner<T>(key, token)
|
|
138
|
+
} finally {
|
|
139
|
+
this.#resolutionStack.pop()
|
|
140
|
+
this.#resolutionSet.delete(key)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Call a method on an instance with dependency injection.
|
|
146
|
+
* Like AdonisJS `container.call()`.
|
|
147
|
+
* Resolves method parameters from reflect-metadata and @inject().
|
|
148
|
+
*/
|
|
149
|
+
async call<T, K extends string & keyof T>(
|
|
150
|
+
instance: T,
|
|
151
|
+
method: K,
|
|
152
|
+
runtimeValues?: unknown[],
|
|
153
|
+
): Promise<unknown> {
|
|
154
|
+
type Ctor = new (...args: unknown[]) => unknown
|
|
155
|
+
// `Object.getPrototypeOf` is typed `any` in lib.dom, so the assignment
|
|
156
|
+
// narrows it back to a typed constructor without a cast expression.
|
|
157
|
+
const target: Ctor = Object.getPrototypeOf(instance).constructor
|
|
158
|
+
const paramTypes: unknown[] =
|
|
159
|
+
Reflect.getMetadata('design:paramtypes', target.prototype, method) ?? []
|
|
160
|
+
|
|
161
|
+
const args = paramTypes.map((type, index) => {
|
|
162
|
+
// Runtime values take precedence
|
|
163
|
+
if (runtimeValues && index < runtimeValues.length) {
|
|
164
|
+
return runtimeValues[index]
|
|
165
|
+
}
|
|
166
|
+
// Auto-resolve class dependencies — type is a constructor function here
|
|
167
|
+
if (isClassConstructor(type)) {
|
|
168
|
+
return this.resolve(type)
|
|
169
|
+
}
|
|
170
|
+
return undefined
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
const member: unknown = instance[method]
|
|
174
|
+
if (!isCallable(member)) {
|
|
175
|
+
throw new Error(`Container.call: '${method}' is not a callable method`)
|
|
176
|
+
}
|
|
177
|
+
return member.apply(instance, args)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ─── Introspection ────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
/** Check if a token is registered or resolvable. */
|
|
183
|
+
has(token: ServiceToken): boolean {
|
|
184
|
+
const key = this.#tokenToKey(token)
|
|
185
|
+
return (
|
|
186
|
+
this.#bindings.has(key) ||
|
|
187
|
+
this.#overrides.has(key) ||
|
|
188
|
+
this.#singletons.has(key) ||
|
|
189
|
+
(typeof token === 'function' && getServiceMetadata(token) !== undefined)
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
get size(): number {
|
|
194
|
+
return this.#bindings.size
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Auto-register all @Service() decorated classes from the global registry. */
|
|
198
|
+
autoRegister(): void {
|
|
199
|
+
for (const [target, metadata] of getServiceRegistry()) {
|
|
200
|
+
const key = metadata.as ?? target.name
|
|
201
|
+
if (!this.#bindings.has(key)) {
|
|
202
|
+
const targetClass = target
|
|
203
|
+
this.#bindings.set(key, {
|
|
204
|
+
token: metadata.as ?? target,
|
|
205
|
+
factory: () => this.#autoConstruct(targetClass),
|
|
206
|
+
scope: metadata.scope,
|
|
207
|
+
dependencies: [],
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ─── Internal resolution ──────────────────────────────────
|
|
214
|
+
|
|
215
|
+
#resolveInner<T>(key: string, token: ServiceToken): T {
|
|
216
|
+
// 1. Check swaps (test overrides)
|
|
217
|
+
if (this.#overrides.has(key)) {
|
|
218
|
+
// biome-ignore lint/suspicious/noExplicitAny: IoC container factory returns unknown; caller brands via T
|
|
219
|
+
return this.#overrides.get(key)?.() as any as T
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 2. Check cached singletons
|
|
223
|
+
if (this.#singletons.has(key)) {
|
|
224
|
+
// biome-ignore lint/suspicious/noExplicitAny: IoC singleton stored as unknown; caller brands via T
|
|
225
|
+
return this.#singletons.get(key) as any as T
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// 3. Check explicit bindings
|
|
229
|
+
const binding = this.#bindings.get(key)
|
|
230
|
+
if (binding) {
|
|
231
|
+
const instance = binding.factory ? binding.factory() : undefined
|
|
232
|
+
if (binding.scope === 'singleton') {
|
|
233
|
+
this.#singletons.set(key, instance)
|
|
234
|
+
}
|
|
235
|
+
// biome-ignore lint/suspicious/noExplicitAny: IoC binding factory returns unknown; caller brands via T
|
|
236
|
+
return instance as any as T
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// 4. Auto-construct if it's a class
|
|
240
|
+
if (typeof token === 'function') {
|
|
241
|
+
// biome-ignore lint/suspicious/noExplicitAny: IoC auto-construct returns unknown; caller brands via T
|
|
242
|
+
return this.#autoConstruct(token) as any as T
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 5. Not found
|
|
246
|
+
const allKeys = [...this.#bindings.keys(), ...this.#overrides.keys()]
|
|
247
|
+
const suggestion = didYouMean(key, allKeys)
|
|
248
|
+
throw new ReamError(
|
|
249
|
+
'CONTAINER_NOT_FOUND',
|
|
250
|
+
`No binding found for '${key}'.${suggestion ? ` ${suggestion}` : ''}`,
|
|
251
|
+
{
|
|
252
|
+
hint: 'Register it with container.singleton() or decorate with @inject().',
|
|
253
|
+
},
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Auto-construct a class by reading its dependency hints.
|
|
259
|
+
*
|
|
260
|
+
* Resolution order (like AdonisJS Fold):
|
|
261
|
+
* 1. static containerInjections._constructor.dependencies — explicit deps array
|
|
262
|
+
* 2. Reflect.getMetadata('design:paramtypes') — decorator metadata (requires SWC/tsc)
|
|
263
|
+
* 3. No params → plain `new Class()`
|
|
264
|
+
*/
|
|
265
|
+
#autoConstruct(target: new (...args: unknown[]) => unknown): unknown {
|
|
266
|
+
const metadata = getServiceMetadata(target)
|
|
267
|
+
const scope = metadata?.scope ?? 'transient'
|
|
268
|
+
const key = metadata?.as ?? target.name
|
|
269
|
+
|
|
270
|
+
if (scope === 'singleton' && this.#singletons.has(key)) {
|
|
271
|
+
return this.#singletons.get(key)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// 1. Check static containerInjections (AdonisJS Fold-compatible, works
|
|
275
|
+
// without emitDecoratorMetadata). The `in` operator narrows the optional
|
|
276
|
+
// field to `unknown`, then `readContainerInjectionDeps` validates the
|
|
277
|
+
// shape at runtime — no type-level cast required.
|
|
278
|
+
const explicitDeps = readContainerInjectionDeps(target)
|
|
279
|
+
|
|
280
|
+
// 2. Fallback to reflect-metadata
|
|
281
|
+
const paramTypes: unknown[] =
|
|
282
|
+
explicitDeps ?? Reflect.getMetadata('design:paramtypes', target) ?? []
|
|
283
|
+
|
|
284
|
+
if (paramTypes.length === 0) {
|
|
285
|
+
const instance = new target()
|
|
286
|
+
if (scope === 'singleton') this.#singletons.set(key, instance)
|
|
287
|
+
return instance
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const injectTokens = getInjectTokens(target)
|
|
291
|
+
const lazyIndices = getLazyParams(target)
|
|
292
|
+
|
|
293
|
+
const deps = paramTypes.map((type, index) => {
|
|
294
|
+
const namedToken = injectTokens.get(index)
|
|
295
|
+
// After the typeof check, type is a constructor function and valid as ServiceToken
|
|
296
|
+
const depToken: ServiceToken | undefined =
|
|
297
|
+
namedToken ??
|
|
298
|
+
// biome-ignore lint/suspicious/noExplicitAny: reflect-metadata returns unknown; constructor functions are valid ServiceTokens
|
|
299
|
+
(typeof type === 'function' && type !== Object ? (type as any as ServiceToken) : undefined)
|
|
300
|
+
|
|
301
|
+
if (!depToken) return undefined
|
|
302
|
+
|
|
303
|
+
if (lazyIndices.includes(index)) {
|
|
304
|
+
// biome-ignore lint/suspicious/noExplicitAny: lazy proxy target must be object; IoC resolution returns unknown
|
|
305
|
+
return createLazyProxy(() => this.resolve(depToken) as any as object)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return this.resolve(depToken)
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
const instance = new target(...deps)
|
|
312
|
+
if (scope === 'singleton') this.#singletons.set(key, instance)
|
|
313
|
+
return instance
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
#tokenToKey(token: ServiceToken): string {
|
|
317
|
+
if (typeof token === 'string') {
|
|
318
|
+
this.#assertNotReserved(token, 'string')
|
|
319
|
+
return token
|
|
320
|
+
}
|
|
321
|
+
if (typeof token === 'symbol') {
|
|
322
|
+
// Reject unique `Symbol(x)` — two `Symbol("foo")` calls create distinct
|
|
323
|
+
// symbols, so an override registered with one would be invisible to a
|
|
324
|
+
// resolver holding the other. Force callers onto `Symbol.for(x)` which
|
|
325
|
+
// is the global registry and round-trips across modules.
|
|
326
|
+
if (Symbol.keyFor(token) === undefined) {
|
|
327
|
+
throw new ReamError(
|
|
328
|
+
'CONTAINER_INVALID_TOKEN',
|
|
329
|
+
`Unique Symbol() tokens are not supported — use Symbol.for("${token.description ?? 'name'}") so different modules see the same symbol.`,
|
|
330
|
+
)
|
|
331
|
+
}
|
|
332
|
+
const desc = token.description
|
|
333
|
+
if (desc === undefined) {
|
|
334
|
+
throw new ReamError('CONTAINER_INVALID_TOKEN', 'Symbol tokens must have a description.')
|
|
335
|
+
}
|
|
336
|
+
this.#assertNotReserved(desc, 'symbol description')
|
|
337
|
+
return `Symbol(${desc})`
|
|
338
|
+
}
|
|
339
|
+
return token.name
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
#assertNotReserved(name: string, kind: 'string' | 'symbol description'): void {
|
|
343
|
+
if (RESERVED_TOKEN_NAMES.has(name)) {
|
|
344
|
+
throw new ReamError('CONTAINER_RESERVED_TOKEN', `Reserved container token name '${name}'.`, {
|
|
345
|
+
hint: `'${name}' would collide with an Object.prototype key. Pick a different ${kind}.`,
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// `design:paramtypes` returns `unknown[]` per our annotation; we narrow each
|
|
352
|
+
// entry before treating it as a class token. `Object` slips through because
|
|
353
|
+
// emitDecoratorMetadata uses it for `any`/`unknown` parameters.
|
|
354
|
+
function isClassConstructor(value: unknown): value is new (...args: unknown[]) => unknown {
|
|
355
|
+
return typeof value === 'function' && value !== Object
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function isCallable(value: unknown): value is (...args: unknown[]) => unknown {
|
|
359
|
+
return typeof value === 'function'
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Read `static containerInjections._constructor.dependencies` from a class,
|
|
364
|
+
* AdonisJS Fold-style, when the static is present. The static is undeclared
|
|
365
|
+
* on the constructor signature, so we walk the shape with `in`-narrowing
|
|
366
|
+
* (TS 4.9+) plus a per-element `isServiceToken` guard — no casts.
|
|
367
|
+
*/
|
|
368
|
+
function readContainerInjectionDeps(target: object): ServiceToken[] | undefined {
|
|
369
|
+
if (!('containerInjections' in target)) return undefined
|
|
370
|
+
const injections = target.containerInjections
|
|
371
|
+
if (typeof injections !== 'object' || injections === null) return undefined
|
|
372
|
+
if (!('_constructor' in injections)) return undefined
|
|
373
|
+
const ctor = injections._constructor
|
|
374
|
+
if (typeof ctor !== 'object' || ctor === null) return undefined
|
|
375
|
+
if (!('dependencies' in ctor)) return undefined
|
|
376
|
+
const deps = ctor.dependencies
|
|
377
|
+
if (!Array.isArray(deps)) return undefined
|
|
378
|
+
return deps.every(isServiceToken) ? deps : undefined
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function isServiceToken(value: unknown): value is ServiceToken {
|
|
382
|
+
return typeof value === 'string' || typeof value === 'symbol' || typeof value === 'function'
|
|
383
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container types and interfaces.
|
|
3
|
+
* @implements FR11, FR14, FR16
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Service scope determines instance lifecycle. */
|
|
7
|
+
export type ServiceScope = 'singleton' | 'transient' | 'request'
|
|
8
|
+
|
|
9
|
+
/** Service metadata stored by @Service() decorator. */
|
|
10
|
+
export interface ServiceMetadata {
|
|
11
|
+
scope: ServiceScope
|
|
12
|
+
/** If set, binds as this named interface instead of the class itself. */
|
|
13
|
+
as?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Token used to resolve a service — class constructor, string name, or registered symbol. */
|
|
17
|
+
// biome-ignore lint/suspicious/noExplicitAny: IoC container tokens must accept any constructor signature
|
|
18
|
+
export type ServiceToken = (new (...args: any[]) => unknown) | string | symbol
|
|
19
|
+
|
|
20
|
+
/** Factory function for creating service instances. */
|
|
21
|
+
export type ServiceFactory = () => unknown
|
|
22
|
+
|
|
23
|
+
/** Binding entry in the container. */
|
|
24
|
+
export interface Binding {
|
|
25
|
+
token: ServiceToken
|
|
26
|
+
factory?: ServiceFactory
|
|
27
|
+
instance?: unknown
|
|
28
|
+
scope: ServiceScope
|
|
29
|
+
dependencies: ServiceToken[]
|
|
30
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Lazy() decorator — deferred injection to break circular dependencies.
|
|
3
|
+
*
|
|
4
|
+
* @implements FR15
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import 'reflect-metadata'
|
|
8
|
+
|
|
9
|
+
const LAZY_KEY = Symbol.for('ream:lazy')
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @Lazy() — marks a constructor parameter for deferred resolution.
|
|
13
|
+
* The dependency is resolved on first property access, not at construction.
|
|
14
|
+
*/
|
|
15
|
+
export function Lazy(): ParameterDecorator {
|
|
16
|
+
return (target, propertyKey, parameterIndex) => {
|
|
17
|
+
const existing: number[] = Reflect.getOwnMetadata(LAZY_KEY, target, propertyKey ?? '') ?? []
|
|
18
|
+
existing.push(parameterIndex)
|
|
19
|
+
Reflect.defineMetadata(LAZY_KEY, existing, target, propertyKey ?? '')
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Get lazy parameter indices for a constructor. */
|
|
24
|
+
export function getLazyParams(target: object): number[] {
|
|
25
|
+
return Reflect.getOwnMetadata(LAZY_KEY, target, '') ?? []
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a lazy proxy that defers resolution until first access.
|
|
30
|
+
* Traps get, set, has, ownKeys, getOwnPropertyDescriptor for full compatibility.
|
|
31
|
+
*/
|
|
32
|
+
export function createLazyProxy<T extends object>(resolver: () => T): T {
|
|
33
|
+
let instance: T | undefined
|
|
34
|
+
const ensure = (): T => {
|
|
35
|
+
if (!instance) instance = resolver()
|
|
36
|
+
return instance
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// biome-ignore lint/suspicious/noExplicitAny: Proxy target must be an object literal; T is branded at call site
|
|
40
|
+
return new Proxy({} as any as T, {
|
|
41
|
+
get(_target, prop, receiver) {
|
|
42
|
+
return Reflect.get(ensure(), prop, receiver)
|
|
43
|
+
},
|
|
44
|
+
set(_target, prop, value, receiver) {
|
|
45
|
+
return Reflect.set(ensure(), prop, value, receiver)
|
|
46
|
+
},
|
|
47
|
+
has(_target, prop) {
|
|
48
|
+
return Reflect.has(ensure(), prop)
|
|
49
|
+
},
|
|
50
|
+
ownKeys() {
|
|
51
|
+
return Reflect.ownKeys(ensure())
|
|
52
|
+
},
|
|
53
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
54
|
+
return Object.getOwnPropertyDescriptor(ensure(), prop)
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Service() and @Inject() decorators for IoC container integration.
|
|
3
|
+
*
|
|
4
|
+
* @implements FR11, FR12, FR14
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import 'reflect-metadata'
|
|
8
|
+
import type { ServiceMetadata, ServiceScope } from '../container/types.js'
|
|
9
|
+
|
|
10
|
+
const SERVICE_METADATA_KEY = Symbol('ream:service')
|
|
11
|
+
const INJECT_METADATA_KEY = Symbol('ream:inject')
|
|
12
|
+
|
|
13
|
+
/** Constructor type — accepts unknown args, returns unknown instance. */
|
|
14
|
+
type AnyConstructor = new (...args: unknown[]) => unknown
|
|
15
|
+
|
|
16
|
+
/** Registry of all decorated services. */
|
|
17
|
+
const serviceRegistry: Map<AnyConstructor, ServiceMetadata> = new Map()
|
|
18
|
+
|
|
19
|
+
export function getServiceRegistry(): ReadonlyMap<AnyConstructor, ServiceMetadata> {
|
|
20
|
+
return serviceRegistry
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Clear the service registry (for test isolation and hot-reload). */
|
|
24
|
+
export function clearServiceRegistry(): void {
|
|
25
|
+
serviceRegistry.clear()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getServiceMetadata(target: AnyConstructor): ServiceMetadata | undefined {
|
|
29
|
+
return serviceRegistry.get(target)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @Service() decorator.
|
|
34
|
+
* Registers the class in the IoC container for auto-resolution.
|
|
35
|
+
*/
|
|
36
|
+
export function Service(options: { scope?: ServiceScope; as?: string } = {}): ClassDecorator {
|
|
37
|
+
return (target) => {
|
|
38
|
+
const metadata: ServiceMetadata = {
|
|
39
|
+
scope: options.scope ?? 'singleton',
|
|
40
|
+
as: options.as,
|
|
41
|
+
}
|
|
42
|
+
// biome-ignore lint/suspicious/noExplicitAny: ClassDecorator target is Function; registry requires AnyConstructor which is a subtype
|
|
43
|
+
serviceRegistry.set(target as any as AnyConstructor, metadata)
|
|
44
|
+
Reflect.defineMetadata(SERVICE_METADATA_KEY, metadata, target)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @Inject() decorator — marks a constructor parameter for named injection.
|
|
50
|
+
*
|
|
51
|
+
* @implements FR16
|
|
52
|
+
*/
|
|
53
|
+
export function Inject(token: string): ParameterDecorator {
|
|
54
|
+
return (target, _propertyKey, parameterIndex) => {
|
|
55
|
+
const existingTokens: Map<number, string> =
|
|
56
|
+
Reflect.getOwnMetadata(INJECT_METADATA_KEY, target) ?? new Map()
|
|
57
|
+
existingTokens.set(parameterIndex, token)
|
|
58
|
+
Reflect.defineMetadata(INJECT_METADATA_KEY, existingTokens, target)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get named injection tokens for a class constructor.
|
|
64
|
+
*/
|
|
65
|
+
export function getInjectTokens(target: AnyConstructor): Map<number, string> {
|
|
66
|
+
return Reflect.getOwnMetadata(INJECT_METADATA_KEY, target) ?? new Map()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @inject() class decorator — marks a controller (or service) for transient IoC resolution.
|
|
71
|
+
*
|
|
72
|
+
* Equivalent to @Service({ scope: 'transient' }). Use on controllers so the container
|
|
73
|
+
* auto-resolves constructor dependencies per-request.
|
|
74
|
+
*
|
|
75
|
+
* Usage:
|
|
76
|
+
* @inject()
|
|
77
|
+
* export default class UsersController {
|
|
78
|
+
* constructor(private userService: UserService) {}
|
|
79
|
+
* }
|
|
80
|
+
*/
|
|
81
|
+
export function inject(): ClassDecorator {
|
|
82
|
+
return Service({ scope: 'transient' })
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export { INJECT_METADATA_KEY, SERVICE_METADATA_KEY }
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy Matcher — suggests corrections for typos using Levenshtein distance.
|
|
3
|
+
*
|
|
4
|
+
* @implements FR72
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Calculate Levenshtein distance between two strings.
|
|
9
|
+
*/
|
|
10
|
+
export function levenshtein(a: string, b: string): number {
|
|
11
|
+
const m = a.length
|
|
12
|
+
const n = b.length
|
|
13
|
+
|
|
14
|
+
if (m === 0) return n
|
|
15
|
+
if (n === 0) return m
|
|
16
|
+
|
|
17
|
+
const dp: number[][] = Array.from({ length: m + 1 }, () => Array<number>(n + 1).fill(0))
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i <= m; i++) dp[i][0] = i
|
|
20
|
+
for (let j = 0; j <= n; j++) dp[0][j] = j
|
|
21
|
+
|
|
22
|
+
for (let i = 1; i <= m; i++) {
|
|
23
|
+
for (let j = 1; j <= n; j++) {
|
|
24
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1
|
|
25
|
+
dp[i][j] = Math.min(
|
|
26
|
+
dp[i - 1][j] + 1, // deletion
|
|
27
|
+
dp[i][j - 1] + 1, // insertion
|
|
28
|
+
dp[i - 1][j - 1] + cost, // substitution
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return dp[m][n]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Find the closest matches from a list of candidates.
|
|
38
|
+
*
|
|
39
|
+
* @param input - The typo'd string
|
|
40
|
+
* @param candidates - Available options
|
|
41
|
+
* @param maxDistance - Maximum Levenshtein distance to consider (default: 3)
|
|
42
|
+
* @param maxResults - Maximum suggestions to return (default: 3)
|
|
43
|
+
* @returns Sorted array of { candidate, distance }
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* Find the closest matches from a list of candidates.
|
|
47
|
+
* Exact matches (distance 0) are excluded — this is for "did you mean" suggestions.
|
|
48
|
+
*
|
|
49
|
+
* @param input - The typo'd string (max 100 chars — longer inputs return empty)
|
|
50
|
+
* @param candidates - Available options
|
|
51
|
+
* @param maxDistance - Maximum Levenshtein distance to consider (default: 3)
|
|
52
|
+
* @param maxResults - Maximum suggestions to return (default: 3)
|
|
53
|
+
* @returns Sorted array of { candidate, distance }
|
|
54
|
+
*/
|
|
55
|
+
export function findClosestMatches(
|
|
56
|
+
input: string,
|
|
57
|
+
candidates: string[],
|
|
58
|
+
maxDistance = 3,
|
|
59
|
+
maxResults = 3,
|
|
60
|
+
): Array<{ candidate: string; distance: number }> {
|
|
61
|
+
// Guard against very long inputs to avoid O(m*n) allocation pressure
|
|
62
|
+
if (input.length > 100 || candidates.length === 0) return []
|
|
63
|
+
|
|
64
|
+
const results = candidates
|
|
65
|
+
.map((candidate) => ({
|
|
66
|
+
candidate,
|
|
67
|
+
distance: levenshtein(input.toLowerCase(), candidate.toLowerCase()),
|
|
68
|
+
}))
|
|
69
|
+
.filter((r) => r.distance <= maxDistance && r.distance > 0)
|
|
70
|
+
.sort((a, b) => a.distance - b.distance)
|
|
71
|
+
.slice(0, maxResults)
|
|
72
|
+
|
|
73
|
+
return results
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Format a "did you mean?" suggestion string.
|
|
78
|
+
*
|
|
79
|
+
* @param input - What the user typed
|
|
80
|
+
* @param candidates - Available options to match against
|
|
81
|
+
* @returns Formatted suggestion string, or empty string if no matches
|
|
82
|
+
*/
|
|
83
|
+
export function didYouMean(input: string, candidates: string[]): string {
|
|
84
|
+
const matches = findClosestMatches(input, candidates)
|
|
85
|
+
if (matches.length === 0) return ''
|
|
86
|
+
|
|
87
|
+
if (matches.length === 1) {
|
|
88
|
+
return `Did you mean '${matches[0].candidate}'?`
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const suggestions = matches.map((m) => `'${m.candidate}'`).join(', ')
|
|
92
|
+
return `Did you mean one of: ${suggestions}?`
|
|
93
|
+
}
|