@effect-app/infra 4.0.0-beta.99 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2884 -0
- package/_check.sh +1 -1
- package/dist/CUPS.d.ts +28 -9
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +35 -14
- package/dist/ClusterCosmos.d.ts +67 -0
- package/dist/ClusterCosmos.d.ts.map +1 -0
- package/dist/ClusterCosmos.js +588 -0
- package/dist/ClusterServiceBus.d.ts +67 -0
- package/dist/ClusterServiceBus.d.ts.map +1 -0
- package/dist/ClusterServiceBus.js +84 -0
- package/dist/{api/ContextProvider.d.ts → ContextProvider.d.ts} +7 -4
- package/dist/ContextProvider.d.ts.map +1 -0
- package/dist/ContextProvider.js +40 -0
- package/dist/Emailer/Sendgrid.d.ts +110 -146
- package/dist/Emailer/Sendgrid.d.ts.map +1 -1
- package/dist/Emailer/Sendgrid.js +24 -19
- package/dist/Emailer/fake.d.ts +1 -1
- package/dist/Emailer/fake.d.ts.map +1 -1
- package/dist/Emailer/fake.js +4 -4
- package/dist/MainFiberSet.d.ts +11 -7
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +22 -6
- package/dist/QueueMaker/errors.d.ts +3 -1
- package/dist/QueueMaker/errors.d.ts.map +1 -1
- package/dist/QueueMaker/errors.js +4 -2
- package/dist/{adapters/SQL → SQL}/Model.d.ts +34 -45
- package/dist/SQL/Model.d.ts.map +1 -0
- package/dist/SQL/Model.js +469 -0
- package/dist/SQL.d.ts.map +1 -0
- package/dist/{adapters/SQL.js → SQL.js} +1 -1
- package/dist/Store/Cosmos/query.d.ts +16 -4
- package/dist/Store/Cosmos/query.d.ts.map +1 -1
- package/dist/Store/Cosmos/query.js +238 -42
- package/dist/Store/Cosmos.d.ts +2 -2
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +394 -319
- package/dist/Store/Disk.d.ts +4 -4
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +91 -39
- package/dist/Store/Memory.d.ts +7 -10
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +348 -78
- package/dist/Store/SQL/Pg.d.ts +2 -2
- package/dist/Store/SQL/Pg.d.ts.map +1 -1
- package/dist/Store/SQL/Pg.js +229 -154
- package/dist/Store/SQL/query.d.ts +19 -3
- package/dist/Store/SQL/query.d.ts.map +1 -1
- package/dist/Store/SQL/query.js +306 -12
- package/dist/Store/SQL.d.ts +22 -5
- package/dist/Store/SQL.d.ts.map +1 -1
- package/dist/Store/SQL.js +369 -98
- package/dist/Store/codeFilter.d.ts +4 -4
- package/dist/Store/codeFilter.d.ts.map +1 -1
- package/dist/Store/codeFilter.js +71 -22
- package/dist/Store/index.d.ts +6 -4
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +9 -6
- package/dist/Store/jsonDocument.d.ts +67 -0
- package/dist/Store/jsonDocument.d.ts.map +1 -0
- package/dist/Store/jsonDocument.js +107 -0
- package/dist/Store/utils.d.ts +21 -2
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +253 -10
- package/dist/WorkflowEngineCosmos.d.ts +30 -0
- package/dist/WorkflowEngineCosmos.d.ts.map +1 -0
- package/dist/WorkflowEngineCosmos.js +542 -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 +6 -3
- package/dist/arbs.d.ts.map +1 -1
- package/dist/arbs.js +20 -4
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +5 -0
- package/dist/cosmos-client.d.ts +40 -0
- package/dist/cosmos-client.d.ts.map +1 -0
- package/dist/cosmos-client.js +41 -0
- package/dist/errorReporter.d.ts +6 -4
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +23 -27
- package/dist/fileUtil.d.ts +1 -1
- package/dist/fileUtil.d.ts.map +1 -1
- package/dist/fileUtil.js +2 -2
- package/dist/index.d.ts +2 -1
- 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/{api/internal → internal}/health.d.ts +1 -1
- package/dist/internal/health.d.ts.map +1 -0
- package/dist/internal/health.js +5 -0
- package/dist/{api/layerUtils.d.ts → layerUtils.d.ts} +10 -2
- package/dist/layerUtils.d.ts.map +1 -0
- package/dist/layerUtils.js +17 -0
- package/dist/logger/jsonLogger.d.ts +1 -1
- package/dist/logger/jsonLogger.d.ts.map +1 -1
- package/dist/logger/jsonLogger.js +5 -3
- package/dist/logger/logFmtLogger.d.ts +1 -1
- package/dist/logger/logFmtLogger.d.ts.map +1 -1
- package/dist/logger/logFmtLogger.js +3 -3
- package/dist/logger/shared.d.ts +2 -2
- package/dist/logger/shared.d.ts.map +1 -1
- package/dist/logger/shared.js +6 -6
- package/dist/logger.d.ts.map +1 -1
- package/dist/middlewares.d.ts.map +1 -0
- package/dist/{api/middlewares.js → middlewares.js} +1 -1
- package/dist/{adapters/mongo-client.d.ts → mongo-client.d.ts} +2 -1
- 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 +84 -0
- package/dist/rateLimit.d.ts +11 -3
- package/dist/rateLimit.d.ts.map +1 -1
- package/dist/rateLimit.js +7 -12
- package/dist/{adapters/redis-client.d.ts → redis-client.d.ts} +4 -1
- package/dist/redis-client.d.ts.map +1 -0
- package/dist/redis-client.js +98 -0
- package/dist/{api/reportError.d.ts → reportError.d.ts} +1 -1
- package/dist/reportError.d.ts.map +1 -0
- package/dist/reportError.js +28 -0
- package/dist/{api/routing → routing}/middleware/RouterMiddleware.d.ts +2 -1
- package/dist/routing/middleware/RouterMiddleware.d.ts.map +1 -0
- package/dist/{api/routing → routing}/middleware/RouterMiddleware.js +1 -1
- package/dist/{api/routing → routing}/middleware/middleware.d.ts +11 -6
- package/dist/routing/middleware/middleware.d.ts.map +1 -0
- package/dist/routing/middleware/middleware.js +152 -0
- package/dist/{api/routing → routing}/middleware.d.ts +0 -1
- 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.map +1 -0
- package/dist/routing/schema/jwt.js +12 -0
- package/dist/routing/tsort.d.ts.map +1 -0
- package/dist/routing/tsort.js +51 -0
- package/dist/{api/routing → routing}/utils.d.ts +3 -3
- package/dist/routing/utils.d.ts.map +1 -0
- package/dist/routing/utils.js +45 -0
- package/dist/routing.d.ts +182 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +256 -0
- package/dist/test.d.ts +2 -2
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +5 -5
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +14 -0
- package/docs/cluster-storage.md +26 -0
- package/docs/workflow-engine.md +262 -0
- package/examples/query.ts +23 -19
- package/package.json +36 -364
- package/src/CUPS.ts +53 -14
- package/src/ClusterCosmos.ts +1170 -0
- package/src/ClusterServiceBus.ts +244 -0
- package/src/{api/ContextProvider.ts → ContextProvider.ts} +10 -7
- package/src/Emailer/Sendgrid.ts +83 -60
- package/src/Emailer/fake.ts +4 -4
- package/src/MainFiberSet.ts +31 -12
- package/src/QueueMaker/errors.ts +3 -1
- package/src/{adapters/SQL → SQL}/Model.ts +48 -47
- package/src/SQL.ts +1 -0
- package/src/Store/Cosmos/query.ts +275 -55
- package/src/Store/Cosmos.ts +597 -511
- package/src/Store/Disk.ts +145 -71
- package/src/Store/Memory.ts +420 -115
- package/src/Store/SQL/Pg.ts +352 -268
- package/src/Store/SQL/query.ts +366 -20
- package/src/Store/SQL.ts +742 -293
- package/src/Store/codeFilter.ts +78 -28
- package/src/Store/index.ts +18 -13
- package/src/Store/jsonDocument.ts +143 -0
- package/src/Store/utils.ts +307 -30
- package/src/WorkflowEngineCosmos.ts +749 -0
- package/src/WorkflowEngineSqlite.ts +813 -0
- package/src/arbs.ts +25 -5
- package/src/cosmos-client.ts +72 -0
- package/src/errorReporter.ts +68 -78
- 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 +18 -9
- package/src/{api/layerUtils.ts → layerUtils.ts} +7 -3
- package/src/logger/jsonLogger.ts +5 -3
- package/src/logger/logFmtLogger.ts +3 -3
- package/src/logger/shared.ts +6 -5
- package/src/middlewares.ts +10 -0
- package/src/{adapters/mongo-client.ts → mongo-client.ts} +3 -1
- package/src/otel.ts +177 -0
- package/src/rateLimit.ts +35 -24
- package/src/{adapters/redis-client.ts → redis-client.ts} +6 -2
- package/src/{api/reportError.ts → reportError.ts} +3 -2
- package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +2 -1
- package/src/{api/routing → routing}/middleware/middleware.ts +75 -50
- package/src/routing/middleware.ts +4 -0
- package/src/{api/routing → routing}/schema/jwt.ts +2 -2
- package/src/{api/routing → routing}/utils.ts +2 -1
- package/src/routing.ts +823 -0
- package/src/test.ts +6 -6
- package/src/vitest.ts +1 -1
- package/test/arbs-magnitude.test.ts +33 -0
- package/test/arbs.test.ts +41 -0
- package/test/auth.test.ts +101 -0
- package/test/cluster-cosmos.test.ts +634 -0
- package/test/cluster-servicebus.test.ts +180 -0
- package/test/cluster-sqlite.test.ts +207 -0
- package/test/contextProvider.test.ts +11 -8
- package/test/controller.test.ts +23 -28
- package/test/cosmos-container-throughput.test.ts +80 -0
- package/test/cosmos-query.test.ts +368 -0
- package/test/dist/arbs-magnitude.test.d.ts.map +1 -0
- package/test/dist/arbs.test.d.ts.map +1 -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-container-throughput.test.d.ts.map +1 -0
- package/test/dist/cosmos-query.test.d.ts.map +1 -0
- package/test/dist/fixtures.d.ts +17 -9
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +8 -3
- package/test/dist/json-lower.test.d.ts.map +1 -0
- 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/repository-legacy-document.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/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 +7 -2
- package/test/json-lower.test.ts +129 -0
- package/test/layerUtils.test.ts +2 -2
- package/test/query.test.ts +1232 -42
- package/test/rawQuery.test.ts +432 -7
- package/test/repository-ext.test.ts +456 -0
- package/test/repository-legacy-document.test.ts +394 -0
- package/test/requires.test.ts +7 -3
- package/test/router-generator.test.ts +187 -0
- package/test/routing-interruptibility.test.ts +66 -0
- package/test/rpc-context-map-streaming.test.ts +265 -0
- package/test/rpc-e2e-invalidation.test.ts +421 -0
- package/test/rpc-multi-middleware.test.ts +17 -11
- package/test/rpc-stream-fullstack.test.ts +307 -0
- package/test/sql-store.test.ts +1202 -47
- package/test/validateSample.test.ts +70 -42
- package/test/workflow-engine-cosmos.test.ts +345 -0
- package/test/workflow-engine-sqlite.test.ts +297 -0
- package/tsconfig.examples.json +1 -1
- package/tsconfig.json +9 -1
- 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 -320
- 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 -43
- 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/SQLQueue.d.ts +0 -14
- package/dist/QueueMaker/SQLQueue.d.ts.map +0 -1
- package/dist/QueueMaker/SQLQueue.js +0 -132
- package/dist/QueueMaker/memQueue.d.ts +0 -11
- package/dist/QueueMaker/memQueue.d.ts.map +0 -1
- package/dist/QueueMaker/memQueue.js +0 -76
- package/dist/QueueMaker/sbqueue.d.ts +0 -12
- package/dist/QueueMaker/sbqueue.d.ts.map +0 -1
- package/dist/QueueMaker/sbqueue.js +0 -91
- 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 -68
- package/dist/RequestContext.d.ts.map +0 -1
- package/dist/RequestContext.js +0 -49
- package/dist/RequestFiberSet.d.ts +0 -36
- package/dist/RequestFiberSet.d.ts.map +0 -1
- package/dist/RequestFiberSet.js +0 -78
- package/dist/Store/ContextMapContainer.d.ts +0 -25
- package/dist/Store/ContextMapContainer.d.ts.map +0 -1
- package/dist/Store/ContextMapContainer.js +0 -77
- package/dist/Store/service.d.ts +0 -111
- package/dist/Store/service.d.ts.map +0 -1
- package/dist/Store/service.js +0 -79
- 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.map +0 -1
- package/dist/adapters/SQL/Model.js +0 -478
- 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.map +0 -1
- package/dist/adapters/mongo-client.js +0 -13
- package/dist/adapters/redis-client.d.ts.map +0 -1
- package/dist/adapters/redis-client.js +0 -94
- package/dist/api/ContextProvider.d.ts.map +0 -1
- package/dist/api/ContextProvider.js +0 -38
- 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.map +0 -1
- package/dist/api/internal/health.js +0 -5
- package/dist/api/layerUtils.d.ts.map +0 -1
- package/dist/api/layerUtils.js +0 -16
- package/dist/api/middlewares.d.ts.map +0 -1
- package/dist/api/reportError.d.ts.map +0 -1
- package/dist/api/reportError.js +0 -27
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
- package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
- package/dist/api/routing/middleware/middleware.js +0 -130
- package/dist/api/routing/middleware.d.ts.map +0 -1
- 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.map +0 -1
- package/dist/api/routing/tsort.js +0 -51
- 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 -134
- package/dist/api/routing.d.ts.map +0 -1
- package/dist/api/routing.js +0 -167
- package/dist/api/setupRequest.d.ts +0 -15
- package/dist/api/setupRequest.d.ts.map +0 -1
- package/dist/api/setupRequest.js +0 -49
- 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 -580
- 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/SQLQueue.ts +0 -190
- package/src/QueueMaker/memQueue.ts +0 -130
- package/src/QueueMaker/sbqueue.ts +0 -137
- package/src/QueueMaker/service.ts +0 -17
- package/src/RequestContext.ts +0 -63
- package/src/RequestFiberSet.ts +0 -105
- package/src/Store/ContextMapContainer.ts +0 -116
- package/src/Store/index.test.ts.bak +0 -112
- package/src/Store/service.ts +0 -193
- package/src/Store.ts +0 -1
- package/src/adapters/SQL.ts +0 -1
- package/src/adapters/ServiceBus.ts +0 -209
- package/src/adapters/cosmos-client.ts +0 -16
- package/src/adapters/index.ts +0 -0
- package/src/adapters/logger.ts +0 -3
- package/src/adapters/memQueue.ts +0 -21
- package/src/api/internal/auth.ts +0 -68
- package/src/api/middlewares.ts +0 -10
- package/src/api/routing/middleware.ts +0 -6
- package/src/api/routing.ts +0 -598
- package/src/api/setupRequest.ts +0 -104
- package/test/dist/date-query.test.d.ts.map +0 -1
- package/tsconfig.json.bak +0 -12
- /package/dist/{adapters/SQL.d.ts → SQL.d.ts} +0 -0
- /package/dist/{api/codec.d.ts → codec.d.ts} +0 -0
- /package/dist/{api/middlewares.d.ts → middlewares.d.ts} +0 -0
- /package/dist/{api/routing → routing}/schema/jwt.d.ts +0 -0
- /package/dist/{api/routing → routing}/tsort.d.ts +0 -0
- /package/dist/{api/util.d.ts → util.d.ts} +0 -0
- /package/src/{api/codec.ts → codec.ts} +0 -0
- /package/src/{api/internal → internal}/health.ts +0 -0
- /package/src/{api/routing → routing}/tsort.ts +0 -0
- /package/src/{api/util.ts → util.ts} +0 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
|
|
3
|
+
import * as Effect from "effect-app/Effect"
|
|
4
|
+
import * as Layer from "effect-app/Layer"
|
|
5
|
+
import { makeRepo } from "effect-app/Model/Repository"
|
|
6
|
+
import { RepositoryRegistryLive } from "effect-app/Model/Repository/Registry"
|
|
7
|
+
import * as Option from "effect-app/Option"
|
|
8
|
+
import * as S from "effect-app/Schema"
|
|
9
|
+
import { setupRequestContextFromCurrent } from "effect-app/setupRequest"
|
|
10
|
+
import type { JsonRecord } from "effect-app/Store"
|
|
11
|
+
import { StoreMaker } from "effect-app/Store"
|
|
12
|
+
import * as Cause from "effect/Cause"
|
|
13
|
+
import * as Exit from "effect/Exit"
|
|
14
|
+
import * as Result from "effect/Result"
|
|
15
|
+
import { describe, expect, it } from "vitest"
|
|
16
|
+
import { decodeStoredMany, decodeStoredOption, makeStoredDecode } from "../src/Store/jsonDocument.js"
|
|
17
|
+
import { makeMemoryStoreInt } from "../src/Store/Memory.js"
|
|
18
|
+
import { makeJsonLower } from "../src/Store/utils.js"
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A Memory store seeded with *raw stored documents* instead of items encoded
|
|
22
|
+
* through the schema, so that a legacy-shaped document can reach the read path -
|
|
23
|
+
* exactly what Cosmos (`fromStored`) and SQL (`parseRow`) do in production.
|
|
24
|
+
*
|
|
25
|
+
* `MemoryStoreLive` cannot be used for this: it strictly `encode`s whatever it
|
|
26
|
+
* is seeded with, so a legacy document never makes it into the store. The inner
|
|
27
|
+
* store here is therefore schemaless (it may not validate what we seed), and
|
|
28
|
+
* the real store read boundary - `defaultValues` merged, then `jitM`, then the
|
|
29
|
+
* strict JSON→Encoded decode - is applied on top via {@link makeStoredDecode}.
|
|
30
|
+
*/
|
|
31
|
+
const LegacyDocStoreLive = (docs: readonly Record<string, unknown>[]) =>
|
|
32
|
+
StoreMaker.toLayer(Effect.sync(() => ({
|
|
33
|
+
make: ((modelName: string, idKey: any, _seed: any, config: any) =>
|
|
34
|
+
Effect.map(
|
|
35
|
+
makeMemoryStoreInt(
|
|
36
|
+
modelName,
|
|
37
|
+
idKey,
|
|
38
|
+
"primary",
|
|
39
|
+
Effect.succeed(docs as any),
|
|
40
|
+
config?.defaultValues,
|
|
41
|
+
undefined,
|
|
42
|
+
makeJsonLower(config)
|
|
43
|
+
),
|
|
44
|
+
(store) => {
|
|
45
|
+
const decode = makeStoredDecode<any>(config?.schema, config?.jitM)
|
|
46
|
+
return {
|
|
47
|
+
...store,
|
|
48
|
+
all: Effect.flatMap(store.all, (rows) => Effect.fromResult(decodeStoredMany(rows, decode))),
|
|
49
|
+
find: (id: any) =>
|
|
50
|
+
Effect.flatMap(store.find(id), (doc) => Effect.fromResult(decodeStoredOption(doc, decode))),
|
|
51
|
+
filter: (f: any) =>
|
|
52
|
+
Effect.flatMap(store.filter(f), (rows: any[]) =>
|
|
53
|
+
f.select ? Effect.succeed(rows) : Effect.fromResult(decodeStoredMany(rows, decode)))
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
)) as any
|
|
57
|
+
})))
|
|
58
|
+
|
|
59
|
+
class Shop extends S.Class<Shop>("LegacyDocShop")({
|
|
60
|
+
id: S.String,
|
|
61
|
+
name: S.NonEmptyString255,
|
|
62
|
+
createdAt: S.Date,
|
|
63
|
+
updatedAt: S.Date,
|
|
64
|
+
vatRate: S.Number,
|
|
65
|
+
tags: S.ReadonlySet(S.String),
|
|
66
|
+
meta: S.ReadonlyMap({ key: S.String, value: S.String })
|
|
67
|
+
}) {}
|
|
68
|
+
|
|
69
|
+
// `vatRate` was added later; stored documents from before that do not have it.
|
|
70
|
+
const jitM = (json: JsonRecord): JsonRecord => "vatRate" in json ? json : { ...json, vatRate: 19 }
|
|
71
|
+
|
|
72
|
+
/** does *not* repair `vatRate`, so `legacyDoc` cannot decode at the store boundary */
|
|
73
|
+
const jitMThatDoesNotRepair = (json: JsonRecord): JsonRecord => ({
|
|
74
|
+
...json,
|
|
75
|
+
updatedAt: json["updatedAt"] ?? "2024-06-03T00:00:00.000Z"
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const legacyDoc = {
|
|
79
|
+
id: "shop-legacy",
|
|
80
|
+
name: "Legacy Shop",
|
|
81
|
+
createdAt: "2024-06-01T00:00:00.000Z",
|
|
82
|
+
updatedAt: "2024-06-03T00:00:00.000Z",
|
|
83
|
+
tags: ["a"],
|
|
84
|
+
meta: [["k", "v"]]
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const currentDoc = {
|
|
88
|
+
id: "shop-current",
|
|
89
|
+
name: "Current Shop",
|
|
90
|
+
createdAt: "2024-06-02T00:00:00.000Z",
|
|
91
|
+
updatedAt: "2024-06-04T00:00:00.000Z",
|
|
92
|
+
vatRate: 7,
|
|
93
|
+
tags: ["b"],
|
|
94
|
+
meta: [["k2", "v2"]]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** a legacy document that stored an explicit `null` where a Date is expected */
|
|
98
|
+
const nullDoc = { ...legacyDoc, id: "shop-null", vatRate: 21, updatedAt: null }
|
|
99
|
+
|
|
100
|
+
/** `vatRate` present but falsy, `updatedAt` an explicit null */
|
|
101
|
+
const zeroDoc = { ...legacyDoc, id: "shop-zero", vatRate: 0, updatedAt: null }
|
|
102
|
+
|
|
103
|
+
const TestLive = Layer.merge(LegacyDocStoreLive([legacyDoc, currentDoc]), RepositoryRegistryLive)
|
|
104
|
+
const NullDocLive = Layer.merge(LegacyDocStoreLive([nullDoc]), RepositoryRegistryLive)
|
|
105
|
+
const CurrentDocLive = Layer.merge(LegacyDocStoreLive([currentDoc]), RepositoryRegistryLive)
|
|
106
|
+
const DefaultsLive = Layer.merge(LegacyDocStoreLive([legacyDoc, zeroDoc]), RepositoryRegistryLive)
|
|
107
|
+
|
|
108
|
+
describe("repository reads of legacy documents", () => {
|
|
109
|
+
it("all: jitM fills the key the stored document is missing", () =>
|
|
110
|
+
Effect
|
|
111
|
+
.gen(function*() {
|
|
112
|
+
const repo = yield* makeRepo("LegacyDocShop", Shop, { jitM })
|
|
113
|
+
|
|
114
|
+
const items = yield* repo.all
|
|
115
|
+
|
|
116
|
+
expect(items).toHaveLength(2)
|
|
117
|
+
const legacy = items.find((_) => _.id === "shop-legacy")!
|
|
118
|
+
expect(legacy).toBeInstanceOf(Shop)
|
|
119
|
+
expect(legacy.vatRate).toBe(19)
|
|
120
|
+
expect(legacy.createdAt).toBeInstanceOf(Date)
|
|
121
|
+
expect(legacy.tags).toBeInstanceOf(Set)
|
|
122
|
+
expect([...legacy.tags]).toEqual(["a"])
|
|
123
|
+
expect(items.find((_) => _.id === "shop-current")!.vatRate).toBe(7)
|
|
124
|
+
})
|
|
125
|
+
.pipe(
|
|
126
|
+
Effect.provide(TestLive),
|
|
127
|
+
setupRequestContextFromCurrent(),
|
|
128
|
+
Effect.scoped,
|
|
129
|
+
Effect.runPromise
|
|
130
|
+
))
|
|
131
|
+
|
|
132
|
+
it("find: a legacy document decodes through jitM", () =>
|
|
133
|
+
Effect
|
|
134
|
+
.gen(function*() {
|
|
135
|
+
const repo = yield* makeRepo("LegacyDocShop", Shop, { jitM })
|
|
136
|
+
|
|
137
|
+
const found = yield* repo.find("shop-legacy")
|
|
138
|
+
|
|
139
|
+
expect(Option.isSome(found)).toBe(true)
|
|
140
|
+
const shop = Option.getOrThrow(found)
|
|
141
|
+
expect(shop.vatRate).toBe(19)
|
|
142
|
+
expect(shop.name).toBe("Legacy Shop")
|
|
143
|
+
expect(shop.createdAt).toBeInstanceOf(Date)
|
|
144
|
+
})
|
|
145
|
+
.pipe(
|
|
146
|
+
Effect.provide(TestLive),
|
|
147
|
+
setupRequestContextFromCurrent(),
|
|
148
|
+
Effect.scoped,
|
|
149
|
+
Effect.runPromise
|
|
150
|
+
))
|
|
151
|
+
|
|
152
|
+
it("validateSample: legacy documents validate because jitM runs at the store boundary", () =>
|
|
153
|
+
Effect
|
|
154
|
+
.gen(function*() {
|
|
155
|
+
const repo = yield* makeRepo("LegacyDocShop", Shop, { jitM })
|
|
156
|
+
|
|
157
|
+
const result = yield* repo.validateSample({ percentage: 1.0 })
|
|
158
|
+
|
|
159
|
+
expect(result.total).toBe(2)
|
|
160
|
+
expect(result.sampled).toBe(2)
|
|
161
|
+
expect(result.errors).toHaveLength(0)
|
|
162
|
+
expect(result.valid).toBe(2)
|
|
163
|
+
})
|
|
164
|
+
.pipe(
|
|
165
|
+
Effect.provide(TestLive),
|
|
166
|
+
setupRequestContextFromCurrent(),
|
|
167
|
+
Effect.scoped,
|
|
168
|
+
Effect.runPromise
|
|
169
|
+
))
|
|
170
|
+
|
|
171
|
+
it("jitM repairs an explicit null, and the document decodes end-to-end", () =>
|
|
172
|
+
Effect
|
|
173
|
+
.gen(function*() {
|
|
174
|
+
// the case the store boundary exists for: `null` is not a valid
|
|
175
|
+
// `S.Date` encoding, so no schema decode could ever fix it
|
|
176
|
+
const repairNull = (json: JsonRecord): JsonRecord =>
|
|
177
|
+
json["updatedAt"] === null ? { ...json, updatedAt: "2024-06-05T00:00:00.000Z" } : json
|
|
178
|
+
|
|
179
|
+
const repo = yield* makeRepo("LegacyDocShop", Shop, { jitM: repairNull })
|
|
180
|
+
|
|
181
|
+
const shop = Option.getOrThrow(yield* repo.find("shop-null"))
|
|
182
|
+
expect(shop.updatedAt).toBeInstanceOf(Date)
|
|
183
|
+
expect(shop.updatedAt.toISOString()).toBe("2024-06-05T00:00:00.000Z")
|
|
184
|
+
|
|
185
|
+
const all = yield* repo.all
|
|
186
|
+
expect(all).toHaveLength(1)
|
|
187
|
+
expect(all[0]!.updatedAt.toISOString()).toBe("2024-06-05T00:00:00.000Z")
|
|
188
|
+
})
|
|
189
|
+
.pipe(
|
|
190
|
+
Effect.provide(NullDocLive),
|
|
191
|
+
setupRequestContextFromCurrent(),
|
|
192
|
+
Effect.scoped,
|
|
193
|
+
Effect.runPromise
|
|
194
|
+
))
|
|
195
|
+
|
|
196
|
+
it("jitM receives the raw JSON document, and its JSON result is what the decode consumes", () =>
|
|
197
|
+
Effect
|
|
198
|
+
.gen(function*() {
|
|
199
|
+
const seen: JsonRecord[] = []
|
|
200
|
+
const observe = (json: JsonRecord): JsonRecord => {
|
|
201
|
+
seen.push(json)
|
|
202
|
+
// returning JSON - an ISO string, not a `Date` - is what decodes
|
|
203
|
+
return { ...json, updatedAt: "2024-06-09T00:00:00.000Z" }
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const repo = yield* makeRepo("LegacyDocShop", Shop, { jitM: observe })
|
|
207
|
+
|
|
208
|
+
const shop = Option.getOrThrow(yield* repo.find("shop-current"))
|
|
209
|
+
|
|
210
|
+
expect(seen).toHaveLength(1)
|
|
211
|
+
const raw = seen[0]!
|
|
212
|
+
// a `Date` field arrives as an ISO string
|
|
213
|
+
expect(typeof raw["createdAt"]).toBe("string")
|
|
214
|
+
expect(raw["createdAt"]).toBe("2024-06-02T00:00:00.000Z")
|
|
215
|
+
// a `ReadonlySet` as an array, a `ReadonlyMap` as an array of pairs
|
|
216
|
+
expect(raw["tags"]).toEqual(["b"])
|
|
217
|
+
expect(raw["meta"]).toEqual([["k2", "v2"]])
|
|
218
|
+
// never native Encoded values
|
|
219
|
+
expect(raw["createdAt"]).not.toBeInstanceOf(Date)
|
|
220
|
+
expect(raw["meta"]).not.toBeInstanceOf(Map)
|
|
221
|
+
// `_etag` is infra metadata and is not part of the document
|
|
222
|
+
expect("_etag" in raw).toBe(false)
|
|
223
|
+
|
|
224
|
+
// the JSON the jitM returned was decoded to the native Encoded value
|
|
225
|
+
expect(shop.updatedAt).toBeInstanceOf(Date)
|
|
226
|
+
expect(shop.updatedAt.toISOString()).toBe("2024-06-09T00:00:00.000Z")
|
|
227
|
+
expect(shop.meta).toBeInstanceOf(Map)
|
|
228
|
+
expect([...shop.meta]).toEqual([["k2", "v2"]])
|
|
229
|
+
})
|
|
230
|
+
.pipe(
|
|
231
|
+
Effect.provide(CurrentDocLive),
|
|
232
|
+
setupRequestContextFromCurrent(),
|
|
233
|
+
Effect.scoped,
|
|
234
|
+
Effect.runPromise
|
|
235
|
+
))
|
|
236
|
+
|
|
237
|
+
it("defaultValues only fill absent keys, and a stored null still reaches jitM", () =>
|
|
238
|
+
Effect
|
|
239
|
+
.gen(function*() {
|
|
240
|
+
const seen: JsonRecord[] = []
|
|
241
|
+
const repo = yield* makeRepo("LegacyDocShop", Shop, {
|
|
242
|
+
jitM: (json) => {
|
|
243
|
+
seen.push(json)
|
|
244
|
+
return json["updatedAt"] === null ? { ...json, updatedAt: "2024-06-08T00:00:00.000Z" } : json
|
|
245
|
+
},
|
|
246
|
+
config: {
|
|
247
|
+
defaultValues: {
|
|
248
|
+
vatRate: 19,
|
|
249
|
+
updatedAt: new Date("2024-01-01T00:00:00.000Z")
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
const items = yield* repo.all
|
|
255
|
+
expect(items).toHaveLength(2)
|
|
256
|
+
|
|
257
|
+
// absent key -> filled by defaultValues
|
|
258
|
+
const missing = items.find((_) => _.id === "shop-legacy")!
|
|
259
|
+
expect(missing.vatRate).toBe(19)
|
|
260
|
+
// present key -> the stored value wins, even when falsy
|
|
261
|
+
const present = items.find((_) => _.id === "shop-zero")!
|
|
262
|
+
expect(present.vatRate).toBe(0)
|
|
263
|
+
|
|
264
|
+
// defaultValues are merged *before* jitM runs
|
|
265
|
+
const filledSeen = seen.find((_) => _["id"] === "shop-legacy")!
|
|
266
|
+
expect(filledSeen["vatRate"]).toBe(19)
|
|
267
|
+
// ...but never overwrite a key the document has
|
|
268
|
+
expect(filledSeen["updatedAt"]).toBe("2024-06-03T00:00:00.000Z")
|
|
269
|
+
|
|
270
|
+
// a stored explicit `null` is NOT replaced by the default: it reaches jitM
|
|
271
|
+
const nullSeen = seen.find((_) => _["id"] === "shop-zero")!
|
|
272
|
+
expect(nullSeen["updatedAt"]).toBe(null)
|
|
273
|
+
expect(present.updatedAt.toISOString()).toBe("2024-06-08T00:00:00.000Z")
|
|
274
|
+
})
|
|
275
|
+
.pipe(
|
|
276
|
+
Effect.provide(DefaultsLive),
|
|
277
|
+
setupRequestContextFromCurrent(),
|
|
278
|
+
Effect.scoped,
|
|
279
|
+
Effect.runPromise
|
|
280
|
+
))
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* The store boundary decode is deliberately **strict**: `jitM` is the one and
|
|
284
|
+
* only place a legacy document can be repaired, and a document it does not
|
|
285
|
+
* repair must fail rather than be read back half-decoded. This test pins that
|
|
286
|
+
* behaviour down on purpose - do not "fix" it by making the decode lenient.
|
|
287
|
+
*
|
|
288
|
+
* It fails as a *typed* `S.SchemaError` in the store's error channel (it used
|
|
289
|
+
* to throw out of `decodeSync` as a defect), which is what lets
|
|
290
|
+
* `validateSample` report the document instead of losing the whole run.
|
|
291
|
+
*/
|
|
292
|
+
it("a document jitM does not repair fails as a typed SchemaError at the store boundary", () =>
|
|
293
|
+
Effect
|
|
294
|
+
.gen(function*() {
|
|
295
|
+
const { make } = yield* StoreMaker
|
|
296
|
+
// `vatRate` is still missing after this jitM ran: it only touches `updatedAt`
|
|
297
|
+
const store = yield* make<"id", any>("LegacyDocShop", "id", undefined, {
|
|
298
|
+
partitionValue: () => "primary",
|
|
299
|
+
schema: Shop,
|
|
300
|
+
jitM: jitMThatDoesNotRepair
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
// `Effect.result` only catches typed failures - a defect would reject here
|
|
304
|
+
const result = yield* store.all.pipe(Effect.result)
|
|
305
|
+
|
|
306
|
+
expect(Result.isFailure(result)).toBe(true)
|
|
307
|
+
if (!Result.isFailure(result)) return
|
|
308
|
+
const failure = result.failure
|
|
309
|
+
expect(S.isSchemaError(failure)).toBe(true)
|
|
310
|
+
expect((failure as S.SchemaError)._tag).toBe("SchemaError")
|
|
311
|
+
// the strict `toCodecJson(toEncoded(schema))` decode still reports the
|
|
312
|
+
// key jitM failed to add, with the path to it
|
|
313
|
+
expect((failure as S.SchemaError).message).toContain("Missing key")
|
|
314
|
+
expect((failure as S.SchemaError).message).toContain("vatRate")
|
|
315
|
+
})
|
|
316
|
+
.pipe(
|
|
317
|
+
Effect.provide(TestLive),
|
|
318
|
+
setupRequestContextFromCurrent(),
|
|
319
|
+
Effect.scoped,
|
|
320
|
+
Effect.runPromise
|
|
321
|
+
))
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* ...and the *public* repository surface is unchanged: `Repository.find`/`all`
|
|
325
|
+
* declare only `DatabaseError`, so the store's `SchemaError` becomes a defect
|
|
326
|
+
* when the public members are produced.
|
|
327
|
+
*/
|
|
328
|
+
it("the same document still dies on a public repository read", () =>
|
|
329
|
+
Effect
|
|
330
|
+
.gen(function*() {
|
|
331
|
+
const repo = yield* makeRepo("LegacyDocShop", Shop, { jitM: jitMThatDoesNotRepair })
|
|
332
|
+
|
|
333
|
+
const exit = yield* repo.all.pipe(Effect.exit)
|
|
334
|
+
expect(Exit.isFailure(exit)).toBe(true)
|
|
335
|
+
if (Exit.isFailure(exit)) {
|
|
336
|
+
expect(Cause.hasDies(exit.cause)).toBe(true)
|
|
337
|
+
expect(Cause.hasFails(exit.cause)).toBe(false)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// a defect is not catchable as a typed failure
|
|
341
|
+
const notCaught = yield* repo.find("shop-legacy").pipe(Effect.result, Effect.exit)
|
|
342
|
+
expect(Exit.isFailure(notCaught)).toBe(true)
|
|
343
|
+
})
|
|
344
|
+
.pipe(
|
|
345
|
+
Effect.provide(TestLive),
|
|
346
|
+
setupRequestContextFromCurrent(),
|
|
347
|
+
Effect.scoped,
|
|
348
|
+
Effect.runPromise
|
|
349
|
+
))
|
|
350
|
+
|
|
351
|
+
it("validateSample reports a document that fails at the store boundary and keeps going", () =>
|
|
352
|
+
Effect
|
|
353
|
+
.gen(function*() {
|
|
354
|
+
const repo = yield* makeRepo("LegacyDocShop", Shop, { jitM: jitMThatDoesNotRepair })
|
|
355
|
+
|
|
356
|
+
const result = yield* repo.validateSample({ percentage: 1.0 })
|
|
357
|
+
|
|
358
|
+
expect(result.total).toBe(2)
|
|
359
|
+
expect(result.sampled).toBe(2)
|
|
360
|
+
// `shop-current` decodes, `shop-legacy` does not
|
|
361
|
+
expect(result.valid).toBe(1)
|
|
362
|
+
expect(result.errors).toHaveLength(1)
|
|
363
|
+
const error = result.errors[0]!
|
|
364
|
+
expect(error.id).toBe("shop-legacy")
|
|
365
|
+
expect(S.isSchemaError(error.error)).toBe(true)
|
|
366
|
+
})
|
|
367
|
+
.pipe(
|
|
368
|
+
Effect.provide(TestLive),
|
|
369
|
+
setupRequestContextFromCurrent(),
|
|
370
|
+
Effect.scoped,
|
|
371
|
+
Effect.runPromise
|
|
372
|
+
))
|
|
373
|
+
|
|
374
|
+
it("_etag is not visible to jitM and survives the round trip", () => {
|
|
375
|
+
const seen: JsonRecord[] = []
|
|
376
|
+
const decode = makeStoredDecode<any>(
|
|
377
|
+
Shop,
|
|
378
|
+
(json) => {
|
|
379
|
+
seen.push(json)
|
|
380
|
+
return { ...json, vatRate: 19 }
|
|
381
|
+
}
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
const decoded = Result.getOrThrow(decode({ ...legacyDoc, _etag: "etag-1" } as any))
|
|
385
|
+
|
|
386
|
+
// jitM sees the document without the infra metadata
|
|
387
|
+
expect(seen).toHaveLength(1)
|
|
388
|
+
expect("_etag" in seen[0]!).toBe(false)
|
|
389
|
+
// ...and the etag is re-attached to the decoded Encoded document
|
|
390
|
+
expect(decoded._etag).toBe("etag-1")
|
|
391
|
+
expect(decoded.vatRate).toBe(19)
|
|
392
|
+
expect(decoded.createdAt).toBeInstanceOf(Date)
|
|
393
|
+
})
|
|
394
|
+
})
|
package/test/requires.test.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { describe, expect, expectTypeOf, it } from "@effect/vitest"
|
|
2
|
-
import { Context, Effect, Layer, Result, S } from "effect-app"
|
|
3
2
|
import { NotLoggedInError, UnauthorizedError } from "effect-app/client"
|
|
3
|
+
import * as Context from "effect-app/Context"
|
|
4
|
+
import * as Effect from "effect-app/Effect"
|
|
4
5
|
import { HttpHeaders } from "effect-app/http"
|
|
6
|
+
import * as Layer from "effect-app/Layer"
|
|
5
7
|
import * as RpcX from "effect-app/rpc"
|
|
6
8
|
import { MiddlewareMaker } from "effect-app/rpc"
|
|
9
|
+
import * as S from "effect-app/Schema"
|
|
7
10
|
import type { unhandled } from "effect-app/Types"
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
11
|
+
import * as Result from "effect/Result"
|
|
12
|
+
import { Rpc } from "effect/rpc"
|
|
13
|
+
import { type SuccessValue } from "effect/rpc/RpcMiddleware"
|
|
10
14
|
import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElseMiddleware, SomeElseMiddlewareLive, SomeMiddleware, SomeMiddlewareLive, SomeService, Test, TestLive } from "./fixtures.js"
|
|
11
15
|
|
|
12
16
|
export class RequiresSomeMiddleware
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { type MakeContext, type MakeErrors, makeRouter } from "@effect-app/infra/routing"
|
|
2
|
+
import { expectTypeOf, it } from "@effect/vitest"
|
|
3
|
+
import { InvalidStateError, makeRpcClient, UnauthorizedError } from "effect-app/client"
|
|
4
|
+
import * as Context from "effect-app/Context"
|
|
5
|
+
import * as Effect from "effect-app/Effect"
|
|
6
|
+
import * as Layer from "effect-app/Layer"
|
|
7
|
+
import { DefaultGenericMiddlewares } from "effect-app/middleware"
|
|
8
|
+
import { makeAllDSL, makeOneDSL } from "effect-app/Model"
|
|
9
|
+
import { type FixEnv } from "effect-app/Pure"
|
|
10
|
+
import * as RpcX from "effect-app/rpc"
|
|
11
|
+
import { MiddlewareMaker } from "effect-app/rpc"
|
|
12
|
+
import * as S from "effect-app/Schema"
|
|
13
|
+
import { type TypeTestId } from "effect-app/TypeTest"
|
|
14
|
+
import { type ConfigError } from "effect/Config"
|
|
15
|
+
import { type RpcSerialization } from "effect/rpc/RpcSerialization"
|
|
16
|
+
import { DefaultGenericMiddlewaresLive, DevModeMiddlewareLive } from "../src/routing/middleware.js"
|
|
17
|
+
import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElse, SomeService, Test, TestLive } from "./fixtures.js"
|
|
18
|
+
|
|
19
|
+
// Inline minimal context provider (provides `Some`)
|
|
20
|
+
class CtxProvider extends RpcX.RpcMiddleware.Tag<CtxProvider, { provides: Some }>()("CtxProvider") {
|
|
21
|
+
static Default = Layer.make(this, {
|
|
22
|
+
*make() {
|
|
23
|
+
return Effect.fnUntraced(function*(effect) {
|
|
24
|
+
return yield* Effect.provideService(effect, Some, Some.of({ a: 1 }))
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Provides `SomeElse` so AllowAnonymous's requirement is met.
|
|
31
|
+
class SomeElseProvider extends RpcX.RpcMiddleware.Tag<SomeElseProvider, { provides: SomeElse }>()("SomeElseProvider") {
|
|
32
|
+
static Default = Layer.make(this, {
|
|
33
|
+
*make() {
|
|
34
|
+
return Effect.fnUntraced(function*(effect) {
|
|
35
|
+
return yield* Effect.provideService(effect, SomeElse, SomeElse.of({ b: 2 }))
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class mw extends MiddlewareMaker
|
|
42
|
+
.Tag<mw>()("mw", RequestContextMap)
|
|
43
|
+
.middleware(RequireRoles, Test)
|
|
44
|
+
.middleware(AllowAnonymous)
|
|
45
|
+
.middleware(CtxProvider)
|
|
46
|
+
.middleware(...DefaultGenericMiddlewares, SomeElseProvider)
|
|
47
|
+
{
|
|
48
|
+
static Default = this.layer.pipe(
|
|
49
|
+
Layer.provide([
|
|
50
|
+
RequireRolesLive,
|
|
51
|
+
TestLive,
|
|
52
|
+
AllowAnonymousLive,
|
|
53
|
+
CtxProvider.Default,
|
|
54
|
+
SomeElseProvider.Default,
|
|
55
|
+
DefaultGenericMiddlewaresLive,
|
|
56
|
+
DevModeMiddlewareLive,
|
|
57
|
+
SomeService.Default
|
|
58
|
+
])
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const { TaggedRequestFor } = makeRpcClient(mw)
|
|
63
|
+
const Req = TaggedRequestFor("GenRouter")
|
|
64
|
+
|
|
65
|
+
class GetThing extends Req.Query<GetThing>()("GetThing", { id: S.String }, {
|
|
66
|
+
success: S.String,
|
|
67
|
+
error: UnauthorizedError
|
|
68
|
+
}) {}
|
|
69
|
+
class DoThing extends Req.Command<DoThing>()("DoThing", { id: S.String }, { success: S.Void }) {}
|
|
70
|
+
|
|
71
|
+
const Resource = { GetThing, DoThing }
|
|
72
|
+
|
|
73
|
+
const { Router, matchAll } = makeRouter(mw.Default)
|
|
74
|
+
|
|
75
|
+
class ThingRepo extends Context.Service<ThingRepo>()("ThingRepo", {
|
|
76
|
+
make: Effect.succeed({ get: (id: string) => Effect.succeed(id + "!") })
|
|
77
|
+
}) {
|
|
78
|
+
static Default = Layer.effect(this, this.make)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Case under test:
|
|
82
|
+
// `match({})` is given handlers as **shorthand generator methods** (`*GetThing(req) { ... }`).
|
|
83
|
+
// tsgo (>= 7 dev) infers `TNext = unknown` for these shorthand generators while TS6 infers `never`.
|
|
84
|
+
// `HandlerWithInputGen` in routing.ts must accept both — see the structural fix.
|
|
85
|
+
const router = Router(Resource)({
|
|
86
|
+
dependencies: [ThingRepo.Default],
|
|
87
|
+
*effect(match) {
|
|
88
|
+
const repo = yield* ThingRepo
|
|
89
|
+
|
|
90
|
+
if (Math.random() > 0.5) return yield* new InvalidStateError("nope")
|
|
91
|
+
|
|
92
|
+
return match({
|
|
93
|
+
*GetThing(req) {
|
|
94
|
+
const some = yield* Some
|
|
95
|
+
if (req.id === "boom") {
|
|
96
|
+
return yield* Effect.fail(new UnauthorizedError())
|
|
97
|
+
}
|
|
98
|
+
return yield* repo.get(req.id + String(some.a))
|
|
99
|
+
},
|
|
100
|
+
*DoThing(_req) {
|
|
101
|
+
yield* Effect.succeed(1)
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// Same scenario but using the `raw:` variant — exercises the `raw` path of `HandlerWithInputGen`.
|
|
108
|
+
const routerRaw = Router({ GetThing })({
|
|
109
|
+
*effect(match) {
|
|
110
|
+
return match({
|
|
111
|
+
GetThing: {
|
|
112
|
+
*raw(req) {
|
|
113
|
+
const some = yield* Some
|
|
114
|
+
return yield* Effect.succeed(req.id + String(some.a))
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it("router with generator-method handlers compiles", () => {
|
|
122
|
+
expectTypeOf(router).toMatchTypeOf<
|
|
123
|
+
Layer.Layer<never, ConfigError | InvalidStateError, SomeService | RpcSerialization>
|
|
124
|
+
>()
|
|
125
|
+
expectTypeOf(routerRaw).toMatchTypeOf<Layer.Layer<never, ConfigError, SomeService | RpcSerialization>>()
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Type-level assertions: verify generator yields propagate to MakeErrors / MakeContext
|
|
129
|
+
type Errors = MakeErrors<typeof router[TypeTestId]>
|
|
130
|
+
type Ctx = MakeContext<typeof router[TypeTestId]>
|
|
131
|
+
expectTypeOf<Errors>().toEqualTypeOf<InvalidStateError>()
|
|
132
|
+
expectTypeOf<Ctx>().toEqualTypeOf<ThingRepo>()
|
|
133
|
+
|
|
134
|
+
const matched = matchAll({ router })
|
|
135
|
+
expectTypeOf(matched).toMatchTypeOf<
|
|
136
|
+
Layer.Layer<never, ConfigError | InvalidStateError, SomeService | RpcSerialization>
|
|
137
|
+
>()
|
|
138
|
+
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// DSL R-inference regression
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// `OneDSL`/`OneDSLExt.update`/`.modify` previously annotated the callback's
|
|
143
|
+
// effect R as `FixEnv<R, Evt, S1, S2>`. That deadlocked inference of `R`
|
|
144
|
+
// (TS6 → `never`, tsgo → `unknown`), causing yielded effects to leak
|
|
145
|
+
// `unknown` in the R slot when consumed by generator handlers.
|
|
146
|
+
// The fix uses bare `R` in the callback and `FixEnv<R, …>` only on the return.
|
|
147
|
+
class Item extends S.Class<Item>("Item")({ id: S.String, label: S.String }) {}
|
|
148
|
+
class Dep extends Context.Service<Dep>()("Dep", { make: Effect.succeed({ tag: "dep" as const }) }) {}
|
|
149
|
+
|
|
150
|
+
type Evt = { _tag: "Updated"; id: string }
|
|
151
|
+
|
|
152
|
+
const Items$ = makeAllDSL<Item, Evt>()
|
|
153
|
+
const Item$ = makeOneDSL<Item, Evt>()
|
|
154
|
+
|
|
155
|
+
// Callback body uses generator syntax (TNext = unknown under tsgo) and yields
|
|
156
|
+
// a service-dependent effect — R must be inferred as `Dep` (plus the
|
|
157
|
+
// canonical PureEnvEnv contributed by FixEnv on the return).
|
|
158
|
+
const oneUpdate = Item$.update((item) =>
|
|
159
|
+
Effect.gen(function*() {
|
|
160
|
+
const dep = yield* Dep
|
|
161
|
+
return new Item({ id: item.id, label: item.label + dep.tag })
|
|
162
|
+
})
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
const allUpdate = Items$.update((items) =>
|
|
166
|
+
Effect.gen(function*() {
|
|
167
|
+
const dep = yield* Dep
|
|
168
|
+
return items.map((_) => new Item({ id: _.id, label: _.label + dep.tag }))
|
|
169
|
+
})
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
const oneModify = Item$.modify((item, _dsl) =>
|
|
173
|
+
Effect.gen(function*() {
|
|
174
|
+
const dep = yield* Dep
|
|
175
|
+
return { ...item, tag: dep.tag }
|
|
176
|
+
})
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
// `R` should be `FixEnv<Dep, Evt, …>` — never collapsed to `unknown`/`never`.
|
|
180
|
+
// The regression manifested as `unknown` here, breaking `Dep` assignability.
|
|
181
|
+
expectTypeOf(oneUpdate).toMatchTypeOf<Effect.Effect<Item, never, FixEnv<Dep, Evt, Item, Item>>>()
|
|
182
|
+
expectTypeOf(allUpdate).toMatchTypeOf<
|
|
183
|
+
Effect.Effect<readonly Item[], never, FixEnv<Dep, Evt, readonly Item[], readonly Item[]>>
|
|
184
|
+
>()
|
|
185
|
+
expectTypeOf(oneModify).toMatchTypeOf<
|
|
186
|
+
Effect.Effect<{ tag: "dep"; id: string; label: string }, never, FixEnv<Dep, Evt, Item, Item>>
|
|
187
|
+
>()
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, expect, it } from "@effect/vitest"
|
|
2
|
+
import { ConfigureInterruptibilityMiddleware } from "effect-app/middleware"
|
|
3
|
+
import * as S from "effect-app/Schema"
|
|
4
|
+
import * as Effect from "effect/Effect"
|
|
5
|
+
import * as Fiber from "effect/Fiber"
|
|
6
|
+
import * as Layer from "effect/Layer"
|
|
7
|
+
import * as Ref from "effect/Ref"
|
|
8
|
+
import { Rpc, RpcGroup, RpcTest } from "effect/rpc"
|
|
9
|
+
import { applyRequestTypeInterruptibility } from "../src/routing.js"
|
|
10
|
+
import { ConfigureInterruptibilityMiddlewareLive, RequestType } from "../src/routing/middleware.js"
|
|
11
|
+
|
|
12
|
+
const InterruptibilityRpcs = RpcGroup.make(
|
|
13
|
+
Rpc
|
|
14
|
+
.make("doCommand", { success: S.Void })
|
|
15
|
+
.annotate(RequestType, "command")
|
|
16
|
+
.middleware(ConfigureInterruptibilityMiddleware),
|
|
17
|
+
Rpc
|
|
18
|
+
.make("doQuery", { success: S.Void })
|
|
19
|
+
.annotate(RequestType, "query")
|
|
20
|
+
.middleware(ConfigureInterruptibilityMiddleware)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const makeImplLayer = (commandDone: Ref.Ref<boolean>, queryDone: Ref.Ref<boolean>) =>
|
|
24
|
+
InterruptibilityRpcs.toLayer({
|
|
25
|
+
doCommand: () =>
|
|
26
|
+
applyRequestTypeInterruptibility(
|
|
27
|
+
"command",
|
|
28
|
+
Effect.sleep("120 millis").pipe(Effect.andThen(Ref.set(commandDone, true)))
|
|
29
|
+
),
|
|
30
|
+
doQuery: () =>
|
|
31
|
+
applyRequestTypeInterruptibility(
|
|
32
|
+
"query",
|
|
33
|
+
Effect.sleep("120 millis").pipe(Effect.andThen(Ref.set(queryDone, true)))
|
|
34
|
+
)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe("routing interruptibility", () => {
|
|
38
|
+
it.live(
|
|
39
|
+
"e2e: command continues after client interrupt, query does not",
|
|
40
|
+
() =>
|
|
41
|
+
Effect.gen(function*() {
|
|
42
|
+
const commandDone = yield* Ref.make(false)
|
|
43
|
+
const queryDone = yield* Ref.make(false)
|
|
44
|
+
|
|
45
|
+
const client = yield* RpcTest
|
|
46
|
+
.makeClient(InterruptibilityRpcs)
|
|
47
|
+
.pipe(
|
|
48
|
+
Effect.provide(
|
|
49
|
+
Layer.mergeAll(makeImplLayer(commandDone, queryDone), ConfigureInterruptibilityMiddlewareLive)
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
const commandFiber = yield* Effect.forkDetach(client.doCommand())
|
|
54
|
+
yield* Effect.sleep("20 millis")
|
|
55
|
+
yield* Fiber.interrupt(commandFiber)
|
|
56
|
+
yield* Effect.sleep("180 millis")
|
|
57
|
+
expect(yield* Ref.get(commandDone)).toBe(true)
|
|
58
|
+
|
|
59
|
+
const queryFiber = yield* Effect.forkDetach(client.doQuery())
|
|
60
|
+
yield* Effect.sleep("20 millis")
|
|
61
|
+
yield* Fiber.interrupt(queryFiber)
|
|
62
|
+
yield* Effect.sleep("180 millis")
|
|
63
|
+
expect(yield* Ref.get(queryDone)).toBe(false)
|
|
64
|
+
})
|
|
65
|
+
)
|
|
66
|
+
})
|