@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
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Effect from "effect-app/Effect"
|
|
2
|
+
import * as Layer from "effect-app/Layer"
|
|
3
|
+
import { makeRepo, ValidationError, ValidationResult } from "effect-app/Model/Repository"
|
|
4
|
+
import { RepositoryRegistryLive } from "effect-app/Model/Repository/Registry"
|
|
5
|
+
import * as S from "effect-app/Schema"
|
|
6
|
+
import { setupRequestContextFromCurrent } from "effect-app/setupRequest"
|
|
7
|
+
import type { JsonRecord } from "effect-app/Store"
|
|
2
8
|
import { describe, expect, it } from "vitest"
|
|
3
|
-
import { setupRequestContextFromCurrent } from "../src/api/setupRequest.js"
|
|
4
|
-
import { makeRepo, ValidationError, ValidationResult } from "../src/Model/Repository.js"
|
|
5
9
|
import { MemoryStoreLive } from "../src/Store/Memory.js"
|
|
6
10
|
|
|
11
|
+
const TestStoreLive = Layer.merge(MemoryStoreLive, RepositoryRegistryLive)
|
|
12
|
+
|
|
7
13
|
// simple schema for valid items
|
|
8
14
|
class SimpleItem extends S.Class<SimpleItem>("SimpleItem")({
|
|
9
15
|
id: S.String,
|
|
@@ -11,6 +17,26 @@ class SimpleItem extends S.Class<SimpleItem>("SimpleItem")({
|
|
|
11
17
|
count: S.NonNegativeInt
|
|
12
18
|
}) {}
|
|
13
19
|
|
|
20
|
+
/**
|
|
21
|
+
* `count` is stored as a *string*, so its Encoded shape accepts anything the
|
|
22
|
+
* JSON document happens to hold while the domain decode still demands a finite
|
|
23
|
+
* number.
|
|
24
|
+
*
|
|
25
|
+
* That split is what `validateSample` exists for, and it is the only way to
|
|
26
|
+
* observe a repository-level validation error now that `jitM` runs at the store
|
|
27
|
+
* boundary: the store's `toCodecJson(toEncoded(schema))` decode is strict, so a
|
|
28
|
+
* `jitM` that produced a value the *Encoded* shape rejects would fail loudly in
|
|
29
|
+
* the store instead of ever reaching the repository.
|
|
30
|
+
*/
|
|
31
|
+
class LooseCountItem extends S.Class<LooseCountItem>("LooseCountItem")({
|
|
32
|
+
id: S.String,
|
|
33
|
+
name: S.NonEmptyString255,
|
|
34
|
+
count: S.FiniteFromString
|
|
35
|
+
}) {}
|
|
36
|
+
|
|
37
|
+
/** build a `LooseCountItem` from its encoded (stored) form */
|
|
38
|
+
const looseItem = (id: string, name: string, count: string) => S.decodeSync(LooseCountItem)({ id, name, count })
|
|
39
|
+
|
|
14
40
|
describe("validateSample", () => {
|
|
15
41
|
it("returns success when all items pass validation", () =>
|
|
16
42
|
Effect
|
|
@@ -25,41 +51,42 @@ describe("validateSample", () => {
|
|
|
25
51
|
|
|
26
52
|
const result = yield* repo.validateSample({ percentage: 1.0 }) // 100%
|
|
27
53
|
|
|
28
|
-
expect(result).
|
|
54
|
+
expect(S.is(ValidationResult)(result)).toBe(true)
|
|
29
55
|
expect(result.total).toBe(3)
|
|
30
56
|
expect(result.sampled).toBe(3)
|
|
31
57
|
expect(result.valid).toBe(3)
|
|
32
58
|
expect(result.errors).toHaveLength(0)
|
|
33
59
|
})
|
|
34
60
|
.pipe(
|
|
35
|
-
Effect.provide(
|
|
61
|
+
Effect.provide(TestStoreLive),
|
|
36
62
|
setupRequestContextFromCurrent(),
|
|
63
|
+
Effect.scoped,
|
|
37
64
|
Effect.runPromise
|
|
38
65
|
))
|
|
39
66
|
|
|
40
|
-
it("returns errors when jitM produces
|
|
67
|
+
it("returns errors when jitM produces data the domain schema rejects", () =>
|
|
41
68
|
Effect
|
|
42
69
|
.gen(function*() {
|
|
43
|
-
// jitM that corrupts
|
|
44
|
-
const corruptingJitM = (
|
|
45
|
-
if (
|
|
46
|
-
return { ...
|
|
70
|
+
// jitM that corrupts two specific items' count into a non-numeric string
|
|
71
|
+
const corruptingJitM = (json: JsonRecord): JsonRecord => {
|
|
72
|
+
if (json["id"] === "2" || json["id"] === "3") {
|
|
73
|
+
return { ...json, count: "not-a-number" } // still a string, so the store decode is happy
|
|
47
74
|
}
|
|
48
|
-
return
|
|
75
|
+
return json
|
|
49
76
|
}
|
|
50
77
|
|
|
51
|
-
const repo = yield* makeRepo("CorruptItem",
|
|
78
|
+
const repo = yield* makeRepo("CorruptItem", LooseCountItem, {
|
|
52
79
|
jitM: corruptingJitM,
|
|
53
80
|
makeInitial: Effect.succeed([
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
81
|
+
looseItem("1", "Valid", "10"),
|
|
82
|
+
looseItem("2", "WillBeInvalid1", "20"),
|
|
83
|
+
looseItem("3", "WillBeInvalid2", "30")
|
|
57
84
|
])
|
|
58
85
|
})
|
|
59
86
|
|
|
60
87
|
const result = yield* repo.validateSample({ percentage: 1.0 }) // 100%
|
|
61
88
|
|
|
62
|
-
expect(result).
|
|
89
|
+
expect(S.is(ValidationResult)(result)).toBe(true)
|
|
63
90
|
expect(result.total).toBe(3)
|
|
64
91
|
expect(result.sampled).toBe(3)
|
|
65
92
|
expect(result.valid).toBe(1)
|
|
@@ -67,7 +94,7 @@ describe("validateSample", () => {
|
|
|
67
94
|
|
|
68
95
|
// verify error structure
|
|
69
96
|
for (const error of result.errors) {
|
|
70
|
-
expect(error).
|
|
97
|
+
expect(S.is(ValidationError)(error)).toBe(true)
|
|
71
98
|
expect(error.id).toBeDefined()
|
|
72
99
|
expect(error.rawData).toBeDefined()
|
|
73
100
|
expect(error.jitMResult).toBeDefined()
|
|
@@ -80,8 +107,9 @@ describe("validateSample", () => {
|
|
|
80
107
|
expect(failingIds).toContain("3")
|
|
81
108
|
})
|
|
82
109
|
.pipe(
|
|
83
|
-
Effect.provide(
|
|
110
|
+
Effect.provide(TestStoreLive),
|
|
84
111
|
setupRequestContextFromCurrent(),
|
|
112
|
+
Effect.scoped,
|
|
85
113
|
Effect.runPromise
|
|
86
114
|
))
|
|
87
115
|
|
|
@@ -98,8 +126,9 @@ describe("validateSample", () => {
|
|
|
98
126
|
expect(result.errors).toHaveLength(0)
|
|
99
127
|
})
|
|
100
128
|
.pipe(
|
|
101
|
-
Effect.provide(
|
|
129
|
+
Effect.provide(TestStoreLive),
|
|
102
130
|
setupRequestContextFromCurrent(),
|
|
131
|
+
Effect.scoped,
|
|
103
132
|
Effect.runPromise
|
|
104
133
|
))
|
|
105
134
|
|
|
@@ -127,8 +156,9 @@ describe("validateSample", () => {
|
|
|
127
156
|
expect(result.errors).toHaveLength(0)
|
|
128
157
|
})
|
|
129
158
|
.pipe(
|
|
130
|
-
Effect.provide(
|
|
159
|
+
Effect.provide(TestStoreLive),
|
|
131
160
|
setupRequestContextFromCurrent(),
|
|
161
|
+
Effect.scoped,
|
|
132
162
|
Effect.runPromise
|
|
133
163
|
))
|
|
134
164
|
|
|
@@ -143,9 +173,9 @@ describe("validateSample", () => {
|
|
|
143
173
|
|
|
144
174
|
// jitM that adds default status for items
|
|
145
175
|
const repo = yield* makeRepo("ItemWithStatus", ItemWithStatus, {
|
|
146
|
-
jitM: (
|
|
147
|
-
...
|
|
148
|
-
status:
|
|
176
|
+
jitM: (json) => ({
|
|
177
|
+
...json,
|
|
178
|
+
status: json["status"] ?? "active" // default to active if missing
|
|
149
179
|
}),
|
|
150
180
|
makeInitial: Effect.succeed([
|
|
151
181
|
new ItemWithStatus({ id: "1", status: "active" }),
|
|
@@ -161,8 +191,9 @@ describe("validateSample", () => {
|
|
|
161
191
|
expect(result.errors).toHaveLength(0)
|
|
162
192
|
})
|
|
163
193
|
.pipe(
|
|
164
|
-
Effect.provide(
|
|
194
|
+
Effect.provide(TestStoreLive),
|
|
165
195
|
setupRequestContextFromCurrent(),
|
|
196
|
+
Effect.scoped,
|
|
166
197
|
Effect.runPromise
|
|
167
198
|
))
|
|
168
199
|
|
|
@@ -170,16 +201,14 @@ describe("validateSample", () => {
|
|
|
170
201
|
Effect
|
|
171
202
|
.gen(function*() {
|
|
172
203
|
// jitM that corrupts the data
|
|
173
|
-
const corruptingJitM = (
|
|
174
|
-
...
|
|
175
|
-
count: -
|
|
204
|
+
const corruptingJitM = (json: JsonRecord): JsonRecord => ({
|
|
205
|
+
...json,
|
|
206
|
+
count: "not-a-number" // always corrupt count
|
|
176
207
|
})
|
|
177
208
|
|
|
178
|
-
const repo = yield* makeRepo("ContextErrorTest",
|
|
209
|
+
const repo = yield* makeRepo("ContextErrorTest", LooseCountItem, {
|
|
179
210
|
jitM: corruptingJitM,
|
|
180
|
-
makeInitial: Effect.succeed([
|
|
181
|
-
new SimpleItem({ id: "bad-item", name: S.NonEmptyString255("Test"), count: S.NonNegativeInt(100) })
|
|
182
|
-
])
|
|
211
|
+
makeInitial: Effect.succeed([looseItem("bad-item", "Test", "100")])
|
|
183
212
|
})
|
|
184
213
|
|
|
185
214
|
const result = yield* repo.validateSample({ percentage: 1.0 })
|
|
@@ -189,27 +218,25 @@ describe("validateSample", () => {
|
|
|
189
218
|
const error = result.errors[0]!
|
|
190
219
|
expect(error.id).toBe("bad-item")
|
|
191
220
|
|
|
192
|
-
// rawData
|
|
221
|
+
// rawData is what the store returned: jitM already ran at the store
|
|
222
|
+
// boundary, so the corrupted count is what the repository decoded
|
|
193
223
|
expect(error.rawData).toMatchObject({
|
|
194
224
|
id: "bad-item",
|
|
195
225
|
name: "Test",
|
|
196
|
-
count:
|
|
226
|
+
count: "not-a-number"
|
|
197
227
|
})
|
|
198
228
|
|
|
199
|
-
// jitMResult
|
|
200
|
-
expect(error.jitMResult).
|
|
201
|
-
id: "bad-item",
|
|
202
|
-
name: "Test",
|
|
203
|
-
count: -999
|
|
204
|
-
})
|
|
229
|
+
// jitMResult is kept for compatibility and is identical to rawData
|
|
230
|
+
expect(error.jitMResult).toEqual(error.rawData)
|
|
205
231
|
|
|
206
232
|
// error should be a SchemaError
|
|
207
233
|
expect(error.error).toBeDefined()
|
|
208
|
-
expect((error.error as
|
|
234
|
+
expect((error.error as { _tag?: string })._tag).toBe("SchemaError")
|
|
209
235
|
})
|
|
210
236
|
.pipe(
|
|
211
|
-
Effect.provide(
|
|
237
|
+
Effect.provide(TestStoreLive),
|
|
212
238
|
setupRequestContextFromCurrent(),
|
|
239
|
+
Effect.scoped,
|
|
213
240
|
Effect.runPromise
|
|
214
241
|
))
|
|
215
242
|
|
|
@@ -230,8 +257,9 @@ describe("validateSample", () => {
|
|
|
230
257
|
expect(result.errors).toHaveLength(0)
|
|
231
258
|
})
|
|
232
259
|
.pipe(
|
|
233
|
-
Effect.provide(
|
|
260
|
+
Effect.provide(TestStoreLive),
|
|
234
261
|
setupRequestContextFromCurrent(),
|
|
262
|
+
Effect.scoped,
|
|
235
263
|
Effect.runPromise
|
|
236
264
|
))
|
|
237
265
|
})
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/**
|
|
3
|
+
* Workflow engine conformance tests.
|
|
4
|
+
*
|
|
5
|
+
* The `runSuite` body is engine-agnostic: it runs against
|
|
6
|
+
* `WorkflowEngine.layerMemory` (fast, always) and — when `COSMOS_TEST_URL`
|
|
7
|
+
* is set — against the Cosmos adapter (`layerCosmos`) pointed at a Cosmos
|
|
8
|
+
* emulator. Both must satisfy the same observable contract: idempotent
|
|
9
|
+
* execution, activity replay (no double side effects), suspend → durable-
|
|
10
|
+
* deferred completion → resume with a correctly decoded result, interrupt
|
|
11
|
+
* propagation, and first-writer-wins deferred completion.
|
|
12
|
+
*
|
|
13
|
+
* A second describe block (Cosmos emulator only) exercises adapter
|
|
14
|
+
* internals — the recovery poller (driving execs whose lease has lapsed)
|
|
15
|
+
* and the clock poller (firing past-due clocks) — by seeding rows
|
|
16
|
+
* directly against the Cosmos container.
|
|
17
|
+
*/
|
|
18
|
+
import { assert, describe, it } from "@effect/vitest"
|
|
19
|
+
import { Context, Duration, Effect, Exit, Layer, Option, Redacted, Schema } from "effect"
|
|
20
|
+
import { Activity, DurableDeferred, Workflow, WorkflowEngine } from "effect/workflow"
|
|
21
|
+
import { CosmosClient, CosmosClientLayer } from "../src/cosmos-client.js"
|
|
22
|
+
import { layerCosmos } from "../src/WorkflowEngineCosmos.js"
|
|
23
|
+
|
|
24
|
+
// --- Shared mutable counter for activity-side-effect assertions --------
|
|
25
|
+
|
|
26
|
+
class CounterRef extends Context.Service<CounterRef, { count: number }>()("CounterRef") {
|
|
27
|
+
static readonly layer = Layer.effect(CounterRef, Effect.sync(() => ({ count: 0 })))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// --- Workflow definitions ----------------------------------------------
|
|
31
|
+
|
|
32
|
+
const IncrementWorkflow = Workflow.make("WorkflowEngineCosmos/IncrementWorkflow", {
|
|
33
|
+
payload: { value: Schema.Number },
|
|
34
|
+
success: Schema.Number,
|
|
35
|
+
idempotencyKey: ({ value }) => String(value)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const IncrementHandler = IncrementWorkflow.toLayer(({ value }) => Effect.succeed(value + 1))
|
|
39
|
+
|
|
40
|
+
// Counts activity body invocations across re-executes so the test can
|
|
41
|
+
// prove side-effects don't repeat when a persisted result is available.
|
|
42
|
+
const CounterWorkflow = Workflow.make("WorkflowEngineCosmos/CounterWorkflow", {
|
|
43
|
+
payload: { id: Schema.String },
|
|
44
|
+
success: Schema.Number,
|
|
45
|
+
idempotencyKey: ({ id }) => id
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const CounterHandler = CounterWorkflow.toLayer(Effect.fn(function*() {
|
|
49
|
+
const counter = yield* CounterRef
|
|
50
|
+
return yield* Activity.make({
|
|
51
|
+
name: "Bump",
|
|
52
|
+
success: Schema.Number,
|
|
53
|
+
execute: Effect.sync(() => {
|
|
54
|
+
counter.count++
|
|
55
|
+
return counter.count
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
}))
|
|
59
|
+
|
|
60
|
+
// Suspends on a deferred, then composes the persisted activity result
|
|
61
|
+
// with the resumed deferred value. Exercises Result/Exit round-trip.
|
|
62
|
+
const Trigger = DurableDeferred.make("WorkflowEngineCosmos/Trigger", { success: Schema.String })
|
|
63
|
+
|
|
64
|
+
const SuspendWorkflow = Workflow.make("WorkflowEngineCosmos/SuspendWorkflow", {
|
|
65
|
+
payload: { id: Schema.String },
|
|
66
|
+
success: Schema.String,
|
|
67
|
+
idempotencyKey: ({ id }) => id
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const SuspendHandler = SuspendWorkflow.toLayer(Effect.fn(function*({ id }) {
|
|
71
|
+
const n = yield* Activity.make({
|
|
72
|
+
name: "step",
|
|
73
|
+
success: Schema.Number,
|
|
74
|
+
execute: Effect.succeed(1)
|
|
75
|
+
})
|
|
76
|
+
const v = yield* DurableDeferred.await(Trigger)
|
|
77
|
+
return `${id}:${n}:${v}`
|
|
78
|
+
}))
|
|
79
|
+
|
|
80
|
+
// Plain durable-deferred await — used to assert first-writer-wins on done().
|
|
81
|
+
const AwaitOnly = Workflow.make("WorkflowEngineCosmos/AwaitOnly", {
|
|
82
|
+
payload: { id: Schema.String },
|
|
83
|
+
success: Schema.String,
|
|
84
|
+
idempotencyKey: ({ id }) => id
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const AwaitOnlyHandler = AwaitOnly.toLayer(Effect.fn(function*() {
|
|
88
|
+
return yield* DurableDeferred.await(Trigger)
|
|
89
|
+
}))
|
|
90
|
+
|
|
91
|
+
const Handlers = Layer.mergeAll(IncrementHandler, SuspendHandler, CounterHandler, AwaitOnlyHandler)
|
|
92
|
+
|
|
93
|
+
// Helper that polls until the workflow completes (or `maxIterations` elapse).
|
|
94
|
+
const waitForComplete = (
|
|
95
|
+
workflow: { readonly poll: (id: string) => Effect.Effect<Option.Option<Workflow.Result<any, any>>, never, any> },
|
|
96
|
+
executionId: string,
|
|
97
|
+
step = Duration.millis(10),
|
|
98
|
+
maxIterations = 200
|
|
99
|
+
) =>
|
|
100
|
+
Effect.gen(function*() {
|
|
101
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
102
|
+
const polled = yield* workflow.poll(executionId)
|
|
103
|
+
if (Option.isSome(polled) && polled.value._tag === "Complete") {
|
|
104
|
+
return polled.value
|
|
105
|
+
}
|
|
106
|
+
yield* Effect.sleep(step)
|
|
107
|
+
}
|
|
108
|
+
return undefined
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
const runSuite = (engineLayer: Layer.Layer<WorkflowEngine.WorkflowEngine>) => {
|
|
112
|
+
const TestLayer = Handlers.pipe(
|
|
113
|
+
Layer.provideMerge(CounterRef.layer),
|
|
114
|
+
Layer.provideMerge(engineLayer)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
it.live("executes a workflow and polls the result", () =>
|
|
118
|
+
Effect
|
|
119
|
+
.gen(function*() {
|
|
120
|
+
const executionId = yield* IncrementWorkflow.execute({ value: 41 }, { discard: true })
|
|
121
|
+
const result = yield* IncrementWorkflow.execute({ value: 41 })
|
|
122
|
+
const polled = yield* IncrementWorkflow.poll(executionId)
|
|
123
|
+
|
|
124
|
+
assert.strictEqual(result, 42)
|
|
125
|
+
assert(
|
|
126
|
+
Option.isSome(polled)
|
|
127
|
+
&& polled.value._tag === "Complete"
|
|
128
|
+
&& Exit.isSuccess(polled.value.exit)
|
|
129
|
+
)
|
|
130
|
+
assert.strictEqual(polled.value.exit.value, 42)
|
|
131
|
+
})
|
|
132
|
+
.pipe(Effect.provide(TestLayer)))
|
|
133
|
+
|
|
134
|
+
it.live("re-executing the same id is idempotent", () =>
|
|
135
|
+
Effect
|
|
136
|
+
.gen(function*() {
|
|
137
|
+
const a = yield* IncrementWorkflow.execute({ value: 7 })
|
|
138
|
+
const b = yield* IncrementWorkflow.execute({ value: 7 })
|
|
139
|
+
assert.strictEqual(a, b)
|
|
140
|
+
})
|
|
141
|
+
.pipe(Effect.provide(TestLayer)))
|
|
142
|
+
|
|
143
|
+
it.live("activity side effects run once across re-executions", () =>
|
|
144
|
+
Effect
|
|
145
|
+
.gen(function*() {
|
|
146
|
+
const counter = yield* CounterRef
|
|
147
|
+
yield* CounterWorkflow.execute({ id: "once" })
|
|
148
|
+
yield* CounterWorkflow.execute({ id: "once" })
|
|
149
|
+
yield* CounterWorkflow.execute({ id: "once" })
|
|
150
|
+
assert.strictEqual(counter.count, 1)
|
|
151
|
+
})
|
|
152
|
+
.pipe(Effect.provide(TestLayer)))
|
|
153
|
+
|
|
154
|
+
it.live("suspends on a durable deferred, then resumes with the decoded result", () =>
|
|
155
|
+
Effect
|
|
156
|
+
.gen(function*() {
|
|
157
|
+
// The execution suspends on `Trigger`; completing the deferred resumes
|
|
158
|
+
// it (the workflow body replays, the `step` activity is served from its
|
|
159
|
+
// persisted result), and the final value round-trips through the engine.
|
|
160
|
+
const executionId = yield* SuspendWorkflow.execute({ id: "abc" }, { discard: true })
|
|
161
|
+
|
|
162
|
+
const token = yield* DurableDeferred.tokenFromPayload(Trigger, {
|
|
163
|
+
workflow: SuspendWorkflow,
|
|
164
|
+
payload: { id: "abc" }
|
|
165
|
+
})
|
|
166
|
+
yield* DurableDeferred.done(Trigger, { token, exit: Exit.succeed("ok") })
|
|
167
|
+
|
|
168
|
+
const done = yield* waitForComplete(SuspendWorkflow, executionId)
|
|
169
|
+
assert(done && Exit.isSuccess(done.exit))
|
|
170
|
+
assert.strictEqual(done.exit.value, "abc:1:ok")
|
|
171
|
+
})
|
|
172
|
+
.pipe(Effect.provide(TestLayer)))
|
|
173
|
+
|
|
174
|
+
it.live("deferredDone is idempotent (first-writer-wins)", () =>
|
|
175
|
+
Effect
|
|
176
|
+
.gen(function*() {
|
|
177
|
+
const executionId = yield* AwaitOnly.execute({ id: "dup" }, { discard: true })
|
|
178
|
+
const token = yield* DurableDeferred.tokenFromPayload(Trigger, {
|
|
179
|
+
workflow: AwaitOnly,
|
|
180
|
+
payload: { id: "dup" }
|
|
181
|
+
})
|
|
182
|
+
yield* DurableDeferred.done(Trigger, { token, exit: Exit.succeed("first") })
|
|
183
|
+
// Second completion must lose; the workflow body sees "first".
|
|
184
|
+
yield* DurableDeferred.done(Trigger, { token, exit: Exit.succeed("second") })
|
|
185
|
+
|
|
186
|
+
const done = yield* waitForComplete(AwaitOnly, executionId)
|
|
187
|
+
assert(done && Exit.isSuccess(done.exit))
|
|
188
|
+
assert.strictEqual(done.exit.value, "first")
|
|
189
|
+
})
|
|
190
|
+
.pipe(Effect.provide(TestLayer)))
|
|
191
|
+
|
|
192
|
+
it.live("interrupt eventually completes a suspended execution", () =>
|
|
193
|
+
Effect
|
|
194
|
+
.gen(function*() {
|
|
195
|
+
const executionId = yield* AwaitOnly.execute({ id: "int" }, { discard: true })
|
|
196
|
+
// Give the workflow time to suspend on the deferred.
|
|
197
|
+
yield* Effect.sleep(Duration.millis(50))
|
|
198
|
+
yield* AwaitOnly.interrupt(executionId)
|
|
199
|
+
|
|
200
|
+
// Interrupt collapses the suspended execution into a completion. A
|
|
201
|
+
// durable engine re-drives across several storage round-trips, so poll
|
|
202
|
+
// until it reports Complete rather than asserting a fixed delay.
|
|
203
|
+
const done = yield* waitForComplete(AwaitOnly, executionId)
|
|
204
|
+
assert(done !== undefined && done._tag === "Complete")
|
|
205
|
+
})
|
|
206
|
+
.pipe(Effect.provide(TestLayer)))
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
describe("WorkflowEngine (in-memory)", () => {
|
|
210
|
+
runSuite(WorkflowEngine.layerMemory)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
// --- Cosmos-emulator-only adapter tests --------------------------------
|
|
214
|
+
//
|
|
215
|
+
// Opt-in. Run with e.g.
|
|
216
|
+
// COSMOS_TEST_URL="https://localhost:8081" COSMOS_TEST_DB="workflow-test" \
|
|
217
|
+
// NODE_TLS_REJECT_UNAUTHORIZED=0 pnpm vitest run workflow-engine-cosmos
|
|
218
|
+
const cosmosUrl = process.env["COSMOS_TEST_URL"]
|
|
219
|
+
const cosmosDb = process.env["COSMOS_TEST_DB"] ?? "workflow-test"
|
|
220
|
+
|
|
221
|
+
describe.skipIf(!cosmosUrl)("WorkflowEngine (Cosmos) — conformance", () => {
|
|
222
|
+
runSuite(
|
|
223
|
+
layerCosmos({
|
|
224
|
+
url: Redacted.make(cosmosUrl ?? ""),
|
|
225
|
+
dbName: cosmosDb,
|
|
226
|
+
prefix: `test-${Date.now()}-`,
|
|
227
|
+
// Tight cadences so the suite doesn't wait on the background pollers.
|
|
228
|
+
recoveryInterval: Duration.seconds(2),
|
|
229
|
+
clockPollInterval: Duration.seconds(1)
|
|
230
|
+
})
|
|
231
|
+
)
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
describe.skipIf(!cosmosUrl)("WorkflowEngine (Cosmos) — adapter internals", () => {
|
|
235
|
+
// Each test gets its own container prefix so seeded docs don't leak.
|
|
236
|
+
const prefixFor = (label: string) => `test-${Date.now()}-${label}-`
|
|
237
|
+
|
|
238
|
+
const adapterLayer = (prefix: string) =>
|
|
239
|
+
Layer
|
|
240
|
+
.mergeAll(
|
|
241
|
+
IncrementHandler,
|
|
242
|
+
AwaitOnlyHandler,
|
|
243
|
+
CounterRef.layer,
|
|
244
|
+
CosmosClientLayer(cosmosUrl ?? "", cosmosDb)
|
|
245
|
+
)
|
|
246
|
+
.pipe(
|
|
247
|
+
Layer.provideMerge(
|
|
248
|
+
layerCosmos({
|
|
249
|
+
url: Redacted.make(cosmosUrl ?? ""),
|
|
250
|
+
dbName: cosmosDb,
|
|
251
|
+
prefix,
|
|
252
|
+
recoveryInterval: Duration.millis(500),
|
|
253
|
+
clockPollInterval: Duration.millis(500)
|
|
254
|
+
})
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
it.live("recovery poller drives execs with stale leases", () => {
|
|
259
|
+
const prefix = prefixFor("recovery")
|
|
260
|
+
const containerId = `${prefix}workflow-engine`
|
|
261
|
+
return Effect
|
|
262
|
+
.gen(function*() {
|
|
263
|
+
const { db } = yield* CosmosClient
|
|
264
|
+
const container = db.container(containerId)
|
|
265
|
+
// Pre-seed a running exec whose lease has already expired and whose
|
|
266
|
+
// payload is the schema-encoded form of `{ value: 99 }`.
|
|
267
|
+
yield* Effect.promise(() =>
|
|
268
|
+
container.items.upsert({
|
|
269
|
+
id: "exec",
|
|
270
|
+
_partitionKey: "recover-1",
|
|
271
|
+
type: "exec",
|
|
272
|
+
workflowName: IncrementWorkflow._tag,
|
|
273
|
+
payload: JSON.stringify({ value: 99 }),
|
|
274
|
+
status: "running",
|
|
275
|
+
suspended: false,
|
|
276
|
+
interrupted: false,
|
|
277
|
+
worker: "ghost",
|
|
278
|
+
leaseExpiresAt: new Date(Date.now() - 60_000).toISOString(),
|
|
279
|
+
etag: "seed"
|
|
280
|
+
})
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
// Wait for the recovery poller (500ms cadence) to drive it.
|
|
284
|
+
yield* Effect.sleep(Duration.seconds(2))
|
|
285
|
+
|
|
286
|
+
const polled = yield* IncrementWorkflow.poll("recover-1")
|
|
287
|
+
assert(
|
|
288
|
+
Option.isSome(polled)
|
|
289
|
+
&& polled.value._tag === "Complete"
|
|
290
|
+
&& Exit.isSuccess(polled.value.exit)
|
|
291
|
+
)
|
|
292
|
+
assert.strictEqual(polled.value.exit.value, 100)
|
|
293
|
+
})
|
|
294
|
+
.pipe(Effect.provide(adapterLayer(prefix)))
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
it.live("clock poller fires past-due clocks", () => {
|
|
298
|
+
const prefix = prefixFor("clocks")
|
|
299
|
+
const containerId = `${prefix}workflow-engine`
|
|
300
|
+
return Effect
|
|
301
|
+
.gen(function*() {
|
|
302
|
+
const { db } = yield* CosmosClient
|
|
303
|
+
const container = db.container(containerId)
|
|
304
|
+
// Seed an exec + a clock that fired in the past. No in-process timer
|
|
305
|
+
// exists (we never called scheduleClock), so only the poller can
|
|
306
|
+
// resolve the deferred.
|
|
307
|
+
yield* Effect.promise(() =>
|
|
308
|
+
container.items.upsert({
|
|
309
|
+
id: "exec",
|
|
310
|
+
_partitionKey: "exec-clock",
|
|
311
|
+
type: "exec",
|
|
312
|
+
workflowName: AwaitOnly._tag,
|
|
313
|
+
payload: JSON.stringify({ id: "wake" }),
|
|
314
|
+
status: "running",
|
|
315
|
+
suspended: false,
|
|
316
|
+
interrupted: false,
|
|
317
|
+
etag: "seed"
|
|
318
|
+
})
|
|
319
|
+
)
|
|
320
|
+
yield* Effect.promise(() =>
|
|
321
|
+
container.items.upsert({
|
|
322
|
+
id: "clock::wake",
|
|
323
|
+
_partitionKey: "exec-clock",
|
|
324
|
+
type: "clock",
|
|
325
|
+
workflowName: AwaitOnly._tag,
|
|
326
|
+
deferredName: Trigger.name,
|
|
327
|
+
fireAt: new Date(Date.now() - 60_000).toISOString()
|
|
328
|
+
})
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
yield* Effect.sleep(Duration.seconds(2))
|
|
332
|
+
|
|
333
|
+
// The clock fire is a deferred-complete; assert the deferred row
|
|
334
|
+
// now exists for this execution.
|
|
335
|
+
const deferred = yield* Effect.promise(() =>
|
|
336
|
+
container.item(encodeURIComponent(`deferred::${Trigger.name}`), "exec-clock").read<{ exit: string }>()
|
|
337
|
+
)
|
|
338
|
+
assert(deferred.resource !== undefined)
|
|
339
|
+
// And the clock doc has been deleted.
|
|
340
|
+
const clockGone = yield* Effect.promise(() => container.item("clock::wake", "exec-clock").read())
|
|
341
|
+
assert.strictEqual(clockGone.statusCode, 404)
|
|
342
|
+
})
|
|
343
|
+
.pipe(Effect.provide(adapterLayer(prefix)))
|
|
344
|
+
})
|
|
345
|
+
})
|