@effect-app/infra 4.0.0-beta.26 → 4.0.0-beta.261
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/CHANGELOG.md +1973 -0
- package/_check.sh +1 -1
- package/dist/CUPS.d.ts +30 -11
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +35 -14
- package/dist/ClusterCosmos.d.ts +64 -0
- package/dist/ClusterCosmos.d.ts.map +1 -0
- package/dist/ClusterCosmos.js +501 -0
- package/dist/ClusterServiceBus.d.ts +67 -0
- package/dist/ClusterServiceBus.d.ts.map +1 -0
- package/dist/ClusterServiceBus.js +82 -0
- package/dist/ContextProvider.d.ts +34 -0
- package/dist/ContextProvider.d.ts.map +1 -0
- package/dist/ContextProvider.js +40 -0
- package/dist/Emailer/Sendgrid.d.ts +111 -147
- package/dist/Emailer/Sendgrid.d.ts.map +1 -1
- package/dist/Emailer/Sendgrid.js +24 -19
- package/dist/Emailer/fake.d.ts +2 -2
- package/dist/Emailer/fake.d.ts.map +1 -1
- package/dist/Emailer/fake.js +4 -4
- package/dist/MainFiberSet.d.ts +12 -9
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +10 -6
- package/dist/QueueMaker/SQLQueue.d.ts +8 -9
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +138 -120
- package/dist/QueueMaker/errors.d.ts +5 -3
- package/dist/QueueMaker/errors.d.ts.map +1 -1
- package/dist/QueueMaker/errors.js +4 -2
- package/dist/QueueMaker/memQueue.d.ts +10 -6
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +84 -68
- package/dist/QueueMaker/sbqueue.d.ts +9 -5
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +60 -58
- package/dist/RequestFiberSet.d.ts +10 -7
- package/dist/RequestFiberSet.d.ts.map +1 -1
- package/dist/RequestFiberSet.js +13 -8
- package/dist/SQL/Model.d.ts +468 -0
- package/dist/SQL/Model.d.ts.map +1 -0
- package/dist/SQL/Model.js +469 -0
- package/dist/SQL.d.ts +2 -0
- package/dist/SQL.d.ts.map +1 -0
- package/dist/{adapters/SQL.js → SQL.js} +1 -1
- package/dist/ServiceBus.d.ts +61 -0
- package/dist/ServiceBus.d.ts.map +1 -0
- package/dist/ServiceBus.js +108 -0
- package/dist/Store/Cosmos/query.d.ts +15 -4
- package/dist/Store/Cosmos/query.d.ts.map +1 -1
- package/dist/Store/Cosmos/query.js +179 -41
- package/dist/Store/Cosmos.d.ts +3 -3
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +344 -246
- package/dist/Store/Disk.d.ts +5 -5
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +78 -38
- package/dist/Store/Memory.d.ts +7 -10
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +326 -66
- package/dist/Store/SQL/Pg.d.ts +4 -0
- package/dist/Store/SQL/Pg.d.ts.map +1 -0
- package/dist/Store/SQL/Pg.js +232 -0
- package/dist/Store/SQL/query.d.ts +49 -0
- package/dist/Store/SQL/query.d.ts.map +1 -0
- package/dist/Store/SQL/query.js +527 -0
- package/dist/Store/SQL.d.ts +21 -0
- package/dist/Store/SQL.d.ts.map +1 -0
- package/dist/Store/SQL.js +449 -0
- package/dist/Store/codeFilter.d.ts +5 -5
- package/dist/Store/codeFilter.d.ts.map +1 -1
- package/dist/Store/codeFilter.js +6 -3
- package/dist/Store/index.d.ts +7 -5
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +18 -5
- package/dist/Store/utils.d.ts +4 -3
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +5 -5
- package/dist/WorkflowEngineCosmos.d.ts +29 -0
- package/dist/WorkflowEngineCosmos.d.ts.map +1 -0
- package/dist/WorkflowEngineCosmos.js +521 -0
- package/dist/WorkflowEngineSqlite.d.ts +24 -0
- package/dist/WorkflowEngineSqlite.d.ts.map +1 -0
- package/dist/WorkflowEngineSqlite.js +550 -0
- package/dist/arbs.d.ts +2 -2
- package/dist/arbs.d.ts.map +1 -1
- package/dist/arbs.js +5 -3
- package/dist/codec.d.ts +5 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +5 -0
- package/dist/cosmos-client.d.ts +16 -0
- package/dist/cosmos-client.d.ts.map +1 -0
- package/dist/cosmos-client.js +11 -0
- package/dist/errorReporter.d.ts +7 -5
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +23 -27
- package/dist/errors.d.ts +1 -1
- package/dist/fileUtil.d.ts +2 -2
- package/dist/fileUtil.d.ts.map +1 -1
- package/dist/fileUtil.js +2 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/internal/RequestContextMiddleware.d.ts +5 -0
- package/dist/internal/RequestContextMiddleware.d.ts.map +1 -0
- package/dist/internal/RequestContextMiddleware.js +45 -0
- package/dist/internal/auth.d.ts +53 -0
- package/dist/internal/auth.d.ts.map +1 -0
- package/dist/internal/auth.js +180 -0
- package/dist/internal/events.d.ts +11 -0
- package/dist/internal/events.d.ts.map +1 -0
- package/dist/internal/events.js +49 -0
- package/dist/internal/health.d.ts +3 -0
- package/dist/internal/health.d.ts.map +1 -0
- package/dist/internal/health.js +5 -0
- package/dist/layerUtils.d.ts +32 -0
- package/dist/layerUtils.d.ts.map +1 -0
- package/dist/layerUtils.js +17 -0
- package/dist/logger/jsonLogger.d.ts +2 -2
- package/dist/logger/jsonLogger.d.ts.map +1 -1
- package/dist/logger/jsonLogger.js +5 -3
- package/dist/logger/logFmtLogger.d.ts +2 -2
- package/dist/logger/logFmtLogger.d.ts.map +1 -1
- package/dist/logger/logFmtLogger.js +3 -3
- package/dist/logger/shared.d.ts +3 -3
- package/dist/logger/shared.d.ts.map +1 -1
- package/dist/logger/shared.js +5 -5
- package/dist/logger.d.ts +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/memQueue.d.ts +15 -0
- package/dist/memQueue.d.ts.map +1 -0
- package/dist/memQueue.js +21 -0
- package/dist/middlewares.d.ts +10 -0
- package/dist/middlewares.d.ts.map +1 -0
- package/dist/{api/middlewares.js → middlewares.js} +1 -1
- package/dist/mongo-client.d.ts +11 -0
- package/dist/mongo-client.d.ts.map +1 -0
- package/dist/mongo-client.js +15 -0
- package/dist/otel.d.ts +75 -0
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +65 -0
- package/dist/rateLimit.d.ts +12 -4
- package/dist/rateLimit.d.ts.map +1 -1
- package/dist/rateLimit.js +7 -12
- package/dist/redis-client.d.ts +42 -0
- package/dist/redis-client.d.ts.map +1 -0
- package/dist/redis-client.js +98 -0
- package/dist/reportError.d.ts +4 -0
- package/dist/reportError.d.ts.map +1 -0
- package/dist/reportError.js +28 -0
- package/dist/routing/middleware/RouterMiddleware.d.ts +16 -0
- package/dist/routing/middleware/RouterMiddleware.d.ts.map +1 -0
- package/dist/{api/routing → routing}/middleware/RouterMiddleware.js +1 -1
- package/dist/routing/middleware/middleware.d.ts +48 -0
- package/dist/routing/middleware/middleware.d.ts.map +1 -0
- package/dist/routing/middleware/middleware.js +128 -0
- package/dist/routing/middleware.d.ts +3 -0
- package/dist/routing/middleware.d.ts.map +1 -0
- package/dist/{api/routing → routing}/middleware.js +1 -2
- package/dist/routing/schema/jwt.d.ts +4 -0
- package/dist/routing/schema/jwt.d.ts.map +1 -0
- package/dist/routing/schema/jwt.js +13 -0
- package/dist/routing/tsort.d.ts +8 -0
- package/dist/routing/tsort.d.ts.map +1 -0
- package/dist/routing/tsort.js +51 -0
- package/dist/routing/utils.d.ts +19 -0
- package/dist/routing/utils.d.ts.map +1 -0
- package/dist/routing/utils.js +45 -0
- package/dist/routing.d.ts +184 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +236 -0
- package/dist/test.d.ts +3 -3
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +2 -2
- package/dist/util.d.ts +3 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +14 -0
- package/dist/vitest.d.ts +1 -1
- package/docs/cluster-storage.md +26 -0
- package/docs/workflow-engine.md +262 -0
- package/examples/query.ts +47 -39
- package/package.json +31 -345
- package/run.sh +1 -0
- package/src/CUPS.ts +52 -13
- package/src/ClusterCosmos.ts +984 -0
- package/src/ClusterServiceBus.ts +242 -0
- package/src/{api/ContextProvider.ts → ContextProvider.ts} +19 -16
- package/src/Emailer/Sendgrid.ts +82 -59
- package/src/Emailer/fake.ts +3 -3
- package/src/MainFiberSet.ts +12 -10
- package/src/QueueMaker/SQLQueue.ts +153 -156
- package/src/QueueMaker/errors.ts +3 -1
- package/src/QueueMaker/memQueue.ts +113 -107
- package/src/QueueMaker/sbqueue.ts +78 -90
- package/src/RequestFiberSet.ts +13 -8
- package/src/{adapters/SQL → SQL}/Model.ts +42 -41
- package/src/ServiceBus.ts +219 -0
- package/src/Store/Cosmos/query.ts +216 -52
- package/src/Store/Cosmos.ts +493 -353
- package/src/Store/Disk.ts +109 -69
- package/src/Store/Memory.ts +365 -96
- package/src/Store/SQL/Pg.ts +363 -0
- package/src/Store/SQL/query.ts +603 -0
- package/src/Store/SQL.ts +735 -0
- package/src/Store/codeFilter.ts +8 -5
- package/src/Store/index.ts +21 -6
- package/src/Store/utils.ts +26 -24
- package/src/WorkflowEngineCosmos.ts +719 -0
- package/src/WorkflowEngineSqlite.ts +813 -0
- package/src/arbs.ts +5 -3
- package/src/{adapters/cosmos-client.ts → cosmos-client.ts} +5 -3
- package/src/errorReporter.ts +66 -76
- package/src/fileUtil.ts +1 -1
- package/src/index.ts +2 -1
- package/src/{api/internal → internal}/RequestContextMiddleware.ts +23 -6
- package/src/internal/auth.ts +272 -0
- package/src/{api/internal → internal}/events.ts +22 -13
- package/src/{api/layerUtils.ts → layerUtils.ts} +14 -10
- package/src/logger/jsonLogger.ts +4 -2
- package/src/logger/logFmtLogger.ts +2 -2
- package/src/logger/shared.ts +5 -4
- package/src/{adapters/memQueue.ts → memQueue.ts} +5 -4
- package/src/{adapters/mongo-client.ts → mongo-client.ts} +4 -2
- package/src/otel.ts +152 -0
- package/src/rateLimit.ts +34 -23
- package/src/{adapters/redis-client.ts → redis-client.ts} +7 -3
- package/src/{api/reportError.ts → reportError.ts} +3 -2
- package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +5 -4
- package/src/{api/routing → routing}/middleware/middleware.ts +62 -17
- package/src/routing/middleware.ts +4 -0
- package/src/{api/routing → routing}/schema/jwt.ts +2 -1
- package/src/{api/routing → routing}/utils.ts +2 -1
- package/src/routing.ts +768 -0
- package/src/test.ts +2 -2
- package/test/auth.test.ts +101 -0
- package/test/cluster-cosmos.test.ts +590 -0
- package/test/cluster-servicebus.test.ts +180 -0
- package/test/cluster-sqlite.test.ts +207 -0
- package/test/contextProvider.test.ts +15 -12
- package/test/controller.test.ts +28 -32
- package/test/cosmos-query.test.ts +159 -0
- package/test/dist/_check-agg-infer.test-d.d.ts +2 -0
- package/test/dist/_check-agg-infer.test-d.d.ts.map +1 -0
- package/test/dist/_check-agg-infer.test-d.js +19 -0
- package/test/dist/_check-proj-infer.test-d.d.ts +2 -0
- package/test/dist/_check-proj-infer.test-d.d.ts.map +1 -0
- package/test/dist/_check-proj-infer.test-d.js +16 -0
- package/test/dist/_check-tighten.test-d.d.ts +2 -0
- package/test/dist/_check-tighten.test-d.d.ts.map +1 -0
- package/test/dist/_check-tighten.test-d.js +21 -0
- package/test/dist/auth.test.d.ts.map +1 -0
- package/test/dist/cluster-cosmos.test.d.ts.map +1 -0
- package/test/dist/cluster-servicebus.test.d.ts.map +1 -0
- package/test/dist/cluster-sqlite.test.d.ts.map +1 -0
- package/test/dist/contextProvider.test.d.ts.map +1 -1
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/cosmos-query.test.d.ts.map +1 -0
- package/test/dist/date-query.test.d.ts.map +1 -0
- package/test/dist/fixtures.d.ts +30 -12
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +17 -10
- package/test/dist/query.test.d.ts.map +1 -1
- package/test/dist/rawQuery.test.d.ts.map +1 -1
- package/test/dist/repository-ext.test.d.ts.map +1 -0
- package/test/dist/requires.test.d.ts.map +1 -1
- package/test/dist/router-generator.test.d.ts.map +1 -0
- package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
- package/test/dist/rpc-context-map-streaming.test.d.ts.map +1 -0
- package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
- package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
- package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
- package/test/dist/sql-store.test.d.ts.map +1 -0
- package/test/dist/workflow-engine-cosmos.test.d.ts.map +1 -0
- package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
- package/test/fixtures.ts +16 -9
- package/test/layerUtils.test.ts +2 -2
- package/test/query.test.ts +905 -40
- package/test/rawQuery.test.ts +340 -22
- package/test/repository-ext.test.ts +62 -0
- package/test/requires.test.ts +10 -5
- package/test/router-generator.test.ts +187 -0
- package/test/routing-interruptibility.test.ts +66 -0
- package/test/rpc-context-map-streaming.test.ts +262 -0
- package/test/rpc-e2e-invalidation.test.ts +256 -0
- package/test/rpc-multi-middleware.test.ts +85 -10
- package/test/rpc-stream-fullstack.test.ts +304 -0
- package/test/sql-store.test.ts +1711 -0
- package/test/validateSample.test.ts +26 -14
- package/test/workflow-engine-cosmos.test.ts +354 -0
- package/test/workflow-engine-sqlite.test.ts +299 -0
- package/tsconfig.examples.json +1 -1
- package/tsconfig.json +2 -1
- package/tsconfig.json.bak +2 -2
- package/tsconfig.src.json +35 -35
- package/tsconfig.test.json +2 -2
- package/dist/Emailer/service.d.ts +0 -55
- package/dist/Emailer/service.d.ts.map +0 -1
- package/dist/Emailer/service.js +0 -6
- package/dist/Emailer.d.ts +0 -2
- package/dist/Emailer.d.ts.map +0 -1
- package/dist/Emailer.js +0 -2
- package/dist/Model/Repository/ext.d.ts +0 -41
- package/dist/Model/Repository/ext.d.ts.map +0 -1
- package/dist/Model/Repository/ext.js +0 -65
- package/dist/Model/Repository/internal/internal.d.ts +0 -59
- package/dist/Model/Repository/internal/internal.d.ts.map +0 -1
- package/dist/Model/Repository/internal/internal.js +0 -316
- package/dist/Model/Repository/legacy.d.ts +0 -19
- package/dist/Model/Repository/legacy.d.ts.map +0 -1
- package/dist/Model/Repository/legacy.js +0 -2
- package/dist/Model/Repository/makeRepo.d.ts +0 -49
- package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
- package/dist/Model/Repository/makeRepo.js +0 -24
- package/dist/Model/Repository/service.d.ts +0 -89
- package/dist/Model/Repository/service.d.ts.map +0 -1
- package/dist/Model/Repository/service.js +0 -2
- package/dist/Model/Repository/validation.d.ts +0 -42
- package/dist/Model/Repository/validation.d.ts.map +0 -1
- package/dist/Model/Repository/validation.js +0 -32
- package/dist/Model/Repository.d.ts +0 -6
- package/dist/Model/Repository.d.ts.map +0 -1
- package/dist/Model/Repository.js +0 -6
- package/dist/Model/dsl.d.ts +0 -32
- package/dist/Model/dsl.d.ts.map +0 -1
- package/dist/Model/dsl.js +0 -44
- package/dist/Model/filter/filterApi.d.ts +0 -30
- package/dist/Model/filter/filterApi.d.ts.map +0 -1
- package/dist/Model/filter/filterApi.js +0 -2
- package/dist/Model/filter/types/errors.d.ts +0 -29
- package/dist/Model/filter/types/errors.d.ts.map +0 -1
- package/dist/Model/filter/types/errors.js +0 -2
- package/dist/Model/filter/types/fields.d.ts +0 -15
- package/dist/Model/filter/types/fields.d.ts.map +0 -1
- package/dist/Model/filter/types/fields.js +0 -2
- package/dist/Model/filter/types/path/common.d.ts +0 -316
- package/dist/Model/filter/types/path/common.d.ts.map +0 -1
- package/dist/Model/filter/types/path/common.js +0 -2
- package/dist/Model/filter/types/path/eager.d.ts +0 -95
- package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
- package/dist/Model/filter/types/path/eager.js +0 -31
- package/dist/Model/filter/types/path/index.d.ts +0 -4
- package/dist/Model/filter/types/path/index.d.ts.map +0 -1
- package/dist/Model/filter/types/path/index.js +0 -3
- package/dist/Model/filter/types/utils.d.ts +0 -79
- package/dist/Model/filter/types/utils.d.ts.map +0 -1
- package/dist/Model/filter/types/utils.js +0 -2
- package/dist/Model/filter/types/validator.d.ts +0 -30
- package/dist/Model/filter/types/validator.d.ts.map +0 -1
- package/dist/Model/filter/types/validator.js +0 -2
- package/dist/Model/filter/types.d.ts +0 -5
- package/dist/Model/filter/types.d.ts.map +0 -1
- package/dist/Model/filter/types.js +0 -7
- package/dist/Model/query/dsl.d.ts +0 -248
- package/dist/Model/query/dsl.d.ts.map +0 -1
- package/dist/Model/query/dsl.js +0 -104
- package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
- package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
- package/dist/Model/query/new-kid-interpreter.js +0 -165
- package/dist/Model/query.d.ts +0 -15
- package/dist/Model/query.d.ts.map +0 -1
- package/dist/Model/query.js +0 -3
- package/dist/Model.d.ts +0 -4
- package/dist/Model.d.ts.map +0 -1
- package/dist/Model.js +0 -4
- package/dist/Operations.d.ts +0 -55
- package/dist/Operations.d.ts.map +0 -1
- package/dist/Operations.js +0 -102
- package/dist/OperationsRepo.d.ts +0 -41
- package/dist/OperationsRepo.d.ts.map +0 -1
- package/dist/OperationsRepo.js +0 -14
- package/dist/QueueMaker/service.d.ts +0 -11
- package/dist/QueueMaker/service.d.ts.map +0 -1
- package/dist/QueueMaker/service.js +0 -4
- package/dist/RequestContext.d.ts +0 -63
- package/dist/RequestContext.d.ts.map +0 -1
- package/dist/RequestContext.js +0 -49
- package/dist/Store/ContextMapContainer.d.ts +0 -14
- package/dist/Store/ContextMapContainer.d.ts.map +0 -1
- package/dist/Store/ContextMapContainer.js +0 -16
- package/dist/Store/service.d.ts +0 -108
- package/dist/Store/service.d.ts.map +0 -1
- package/dist/Store/service.js +0 -71
- package/dist/Store.d.ts +0 -2
- package/dist/Store.d.ts.map +0 -1
- package/dist/Store.js +0 -2
- package/dist/adapters/SQL/Model.d.ts +0 -479
- package/dist/adapters/SQL/Model.d.ts.map +0 -1
- package/dist/adapters/SQL/Model.js +0 -478
- package/dist/adapters/SQL.d.ts +0 -2
- package/dist/adapters/SQL.d.ts.map +0 -1
- package/dist/adapters/ServiceBus.d.ts +0 -58
- package/dist/adapters/ServiceBus.d.ts.map +0 -1
- package/dist/adapters/ServiceBus.js +0 -99
- package/dist/adapters/cosmos-client.d.ts +0 -14
- package/dist/adapters/cosmos-client.d.ts.map +0 -1
- package/dist/adapters/cosmos-client.js +0 -9
- package/dist/adapters/index.d.ts +0 -2
- package/dist/adapters/index.d.ts.map +0 -1
- package/dist/adapters/index.js +0 -2
- package/dist/adapters/logger.d.ts +0 -9
- package/dist/adapters/logger.d.ts.map +0 -1
- package/dist/adapters/logger.js +0 -3
- package/dist/adapters/memQueue.d.ts +0 -13
- package/dist/adapters/memQueue.d.ts.map +0 -1
- package/dist/adapters/memQueue.js +0 -20
- package/dist/adapters/mongo-client.d.ts +0 -10
- package/dist/adapters/mongo-client.d.ts.map +0 -1
- package/dist/adapters/mongo-client.js +0 -13
- package/dist/adapters/redis-client.d.ts +0 -39
- package/dist/adapters/redis-client.d.ts.map +0 -1
- package/dist/adapters/redis-client.js +0 -94
- package/dist/api/ContextProvider.d.ts +0 -31
- package/dist/api/ContextProvider.d.ts.map +0 -1
- package/dist/api/ContextProvider.js +0 -38
- package/dist/api/codec.d.ts +0 -5
- package/dist/api/codec.d.ts.map +0 -1
- package/dist/api/codec.js +0 -5
- package/dist/api/internal/RequestContextMiddleware.d.ts +0 -5
- package/dist/api/internal/RequestContextMiddleware.d.ts.map +0 -1
- package/dist/api/internal/RequestContextMiddleware.js +0 -35
- package/dist/api/internal/auth.d.ts +0 -15
- package/dist/api/internal/auth.d.ts.map +0 -1
- package/dist/api/internal/auth.js +0 -47
- package/dist/api/internal/events.d.ts +0 -9
- package/dist/api/internal/events.d.ts.map +0 -1
- package/dist/api/internal/events.js +0 -42
- package/dist/api/internal/health.d.ts +0 -3
- package/dist/api/internal/health.d.ts.map +0 -1
- package/dist/api/internal/health.js +0 -5
- package/dist/api/layerUtils.d.ts +0 -24
- package/dist/api/layerUtils.d.ts.map +0 -1
- package/dist/api/layerUtils.js +0 -16
- package/dist/api/middlewares.d.ts +0 -10
- package/dist/api/middlewares.d.ts.map +0 -1
- package/dist/api/reportError.d.ts +0 -4
- package/dist/api/reportError.d.ts.map +0 -1
- package/dist/api/reportError.js +0 -27
- package/dist/api/routing/middleware/RouterMiddleware.d.ts +0 -15
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
- package/dist/api/routing/middleware/middleware.d.ts +0 -9
- package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
- package/dist/api/routing/middleware/middleware.js +0 -92
- package/dist/api/routing/middleware.d.ts +0 -4
- package/dist/api/routing/middleware.d.ts.map +0 -1
- package/dist/api/routing/schema/jwt.d.ts +0 -4
- package/dist/api/routing/schema/jwt.d.ts.map +0 -1
- package/dist/api/routing/schema/jwt.js +0 -12
- package/dist/api/routing/tsort.d.ts +0 -8
- package/dist/api/routing/tsort.d.ts.map +0 -1
- package/dist/api/routing/tsort.js +0 -51
- package/dist/api/routing/utils.d.ts +0 -19
- package/dist/api/routing/utils.d.ts.map +0 -1
- package/dist/api/routing/utils.js +0 -44
- package/dist/api/routing.d.ts +0 -138
- package/dist/api/routing.d.ts.map +0 -1
- package/dist/api/routing.js +0 -166
- package/dist/api/setupRequest.d.ts +0 -12
- package/dist/api/setupRequest.d.ts.map +0 -1
- package/dist/api/setupRequest.js +0 -44
- package/dist/api/util.d.ts +0 -3
- package/dist/api/util.d.ts.map +0 -1
- package/dist/api/util.js +0 -14
- package/eslint.config.mjs +0 -24
- package/src/Emailer/service.ts +0 -52
- package/src/Emailer.ts +0 -1
- package/src/Model/Repository/ext.ts +0 -283
- package/src/Model/Repository/internal/internal.ts +0 -577
- package/src/Model/Repository/legacy.ts +0 -27
- package/src/Model/Repository/makeRepo.ts +0 -139
- package/src/Model/Repository/service.ts +0 -627
- package/src/Model/Repository/validation.ts +0 -31
- package/src/Model/Repository.ts +0 -5
- package/src/Model/dsl.ts +0 -128
- package/src/Model/filter/filterApi.ts +0 -60
- package/src/Model/filter/types/errors.ts +0 -47
- package/src/Model/filter/types/fields.ts +0 -50
- package/src/Model/filter/types/path/common.ts +0 -404
- package/src/Model/filter/types/path/eager.ts +0 -298
- package/src/Model/filter/types/path/index.ts +0 -4
- package/src/Model/filter/types/utils.ts +0 -128
- package/src/Model/filter/types/validator.ts +0 -46
- package/src/Model/filter/types.ts +0 -6
- package/src/Model/query/dsl.ts +0 -2110
- package/src/Model/query/new-kid-interpreter.ts +0 -210
- package/src/Model/query.ts +0 -13
- package/src/Model.ts +0 -3
- package/src/Operations.ts +0 -235
- package/src/OperationsRepo.ts +0 -16
- package/src/QueueMaker/service.ts +0 -17
- package/src/RequestContext.ts +0 -63
- package/src/Store/ContextMapContainer.ts +0 -20
- package/src/Store/service.ts +0 -184
- package/src/Store.ts +0 -1
- package/src/adapters/ServiceBus.ts +0 -209
- package/src/adapters/index.ts +0 -0
- package/src/adapters/logger.ts +0 -3
- package/src/api/internal/auth.ts +0 -68
- package/src/api/routing/middleware.ts +0 -6
- package/src/api/routing.ts +0 -598
- package/src/api/setupRequest.ts +0 -84
- /package/src/{adapters/SQL.ts → SQL.ts} +0 -0
- /package/src/{api/codec.ts → codec.ts} +0 -0
- /package/src/{api/internal → internal}/health.ts +0 -0
- /package/src/{api/middlewares.ts → middlewares.ts} +0 -0
- /package/src/{api/routing → routing}/tsort.ts +0 -0
- /package/src/{api/util.ts → util.ts} +0 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E tests for the invalidation key flow exercised end-to-end via the
|
|
3
|
+
* production wrap/unwrap path:
|
|
4
|
+
*
|
|
5
|
+
* server: routing.ts wraps command success with `CommandResponseWithMetaData`
|
|
6
|
+
* and handler-thrown failure with `CommandFailureWithMetaData`;
|
|
7
|
+
* routing wraps stream values into `{_tag:"value"|"metadata"|"done"}`
|
|
8
|
+
* chunks. `InvalidationSet.use(...)` inside a handler accumulates keys.
|
|
9
|
+
* client: apiClientFactory unwraps both envelopes and forwards keys to
|
|
10
|
+
* `InvalidationKeysFromServer`.
|
|
11
|
+
*
|
|
12
|
+
* Transport is real HTTP (NodeHttpServer on a loopback port) so the wire
|
|
13
|
+
* encoding is exercised too.
|
|
14
|
+
*/
|
|
15
|
+
import { NodeHttpServer } from "@effect/platform-node"
|
|
16
|
+
import { expect, it } from "@effect/vitest"
|
|
17
|
+
import { ApiClientFactory, InvalidationKeysFromServer, makeInvalidationKeysService, makeRpcClient } from "effect-app/client"
|
|
18
|
+
import { HttpRouter, HttpServer } from "effect-app/http"
|
|
19
|
+
import { DefaultGenericMiddlewares } from "effect-app/middleware"
|
|
20
|
+
import { Invalidation, MiddlewareMaker } from "effect-app/rpc"
|
|
21
|
+
import * as S from "effect-app/Schema"
|
|
22
|
+
import { TaggedErrorClass } from "effect-app/Schema"
|
|
23
|
+
import * as Effect from "effect/Effect"
|
|
24
|
+
import * as Exit from "effect/Exit"
|
|
25
|
+
import * as Layer from "effect/Layer"
|
|
26
|
+
import * as Option from "effect/Option"
|
|
27
|
+
import * as Ref from "effect/Ref"
|
|
28
|
+
import * as Stream from "effect/Stream"
|
|
29
|
+
import { FetchHttpClient } from "effect/unstable/http"
|
|
30
|
+
import { RpcSerialization } from "effect/unstable/rpc"
|
|
31
|
+
import { createServer } from "http"
|
|
32
|
+
import { makeRouter } from "../src/routing.js"
|
|
33
|
+
import { DefaultGenericMiddlewaresLive } from "../src/routing/middleware.js"
|
|
34
|
+
import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, SomeElseMiddleware, SomeElseMiddlewareLive, SomeService, Test, TestLive } from "./fixtures.js"
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Middleware (mirrors AppMiddleware shape — same composite as other e2e tests).
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
class AppMiddleware extends MiddlewareMaker
|
|
41
|
+
.Tag<AppMiddleware>()("AppMiddleware", RequestContextMap)
|
|
42
|
+
.middleware(RequireRoles, Test)
|
|
43
|
+
.middleware(AllowAnonymous)
|
|
44
|
+
.middleware(SomeElseMiddleware)
|
|
45
|
+
.middleware(...DefaultGenericMiddlewares)
|
|
46
|
+
{
|
|
47
|
+
static Default = this.layer.pipe(
|
|
48
|
+
Layer.provide(
|
|
49
|
+
[
|
|
50
|
+
RequireRolesLive.pipe(Layer.provide(SomeService.Default)),
|
|
51
|
+
AllowAnonymousLive,
|
|
52
|
+
TestLive,
|
|
53
|
+
SomeElseMiddlewareLive,
|
|
54
|
+
DefaultGenericMiddlewaresLive
|
|
55
|
+
] as const
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const { Router, matchAll } = makeRouter(AppMiddleware.Default)
|
|
61
|
+
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Resources
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
const DynamicKey: Invalidation.InvalidationKey = ["dynamic", "key"]
|
|
67
|
+
const ExtraKey: Invalidation.InvalidationKey = ["extra", "key"]
|
|
68
|
+
const StreamKey: Invalidation.InvalidationKey = ["stream", "key"]
|
|
69
|
+
|
|
70
|
+
const { TaggedRequestFor } = makeRpcClient(AppMiddleware)
|
|
71
|
+
const Req = TaggedRequestFor("Inv")
|
|
72
|
+
|
|
73
|
+
class CmdBoom extends TaggedErrorClass<CmdBoom>()("CmdBoom", { reason: S.String }) {}
|
|
74
|
+
|
|
75
|
+
class DoNothing extends Req.Command<DoNothing>()("DoNothing", {}, {
|
|
76
|
+
allowAnonymous: true,
|
|
77
|
+
success: S.Void
|
|
78
|
+
}) {}
|
|
79
|
+
|
|
80
|
+
class DoWithDynamicKey extends Req.Command<DoWithDynamicKey>()("DoWithDynamicKey", {}, {
|
|
81
|
+
allowAnonymous: true,
|
|
82
|
+
success: S.String
|
|
83
|
+
}) {}
|
|
84
|
+
|
|
85
|
+
class DoWithBothKeys extends Req.Command<DoWithBothKeys>()("DoWithBothKeys", {}, {
|
|
86
|
+
allowAnonymous: true,
|
|
87
|
+
success: S.Number
|
|
88
|
+
}) {}
|
|
89
|
+
|
|
90
|
+
class DoAndFail extends Req.Command<DoAndFail>()("DoAndFail", {}, {
|
|
91
|
+
allowAnonymous: true,
|
|
92
|
+
success: S.Void,
|
|
93
|
+
error: CmdBoom
|
|
94
|
+
}) {}
|
|
95
|
+
|
|
96
|
+
class StreamWithKey extends Req.Command<StreamWithKey>()("StreamWithKey", {}, {
|
|
97
|
+
stream: true,
|
|
98
|
+
allowAnonymous: true,
|
|
99
|
+
success: S.Number
|
|
100
|
+
}) {}
|
|
101
|
+
|
|
102
|
+
const InvRsc = { DoNothing, DoWithDynamicKey, DoWithBothKeys, DoAndFail, StreamWithKey }
|
|
103
|
+
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Controllers / router
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
const router = Router(InvRsc)({
|
|
109
|
+
*effect(match) {
|
|
110
|
+
return match({
|
|
111
|
+
DoNothing: () => Effect.void,
|
|
112
|
+
DoWithDynamicKey: Effect.fnUntraced(function*() {
|
|
113
|
+
yield* Invalidation.InvalidationSet.use((_) => _.add(DynamicKey))
|
|
114
|
+
return "done"
|
|
115
|
+
}),
|
|
116
|
+
DoWithBothKeys: Effect.fnUntraced(function*() {
|
|
117
|
+
yield* Invalidation.InvalidationSet.use((_) => _.add(DynamicKey))
|
|
118
|
+
yield* Invalidation.InvalidationSet.use((_) => _.add(ExtraKey))
|
|
119
|
+
return 99
|
|
120
|
+
}),
|
|
121
|
+
DoAndFail: Effect.fnUntraced(function*() {
|
|
122
|
+
yield* Invalidation.InvalidationSet.use((_) => _.add(DynamicKey))
|
|
123
|
+
return yield* Effect.fail(new CmdBoom({ reason: "intentional failure" }))
|
|
124
|
+
}),
|
|
125
|
+
StreamWithKey: () =>
|
|
126
|
+
Stream.fromIterable([1, 2, 3]).pipe(
|
|
127
|
+
Stream.tap(() => Invalidation.InvalidationSet.use((_) => _.add(StreamKey)))
|
|
128
|
+
)
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
const RpcRouterLayer = matchAll({ router })
|
|
134
|
+
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// HTTP wiring — fresh server on loopback per `it.live`.
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
const NodeServerLayer = NodeHttpServer.layer(() => createServer(), { port: 0 })
|
|
140
|
+
|
|
141
|
+
const ServerLayer = HttpRouter
|
|
142
|
+
.serve(RpcRouterLayer)
|
|
143
|
+
.pipe(
|
|
144
|
+
Layer.provide(NodeServerLayer),
|
|
145
|
+
Layer.provide(RpcSerialization.layerNdjson)
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
const ClientLayer = Layer
|
|
149
|
+
.unwrap(
|
|
150
|
+
Effect.gen(function*() {
|
|
151
|
+
const server = yield* HttpServer.HttpServer
|
|
152
|
+
const addr = server.address
|
|
153
|
+
if (addr._tag !== "TcpAddress") return yield* Effect.die(new Error("expected TcpAddress"))
|
|
154
|
+
const host = addr.hostname === "0.0.0.0" ? "127.0.0.1" : addr.hostname
|
|
155
|
+
const url = `http://${host}:${addr.port}`
|
|
156
|
+
return ApiClientFactory
|
|
157
|
+
.layer({ url, headers: Option.none() })
|
|
158
|
+
.pipe(Layer.provide(FetchHttpClient.layer))
|
|
159
|
+
})
|
|
160
|
+
)
|
|
161
|
+
.pipe(Layer.provide(NodeServerLayer))
|
|
162
|
+
|
|
163
|
+
const TestLayer = Layer.mergeAll(ServerLayer, ClientLayer)
|
|
164
|
+
|
|
165
|
+
// Helper: provide a fresh `InvalidationKeysFromServer` and capture forwarded keys.
|
|
166
|
+
const withCapture = <A, E, R>(eff: Effect.Effect<A, E, R>) =>
|
|
167
|
+
Effect.gen(function*() {
|
|
168
|
+
const ref = yield* Ref.make<ReadonlyArray<Invalidation.InvalidationKey>>([])
|
|
169
|
+
const svc = makeInvalidationKeysService(ref)
|
|
170
|
+
const result = yield* eff.pipe(Effect.provideService(InvalidationKeysFromServer, svc), Effect.exit)
|
|
171
|
+
return { result, keys: yield* Ref.get(ref) }
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// Tests
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
it.live(
|
|
179
|
+
"command with no invalidation keys: caller sees raw payload, no keys forwarded",
|
|
180
|
+
Effect.fnUntraced(function*() {
|
|
181
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(InvRsc)
|
|
182
|
+
const { result, keys } = yield* withCapture(client.DoNothing.handler())
|
|
183
|
+
expect(Exit.isSuccess(result)).toBe(true)
|
|
184
|
+
expect(keys).toStrictEqual([])
|
|
185
|
+
}, Effect.provide(TestLayer)),
|
|
186
|
+
{ timeout: 10_000 }
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
it.live(
|
|
190
|
+
"command with dynamic InvalidationSet.use: payload + key forwarded",
|
|
191
|
+
Effect.fnUntraced(function*() {
|
|
192
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(InvRsc)
|
|
193
|
+
const { result, keys } = yield* withCapture(client.DoWithDynamicKey.handler())
|
|
194
|
+
expect(Exit.isSuccess(result) && result.value).toBe("done")
|
|
195
|
+
expect(keys).toStrictEqual([DynamicKey])
|
|
196
|
+
}, Effect.provide(TestLayer)),
|
|
197
|
+
{ timeout: 10_000 }
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
it.live(
|
|
201
|
+
"command accumulating multiple dynamic keys: all keys forwarded in order",
|
|
202
|
+
Effect.fnUntraced(function*() {
|
|
203
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(InvRsc)
|
|
204
|
+
const { result, keys } = yield* withCapture(client.DoWithBothKeys.handler())
|
|
205
|
+
expect(Exit.isSuccess(result) && result.value).toBe(99)
|
|
206
|
+
expect(keys).toStrictEqual([DynamicKey, ExtraKey])
|
|
207
|
+
}, Effect.provide(TestLayer)),
|
|
208
|
+
{ timeout: 10_000 }
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
it.live(
|
|
212
|
+
"per-request isolation: each command call starts with a fresh InvalidationSet",
|
|
213
|
+
Effect.fnUntraced(function*() {
|
|
214
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(InvRsc)
|
|
215
|
+
const r1 = yield* withCapture(client.DoWithDynamicKey.handler())
|
|
216
|
+
const r2 = yield* withCapture(client.DoWithDynamicKey.handler())
|
|
217
|
+
// Each call must have exactly one key — no accumulation across calls
|
|
218
|
+
expect(r1.keys).toStrictEqual([DynamicKey])
|
|
219
|
+
expect(r2.keys).toStrictEqual([DynamicKey])
|
|
220
|
+
}, Effect.provide(TestLayer)),
|
|
221
|
+
{ timeout: 10_000 }
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
it.live(
|
|
225
|
+
"command failure (V2): keys accumulated before fail still reach the client; original error re-thrown",
|
|
226
|
+
Effect.fnUntraced(function*() {
|
|
227
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(InvRsc)
|
|
228
|
+
const { result, keys } = yield* withCapture(client.DoAndFail.handler())
|
|
229
|
+
expect(Exit.isFailure(result)).toBe(true)
|
|
230
|
+
if (Exit.isFailure(result)) {
|
|
231
|
+
const failures = (result.cause as any).reasons as ReadonlyArray<{ _tag: "Fail"; error: any }>
|
|
232
|
+
expect(failures[0]?.error?._tag).toBe("CmdBoom")
|
|
233
|
+
expect(failures[0]?.error?.reason).toBe("intentional failure")
|
|
234
|
+
}
|
|
235
|
+
expect(keys).toStrictEqual([DynamicKey])
|
|
236
|
+
}, Effect.provide(TestLayer)),
|
|
237
|
+
{ timeout: 10_000 }
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
it.live(
|
|
241
|
+
"stream: per-chunk metadata drains keys mid-stream",
|
|
242
|
+
Effect.fnUntraced(function*() {
|
|
243
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(InvRsc)
|
|
244
|
+
const ref = yield* Ref.make<ReadonlyArray<Invalidation.InvalidationKey>>([])
|
|
245
|
+
const svc = makeInvalidationKeysService(ref)
|
|
246
|
+
const values = yield* Stream.runCollect(client.StreamWithKey.handler()).pipe(
|
|
247
|
+
Effect.provideService(InvalidationKeysFromServer, svc)
|
|
248
|
+
)
|
|
249
|
+
const keys = yield* Ref.get(ref)
|
|
250
|
+
expect(values).toStrictEqual([1, 2, 3])
|
|
251
|
+
// Handler taps `InvalidationSet.use` once per emitted value; routing's V3 mid-stream
|
|
252
|
+
// metadata drain forwards each batch as it arrives.
|
|
253
|
+
expect(keys).toStrictEqual([StreamKey, StreamKey, StreamKey])
|
|
254
|
+
}, Effect.provide(TestLayer)),
|
|
255
|
+
{ timeout: 10_000 }
|
|
256
|
+
)
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import { NodeHttpServer } from "@effect/platform-node"
|
|
2
2
|
import { expect, expectTypeOf, it } from "@effect/vitest"
|
|
3
|
-
import { Console, Effect, Layer, Result } from "effect"
|
|
4
|
-
import { S } from "effect-app"
|
|
5
3
|
import { NotLoggedInError } from "effect-app/client"
|
|
4
|
+
import * as Context from "effect-app/Context"
|
|
6
5
|
import { HttpRouter } from "effect-app/http"
|
|
7
6
|
import { DefaultGenericMiddlewares } from "effect-app/middleware"
|
|
8
|
-
import
|
|
9
|
-
import
|
|
7
|
+
import * as RpcX from "effect-app/rpc"
|
|
8
|
+
import * as S from "effect-app/Schema"
|
|
9
|
+
import * as Console from "effect/Console"
|
|
10
|
+
import * as Effect from "effect/Effect"
|
|
11
|
+
import * as Layer from "effect/Layer"
|
|
12
|
+
import * as Ref from "effect/Ref"
|
|
13
|
+
import * as Result from "effect/Result"
|
|
10
14
|
import { FetchHttpClient } from "effect/unstable/http"
|
|
11
|
-
import { RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "effect/unstable/rpc"
|
|
15
|
+
import { Rpc, RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "effect/unstable/rpc"
|
|
12
16
|
import { createServer } from "http"
|
|
13
|
-
import { DefaultGenericMiddlewaresLive } from "../src/
|
|
17
|
+
import { DefaultGenericMiddlewaresLive } from "../src/routing.js"
|
|
14
18
|
import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElseMiddleware, SomeElseMiddlewareLive, SomeMiddleware, SomeMiddlewareLive, SomeService, Test, TestLive, UserProfile } from "./fixtures.js"
|
|
15
19
|
|
|
16
|
-
const incomplete =
|
|
20
|
+
const incomplete = RpcX
|
|
21
|
+
.MiddlewareMaker
|
|
17
22
|
.Tag<middleware>()("MiddlewareMaker", RequestContextMap)
|
|
18
23
|
.middleware(RequireRoles)
|
|
19
24
|
.middleware(AllowAnonymous, Test)
|
|
@@ -21,7 +26,8 @@ const incomplete = MiddlewareMaker
|
|
|
21
26
|
// this extension is allowed otherwise the error is quite obscure
|
|
22
27
|
export class incompleteMiddleware extends incomplete {}
|
|
23
28
|
|
|
24
|
-
class middleware extends
|
|
29
|
+
class middleware extends RpcX
|
|
30
|
+
.MiddlewareMaker
|
|
25
31
|
.Tag<middleware>()("MiddlewareMaker", RequestContextMap)
|
|
26
32
|
.middleware(RequireRoles)
|
|
27
33
|
.middleware(AllowAnonymous, Test)
|
|
@@ -29,7 +35,7 @@ class middleware extends MiddlewareMaker
|
|
|
29
35
|
.middleware(...DefaultGenericMiddlewares)
|
|
30
36
|
{}
|
|
31
37
|
|
|
32
|
-
const UserRpcs = middlewareGroup(middleware)(
|
|
38
|
+
const UserRpcs = RpcX.MiddlewareMaker.middlewareGroup(middleware)(
|
|
33
39
|
RpcGroup.make(
|
|
34
40
|
middleware.rpc("getUser", {
|
|
35
41
|
success: S.Literal("awesome")
|
|
@@ -56,7 +62,7 @@ const impl = UserRpcs
|
|
|
56
62
|
|
|
57
63
|
expectTypeOf<Layer.Services<typeof impl>>().toEqualTypeOf<never>()
|
|
58
64
|
|
|
59
|
-
const UserRpcsBad = middlewareGroup(middleware)(
|
|
65
|
+
const UserRpcsBad = RpcX.MiddlewareMaker.middlewareGroup(middleware)(
|
|
60
66
|
RpcGroup.make(
|
|
61
67
|
middleware.rpc("doSomethingElse", {
|
|
62
68
|
success: S.Literal("also-awesome2"),
|
|
@@ -136,3 +142,72 @@ it.live(
|
|
|
136
142
|
Effect.provide(RpcTestLayer)
|
|
137
143
|
)
|
|
138
144
|
)
|
|
145
|
+
|
|
146
|
+
// Per-request service isolation test
|
|
147
|
+
|
|
148
|
+
class PerRequestCounter extends Context.Service<PerRequestCounter>()(
|
|
149
|
+
"PerRequestCounter",
|
|
150
|
+
{ make: Effect.sync(() => ({ a: 0 })) }
|
|
151
|
+
) {
|
|
152
|
+
static Default = Layer.effect(this, this.make)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
class GlobalCounter extends Context.Service<GlobalCounter, {
|
|
156
|
+
readonly ref: Ref.Ref<number>
|
|
157
|
+
}>()("GlobalCounter") {}
|
|
158
|
+
|
|
159
|
+
const CounterRpcs = RpcGroup.make(
|
|
160
|
+
Rpc.make("incrementA", {
|
|
161
|
+
success: S.Number
|
|
162
|
+
}),
|
|
163
|
+
Rpc.make("incrementB", {
|
|
164
|
+
success: S.Number
|
|
165
|
+
})
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
const counterImpl = CounterRpcs
|
|
169
|
+
.toLayer({
|
|
170
|
+
incrementA: Effect.fn(function*() {
|
|
171
|
+
const counter = yield* PerRequestCounter
|
|
172
|
+
counter.a++
|
|
173
|
+
const global = yield* GlobalCounter
|
|
174
|
+
yield* Ref.update(global.ref, (n) => n + 1)
|
|
175
|
+
return counter.a
|
|
176
|
+
}, Effect.provide(PerRequestCounter.Default)),
|
|
177
|
+
incrementB: Effect.fn(function*() {
|
|
178
|
+
const counter = yield* PerRequestCounter
|
|
179
|
+
counter.a++
|
|
180
|
+
const global = yield* GlobalCounter
|
|
181
|
+
yield* Ref.update(global.ref, (n) => n + 1)
|
|
182
|
+
return counter.a
|
|
183
|
+
}, Effect.provide(PerRequestCounter.Default))
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
const GlobalCounterLive = Layer.effect(
|
|
187
|
+
GlobalCounter,
|
|
188
|
+
Ref.make(0).pipe(Effect.map((ref) => ({ ref })))
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
const CounterTestLayer = counterImpl.pipe(Layer.provideMerge(GlobalCounterLive))
|
|
192
|
+
|
|
193
|
+
it.live(
|
|
194
|
+
"per-request service isolation with shared global counter",
|
|
195
|
+
Effect.fnUntraced(
|
|
196
|
+
function*() {
|
|
197
|
+
const client = yield* RpcTest.makeClient(CounterRpcs)
|
|
198
|
+
const global = yield* GlobalCounter
|
|
199
|
+
|
|
200
|
+
const r1 = yield* client.incrementA()
|
|
201
|
+
const r2 = yield* client.incrementB()
|
|
202
|
+
|
|
203
|
+
// per-request counter is fresh each time → both return 1
|
|
204
|
+
expect(r1).toBe(1)
|
|
205
|
+
expect(r2).toBe(1)
|
|
206
|
+
|
|
207
|
+
// global counter is shared across requests → accumulates to 2
|
|
208
|
+
const globalCount = yield* Ref.get(global.ref)
|
|
209
|
+
expect(globalCount).toBe(2)
|
|
210
|
+
},
|
|
211
|
+
Effect.provide(CounterTestLayer)
|
|
212
|
+
)
|
|
213
|
+
)
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full-stack stream test exercising the entire wrapper:
|
|
3
|
+
* resources (TaggedRequestFor)
|
|
4
|
+
* → controllers (Router(...)({ effect }))
|
|
5
|
+
* → router (makeRouter / matchAll)
|
|
6
|
+
* → api ClientFactory (ApiClientFactory.makeFor)
|
|
7
|
+
*
|
|
8
|
+
* Server runs over real HTTP (NodeHttpServer on a loopback port). Client uses
|
|
9
|
+
* FetchHttpClient through ApiClientFactory. This covers the wrapper-level
|
|
10
|
+
* `Stream` request constructor end-to-end.
|
|
11
|
+
*/
|
|
12
|
+
import { NodeHttpServer } from "@effect/platform-node"
|
|
13
|
+
import { expect, it } from "@effect/vitest"
|
|
14
|
+
import { ApiClientFactory, makeRpcClient } from "effect-app/client"
|
|
15
|
+
import { HttpRouter, HttpServer } from "effect-app/http"
|
|
16
|
+
import { DefaultGenericMiddlewares } from "effect-app/middleware"
|
|
17
|
+
import { MiddlewareMaker } from "effect-app/rpc"
|
|
18
|
+
import * as S from "effect-app/Schema"
|
|
19
|
+
import { TaggedErrorClass } from "effect-app/Schema"
|
|
20
|
+
import * as Effect from "effect/Effect"
|
|
21
|
+
import * as Exit from "effect/Exit"
|
|
22
|
+
import * as Layer from "effect/Layer"
|
|
23
|
+
import * as Option from "effect/Option"
|
|
24
|
+
import * as Stream from "effect/Stream"
|
|
25
|
+
import { FetchHttpClient } from "effect/unstable/http"
|
|
26
|
+
import { RpcSerialization } from "effect/unstable/rpc"
|
|
27
|
+
import { createServer } from "http"
|
|
28
|
+
import { makeRouter } from "../src/routing.js"
|
|
29
|
+
import { DefaultGenericMiddlewaresLive } from "../src/routing/middleware.js"
|
|
30
|
+
import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, SomeElseMiddleware, SomeElseMiddlewareLive, SomeService, Test, TestLive } from "./fixtures.js"
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Middleware (mirrors the boilerplate AppMiddleware shape).
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
class AppMiddleware extends MiddlewareMaker
|
|
37
|
+
.Tag<AppMiddleware>()("AppMiddleware", RequestContextMap)
|
|
38
|
+
.middleware(RequireRoles, Test)
|
|
39
|
+
.middleware(AllowAnonymous)
|
|
40
|
+
.middleware(SomeElseMiddleware)
|
|
41
|
+
.middleware(...DefaultGenericMiddlewares)
|
|
42
|
+
{
|
|
43
|
+
static Default = this.layer.pipe(
|
|
44
|
+
Layer.provide(
|
|
45
|
+
[
|
|
46
|
+
RequireRolesLive.pipe(Layer.provide(SomeService.Default)),
|
|
47
|
+
AllowAnonymousLive,
|
|
48
|
+
TestLive,
|
|
49
|
+
SomeElseMiddlewareLive,
|
|
50
|
+
DefaultGenericMiddlewaresLive
|
|
51
|
+
] as const
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const { Router, matchAll } = makeRouter(AppMiddleware.Default)
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Resources — Stream with and without payload.
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
const { TaggedRequestFor } = makeRpcClient(AppMiddleware)
|
|
63
|
+
const Req = TaggedRequestFor("Streamy")
|
|
64
|
+
|
|
65
|
+
class StreamTicks extends Req.Command<StreamTicks>()("StreamTicks", {}, {
|
|
66
|
+
stream: true,
|
|
67
|
+
allowAnonymous: true,
|
|
68
|
+
success: S.Number
|
|
69
|
+
}) {}
|
|
70
|
+
|
|
71
|
+
class StreamCountTo extends Req.Command<StreamCountTo>()("StreamCountTo", {
|
|
72
|
+
to: S.Number
|
|
73
|
+
}, {
|
|
74
|
+
stream: true,
|
|
75
|
+
allowAnonymous: true,
|
|
76
|
+
success: S.Number
|
|
77
|
+
}) {}
|
|
78
|
+
|
|
79
|
+
class StreamRealtime extends Req.Command<StreamRealtime>()("StreamRealtime", {}, {
|
|
80
|
+
stream: true,
|
|
81
|
+
allowAnonymous: true,
|
|
82
|
+
success: S.Number
|
|
83
|
+
}) {}
|
|
84
|
+
|
|
85
|
+
class StreamBoom extends TaggedErrorClass<StreamBoom>()("StreamBoom", { reason: S.String }) {}
|
|
86
|
+
|
|
87
|
+
class StreamFailStream extends Req.Command<StreamFailStream>()("StreamFailStream", {}, {
|
|
88
|
+
stream: true,
|
|
89
|
+
allowAnonymous: true,
|
|
90
|
+
success: S.Number,
|
|
91
|
+
error: StreamBoom
|
|
92
|
+
}) {}
|
|
93
|
+
|
|
94
|
+
class StreamNoSuccess extends Req.Command<StreamNoSuccess>()("StreamNoSuccess", {}, {
|
|
95
|
+
stream: true,
|
|
96
|
+
allowAnonymous: true
|
|
97
|
+
}) {}
|
|
98
|
+
|
|
99
|
+
// Defaults to allowAnonymous: false → AllowAnonymous middleware fails with NotLoggedInError
|
|
100
|
+
// when the request lacks the `x-user` header.
|
|
101
|
+
class StreamRequiresAuth extends Req.Command<StreamRequiresAuth>()("StreamRequiresAuth", {}, {
|
|
102
|
+
stream: true,
|
|
103
|
+
success: S.Number
|
|
104
|
+
}) {}
|
|
105
|
+
|
|
106
|
+
class CommandRequiresAuth extends Req.Command<CommandRequiresAuth>()("CommandRequiresAuth", {}, {
|
|
107
|
+
success: S.Number
|
|
108
|
+
}) {}
|
|
109
|
+
|
|
110
|
+
class QueryRequiresAuth extends Req.Query<QueryRequiresAuth>()("QueryRequiresAuth", {}, {
|
|
111
|
+
success: S.Number
|
|
112
|
+
}) {}
|
|
113
|
+
|
|
114
|
+
const StreamyRsc = {
|
|
115
|
+
StreamTicks,
|
|
116
|
+
StreamCountTo,
|
|
117
|
+
StreamRealtime,
|
|
118
|
+
StreamFailStream,
|
|
119
|
+
StreamNoSuccess,
|
|
120
|
+
StreamRequiresAuth,
|
|
121
|
+
CommandRequiresAuth,
|
|
122
|
+
QueryRequiresAuth
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// Controllers / router — Stream impls returned from the match callback.
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
const router = Router(StreamyRsc)({
|
|
130
|
+
*effect(match) {
|
|
131
|
+
return match({
|
|
132
|
+
StreamTicks: () => Stream.fromIterable([10, 20, 30]),
|
|
133
|
+
StreamCountTo: ({ to }: { readonly to: number }) =>
|
|
134
|
+
Effect
|
|
135
|
+
.gen(function*() {
|
|
136
|
+
return Stream.range(1, to)
|
|
137
|
+
})
|
|
138
|
+
.pipe(Stream.unwrap),
|
|
139
|
+
// emits 3 values 100ms apart so the test can prove element-by-element
|
|
140
|
+
// delivery rather than a single batched response
|
|
141
|
+
StreamRealtime: () =>
|
|
142
|
+
Stream.fromIterable([1, 2, 3]).pipe(
|
|
143
|
+
Stream.mapEffect((n) => Effect.sleep("100 millis").pipe(Effect.as(n)))
|
|
144
|
+
),
|
|
145
|
+
StreamFailStream: () => Stream.fail(new StreamBoom({ reason: "from-stream" })),
|
|
146
|
+
StreamNoSuccess: () => Stream.empty,
|
|
147
|
+
// handlers below are unreachable when middleware-auth fails; bodies exist
|
|
148
|
+
// only so the resource type-checks
|
|
149
|
+
StreamRequiresAuth: () => Stream.fromIterable([1, 2, 3]),
|
|
150
|
+
CommandRequiresAuth: () => Effect.succeed(1),
|
|
151
|
+
QueryRequiresAuth: () => Effect.succeed(1)
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
const RpcRouterLayer = matchAll({ router })
|
|
157
|
+
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// HTTP wiring — real server on a loopback port + FetchHttpClient on the client.
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
// Server binds an ephemeral port (port: 0). The actual URL is read from the
|
|
163
|
+
// `HttpServer` service after binding, then fed into `ApiClientFactory.layer` so
|
|
164
|
+
// each `it.live` scope gets a fresh server without colliding on a fixed port.
|
|
165
|
+
const NodeServerLayer = NodeHttpServer.layer(() => createServer(), { port: 0 })
|
|
166
|
+
|
|
167
|
+
const ServerLayer = HttpRouter
|
|
168
|
+
.serve(RpcRouterLayer)
|
|
169
|
+
.pipe(
|
|
170
|
+
Layer.provide(NodeServerLayer),
|
|
171
|
+
Layer.provide(RpcSerialization.layerNdjson)
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
const ClientLayer = Layer
|
|
175
|
+
.unwrap(
|
|
176
|
+
Effect.gen(function*() {
|
|
177
|
+
const server = yield* HttpServer.HttpServer
|
|
178
|
+
const addr = server.address
|
|
179
|
+
if (addr._tag !== "TcpAddress") return yield* Effect.die(new Error("expected TcpAddress"))
|
|
180
|
+
const host = addr.hostname === "0.0.0.0" ? "127.0.0.1" : addr.hostname
|
|
181
|
+
const url = `http://${host}:${addr.port}`
|
|
182
|
+
return ApiClientFactory
|
|
183
|
+
.layer({ url, headers: Option.none() })
|
|
184
|
+
.pipe(Layer.provide(FetchHttpClient.layer))
|
|
185
|
+
})
|
|
186
|
+
)
|
|
187
|
+
.pipe(Layer.provide(NodeServerLayer))
|
|
188
|
+
|
|
189
|
+
const TestLayer = Layer.mergeAll(ServerLayer, ClientLayer)
|
|
190
|
+
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// Tests
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
|
|
195
|
+
it.live(
|
|
196
|
+
"stream resource without input: ApiClientFactory client emits all values",
|
|
197
|
+
Effect.fnUntraced(function*() {
|
|
198
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
|
|
199
|
+
const values = yield* Stream.runCollect(client.StreamTicks.handler())
|
|
200
|
+
expect(values).toStrictEqual([10, 20, 30])
|
|
201
|
+
}, Effect.provide(TestLayer)),
|
|
202
|
+
{ timeout: 10_000 }
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
it.live(
|
|
206
|
+
"stream resource with input: payload drives the emitted values",
|
|
207
|
+
Effect.fnUntraced(function*() {
|
|
208
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
|
|
209
|
+
const values = yield* Stream.runCollect(client.StreamCountTo.handler({ to: 4 }))
|
|
210
|
+
expect(values).toStrictEqual([1, 2, 3, 4])
|
|
211
|
+
}, Effect.provide(TestLayer)),
|
|
212
|
+
{ timeout: 10_000 }
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
it.live(
|
|
216
|
+
"stream resource is delivered element-by-element in real time (not batched)",
|
|
217
|
+
Effect.fnUntraced(function*() {
|
|
218
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
|
|
219
|
+
const start = Date.now()
|
|
220
|
+
const arrivals = yield* Stream.runCollect(
|
|
221
|
+
client.StreamRealtime.handler().pipe(
|
|
222
|
+
Stream.map((n) => ({ n, at: Date.now() - start }))
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
expect(arrivals.map((_) => _.n)).toStrictEqual([1, 2, 3])
|
|
226
|
+
// server emits each value 100ms after the previous one. If the response
|
|
227
|
+
// were batched, deltas would be ~0ms. Allow generous slack for CI jitter
|
|
228
|
+
// but require clear separation between consecutive arrivals.
|
|
229
|
+
const delta1 = arrivals[1]!.at - arrivals[0]!.at
|
|
230
|
+
const delta2 = arrivals[2]!.at - arrivals[1]!.at
|
|
231
|
+
expect(delta1).toBeGreaterThan(50)
|
|
232
|
+
expect(delta2).toBeGreaterThan(50)
|
|
233
|
+
// first element should not be withheld until the whole stream completes
|
|
234
|
+
expect(arrivals[0]!.at).toBeLessThan(arrivals[2]!.at - 50)
|
|
235
|
+
}, Effect.provide(TestLayer)),
|
|
236
|
+
{ timeout: 10_000 }
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
it.live(
|
|
240
|
+
"stream handler returning Stream.fail surfaces as failing stream on client",
|
|
241
|
+
Effect.fnUntraced(function*() {
|
|
242
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
|
|
243
|
+
const exit = yield* Stream.runCollect(client.StreamFailStream.handler()).pipe(Effect.exit)
|
|
244
|
+
expect(Exit.isFailure(exit)).toBe(true)
|
|
245
|
+
if (Exit.isFailure(exit)) {
|
|
246
|
+
const failures = (exit.cause as any).reasons as ReadonlyArray<{ _tag: "Fail"; error: StreamBoom }>
|
|
247
|
+
expect(failures.length).toBeGreaterThan(0)
|
|
248
|
+
expect(failures[0]!.error._tag).toBe("StreamBoom")
|
|
249
|
+
expect(failures[0]!.error.reason).toBe("from-stream")
|
|
250
|
+
}
|
|
251
|
+
}, Effect.provide(TestLayer)),
|
|
252
|
+
{ timeout: 10_000 }
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
it.live(
|
|
256
|
+
"stream resource without `success` exposes handler as a Stream on the client",
|
|
257
|
+
Effect.fnUntraced(function*() {
|
|
258
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
|
|
259
|
+
const exit = yield* Stream.runCollect(client.StreamNoSuccess.handler()).pipe(Effect.exit)
|
|
260
|
+
expect(Exit.isSuccess(exit)).toBe(true)
|
|
261
|
+
}, Effect.provide(TestLayer)),
|
|
262
|
+
{ timeout: 10_000 }
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
const expectNotLoggedIn = (exit: Exit.Exit<unknown, unknown>) => {
|
|
266
|
+
expect(Exit.isFailure(exit)).toBe(true)
|
|
267
|
+
if (!Exit.isFailure(exit)) return
|
|
268
|
+
const failures = (exit.cause as any).reasons as ReadonlyArray<{ _tag: "Fail"; error: any }>
|
|
269
|
+
expect(failures.length).toBeGreaterThan(0)
|
|
270
|
+
// The bug surfaces here as a SchemaError ("Expected never | { _tag: 'error', ... }")
|
|
271
|
+
// because the middleware-thrown NotLoggedInError doesn't match the
|
|
272
|
+
// wire StreamFailureChunk / CommandFailureWithMetaData wrapping.
|
|
273
|
+
expect(failures[0]!.error?._tag).toBe("NotLoggedInError")
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
it.live(
|
|
277
|
+
"stream resource: middleware-emitted NotLoggedInError surfaces cleanly on the client",
|
|
278
|
+
Effect.fnUntraced(function*() {
|
|
279
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
|
|
280
|
+
const exit = yield* Stream.runCollect(client.StreamRequiresAuth.handler()).pipe(Effect.exit)
|
|
281
|
+
expectNotLoggedIn(exit)
|
|
282
|
+
}, Effect.provide(TestLayer)),
|
|
283
|
+
{ timeout: 10_000 }
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
it.live(
|
|
287
|
+
"command resource: middleware-emitted NotLoggedInError surfaces cleanly on the client",
|
|
288
|
+
Effect.fnUntraced(function*() {
|
|
289
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
|
|
290
|
+
const exit = yield* client.CommandRequiresAuth.handler().pipe(Effect.exit)
|
|
291
|
+
expectNotLoggedIn(exit)
|
|
292
|
+
}, Effect.provide(TestLayer)),
|
|
293
|
+
{ timeout: 10_000 }
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
it.live(
|
|
297
|
+
"query resource: middleware-emitted NotLoggedInError surfaces cleanly on the client",
|
|
298
|
+
Effect.fnUntraced(function*() {
|
|
299
|
+
const client = yield* ApiClientFactory.makeFor(Layer.empty)(StreamyRsc)
|
|
300
|
+
const exit = yield* client.QueryRequiresAuth.handler().pipe(Effect.exit)
|
|
301
|
+
expectNotLoggedIn(exit)
|
|
302
|
+
}, Effect.provide(TestLayer)),
|
|
303
|
+
{ timeout: 10_000 }
|
|
304
|
+
)
|