@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,420 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory `EventBus` substitute for tests — captures every
|
|
3
|
+
* `emit(name, data)` / `request(name, data)` call and exposes
|
|
4
|
+
* `assertEmitted` / `assertNotEmitted` helpers in the same shape as
|
|
5
|
+
* Rover's `FakeMail`, Bay's `FakeQueue`, Storage's `FakeStorage`,
|
|
6
|
+
* Nova's `FakeNova`, and Relay's `FakeRelay`.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors the public `EventBus` surface (`emit`, `subscribe`,
|
|
9
|
+
* `unsubscribe`, `onRequest`, `request`, `matchesWildcard`,
|
|
10
|
+
* `subscriptionCount`) with pure-TS, NAPI-free semantics. Wildcard
|
|
11
|
+
* matching is ported from `crates/ream-events/src/router.rs` — the
|
|
12
|
+
* Rust router is authoritative; any divergence is a bug here.
|
|
13
|
+
*
|
|
14
|
+
* Reach via `@c9up/ream/events/testing` (NOT the main barrel).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export interface CapturedEmit {
|
|
18
|
+
kind: 'emit'
|
|
19
|
+
name: string
|
|
20
|
+
data: string
|
|
21
|
+
parsedData?: unknown
|
|
22
|
+
correlationId?: string
|
|
23
|
+
causationId?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CapturedRequest {
|
|
27
|
+
kind: 'request'
|
|
28
|
+
name: string
|
|
29
|
+
data: string
|
|
30
|
+
parsedData?: unknown
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface FakeBusPredicate {
|
|
34
|
+
name?: string
|
|
35
|
+
dataMatches?: (data: unknown) => boolean
|
|
36
|
+
correlationId?: string
|
|
37
|
+
causationId?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type FakeBusPredicateArg = FakeBusPredicate | ((event: CapturedEmit) => boolean)
|
|
41
|
+
|
|
42
|
+
interface Subscription {
|
|
43
|
+
pattern: string
|
|
44
|
+
/** Subscribers may return `void` (sync) or `PromiseLike<void>` (async).
|
|
45
|
+
* Async rejections are logged via `console.error` to keep them from
|
|
46
|
+
* escaping to `unhandledRejection`. */
|
|
47
|
+
callback: (eventJson: string) => void | PromiseLike<void>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Request handler may return `void` (sync, fire-and-forget after `reply`)
|
|
51
|
+
* or `PromiseLike<void>` (async — the bus awaits and routes any rejection
|
|
52
|
+
* through `request()`'s reject path). */
|
|
53
|
+
export type FakeBusRequestHandler = (
|
|
54
|
+
eventJson: string,
|
|
55
|
+
reply: (response: string) => void,
|
|
56
|
+
) => void | PromiseLike<void>
|
|
57
|
+
|
|
58
|
+
export class FakeBus {
|
|
59
|
+
#emitted: CapturedEmit[] = []
|
|
60
|
+
#requests: CapturedRequest[] = []
|
|
61
|
+
#subscribers = new Map<number, Subscription>()
|
|
62
|
+
#requestHandlers = new Map<string, FakeBusRequestHandler>()
|
|
63
|
+
#nextSubId = 1
|
|
64
|
+
|
|
65
|
+
emit(name: string, data: string): Promise<string> {
|
|
66
|
+
const { parsedData, correlationId, causationId } = parseEmitData(data)
|
|
67
|
+
const entry: CapturedEmit = {
|
|
68
|
+
kind: 'emit',
|
|
69
|
+
name,
|
|
70
|
+
data,
|
|
71
|
+
parsedData,
|
|
72
|
+
correlationId,
|
|
73
|
+
causationId,
|
|
74
|
+
}
|
|
75
|
+
this.#emitted.push(entry)
|
|
76
|
+
// Snapshot subscribers so a callback adding/removing during dispatch
|
|
77
|
+
// can't perturb the in-flight iteration — matches the router.rs
|
|
78
|
+
// "clone matching handlers; release lock; call without holding lock"
|
|
79
|
+
// pattern.
|
|
80
|
+
const targets: Subscription[] = []
|
|
81
|
+
for (const sub of this.#subscribers.values()) {
|
|
82
|
+
if (this.matchesWildcard(sub.pattern, name)) targets.push(sub)
|
|
83
|
+
}
|
|
84
|
+
const payloadJson = buildEventJson(entry)
|
|
85
|
+
for (const sub of targets) {
|
|
86
|
+
// Subscribers can be sync (`(json) => void`) or async (`(json) =>
|
|
87
|
+
// Promise<void>`). Sync throws surface via the `try/catch`; async
|
|
88
|
+
// rejections would otherwise escape to `unhandledRejection`. Wrap
|
|
89
|
+
// both paths so a misbehaving subscriber logs loudly instead of
|
|
90
|
+
// crashing the test process — mirror of the request-handler error
|
|
91
|
+
// surface at `onError` below.
|
|
92
|
+
try {
|
|
93
|
+
const ret = sub.callback(payloadJson)
|
|
94
|
+
if (isThenable(ret)) {
|
|
95
|
+
ret.then(undefined, (err: unknown) => {
|
|
96
|
+
console.error(
|
|
97
|
+
`[FakeBus] subscriber for "${sub.pattern}" rejected on event "${name}":`,
|
|
98
|
+
err,
|
|
99
|
+
)
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error(`[FakeBus] subscriber for "${sub.pattern}" threw on event "${name}":`, err)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return Promise.resolve('ok')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
subscribe(pattern: string, callback: (eventJson: string) => void | PromiseLike<void>): number {
|
|
110
|
+
const id = this.#nextSubId++
|
|
111
|
+
this.#subscribers.set(id, { pattern, callback })
|
|
112
|
+
return id
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
unsubscribe(subscriptionId: number): Promise<void> {
|
|
116
|
+
this.#subscribers.delete(subscriptionId)
|
|
117
|
+
return Promise.resolve()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
onRequest(name: string, callback: FakeBusRequestHandler): void {
|
|
121
|
+
if (this.#requestHandlers.has(name)) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`FakeBus.onRequest: a handler is already registered for "${name}". Call reset() between registrations.`,
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
this.#requestHandlers.set(name, callback)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
request(name: string, data: string, timeoutMs: number = 5000): Promise<string> {
|
|
130
|
+
const handler = this.#requestHandlers.get(name)
|
|
131
|
+
if (!handler) {
|
|
132
|
+
return Promise.reject(new Error(`FakeBus: no request handler registered for "${name}"`))
|
|
133
|
+
}
|
|
134
|
+
// Only log requests the bus accepted. A handler-missing rejection
|
|
135
|
+
// represents "code-under-test attempted a request the system was
|
|
136
|
+
// not configured for" — that's a setup bug, not a bus call, and it
|
|
137
|
+
// should not poison `getRequests()` symmetry with `getEmitted()`.
|
|
138
|
+
const { parsedData } = parseEmitData(data)
|
|
139
|
+
this.#requests.push({ kind: 'request', name, data, parsedData })
|
|
140
|
+
return new Promise<string>((resolve, reject) => {
|
|
141
|
+
let settled = false
|
|
142
|
+
const timer = setTimeout(() => {
|
|
143
|
+
if (settled) return
|
|
144
|
+
settled = true
|
|
145
|
+
reject(
|
|
146
|
+
new Error(`FakeBus.request: handler for "${name}" did not reply within ${timeoutMs}ms`),
|
|
147
|
+
)
|
|
148
|
+
}, timeoutMs)
|
|
149
|
+
// Fake-timers note: `vi.useFakeTimers()` mocks the `setTimeout`
|
|
150
|
+
// scheduled above. The happy-path resolve via `reply(...)` does
|
|
151
|
+
// not depend on timer advancement, but the rejection branch does
|
|
152
|
+
// — call `vi.advanceTimersByTime(timeoutMs)` to surface the
|
|
153
|
+
// timeout when fake timers are active.
|
|
154
|
+
const reply = (response: string): void => {
|
|
155
|
+
if (settled) return
|
|
156
|
+
settled = true
|
|
157
|
+
clearTimeout(timer)
|
|
158
|
+
resolve(response)
|
|
159
|
+
}
|
|
160
|
+
const onError = (err: unknown): void => {
|
|
161
|
+
if (settled) {
|
|
162
|
+
console.error(`[FakeBus] request handler for "${name}" threw after reply:`, err)
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
settled = true
|
|
166
|
+
clearTimeout(timer)
|
|
167
|
+
reject(err instanceof Error ? err : new Error(String(err)))
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const ret = handler(data, reply)
|
|
171
|
+
if (isThenable(ret)) ret.then(undefined, onError)
|
|
172
|
+
} catch (err) {
|
|
173
|
+
onError(err)
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
matchesWildcard(pattern: string, eventName: string): boolean {
|
|
179
|
+
return wildcardMatches(pattern, eventName)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
subscriptionCount(): Promise<number> {
|
|
183
|
+
return Promise.resolve(this.#subscribers.size)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Defensive clone of the captured emits. **Clone is lossy** for
|
|
188
|
+
* non-cloneable values: `structuredClone` is tried first, JSON
|
|
189
|
+
* round-trip is the fallback. BigInt, functions, and other
|
|
190
|
+
* non-stringifiable payloads collapse to `undefined` in the fallback
|
|
191
|
+
* path (with a `[FakeBus] non-cloneable capture` `console.warn`).
|
|
192
|
+
* Internal assertions (`assertEmitted` etc.) read the live capture,
|
|
193
|
+
* not the clone, so this caveat only affects external readers.
|
|
194
|
+
*/
|
|
195
|
+
getEmitted(): CapturedEmit[] {
|
|
196
|
+
return this.#emitted.map(cloneEmit)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** See `getEmitted` — same lossy-clone caveat for non-cloneable values. */
|
|
200
|
+
getRequests(): CapturedRequest[] {
|
|
201
|
+
return this.#requests.map(cloneRequest)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
reset(): void {
|
|
205
|
+
this.#emitted = []
|
|
206
|
+
this.#requests = []
|
|
207
|
+
this.#subscribers.clear()
|
|
208
|
+
this.#requestHandlers.clear()
|
|
209
|
+
this.#nextSubId = 1
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
assertEmitted(name: string, predicate?: FakeBusPredicateArg): void {
|
|
213
|
+
const match = makeMatcher(name, predicate)
|
|
214
|
+
if (this.#emitted.some(match)) return
|
|
215
|
+
throw new Error(
|
|
216
|
+
`FakeBus.assertEmitted('${name}'${describePredicate(predicate)}) failed — no captured emit matches.\n${describeCaptured(this.#emitted)}`,
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
assertNotEmitted(name: string, predicate?: FakeBusPredicateArg): void {
|
|
221
|
+
const match = makeMatcher(name, predicate)
|
|
222
|
+
if (!this.#emitted.some(match)) return
|
|
223
|
+
throw new Error(
|
|
224
|
+
`FakeBus.assertNotEmitted('${name}'${describePredicate(predicate)}) failed — at least one captured emit matches.\n${describeCaptured(this.#emitted)}`,
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Port of `wildcard_matches` from `crates/ream-events/src/router.rs`.
|
|
230
|
+
* Authoritative source is the Rust router; any divergence is a bug. */
|
|
231
|
+
function wildcardMatches(pattern: string, eventName: string): boolean {
|
|
232
|
+
if (pattern === '**') return true
|
|
233
|
+
if (pattern === '*') return !eventName.includes('.')
|
|
234
|
+
|
|
235
|
+
const patternParts = pattern.split('.')
|
|
236
|
+
const nameParts = eventName.split('.')
|
|
237
|
+
|
|
238
|
+
const last = patternParts[patternParts.length - 1]
|
|
239
|
+
if (last === '**') {
|
|
240
|
+
const prefix = patternParts.slice(0, patternParts.length - 1)
|
|
241
|
+
if (nameParts.length < prefix.length) return false
|
|
242
|
+
for (let i = 0; i < prefix.length; i += 1) {
|
|
243
|
+
if (prefix[i] !== nameParts[i]) return false
|
|
244
|
+
}
|
|
245
|
+
return true
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (patternParts.length !== nameParts.length) return false
|
|
249
|
+
for (let i = 0; i < patternParts.length; i += 1) {
|
|
250
|
+
const p = patternParts[i]
|
|
251
|
+
if (p !== '*' && p !== nameParts[i]) return false
|
|
252
|
+
}
|
|
253
|
+
return true
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
interface ParsedHeader {
|
|
257
|
+
parsedData?: unknown
|
|
258
|
+
correlationId?: string
|
|
259
|
+
causationId?: string
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function parseEmitData(data: string): ParsedHeader {
|
|
263
|
+
let parsed: unknown
|
|
264
|
+
try {
|
|
265
|
+
parsed = JSON.parse(data)
|
|
266
|
+
} catch {
|
|
267
|
+
return {}
|
|
268
|
+
}
|
|
269
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
270
|
+
return { parsedData: parsed }
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
parsedData: parsed,
|
|
274
|
+
correlationId: readStringField(parsed, 'correlationId'),
|
|
275
|
+
causationId: readStringField(parsed, 'causationId'),
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function readStringField(source: object, key: string): string | undefined {
|
|
280
|
+
const record = source as Record<string, unknown>
|
|
281
|
+
const value = record[key]
|
|
282
|
+
return typeof value === 'string' ? value : undefined
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function buildEventJson(entry: CapturedEmit): string {
|
|
286
|
+
// Mirror the Rust `Event` envelope shape (`crates/ream-events/src/event.rs` —
|
|
287
|
+
// serde `rename_all = "camelCase"`): `{ id, name, data, correlationId,
|
|
288
|
+
// causationId?, timestamp, sourceService, nodeId, ttl }`. `data` stays the
|
|
289
|
+
// raw payload string the caller passed to `emit`, so subscribers using
|
|
290
|
+
// `JSON.parse(event.data)` after `JSON.parse(eventJson)` work the same
|
|
291
|
+
// against the fake as against the real bus. The five envelope-metadata
|
|
292
|
+
// fields (`id`, `timestamp`, `sourceService`, `nodeId`, `ttl`) are
|
|
293
|
+
// stubbed to the same defaults `Event::new` sets in Rust — synthetic but
|
|
294
|
+
// type-shape complete, so code-under-test reading them does not branch
|
|
295
|
+
// differently fake-vs-prod.
|
|
296
|
+
const generatedId = globalThis.crypto.randomUUID()
|
|
297
|
+
const envelope: Record<string, unknown> = {
|
|
298
|
+
id: generatedId,
|
|
299
|
+
name: entry.name,
|
|
300
|
+
data: entry.data,
|
|
301
|
+
correlationId: entry.correlationId ?? generatedId,
|
|
302
|
+
timestamp: new Date().toISOString(),
|
|
303
|
+
sourceService: '',
|
|
304
|
+
nodeId: 'local',
|
|
305
|
+
ttl: 255,
|
|
306
|
+
}
|
|
307
|
+
if (entry.causationId !== undefined) envelope.causationId = entry.causationId
|
|
308
|
+
return JSON.stringify(envelope)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function cloneEmit(entry: CapturedEmit): CapturedEmit {
|
|
312
|
+
try {
|
|
313
|
+
return structuredClone(entry)
|
|
314
|
+
} catch {
|
|
315
|
+
console.warn(
|
|
316
|
+
`[FakeBus] non-cloneable capture for "${entry.name}"; falling back to JSON round-trip.`,
|
|
317
|
+
)
|
|
318
|
+
return {
|
|
319
|
+
kind: 'emit',
|
|
320
|
+
name: entry.name,
|
|
321
|
+
data: entry.data,
|
|
322
|
+
parsedData: cloneViaJson(entry.parsedData),
|
|
323
|
+
correlationId: entry.correlationId,
|
|
324
|
+
causationId: entry.causationId,
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function cloneRequest(entry: CapturedRequest): CapturedRequest {
|
|
330
|
+
try {
|
|
331
|
+
return structuredClone(entry)
|
|
332
|
+
} catch {
|
|
333
|
+
console.warn(
|
|
334
|
+
`[FakeBus] non-cloneable capture for "${entry.name}"; falling back to JSON round-trip.`,
|
|
335
|
+
)
|
|
336
|
+
return {
|
|
337
|
+
kind: 'request',
|
|
338
|
+
name: entry.name,
|
|
339
|
+
data: entry.data,
|
|
340
|
+
parsedData: cloneViaJson(entry.parsedData),
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function cloneViaJson(value: unknown): unknown {
|
|
346
|
+
try {
|
|
347
|
+
return JSON.parse(JSON.stringify(value))
|
|
348
|
+
} catch {
|
|
349
|
+
return undefined
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function makeMatcher(
|
|
354
|
+
name: string,
|
|
355
|
+
predicate: FakeBusPredicateArg | undefined,
|
|
356
|
+
): (e: CapturedEmit) => boolean {
|
|
357
|
+
if (typeof predicate === 'function') {
|
|
358
|
+
return (e) => wildcardMatches(name, e.name) && predicate(e)
|
|
359
|
+
}
|
|
360
|
+
if (predicate === undefined) {
|
|
361
|
+
return (e) => wildcardMatches(name, e.name)
|
|
362
|
+
}
|
|
363
|
+
return (e) => {
|
|
364
|
+
if (!wildcardMatches(name, e.name)) return false
|
|
365
|
+
if (predicate.name !== undefined && !wildcardMatches(predicate.name, e.name)) return false
|
|
366
|
+
if (predicate.correlationId !== undefined && e.correlationId !== predicate.correlationId)
|
|
367
|
+
return false
|
|
368
|
+
if (predicate.causationId !== undefined && e.causationId !== predicate.causationId) return false
|
|
369
|
+
if (predicate.dataMatches) {
|
|
370
|
+
const arg = e.parsedData !== undefined ? e.parsedData : e.data
|
|
371
|
+
if (!predicate.dataMatches(arg)) return false
|
|
372
|
+
}
|
|
373
|
+
return true
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function describePredicate(predicate: FakeBusPredicateArg | undefined): string {
|
|
378
|
+
if (predicate === undefined) return ''
|
|
379
|
+
if (typeof predicate === 'function') return ', <function predicate>'
|
|
380
|
+
if (Object.keys(predicate).length === 0) {
|
|
381
|
+
return ', <empty predicate>'
|
|
382
|
+
}
|
|
383
|
+
return `, ${safeStringify(predicate)}`
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function describeCaptured(captured: CapturedEmit[]): string {
|
|
387
|
+
if (captured.length === 0) return 'Captured: (none)'
|
|
388
|
+
const lines = captured.map(
|
|
389
|
+
(c, i) =>
|
|
390
|
+
` [${i}] name="${c.name}" data=${safeStringify(c.parsedData ?? c.data)}` +
|
|
391
|
+
(c.correlationId !== undefined ? ` correlationId="${c.correlationId}"` : '') +
|
|
392
|
+
(c.causationId !== undefined ? ` causationId="${c.causationId}"` : ''),
|
|
393
|
+
)
|
|
394
|
+
return `Captured (${captured.length}):\n${lines.join('\n')}`
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function safeStringify(value: unknown): string {
|
|
398
|
+
// JSON.stringify already detects true cycles and throws TypeError —
|
|
399
|
+
// the outer catch handles that. A previous manual WeakSet walk
|
|
400
|
+
// mis-labelled DAG sibling references as `<circular>` because it
|
|
401
|
+
// never released entries on subtree exit.
|
|
402
|
+
try {
|
|
403
|
+
return JSON.stringify(value, (_key, v: unknown) => {
|
|
404
|
+
if (typeof v === 'function') return '<function>'
|
|
405
|
+
// JSON.stringify throws on BigInt; preserve the value lossy-but-
|
|
406
|
+
// readable so a single BigInt in a captured payload does not
|
|
407
|
+
// collapse the whole failure message to "<unstringifiable>".
|
|
408
|
+
if (typeof v === 'bigint') return `${v}n`
|
|
409
|
+
return v
|
|
410
|
+
})
|
|
411
|
+
} catch {
|
|
412
|
+
return '<unstringifiable>'
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function isThenable(value: unknown): value is PromiseLike<unknown> {
|
|
417
|
+
if (value === null || typeof value !== 'object') return false
|
|
418
|
+
if (!('then' in value)) return false
|
|
419
|
+
return typeof value.then === 'function'
|
|
420
|
+
}
|