@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,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-specific error subclasses.
|
|
3
|
+
*
|
|
4
|
+
* Each module gets its own error class with appropriate defaults.
|
|
5
|
+
*
|
|
6
|
+
* @implements FR71
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ReamError } from './ReamError.js'
|
|
10
|
+
|
|
11
|
+
/** Container / IoC errors */
|
|
12
|
+
export class ContainerError extends ReamError {
|
|
13
|
+
constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
|
|
14
|
+
super(`CONTAINER_${code}`, message, options)
|
|
15
|
+
this.name = 'ContainerError'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Router errors */
|
|
20
|
+
export class RouterError extends ReamError {
|
|
21
|
+
constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
|
|
22
|
+
super(`ROUTER_${code}`, message, options)
|
|
23
|
+
this.name = 'RouterError'
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Pipeline / Middleware errors */
|
|
28
|
+
export class PipelineError extends ReamError {
|
|
29
|
+
constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
|
|
30
|
+
super(`PIPELINE_${code}`, message, options)
|
|
31
|
+
this.name = 'PipelineError'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Atlas ORM errors */
|
|
36
|
+
export class AtlasError extends ReamError {
|
|
37
|
+
constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
|
|
38
|
+
super(`ATLAS_${code}`, message, options)
|
|
39
|
+
this.name = 'AtlasError'
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Rune validation errors */
|
|
44
|
+
export class RuneError extends ReamError {
|
|
45
|
+
constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
|
|
46
|
+
super(`RUNE_${code}`, message, options)
|
|
47
|
+
this.name = 'RuneError'
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Warden auth errors */
|
|
52
|
+
export class WardenError extends ReamError {
|
|
53
|
+
constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
|
|
54
|
+
super(`WARDEN_${code}`, message, options)
|
|
55
|
+
this.name = 'WardenError'
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Event bus errors */
|
|
60
|
+
export class EventsError extends ReamError {
|
|
61
|
+
constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
|
|
62
|
+
super(`EVENTS_${code}`, message, options)
|
|
63
|
+
this.name = 'EventsError'
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Forge CLI errors */
|
|
68
|
+
export class ForgeError extends ReamError {
|
|
69
|
+
constructor(code: string, message: string, options?: ConstructorParameters<typeof ReamError>[2]) {
|
|
70
|
+
super(`FORGE_${code}`, message, options)
|
|
71
|
+
this.name = 'ForgeError'
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline Stage Error — indicates which pipeline stage failed.
|
|
3
|
+
*
|
|
4
|
+
* @implements FR73
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ReamError } from './ReamError.js'
|
|
8
|
+
|
|
9
|
+
/** Pipeline stage names in execution order. */
|
|
10
|
+
export const PIPELINE_STAGES = [
|
|
11
|
+
'Security (Blackhole)',
|
|
12
|
+
'Logging',
|
|
13
|
+
'Global Middleware',
|
|
14
|
+
'Named Middleware',
|
|
15
|
+
'Guard',
|
|
16
|
+
'Validate',
|
|
17
|
+
'Transaction',
|
|
18
|
+
'Handler',
|
|
19
|
+
'After Middleware',
|
|
20
|
+
'Response Logging',
|
|
21
|
+
] as const
|
|
22
|
+
|
|
23
|
+
export type PipelineStageName = (typeof PIPELINE_STAGES)[number]
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create a pipeline stage error with position context.
|
|
27
|
+
*
|
|
28
|
+
* @param stageIndex - Zero-based index in PIPELINE_STAGES (clamped to valid range)
|
|
29
|
+
* @param originalError - The error that occurred
|
|
30
|
+
* @param context - Additional context
|
|
31
|
+
*/
|
|
32
|
+
export function createPipelineError(
|
|
33
|
+
stageIndex: number,
|
|
34
|
+
originalError: Error,
|
|
35
|
+
context?: Record<string, string>,
|
|
36
|
+
): ReamError {
|
|
37
|
+
const total = PIPELINE_STAGES.length
|
|
38
|
+
const clampedIndex = Math.max(0, Math.min(stageIndex, total - 1))
|
|
39
|
+
const stageName = PIPELINE_STAGES[clampedIndex]
|
|
40
|
+
const position = `${clampedIndex + 1}/${total}`
|
|
41
|
+
const nextStage = clampedIndex + 1 < total ? PIPELINE_STAGES[clampedIndex + 1] : '(end)'
|
|
42
|
+
|
|
43
|
+
const errorCode = originalError instanceof ReamError ? originalError.code : 'PIPELINE_ERROR'
|
|
44
|
+
|
|
45
|
+
return new ReamError(errorCode, originalError.message, {
|
|
46
|
+
pipelineStage: `${position} (${stageName})`,
|
|
47
|
+
hint: `Error occurred at pipeline stage ${position} (${stageName}). Next stage would have been: ${nextStage}.`,
|
|
48
|
+
context: {
|
|
49
|
+
...context,
|
|
50
|
+
stage: stageName,
|
|
51
|
+
position,
|
|
52
|
+
nextStage,
|
|
53
|
+
},
|
|
54
|
+
sourceFile: originalError instanceof ReamError ? originalError.sourceFile : undefined,
|
|
55
|
+
sourceLine: originalError instanceof ReamError ? originalError.sourceLine : undefined,
|
|
56
|
+
docsUrl: originalError instanceof ReamError ? originalError.docsUrl : undefined,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Validate pipeline configuration at boot time.
|
|
62
|
+
* Detects misconfiguration before accepting traffic.
|
|
63
|
+
*
|
|
64
|
+
* @implements FR74
|
|
65
|
+
*/
|
|
66
|
+
export function validatePipelineConfig(config: {
|
|
67
|
+
globalMiddleware?: unknown[]
|
|
68
|
+
namedMiddleware?: Record<string, unknown>
|
|
69
|
+
routes?: Array<{ middleware?: string[] }>
|
|
70
|
+
}): ReamError[] {
|
|
71
|
+
const errors: ReamError[] = []
|
|
72
|
+
|
|
73
|
+
// Check named middleware references exist (deduplicated)
|
|
74
|
+
if (config.routes && config.namedMiddleware) {
|
|
75
|
+
const registered = new Set(Object.keys(config.namedMiddleware))
|
|
76
|
+
const reported = new Set<string>()
|
|
77
|
+
|
|
78
|
+
for (const route of config.routes) {
|
|
79
|
+
for (const mw of route.middleware ?? []) {
|
|
80
|
+
if (!registered.has(mw) && !reported.has(mw)) {
|
|
81
|
+
reported.add(mw)
|
|
82
|
+
errors.push(
|
|
83
|
+
new ReamError('PIPELINE_UNKNOWN_MIDDLEWARE', `Middleware '${mw}' is not registered`, {
|
|
84
|
+
hint: `Register it with middleware.register('${mw}', handler) before using it on routes.`,
|
|
85
|
+
context: {
|
|
86
|
+
middleware: mw,
|
|
87
|
+
registered: [...registered].join(', '),
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return errors
|
|
97
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base ReamError class for TypeScript.
|
|
3
|
+
*
|
|
4
|
+
* Parses structured JSON errors from Rust NAPI boundary and provides
|
|
5
|
+
* rich error context (code, hint, source location, docs URL).
|
|
6
|
+
*
|
|
7
|
+
* @implements FR71, FR72, FR73, FR77
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export class ReamError extends Error {
|
|
11
|
+
/** Error code (e.g., "ATLAS_QUERY_ERROR", "CONTAINER_NOT_FOUND") */
|
|
12
|
+
readonly code: string
|
|
13
|
+
|
|
14
|
+
/** Additional context key-value pairs */
|
|
15
|
+
readonly context: Record<string, string>
|
|
16
|
+
|
|
17
|
+
/** Actionable hint for the developer */
|
|
18
|
+
readonly hint?: string
|
|
19
|
+
|
|
20
|
+
/** Source file where the error originated */
|
|
21
|
+
readonly sourceFile?: string
|
|
22
|
+
|
|
23
|
+
/** Line number in the source file */
|
|
24
|
+
readonly sourceLine?: number
|
|
25
|
+
|
|
26
|
+
/** URL to the error documentation page */
|
|
27
|
+
readonly docsUrl?: string
|
|
28
|
+
|
|
29
|
+
/** Pipeline stage where the error occurred (if applicable) */
|
|
30
|
+
readonly pipelineStage?: string
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
code: string,
|
|
34
|
+
message: string,
|
|
35
|
+
options?: {
|
|
36
|
+
context?: Record<string, string>
|
|
37
|
+
hint?: string
|
|
38
|
+
sourceFile?: string
|
|
39
|
+
sourceLine?: number
|
|
40
|
+
docsUrl?: string
|
|
41
|
+
pipelineStage?: string
|
|
42
|
+
},
|
|
43
|
+
) {
|
|
44
|
+
super(message)
|
|
45
|
+
this.name = 'ReamError'
|
|
46
|
+
this.code = code
|
|
47
|
+
this.context = options?.context ?? {}
|
|
48
|
+
this.hint = options?.hint
|
|
49
|
+
this.sourceFile = options?.sourceFile
|
|
50
|
+
this.sourceLine = options?.sourceLine
|
|
51
|
+
this.docsUrl = options?.docsUrl ?? `https://docs.ream.dev/errors/${code}`
|
|
52
|
+
this.pipelineStage = options?.pipelineStage
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Parse a NAPI error message (JSON string from Rust ReamError) into a TS ReamError.
|
|
57
|
+
* Falls back to a generic error if parsing fails.
|
|
58
|
+
*/
|
|
59
|
+
static fromNapi(error: Error): ReamError {
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(error.message)
|
|
62
|
+
if (typeof parsed !== 'object' || parsed === null || typeof parsed.code !== 'string') {
|
|
63
|
+
return new ReamError('UNKNOWN', error.message)
|
|
64
|
+
}
|
|
65
|
+
return new ReamError(parsed.code, parsed.message ?? error.message, {
|
|
66
|
+
context:
|
|
67
|
+
typeof parsed.context === 'object' && parsed.context !== null ? parsed.context : {},
|
|
68
|
+
hint: typeof parsed.hint === 'string' ? parsed.hint : undefined,
|
|
69
|
+
sourceFile: typeof parsed.sourceFile === 'string' ? parsed.sourceFile : undefined,
|
|
70
|
+
sourceLine: typeof parsed.sourceLine === 'number' ? parsed.sourceLine : undefined,
|
|
71
|
+
docsUrl: typeof parsed.docsUrl === 'string' ? parsed.docsUrl : undefined,
|
|
72
|
+
})
|
|
73
|
+
} catch {
|
|
74
|
+
// Not a JSON error — wrap as generic
|
|
75
|
+
return new ReamError('UNKNOWN', error.message)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Format for dev console — full box display with all details.
|
|
81
|
+
*/
|
|
82
|
+
toDevString(): string {
|
|
83
|
+
const content: string[] = []
|
|
84
|
+
content.push(`[${this.code}] ${this.message}`)
|
|
85
|
+
|
|
86
|
+
if (this.pipelineStage) {
|
|
87
|
+
content.push(`Pipeline: ${this.pipelineStage}`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (this.sourceFile) {
|
|
91
|
+
content.push(`at ${this.sourceFile}${this.sourceLine ? `:${this.sourceLine}` : ''}`)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (Object.keys(this.context).length > 0) {
|
|
95
|
+
content.push('')
|
|
96
|
+
for (const [key, value] of Object.entries(this.context)) {
|
|
97
|
+
// Escape newlines in values to prevent box corruption
|
|
98
|
+
const safeValue = String(value).replace(/\n/g, '\\n')
|
|
99
|
+
content.push(`${key}: ${safeValue}`)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (this.hint) {
|
|
104
|
+
content.push('')
|
|
105
|
+
content.push(`Hint: ${this.hint}`)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (this.docsUrl) {
|
|
109
|
+
content.push(`Docs: ${this.docsUrl}`)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Compute border width from the longest content line
|
|
113
|
+
const maxLen = Math.min(Math.max(...content.map((l) => l.length)) + 4, 80)
|
|
114
|
+
const border = '─'.repeat(maxLen)
|
|
115
|
+
|
|
116
|
+
const lines = [`┌${border}┐`]
|
|
117
|
+
for (const line of content) {
|
|
118
|
+
lines.push(`│ ${line}`)
|
|
119
|
+
}
|
|
120
|
+
lines.push(`└${border}┘`)
|
|
121
|
+
|
|
122
|
+
return lines.join('\n')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Format for prod — code + message only.
|
|
127
|
+
*/
|
|
128
|
+
toProdString(): string {
|
|
129
|
+
return `[${this.code}] ${this.message}`
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Auto-format based on NODE_ENV.
|
|
134
|
+
*/
|
|
135
|
+
toFormattedString(): string {
|
|
136
|
+
return process.env.NODE_ENV === 'production' ? this.toProdString() : this.toDevString()
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emitter — AdonisJS-compatible typed event system backed by EventBus.
|
|
3
|
+
*
|
|
4
|
+
* Class-based events:
|
|
5
|
+
* emitter.on(TaskDeclared, SendNotification)
|
|
6
|
+
* await new TaskDeclared(task).emit()
|
|
7
|
+
*
|
|
8
|
+
* String-based events:
|
|
9
|
+
* emitter.on('user:registered', (user) => { ... })
|
|
10
|
+
* emitter.emit('user:registered', user)
|
|
11
|
+
*
|
|
12
|
+
* Listeners support @inject() for DI when a container resolver is provided.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { EventBus } from './native.js'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Listener class interface — must have a handle() method.
|
|
19
|
+
*/
|
|
20
|
+
export interface ListenerClass<T = unknown> {
|
|
21
|
+
handle(event: T): Promise<void> | void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type EventConstructor<T = unknown> = new (...args: never[]) => T
|
|
25
|
+
|
|
26
|
+
type ListenerFn<T = unknown> = (event: T) => Promise<void> | void
|
|
27
|
+
|
|
28
|
+
type ListenerConstructor<T = unknown> = new (...args: never[]) => ListenerClass<T>
|
|
29
|
+
|
|
30
|
+
type Listener<T = unknown> = ListenerFn<T> | ListenerConstructor<T>
|
|
31
|
+
|
|
32
|
+
/** Resolver for instantiating listener classes with DI. */
|
|
33
|
+
export interface ContainerResolver {
|
|
34
|
+
make<T>(target: new (...args: never[]) => T): T
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class Emitter {
|
|
38
|
+
private bus: EventBus
|
|
39
|
+
private resolver?: ContainerResolver
|
|
40
|
+
private classListeners: Map<EventConstructor, Listener[]> = new Map()
|
|
41
|
+
private stringListeners: Map<string, ListenerFn[]> = new Map()
|
|
42
|
+
|
|
43
|
+
constructor(bus: EventBus, resolver?: ContainerResolver) {
|
|
44
|
+
this.bus = bus
|
|
45
|
+
this.resolver = resolver
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ─── Class-based events ───────────────────────────────────
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Listen for a class-based event.
|
|
52
|
+
* emitter.on(TaskDeclared, SendNotification) // listener class
|
|
53
|
+
* emitter.on(TaskDeclared, (event) => { ... }) // inline function
|
|
54
|
+
*/
|
|
55
|
+
on<T>(event: EventConstructor<T>, listener: Listener<T>): void
|
|
56
|
+
/**
|
|
57
|
+
* Listen for a string-based event.
|
|
58
|
+
* emitter.on('user:registered', (user) => { ... })
|
|
59
|
+
*/
|
|
60
|
+
on(event: string, listener: ListenerFn): void
|
|
61
|
+
on(event: EventConstructor | string, listener: Listener): void {
|
|
62
|
+
if (typeof event === 'string') {
|
|
63
|
+
const list = this.stringListeners.get(event) ?? []
|
|
64
|
+
list.push(listener as ListenerFn)
|
|
65
|
+
this.stringListeners.set(event, list)
|
|
66
|
+
} else {
|
|
67
|
+
const list = this.classListeners.get(event) ?? []
|
|
68
|
+
list.push(listener)
|
|
69
|
+
this.classListeners.set(event, list)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ─── Emit ─────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Emit a string-based event.
|
|
77
|
+
*/
|
|
78
|
+
emit(event: string, data: unknown): void {
|
|
79
|
+
// Dispatch to string listeners
|
|
80
|
+
const listeners = this.stringListeners.get(event)
|
|
81
|
+
if (listeners) {
|
|
82
|
+
for (const fn of listeners) {
|
|
83
|
+
Promise.resolve(fn(data)).catch((err) => {
|
|
84
|
+
// Surface listener errors instead of swallowing them
|
|
85
|
+
this.emitError(event, err)
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Also push through EventBus for cross-service / Rust subscribers.
|
|
91
|
+
// `#wrapForBus` prepends the correlation envelope when set so the ID
|
|
92
|
+
// reaches the other side; without it `setCorrelationId()` was a no-op.
|
|
93
|
+
void this.bus.emit(event, JSON.stringify(this.#wrapForBus(data))).catch((err: unknown) => {
|
|
94
|
+
this.emitError(event, err)
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Emit an error event for listener failures. */
|
|
99
|
+
private emitError(event: string, error: unknown): void {
|
|
100
|
+
const errorListeners = this.stringListeners.get('emitter:error')
|
|
101
|
+
if (errorListeners && errorListeners.length > 0) {
|
|
102
|
+
for (const fn of errorListeners) {
|
|
103
|
+
try {
|
|
104
|
+
fn({ event, error })
|
|
105
|
+
} catch {
|
|
106
|
+
/* prevent infinite loop */
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
// No error listener — log to stderr as last resort
|
|
111
|
+
process.stderr.write(`[events] Listener error for '${event}': ${error}\n`)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Dispatch a class-based event. Called by BaseEvent#emit().
|
|
117
|
+
*/
|
|
118
|
+
async dispatchEvent<T extends object>(event: T): Promise<void> {
|
|
119
|
+
const EventClass = event.constructor as EventConstructor<T>
|
|
120
|
+
const listeners = this.classListeners.get(EventClass) ?? []
|
|
121
|
+
const name = (EventClass as { eventName?: string }).eventName ?? classToEventName(EventClass)
|
|
122
|
+
|
|
123
|
+
// Per-listener error isolation — mirrors the string `emit()` contract.
|
|
124
|
+
// A side-effect listener that throws (SMTP down, etc.) MUST NOT abort
|
|
125
|
+
// the sibling listeners NOR suppress the cross-service `bus.emit` below:
|
|
126
|
+
// the domain event already happened, so distributed subscribers must
|
|
127
|
+
// still receive it. Failures surface on the `emitter:error` channel.
|
|
128
|
+
for (const listener of listeners) {
|
|
129
|
+
try {
|
|
130
|
+
if (isListenerClass(listener)) {
|
|
131
|
+
// Listener class — resolve via container for @inject() support
|
|
132
|
+
const instance = this.resolver
|
|
133
|
+
? this.resolver.make(listener as ListenerConstructor<T>)
|
|
134
|
+
: new (listener as ListenerConstructor<T>)()
|
|
135
|
+
await instance.handle(event)
|
|
136
|
+
} else {
|
|
137
|
+
await (listener as ListenerFn<T>)(event)
|
|
138
|
+
}
|
|
139
|
+
} catch (err) {
|
|
140
|
+
this.emitError(name, err)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Also push through EventBus — same correlation-envelope wrapping as
|
|
145
|
+
// the string-event path so distributed tracing covers class events too.
|
|
146
|
+
// Reached unconditionally: a listener failure above no longer skips it.
|
|
147
|
+
await this.bus.emit(name, JSON.stringify(this.#wrapForBus(event)))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ─── Wildcard subscriptions (via Rust NAPI) ───────────────
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Subscribe to events matching a wildcard pattern.
|
|
154
|
+
* Uses Rust wildcard engine via NAPI for pattern matching.
|
|
155
|
+
*
|
|
156
|
+
* emitter.onAny('order.*', (name, data) => { ... }) // single segment
|
|
157
|
+
* emitter.onAny('order.**', (name, data) => { ... }) // deep match
|
|
158
|
+
*/
|
|
159
|
+
async onAny(
|
|
160
|
+
pattern: string,
|
|
161
|
+
listener: (eventName: string, data: unknown) => void,
|
|
162
|
+
): Promise<number> {
|
|
163
|
+
return this.bus.subscribe(pattern, (eventJson: string) => {
|
|
164
|
+
try {
|
|
165
|
+
const event = JSON.parse(eventJson)
|
|
166
|
+
// The bus delivers a Rust `Event` envelope whose `correlationId`
|
|
167
|
+
// sits at the top of the parsed JSON. Propagate it onto the
|
|
168
|
+
// emitter so nested emits inside the listener body inherit the
|
|
169
|
+
// trace context — that's the whole point of the API. `data` is
|
|
170
|
+
// the raw user payload string (still JSON-encoded if the
|
|
171
|
+
// emitter serialized an object), kept as-is for back-compat.
|
|
172
|
+
if (
|
|
173
|
+
event !== null &&
|
|
174
|
+
typeof event === 'object' &&
|
|
175
|
+
typeof event.correlationId === 'string'
|
|
176
|
+
) {
|
|
177
|
+
this.setCorrelationId(event.correlationId)
|
|
178
|
+
}
|
|
179
|
+
listener(event.name ?? pattern, event.data ?? event)
|
|
180
|
+
} catch {
|
|
181
|
+
listener(pattern, eventJson)
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Unsubscribe a wildcard subscription by ID. */
|
|
187
|
+
async offAny(subscriptionId: number): Promise<void> {
|
|
188
|
+
await this.bus.unsubscribe(subscriptionId)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Check if a pattern matches an event name (wildcard matching via Rust). */
|
|
192
|
+
matchesPattern(pattern: string, eventName: string): boolean {
|
|
193
|
+
return this.bus.matchesWildcard(pattern, eventName)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ─── Request / Reply ──────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Send a request event and await a response.
|
|
200
|
+
*
|
|
201
|
+
* const user = await emitter.request('query:user.find', { id: 1 })
|
|
202
|
+
*/
|
|
203
|
+
async request<T = unknown>(name: string, data: unknown, timeoutMs = 5000): Promise<T> {
|
|
204
|
+
// Same correlation injection as `emit()` so a `setCorrelationId`
|
|
205
|
+
// preceding a request actually reaches the responder via the
|
|
206
|
+
// top-level `correlationId` field on the parsed payload.
|
|
207
|
+
const result = await this.bus.request(name, JSON.stringify(this.#wrapForBus(data)), timeoutMs)
|
|
208
|
+
try {
|
|
209
|
+
return JSON.parse(result) as T
|
|
210
|
+
} catch {
|
|
211
|
+
return result as T
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Register a request handler.
|
|
217
|
+
*
|
|
218
|
+
* emitter.onRequest('query:user.find', (params, reply) => {
|
|
219
|
+
* const user = db.find(params.id)
|
|
220
|
+
* reply(JSON.stringify(user))
|
|
221
|
+
* })
|
|
222
|
+
*/
|
|
223
|
+
onRequest(
|
|
224
|
+
name: string,
|
|
225
|
+
handler: (eventJson: string, reply: (response: string) => void) => void,
|
|
226
|
+
): void {
|
|
227
|
+
this.bus.onRequest(name, handler)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ─── Correlation context ──────────────────────────────────
|
|
231
|
+
|
|
232
|
+
/** Current correlation ID for event chain tracing. */
|
|
233
|
+
private _correlationId?: string
|
|
234
|
+
|
|
235
|
+
/** Set the correlation ID for subsequent events (typically from HTTP request context). */
|
|
236
|
+
setCorrelationId(id: string): void {
|
|
237
|
+
this._correlationId = id
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Get the current correlation ID. */
|
|
241
|
+
getCorrelationId(): string | undefined {
|
|
242
|
+
return this._correlationId
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Inject the active correlation ID at the top level of the payload so
|
|
247
|
+
* the bus's Event envelope (`{ id, name, data, correlationId, ... }`)
|
|
248
|
+
* surfaces it on the subscriber side — both the Rust `ream-events`
|
|
249
|
+
* envelope serde and `FakeBus.parseEmitData()` already read
|
|
250
|
+
* `correlationId` from the parsed `data` string. Without this injection
|
|
251
|
+
* `setCorrelationId()` was a no-op functional API. Returns `data`
|
|
252
|
+
* unchanged when no correlation ID is set OR when `data` is not a plain
|
|
253
|
+
* object (primitives / arrays / null can't carry a sibling field).
|
|
254
|
+
*/
|
|
255
|
+
#wrapForBus(data: unknown): unknown {
|
|
256
|
+
if (this._correlationId === undefined) return data
|
|
257
|
+
if (data === null || typeof data !== 'object' || Array.isArray(data)) {
|
|
258
|
+
return data
|
|
259
|
+
}
|
|
260
|
+
const obj = data as Record<string, unknown>
|
|
261
|
+
// User-supplied correlationId wins — don't clobber an explicit value.
|
|
262
|
+
if (typeof obj.correlationId === 'string') return data
|
|
263
|
+
return { ...obj, correlationId: this._correlationId }
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ─── Introspection ────────────────────────────────────────
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Check if any listeners are registered for an event.
|
|
270
|
+
*/
|
|
271
|
+
hasListeners(event: EventConstructor | string): boolean {
|
|
272
|
+
if (typeof event === 'string') {
|
|
273
|
+
return (this.stringListeners.get(event)?.length ?? 0) > 0
|
|
274
|
+
}
|
|
275
|
+
return (this.classListeners.get(event)?.length ?? 0) > 0
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** Get the number of Rust-side subscriptions. */
|
|
279
|
+
async subscriptionCount(): Promise<number> {
|
|
280
|
+
return this.bus.subscriptionCount()
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* BaseEvent — extend to create typed event classes.
|
|
286
|
+
*
|
|
287
|
+
* Usage:
|
|
288
|
+
* class TaskDeclared extends BaseEvent {
|
|
289
|
+
* constructor(public task: Task) { super() }
|
|
290
|
+
* }
|
|
291
|
+
*
|
|
292
|
+
* // In controller:
|
|
293
|
+
* await new TaskDeclared(task).emit()
|
|
294
|
+
*
|
|
295
|
+
* // In events.ts:
|
|
296
|
+
* emitter.on(TaskDeclared, LogTaskEvent)
|
|
297
|
+
*/
|
|
298
|
+
export class BaseEvent {
|
|
299
|
+
static eventName?: string
|
|
300
|
+
private static _emitter: Emitter | undefined
|
|
301
|
+
|
|
302
|
+
/** @internal Wire the emitter (called by EventsProvider). */
|
|
303
|
+
static useEmitter(emitter: Emitter): void {
|
|
304
|
+
BaseEvent._emitter = emitter
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
static resetEmitter(): void {
|
|
308
|
+
BaseEvent._emitter = undefined
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Dispatch this event instance via the wired emitter.
|
|
313
|
+
* No-op if no emitter is wired (test/standalone mode).
|
|
314
|
+
*/
|
|
315
|
+
async emit(): Promise<void> {
|
|
316
|
+
if (!BaseEvent._emitter) return
|
|
317
|
+
try {
|
|
318
|
+
await BaseEvent._emitter.dispatchEvent(this)
|
|
319
|
+
} catch (err) {
|
|
320
|
+
process.stderr.write(`[events] dispatch error for ${this.constructor.name}: ${err}\n`)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** Convert PascalCase class name to dot.separated event name. */
|
|
326
|
+
function classToEventName(cls: EventConstructor): string {
|
|
327
|
+
return cls.name.replace(/([a-z])([A-Z])/g, '$1.$2').toLowerCase()
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Check if a listener entry is a class (has prototype.handle) vs a function. */
|
|
331
|
+
function isListenerClass(listener: Listener): boolean {
|
|
332
|
+
return typeof listener === 'function' && typeof listener.prototype?.handle === 'function'
|
|
333
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { BaseEvent, Emitter } from './Emitter.js'
|
|
2
|
+
import { EventBus } from './native.js'
|
|
3
|
+
import { setEmitter } from './services/main.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Host context for the events provider. The container is referenced
|
|
7
|
+
* structurally (not via a concrete import) so the provider stays a thin
|
|
8
|
+
* seam over ream's IoC container.
|
|
9
|
+
*
|
|
10
|
+
* `make()` is the listener-class resolver Emitter needs. When the host
|
|
11
|
+
* doesn't expose it, listener CLASSES can't be instantiated — only inline
|
|
12
|
+
* function listeners work. The optional `make` field mirrors Emitter's
|
|
13
|
+
* `ContainerResolver` interface so the assignment type-checks without a
|
|
14
|
+
* load-bearing cast.
|
|
15
|
+
*/
|
|
16
|
+
interface EventsContainer {
|
|
17
|
+
singleton(token: unknown, factory: () => unknown): void
|
|
18
|
+
resolve<T = unknown>(token: unknown): T
|
|
19
|
+
make?<T>(target: new (...args: never[]) => T): T
|
|
20
|
+
}
|
|
21
|
+
export interface EventsAppContext {
|
|
22
|
+
container: EventsContainer
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default class EventsProvider {
|
|
26
|
+
constructor(protected app: EventsAppContext) {}
|
|
27
|
+
|
|
28
|
+
register() {
|
|
29
|
+
this.app.container.singleton(EventBus, () => new EventBus())
|
|
30
|
+
this.app.container.singleton('bus', () => this.app.container.resolve<EventBus>(EventBus))
|
|
31
|
+
|
|
32
|
+
this.app.container.singleton(Emitter, () => {
|
|
33
|
+
const bus = this.app.container.resolve<EventBus>(EventBus)
|
|
34
|
+
// Forward `make` only when the host actually exposes it.
|
|
35
|
+
// Emitter falls back to direct `new Listener()` instantiation
|
|
36
|
+
// otherwise, which keeps zero-arg listener classes working
|
|
37
|
+
// without a full IoC container.
|
|
38
|
+
const resolver =
|
|
39
|
+
typeof this.app.container.make === 'function'
|
|
40
|
+
? { make: this.app.container.make.bind(this.app.container) }
|
|
41
|
+
: undefined
|
|
42
|
+
return new Emitter(bus, resolver)
|
|
43
|
+
})
|
|
44
|
+
this.app.container.singleton('emitter', () => this.app.container.resolve<Emitter>(Emitter))
|
|
45
|
+
// Primary public token — `ctx.events` and `container.resolve('events')`.
|
|
46
|
+
this.app.container.singleton('events', () => this.app.container.resolve<Emitter>(Emitter))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async boot() {
|
|
50
|
+
const emitter = this.app.container.resolve<Emitter>(Emitter)
|
|
51
|
+
BaseEvent.useEmitter(emitter)
|
|
52
|
+
setEmitter(emitter)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async shutdown() {
|
|
56
|
+
BaseEvent.resetEmitter()
|
|
57
|
+
}
|
|
58
|
+
}
|