@effect-app/infra 4.0.0-beta.3 → 4.0.0-beta.300
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 +2485 -0
- package/_check.sh +1 -1
- package/dist/CUPS.d.ts +29 -10
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +35 -14
- package/dist/ClusterCosmos.d.ts +64 -0
- package/dist/ClusterCosmos.d.ts.map +1 -0
- package/dist/ClusterCosmos.js +547 -0
- package/dist/ClusterServiceBus.d.ts +67 -0
- package/dist/ClusterServiceBus.d.ts.map +1 -0
- package/dist/ClusterServiceBus.js +82 -0
- package/dist/ContextProvider.d.ts +34 -0
- package/dist/ContextProvider.d.ts.map +1 -0
- package/dist/ContextProvider.js +40 -0
- package/dist/Emailer/Sendgrid.d.ts +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 +12 -8
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +23 -7
- package/dist/QueueMaker/errors.d.ts +4 -2
- package/dist/QueueMaker/errors.d.ts.map +1 -1
- package/dist/QueueMaker/errors.js +4 -2
- package/dist/{adapters/SQL → SQL}/Model.d.ts +33 -47
- 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 +14 -3
- package/dist/Store/Cosmos/query.d.ts.map +1 -1
- package/dist/Store/Cosmos/query.js +179 -41
- package/dist/Store/Cosmos.d.ts +2 -2
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +385 -254
- package/dist/Store/Disk.d.ts +4 -4
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +79 -39
- package/dist/Store/Memory.d.ts +5 -9
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +324 -67
- package/dist/Store/SQL/Pg.d.ts +4 -0
- package/dist/Store/SQL/Pg.d.ts.map +1 -0
- package/dist/Store/SQL/Pg.js +232 -0
- package/dist/Store/SQL/query.d.ts +49 -0
- package/dist/Store/SQL/query.d.ts.map +1 -0
- package/dist/Store/SQL/query.js +527 -0
- package/dist/Store/SQL.d.ts +21 -0
- package/dist/Store/SQL.d.ts.map +1 -0
- package/dist/Store/SQL.js +456 -0
- package/dist/Store/codeFilter.d.ts +4 -4
- package/dist/Store/codeFilter.d.ts.map +1 -1
- package/dist/Store/codeFilter.js +6 -4
- package/dist/Store/index.d.ts +6 -4
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +18 -5
- package/dist/Store/utils.d.ts +5 -2
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +9 -5
- package/dist/WorkflowEngineCosmos.d.ts +29 -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 +1 -1
- package/dist/arbs.d.ts.map +1 -1
- package/dist/arbs.js +5 -3
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +5 -0
- package/dist/{adapters/cosmos-client.d.ts → cosmos-client.d.ts} +4 -2
- package/dist/cosmos-client.d.ts.map +1 -0
- package/dist/cosmos-client.js +11 -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 +4 -3
- 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/internal/health.d.ts.map +1 -0
- package/dist/internal/health.js +5 -0
- package/dist/{api/layerUtils.d.ts → layerUtils.d.ts} +14 -6
- 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 +5 -5
- package/dist/logger.d.ts +4 -4
- 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} +3 -2
- package/dist/mongo-client.d.ts.map +1 -0
- package/dist/mongo-client.js +15 -0
- package/dist/otel.d.ts +75 -0
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +65 -0
- package/dist/rateLimit.d.ts +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} +6 -3
- 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 +4 -3
- package/dist/routing/middleware/RouterMiddleware.d.ts.map +1 -0
- package/dist/{api/routing → routing}/middleware/RouterMiddleware.js +1 -1
- package/dist/routing/middleware/middleware.d.ts +48 -0
- package/dist/routing/middleware/middleware.d.ts.map +1 -0
- package/dist/routing/middleware/middleware.js +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/{api/routing → routing}/schema/jwt.d.ts +1 -1
- package/dist/routing/schema/jwt.d.ts.map +1 -0
- package/dist/routing/schema/jwt.js +13 -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 +2 -2
- 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 +47 -39
- package/package.json +38 -352
- package/src/CUPS.ts +53 -14
- package/src/ClusterCosmos.ts +1118 -0
- package/src/ClusterServiceBus.ts +242 -0
- package/src/{api/ContextProvider.ts → ContextProvider.ts} +20 -17
- package/src/Emailer/Sendgrid.ts +83 -60
- package/src/Emailer/fake.ts +4 -4
- package/src/MainFiberSet.ts +32 -13
- package/src/QueueMaker/errors.ts +3 -1
- package/src/{adapters/SQL → SQL}/Model.ts +116 -110
- package/src/SQL.ts +1 -0
- package/src/Store/Cosmos/query.ts +218 -54
- package/src/Store/Cosmos.ts +529 -365
- package/src/Store/Disk.ts +112 -70
- package/src/Store/Memory.ts +366 -102
- package/src/Store/SQL/Pg.ts +363 -0
- package/src/Store/SQL/query.ts +603 -0
- package/src/Store/SQL.ts +746 -0
- package/src/Store/codeFilter.ts +8 -6
- package/src/Store/index.ts +25 -10
- package/src/Store/utils.ts +32 -25
- package/src/WorkflowEngineCosmos.ts +749 -0
- package/src/WorkflowEngineSqlite.ts +813 -0
- package/src/arbs.ts +5 -3
- package/src/{adapters/cosmos-client.ts → cosmos-client.ts} +5 -3
- package/src/errorReporter.ts +68 -78
- package/src/fileUtil.ts +3 -2
- 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 +25 -15
- package/src/{api/layerUtils.ts → layerUtils.ts} +14 -10
- package/src/logger/jsonLogger.ts +5 -3
- package/src/logger/logFmtLogger.ts +3 -3
- package/src/logger/shared.ts +5 -4
- package/src/middlewares.ts +10 -0
- package/src/{adapters/mongo-client.ts → mongo-client.ts} +4 -2
- package/src/otel.ts +152 -0
- package/src/rateLimit.ts +34 -23
- package/src/{adapters/redis-client.ts → redis-client.ts} +7 -3
- package/src/{api/reportError.ts → reportError.ts} +3 -2
- package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +5 -4
- package/src/routing/middleware/middleware.ts +196 -0
- package/src/routing/middleware.ts +4 -0
- package/src/{api/routing → routing}/schema/jwt.ts +4 -4
- package/src/{api/routing → routing}/utils.ts +2 -1
- package/src/routing.ts +823 -0
- package/src/test.ts +4 -4
- package/src/vitest.ts +1 -1
- package/test/auth.test.ts +101 -0
- package/test/cluster-cosmos.test.ts +612 -0
- package/test/cluster-servicebus.test.ts +180 -0
- package/test/cluster-sqlite.test.ts +205 -0
- package/test/contextProvider.test.ts +15 -12
- package/test/controller.test.ts +30 -32
- package/test/cosmos-query.test.ts +228 -0
- package/test/dist/auth.test.d.ts.map +1 -0
- package/test/dist/cluster-cosmos.test.d.ts.map +1 -0
- package/test/dist/cluster-servicebus.test.d.ts.map +1 -0
- package/test/dist/cluster-sqlite.test.d.ts.map +1 -0
- package/test/dist/contextProvider.test.d.ts.map +1 -1
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/cosmos-query.test.d.ts.map +1 -0
- package/test/dist/fixtures.d.ts +29 -11
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +17 -10
- package/test/dist/query.test.d.ts.map +1 -1
- package/test/dist/rawQuery.test.d.ts.map +1 -1
- package/test/dist/repository-ext.test.d.ts.map +1 -0
- package/test/dist/requires.test.d.ts.map +1 -1
- package/test/dist/router-generator.test.d.ts.map +1 -0
- package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
- package/test/dist/rpc-context-map-streaming.test.d.ts.map +1 -0
- package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
- package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
- package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
- package/test/dist/sql-store.test.d.ts.map +1 -0
- package/test/dist/workflow-engine-cosmos.test.d.ts.map +1 -0
- package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
- package/test/fixtures.ts +16 -9
- package/test/layerUtils.test.ts +2 -2
- package/test/query.test.ts +1007 -43
- package/test/rawQuery.test.ts +340 -22
- package/test/repository-ext.test.ts +178 -0
- package/test/requires.test.ts +10 -5
- package/test/router-generator.test.ts +187 -0
- package/test/routing-interruptibility.test.ts +66 -0
- package/test/rpc-context-map-streaming.test.ts +262 -0
- package/test/rpc-e2e-invalidation.test.ts +418 -0
- package/test/rpc-multi-middleware.test.ts +86 -11
- package/test/rpc-stream-fullstack.test.ts +304 -0
- package/test/sql-store.test.ts +1711 -0
- package/test/validateSample.test.ts +26 -14
- package/test/workflow-engine-cosmos.test.ts +345 -0
- package/test/workflow-engine-sqlite.test.ts +297 -0
- package/tsconfig.examples.json +1 -1
- package/tsconfig.json +3 -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 -316
- package/dist/Model/Repository/legacy.d.ts +0 -19
- package/dist/Model/Repository/legacy.d.ts.map +0 -1
- package/dist/Model/Repository/legacy.js +0 -2
- package/dist/Model/Repository/makeRepo.d.ts +0 -49
- package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
- package/dist/Model/Repository/makeRepo.js +0 -24
- package/dist/Model/Repository/service.d.ts +0 -89
- package/dist/Model/Repository/service.d.ts.map +0 -1
- package/dist/Model/Repository/service.js +0 -2
- package/dist/Model/Repository/validation.d.ts +0 -42
- package/dist/Model/Repository/validation.d.ts.map +0 -1
- package/dist/Model/Repository/validation.js +0 -32
- package/dist/Model/Repository.d.ts +0 -6
- package/dist/Model/Repository.d.ts.map +0 -1
- package/dist/Model/Repository.js +0 -6
- package/dist/Model/dsl.d.ts +0 -32
- package/dist/Model/dsl.d.ts.map +0 -1
- package/dist/Model/dsl.js +0 -44
- package/dist/Model/filter/filterApi.d.ts +0 -30
- package/dist/Model/filter/filterApi.d.ts.map +0 -1
- package/dist/Model/filter/filterApi.js +0 -2
- package/dist/Model/filter/types/errors.d.ts +0 -29
- package/dist/Model/filter/types/errors.d.ts.map +0 -1
- package/dist/Model/filter/types/errors.js +0 -2
- package/dist/Model/filter/types/fields.d.ts +0 -15
- package/dist/Model/filter/types/fields.d.ts.map +0 -1
- package/dist/Model/filter/types/fields.js +0 -2
- package/dist/Model/filter/types/path/common.d.ts +0 -316
- package/dist/Model/filter/types/path/common.d.ts.map +0 -1
- package/dist/Model/filter/types/path/common.js +0 -2
- package/dist/Model/filter/types/path/eager.d.ts +0 -95
- package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
- package/dist/Model/filter/types/path/eager.js +0 -31
- package/dist/Model/filter/types/path/index.d.ts +0 -4
- package/dist/Model/filter/types/path/index.d.ts.map +0 -1
- package/dist/Model/filter/types/path/index.js +0 -3
- package/dist/Model/filter/types/utils.d.ts +0 -79
- package/dist/Model/filter/types/utils.d.ts.map +0 -1
- package/dist/Model/filter/types/utils.js +0 -2
- package/dist/Model/filter/types/validator.d.ts +0 -30
- package/dist/Model/filter/types/validator.d.ts.map +0 -1
- package/dist/Model/filter/types/validator.js +0 -2
- package/dist/Model/filter/types.d.ts +0 -5
- package/dist/Model/filter/types.d.ts.map +0 -1
- package/dist/Model/filter/types.js +0 -7
- package/dist/Model/query/dsl.d.ts +0 -248
- package/dist/Model/query/dsl.d.ts.map +0 -1
- package/dist/Model/query/dsl.js +0 -104
- package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
- package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
- package/dist/Model/query/new-kid-interpreter.js +0 -165
- package/dist/Model/query.d.ts +0 -15
- package/dist/Model/query.d.ts.map +0 -1
- package/dist/Model/query.js +0 -3
- package/dist/Model.d.ts +0 -4
- package/dist/Model.d.ts.map +0 -1
- package/dist/Model.js +0 -4
- package/dist/Operations.d.ts +0 -55
- package/dist/Operations.d.ts.map +0 -1
- package/dist/Operations.js +0 -102
- package/dist/OperationsRepo.d.ts +0 -41
- package/dist/OperationsRepo.d.ts.map +0 -1
- package/dist/OperationsRepo.js +0 -14
- package/dist/QueueMaker/SQLQueue.d.ts +0 -16
- package/dist/QueueMaker/SQLQueue.d.ts.map +0 -1
- package/dist/QueueMaker/SQLQueue.js +0 -130
- package/dist/QueueMaker/memQueue.d.ts +0 -11
- package/dist/QueueMaker/memQueue.d.ts.map +0 -1
- package/dist/QueueMaker/memQueue.js +0 -75
- package/dist/QueueMaker/sbqueue.d.ts +0 -12
- package/dist/QueueMaker/sbqueue.d.ts.map +0 -1
- package/dist/QueueMaker/sbqueue.js +0 -89
- package/dist/QueueMaker/service.d.ts +0 -11
- package/dist/QueueMaker/service.d.ts.map +0 -1
- package/dist/QueueMaker/service.js +0 -4
- package/dist/RequestContext.d.ts +0 -63
- package/dist/RequestContext.d.ts.map +0 -1
- package/dist/RequestContext.js +0 -49
- package/dist/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 -14
- package/dist/Store/ContextMapContainer.d.ts.map +0 -1
- package/dist/Store/ContextMapContainer.js +0 -16
- package/dist/Store/service.d.ts +0 -108
- package/dist/Store/service.d.ts.map +0 -1
- package/dist/Store/service.js +0 -71
- package/dist/Store.d.ts +0 -2
- package/dist/Store.d.ts.map +0 -1
- package/dist/Store.js +0 -2
- package/dist/adapters/SQL/Model.d.ts.map +0 -1
- package/dist/adapters/SQL/Model.js +0 -470
- 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.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 +0 -31
- 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 -40
- 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 +0 -9
- package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
- package/dist/api/routing/middleware/middleware.js +0 -92
- package/dist/api/routing/middleware.d.ts.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 -138
- package/dist/api/routing.d.ts.map +0 -1
- package/dist/api/routing.js +0 -166
- package/dist/api/setupRequest.d.ts +0 -12
- package/dist/api/setupRequest.d.ts.map +0 -1
- package/dist/api/setupRequest.js +0 -44
- package/dist/api/util.d.ts.map +0 -1
- package/dist/api/util.js +0 -14
- package/eslint.config.mjs +0 -24
- package/src/Emailer/service.ts +0 -52
- package/src/Emailer.ts +0 -1
- package/src/Model/Repository/ext.ts +0 -283
- package/src/Model/Repository/internal/internal.ts +0 -577
- package/src/Model/Repository/legacy.ts +0 -27
- package/src/Model/Repository/makeRepo.ts +0 -139
- package/src/Model/Repository/service.ts +0 -627
- package/src/Model/Repository/validation.ts +0 -31
- package/src/Model/Repository.ts +0 -5
- package/src/Model/dsl.ts +0 -128
- package/src/Model/filter/filterApi.ts +0 -60
- package/src/Model/filter/types/errors.ts +0 -47
- package/src/Model/filter/types/fields.ts +0 -50
- package/src/Model/filter/types/path/common.ts +0 -404
- package/src/Model/filter/types/path/eager.ts +0 -298
- package/src/Model/filter/types/path/index.ts +0 -4
- package/src/Model/filter/types/utils.ts +0 -128
- package/src/Model/filter/types/validator.ts +0 -46
- package/src/Model/filter/types.ts +0 -6
- package/src/Model/query/dsl.ts +0 -2110
- package/src/Model/query/new-kid-interpreter.ts +0 -210
- package/src/Model/query.ts +0 -13
- package/src/Model.ts +0 -3
- package/src/Operations.ts +0 -235
- package/src/OperationsRepo.ts +0 -16
- package/src/QueueMaker/SQLQueue.ts +0 -190
- package/src/QueueMaker/memQueue.ts +0 -131
- package/src/QueueMaker/sbqueue.ts +0 -134
- 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 -20
- package/src/Store/index.test.ts.bak +0 -112
- package/src/Store/service.ts +0 -184
- package/src/Store.ts +0 -1
- package/src/adapters/SQL.ts +0 -1
- package/src/adapters/ServiceBus.ts +0 -209
- package/src/adapters/index.ts +0 -0
- package/src/adapters/logger.ts +0 -3
- package/src/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/middleware.ts +0 -128
- package/src/api/routing/middleware.ts +0 -6
- package/src/api/routing.ts +0 -597
- package/src/api/setupRequest.ts +0 -84
- 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/internal → internal}/health.d.ts +0 -0
- /package/dist/{api/middlewares.d.ts → middlewares.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
package/test/query.test.ts
CHANGED
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
/* eslint-disable unused-imports/no-unused-vars */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
|
-
import
|
|
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 { and, computed, count, expr, make, one, or, order, page, project, projectComputed, type QueryEnd, type QueryProjection, type QueryWhere, relation, toFilter, where } from "effect-app/Model/query"
|
|
8
|
+
import { makeRepo } from "effect-app/Model/Repository"
|
|
9
|
+
import { RepositoryRegistryLive } from "effect-app/Model/Repository/Registry"
|
|
10
|
+
import * as Option from "effect-app/Option"
|
|
11
|
+
import * as S from "effect-app/Schema"
|
|
12
|
+
import { setupRequestContextFromCurrent } from "effect-app/setupRequest"
|
|
13
|
+
import { flow, pipe } from "effect/Function"
|
|
14
|
+
import * as SchemaTransformation from "effect/SchemaTransformation"
|
|
15
|
+
import * as Struct from "effect/Struct"
|
|
5
16
|
import { inspect } from "util"
|
|
6
17
|
import { expect, expectTypeOf, it } from "vitest"
|
|
7
|
-
import { setupRequestContextFromCurrent } from "../src/api/setupRequest.js"
|
|
8
|
-
import { and, count, make, one, or, order, page, project, type QueryEnd, type QueryProjection, type QueryWhere, toFilter, where } from "../src/Model/query.js"
|
|
9
|
-
import { makeRepo } from "../src/Model/Repository.js"
|
|
10
18
|
import { memFilter, MemoryStoreLive } from "../src/Store/Memory.js"
|
|
11
19
|
import { SomeService } from "./fixtures.js"
|
|
12
20
|
|
|
21
|
+
const TestStoreLive = Layer.merge(MemoryStoreLive, RepositoryRegistryLive)
|
|
22
|
+
|
|
13
23
|
const str = S.Struct({ _tag: S.Literal("string"), value: S.String })
|
|
14
|
-
const num = S.Struct({ _tag: S.Literal("number"), value: S.
|
|
24
|
+
const num = S.Struct({ _tag: S.Literal("number"), value: S.Finite })
|
|
15
25
|
const someUnion = S.Union([str, num])
|
|
16
26
|
|
|
17
27
|
export class Something extends S.Class<Something>("Something")({
|
|
18
|
-
id: S.StringId.
|
|
28
|
+
id: S.StringId.withConstructorDefault,
|
|
19
29
|
displayName: S.NonEmptyString255,
|
|
20
|
-
name: S.NullOr(S.NonEmptyString255).
|
|
21
|
-
n: S.Date.
|
|
22
|
-
union: someUnion.pipe(S.
|
|
30
|
+
name: S.NullOr(S.NonEmptyString255).withConstructorDefault,
|
|
31
|
+
n: S.Date.withConstructorDefault,
|
|
32
|
+
union: someUnion.pipe(S.withConstructorDefault(Effect.succeed({ _tag: "string" as const, value: "hi" })))
|
|
23
33
|
}) {}
|
|
24
34
|
export declare namespace Something {
|
|
25
35
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
@@ -90,8 +100,8 @@ it("works", () => {
|
|
|
90
100
|
|
|
91
101
|
const processed = memFilter(interpreted)(items.map((_) =>
|
|
92
102
|
S.encodeUnknownSync(S.Struct({
|
|
93
|
-
...
|
|
94
|
-
displayName: S.
|
|
103
|
+
...Struct.omit(Something.fields, ["displayName"]),
|
|
104
|
+
displayName: S.Literals(["Verona", "Riley"])
|
|
95
105
|
}))(_)
|
|
96
106
|
))
|
|
97
107
|
|
|
@@ -99,7 +109,7 @@ it("works", () => {
|
|
|
99
109
|
})
|
|
100
110
|
|
|
101
111
|
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
102
|
-
class SomethingRepo extends
|
|
112
|
+
class SomethingRepo extends Context.Service<SomethingRepo>()("SomethingRepo", {
|
|
103
113
|
make: Effect.gen(function*() {
|
|
104
114
|
return yield* makeRepo("Something", Something, {})
|
|
105
115
|
})
|
|
@@ -112,7 +122,7 @@ class SomethingRepo extends ServiceMap.Service<SomethingRepo>()("SomethingRepo",
|
|
|
112
122
|
})
|
|
113
123
|
)
|
|
114
124
|
.pipe(
|
|
115
|
-
Layer.provide(
|
|
125
|
+
Layer.provide(TestStoreLive)
|
|
116
126
|
)
|
|
117
127
|
}
|
|
118
128
|
|
|
@@ -156,14 +166,13 @@ it("works with repo", () =>
|
|
|
156
166
|
|
|
157
167
|
expectTypeOf(smtArr).toEqualTypeOf<readonly Something[]>()
|
|
158
168
|
|
|
159
|
-
console.log(" $$$$$$")
|
|
160
|
-
console.log(Struct.pick(["id", "displayName"]))
|
|
161
169
|
expect(q1).toEqual(items.slice(0, 2).toReversed().map(Struct.pick(["id", "displayName"])))
|
|
162
170
|
expect(q2).toEqual(items.slice(0, 2).toReversed().map(Struct.pick(["displayName"])))
|
|
163
171
|
})
|
|
164
172
|
.pipe(
|
|
165
173
|
Effect.provide(Layer.mergeAll(SomethingRepo.Test, SomeService.Default)),
|
|
166
174
|
setupRequestContextFromCurrent(),
|
|
175
|
+
Effect.scoped,
|
|
167
176
|
Effect.runPromise
|
|
168
177
|
))
|
|
169
178
|
|
|
@@ -235,18 +244,19 @@ it("collect", () =>
|
|
|
235
244
|
.pipe(
|
|
236
245
|
Effect.provide(Layer.mergeAll(SomethingRepo.Test, SomeService.Default)),
|
|
237
246
|
setupRequestContextFromCurrent(),
|
|
247
|
+
Effect.scoped,
|
|
238
248
|
Effect.runPromise
|
|
239
249
|
))
|
|
240
250
|
|
|
241
|
-
class Person extends S.
|
|
251
|
+
class Person extends S.TaggedClass<Person, Person.Encoded>()("person", {
|
|
242
252
|
id: S.String,
|
|
243
253
|
surname: S.String
|
|
244
254
|
}) {}
|
|
245
|
-
class Animal extends S.
|
|
255
|
+
class Animal extends S.TaggedClass<Animal, Animal.Encoded>()("animal", {
|
|
246
256
|
id: S.String,
|
|
247
257
|
surname: S.String
|
|
248
258
|
}) {}
|
|
249
|
-
class Test extends S.
|
|
259
|
+
class Test extends S.TaggedClass<Test, Test.Encoded>()("test", {
|
|
250
260
|
id: S.String
|
|
251
261
|
}) {}
|
|
252
262
|
|
|
@@ -281,7 +291,7 @@ it(
|
|
|
281
291
|
expect(result).toEqual([])
|
|
282
292
|
expect(result2).toEqual([])
|
|
283
293
|
})
|
|
284
|
-
.pipe(Effect.provide(
|
|
294
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
285
295
|
)
|
|
286
296
|
|
|
287
297
|
it(
|
|
@@ -379,7 +389,6 @@ it(
|
|
|
379
389
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
380
390
|
const query5 = make<Union>().pipe(
|
|
381
391
|
where("id", "bla"),
|
|
382
|
-
// @ts-expect-error cannot project over fields that are not in common between the union members (you must refine the union first)
|
|
383
392
|
project(S.Struct({ id: S.String, a: S.Unknown }))
|
|
384
393
|
)
|
|
385
394
|
console.log(query5)
|
|
@@ -467,7 +476,7 @@ it(
|
|
|
467
476
|
|
|
468
477
|
expect([]).toEqual([])
|
|
469
478
|
})
|
|
470
|
-
.pipe(Effect.provide(
|
|
479
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
471
480
|
)
|
|
472
481
|
|
|
473
482
|
it(
|
|
@@ -510,7 +519,7 @@ it(
|
|
|
510
519
|
|
|
511
520
|
expect([]).toEqual([])
|
|
512
521
|
})
|
|
513
|
-
.pipe(Effect.provide(
|
|
522
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
514
523
|
)
|
|
515
524
|
|
|
516
525
|
it(
|
|
@@ -521,8 +530,8 @@ it(
|
|
|
521
530
|
const schema = S.Struct({
|
|
522
531
|
id: S.String,
|
|
523
532
|
createdAt: S.Date.pipe(
|
|
524
|
-
S.withDecodingDefault(() => new Date().toISOString()),
|
|
525
|
-
S.withConstructorDefault(() =>
|
|
533
|
+
S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
|
|
534
|
+
S.withConstructorDefault(Effect.sync(() => new Date()))
|
|
526
535
|
)
|
|
527
536
|
})
|
|
528
537
|
const repo = yield* makeRepo(
|
|
@@ -534,8 +543,8 @@ it(
|
|
|
534
543
|
const outputSchema = S.Struct({
|
|
535
544
|
id: S.Literal("123"),
|
|
536
545
|
createdAt: S.Date.pipe(
|
|
537
|
-
S.withDecodingDefault(() => new Date().toISOString()),
|
|
538
|
-
S.withConstructorDefault(() =>
|
|
546
|
+
S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
|
|
547
|
+
S.withConstructorDefault(Effect.sync(() => new Date()))
|
|
539
548
|
)
|
|
540
549
|
})
|
|
541
550
|
|
|
@@ -543,9 +552,347 @@ it(
|
|
|
543
552
|
|
|
544
553
|
expect(result).toEqual([])
|
|
545
554
|
})
|
|
546
|
-
.pipe(Effect.provide(
|
|
555
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
547
556
|
)
|
|
548
557
|
|
|
558
|
+
it(
|
|
559
|
+
"project with encodeKeys in projection maps encoded keys",
|
|
560
|
+
() =>
|
|
561
|
+
Effect
|
|
562
|
+
.gen(function*() {
|
|
563
|
+
const schema = S.Struct({
|
|
564
|
+
id: S.String,
|
|
565
|
+
a: S.Number
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
const repo = yield* makeRepo(
|
|
569
|
+
"test",
|
|
570
|
+
schema,
|
|
571
|
+
{
|
|
572
|
+
makeInitial: Effect.sync(() => [{ id: "1", a: 1 }])
|
|
573
|
+
}
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
const outputSchema = S.Struct({ b: S.Number }).pipe(S.encodeKeys({ b: "a" }))
|
|
577
|
+
|
|
578
|
+
const result = yield* repo.query(project(outputSchema))
|
|
579
|
+
|
|
580
|
+
expect(result).toStrictEqual([{ b: 1 }])
|
|
581
|
+
})
|
|
582
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
it("projectComputed sets computed IR and forces project mode", () => {
|
|
586
|
+
const baseSchema = S.Struct({
|
|
587
|
+
id: S.String,
|
|
588
|
+
items: S.Array(S.Struct({
|
|
589
|
+
state: S.Struct({
|
|
590
|
+
_tag: S.String
|
|
591
|
+
})
|
|
592
|
+
}))
|
|
593
|
+
})
|
|
594
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
595
|
+
projectComputed(
|
|
596
|
+
S.Struct({
|
|
597
|
+
pickedCount: S.NonNegativeInt
|
|
598
|
+
}),
|
|
599
|
+
computed({
|
|
600
|
+
pickedCount: relation<S.Codec.Encoded<typeof baseSchema>>("items").count(where("state._tag", "Picked"))
|
|
601
|
+
})
|
|
602
|
+
)
|
|
603
|
+
)
|
|
604
|
+
const interpreted = toFilter(query, baseSchema)
|
|
605
|
+
expect(interpreted.mode).toBe("project")
|
|
606
|
+
expect(interpreted.select).toEqual([
|
|
607
|
+
{
|
|
608
|
+
key: "pickedCount",
|
|
609
|
+
computed: {
|
|
610
|
+
_tag: "relation-count",
|
|
611
|
+
path: "items",
|
|
612
|
+
filter: [{ t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }]
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
])
|
|
616
|
+
expect(interpreted.computed?.["pickedCount"]?._tag).toBe("relation-count")
|
|
617
|
+
expect(interpreted.computed?.["pickedCount"]?.path).toBe("items")
|
|
618
|
+
expect((interpreted.computed?.["pickedCount"] as { filter: unknown } | undefined)?.filter).toEqual([
|
|
619
|
+
{ t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }
|
|
620
|
+
])
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
it("projectComputed validates extra computed keys", () => {
|
|
624
|
+
const baseSchema = S.Struct({
|
|
625
|
+
id: S.String,
|
|
626
|
+
items: S.Array(S.Struct({ value: S.Number }))
|
|
627
|
+
})
|
|
628
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
629
|
+
projectComputed(
|
|
630
|
+
S.Struct({ id: S.String }),
|
|
631
|
+
// @ts-expect-error extra computed keys are rejected statically; keep runtime guard covered
|
|
632
|
+
computed({
|
|
633
|
+
pickedCount: relation<S.Codec.Encoded<typeof baseSchema>>("items").count()
|
|
634
|
+
})
|
|
635
|
+
)
|
|
636
|
+
)
|
|
637
|
+
expect(() => toFilter(query, baseSchema)).toThrowError("Computed projection keys must exist in projection schema")
|
|
638
|
+
})
|
|
639
|
+
|
|
640
|
+
it("projectComputed constrains projection schema to encoded repo fields and computed outputs", () => {
|
|
641
|
+
const baseSchema = S.Struct({
|
|
642
|
+
id: S.String,
|
|
643
|
+
name: S.String,
|
|
644
|
+
items: S.Array(S.Struct({ articleId: S.String, weight: S.Number }))
|
|
645
|
+
})
|
|
646
|
+
type Encoded = S.Codec.Encoded<typeof baseSchema>
|
|
647
|
+
|
|
648
|
+
make<Encoded>().pipe(
|
|
649
|
+
projectComputed(
|
|
650
|
+
S.Struct({
|
|
651
|
+
id: S.String,
|
|
652
|
+
itemCount: S.Number,
|
|
653
|
+
hasItems: S.Boolean,
|
|
654
|
+
articleIds: S.Array(S.String)
|
|
655
|
+
}),
|
|
656
|
+
({ relation }) => ({
|
|
657
|
+
itemCount: relation("items").count(),
|
|
658
|
+
hasItems: relation("items").any(),
|
|
659
|
+
articleIds: relation("items").collect("articleId")
|
|
660
|
+
})
|
|
661
|
+
)
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
make<Encoded>().pipe(
|
|
665
|
+
projectComputed(S.Struct({ missingField: S.String }), computed({}))
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
make<Encoded>().pipe(
|
|
669
|
+
projectComputed(
|
|
670
|
+
S.Struct({ name: S.Number }),
|
|
671
|
+
computed({})
|
|
672
|
+
)
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
make<Encoded>().pipe(
|
|
676
|
+
projectComputed(
|
|
677
|
+
S.Struct({ itemCount: S.String }),
|
|
678
|
+
computed({ itemCount: relation<Encoded>("items").count() })
|
|
679
|
+
)
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
make<Encoded>().pipe(
|
|
683
|
+
projectComputed(
|
|
684
|
+
S.Struct({ itemCount: S.Number }),
|
|
685
|
+
// @ts-expect-error extra computed keys must exist in the projection schema
|
|
686
|
+
computed({
|
|
687
|
+
itemCount: relation<Encoded>("items").count(),
|
|
688
|
+
extraCount: relation<Encoded>("items").count()
|
|
689
|
+
})
|
|
690
|
+
)
|
|
691
|
+
)
|
|
692
|
+
})
|
|
693
|
+
|
|
694
|
+
it("projectComputed supports union projection schemas", () => {
|
|
695
|
+
const baseSchema = S.Union([
|
|
696
|
+
S.Struct({
|
|
697
|
+
_tag: S.Literal("open"),
|
|
698
|
+
id: S.String,
|
|
699
|
+
items: S.Array(S.Struct({ articleId: S.String, weight: S.Number }))
|
|
700
|
+
}),
|
|
701
|
+
S.Struct({
|
|
702
|
+
_tag: S.Literal("closed"),
|
|
703
|
+
id: S.String,
|
|
704
|
+
closedAt: S.String
|
|
705
|
+
})
|
|
706
|
+
])
|
|
707
|
+
type Encoded = S.Codec.Encoded<typeof baseSchema>
|
|
708
|
+
const query = make<Encoded>().pipe(
|
|
709
|
+
projectComputed(
|
|
710
|
+
S.Union([
|
|
711
|
+
S.Struct({
|
|
712
|
+
_tag: S.Literal("open"),
|
|
713
|
+
id: S.String,
|
|
714
|
+
articleCount: S.Number,
|
|
715
|
+
weight: S.Number,
|
|
716
|
+
articleIds: S.Array(S.String)
|
|
717
|
+
}),
|
|
718
|
+
S.Struct({
|
|
719
|
+
_tag: S.Literal("closed"),
|
|
720
|
+
id: S.String,
|
|
721
|
+
closedAt: S.String
|
|
722
|
+
})
|
|
723
|
+
]),
|
|
724
|
+
computed({
|
|
725
|
+
articleCount: relation<Encoded>("items").count(),
|
|
726
|
+
weight: relation<Encoded>("items").sum("weight"),
|
|
727
|
+
articleIds: relation<Encoded>("items").collect("articleId")
|
|
728
|
+
})
|
|
729
|
+
)
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
const interpreted = toFilter(query, baseSchema)
|
|
733
|
+
expect(interpreted.computed && Object.keys(interpreted.computed).toSorted()).toEqual([
|
|
734
|
+
"articleCount",
|
|
735
|
+
"articleIds",
|
|
736
|
+
"weight"
|
|
737
|
+
])
|
|
738
|
+
})
|
|
739
|
+
|
|
740
|
+
it("projection schema with computed fields fails without computed map", () => {
|
|
741
|
+
const baseSchema = S.Struct({
|
|
742
|
+
id: S.String,
|
|
743
|
+
items: S.Array(S.Struct({ value: S.Number }))
|
|
744
|
+
})
|
|
745
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
746
|
+
projectComputed(S.Struct({ pickedCount: S.NonNegativeInt }), computed({}))
|
|
747
|
+
)
|
|
748
|
+
expect(() => toFilter(query, baseSchema)).toThrowError("Missing computed projections for schema keys")
|
|
749
|
+
})
|
|
750
|
+
|
|
751
|
+
it("projectComputed.every emits relation-every IR", () => {
|
|
752
|
+
const baseSchema = S.Struct({
|
|
753
|
+
id: S.String,
|
|
754
|
+
items: S.Array(S.Struct({ state: S.Struct({ _tag: S.String }) }))
|
|
755
|
+
})
|
|
756
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
757
|
+
projectComputed(
|
|
758
|
+
S.Struct({ allPicked: S.Boolean }),
|
|
759
|
+
computed({
|
|
760
|
+
allPicked: relation<S.Codec.Encoded<typeof baseSchema>>("items").every(where("state._tag", "Picked"))
|
|
761
|
+
})
|
|
762
|
+
)
|
|
763
|
+
)
|
|
764
|
+
const interpreted = toFilter(query, baseSchema)
|
|
765
|
+
expect(interpreted.computed?.["allPicked"]?._tag).toBe("relation-every")
|
|
766
|
+
expect(interpreted.computed?.["allPicked"]?.path).toBe("items")
|
|
767
|
+
expect((interpreted.computed?.["allPicked"] as { filter: unknown } | undefined)?.filter).toEqual([
|
|
768
|
+
{ t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }
|
|
769
|
+
])
|
|
770
|
+
})
|
|
771
|
+
|
|
772
|
+
it("projectComputed.distinctCount emits relation-distinct-count IR with field", () => {
|
|
773
|
+
const baseSchema = S.Struct({
|
|
774
|
+
id: S.String,
|
|
775
|
+
items: S.Array(S.Struct({ rowId: S.String, state: S.Struct({ _tag: S.String }) }))
|
|
776
|
+
})
|
|
777
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
778
|
+
projectComputed(
|
|
779
|
+
S.Struct({ positionCount: S.NonNegativeInt }),
|
|
780
|
+
computed({
|
|
781
|
+
positionCount: relation<S.Codec.Encoded<typeof baseSchema>>("items").distinctCount(
|
|
782
|
+
"rowId",
|
|
783
|
+
where("state._tag", "neq", "cancelled")
|
|
784
|
+
)
|
|
785
|
+
})
|
|
786
|
+
)
|
|
787
|
+
)
|
|
788
|
+
const interpreted = toFilter(query, baseSchema)
|
|
789
|
+
const ir = interpreted.computed?.["positionCount"]
|
|
790
|
+
expect(ir?._tag).toBe("relation-distinct-count")
|
|
791
|
+
expect((ir as { field: string } | undefined)?.field).toBe("rowId")
|
|
792
|
+
expect((ir as { filter: unknown } | undefined)?.filter).toEqual([
|
|
793
|
+
{ t: "where", path: "items.-1.state._tag", op: "neq", value: "cancelled" }
|
|
794
|
+
])
|
|
795
|
+
})
|
|
796
|
+
|
|
797
|
+
it("projectComputed.sum emits relation-sum IR with field", () => {
|
|
798
|
+
const baseSchema = S.Struct({
|
|
799
|
+
id: S.String,
|
|
800
|
+
items: S.Array(S.Struct({ weight: S.Number }))
|
|
801
|
+
})
|
|
802
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
803
|
+
projectComputed(
|
|
804
|
+
S.Struct({ totalWeight: S.Number }),
|
|
805
|
+
computed({ totalWeight: relation<S.Codec.Encoded<typeof baseSchema>>("items").sum("weight") })
|
|
806
|
+
)
|
|
807
|
+
)
|
|
808
|
+
const interpreted = toFilter(query, baseSchema)
|
|
809
|
+
const ir = interpreted.computed?.["totalWeight"]
|
|
810
|
+
expect(ir?._tag).toBe("relation-sum")
|
|
811
|
+
expect((ir as { field: string } | undefined)?.field).toBe("weight")
|
|
812
|
+
expect((ir as { filter: unknown } | undefined)?.filter).toEqual([])
|
|
813
|
+
})
|
|
814
|
+
|
|
815
|
+
it("projectComputed.collect / collectDistinct emit relation-collect IR", () => {
|
|
816
|
+
const baseSchema = S.Struct({
|
|
817
|
+
id: S.String,
|
|
818
|
+
items: S.Array(S.Struct({ articleId: S.String }))
|
|
819
|
+
})
|
|
820
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
821
|
+
projectComputed(
|
|
822
|
+
S.Struct({
|
|
823
|
+
all: S.Array(S.String),
|
|
824
|
+
distinct: S.Array(S.String)
|
|
825
|
+
}),
|
|
826
|
+
computed({
|
|
827
|
+
all: relation<S.Codec.Encoded<typeof baseSchema>>("items").collect("articleId"),
|
|
828
|
+
distinct: relation<S.Codec.Encoded<typeof baseSchema>>("items").collectDistinct("articleId")
|
|
829
|
+
})
|
|
830
|
+
)
|
|
831
|
+
)
|
|
832
|
+
const interpreted = toFilter(query, baseSchema)
|
|
833
|
+
const all = interpreted.computed?.["all"]
|
|
834
|
+
const distinct = interpreted.computed?.["distinct"]
|
|
835
|
+
expect(all?._tag).toBe("relation-collect")
|
|
836
|
+
expect((all as { distinct: boolean } | undefined)?.distinct).toBe(false)
|
|
837
|
+
expect(distinct?._tag).toBe("relation-collect")
|
|
838
|
+
expect((distinct as { distinct: boolean } | undefined)?.distinct).toBe(true)
|
|
839
|
+
})
|
|
840
|
+
|
|
841
|
+
it("projectComputed.sumExpr emits relation-sum-expr IR", () => {
|
|
842
|
+
const baseSchema = S.Struct({
|
|
843
|
+
id: S.String,
|
|
844
|
+
items: S.Array(S.Struct({
|
|
845
|
+
weight: S.Number,
|
|
846
|
+
tradeUnit: S.Struct({ amount: S.Number, unit: S.String })
|
|
847
|
+
}))
|
|
848
|
+
})
|
|
849
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
850
|
+
projectComputed(
|
|
851
|
+
S.Struct({ total: S.Number }),
|
|
852
|
+
computed({
|
|
853
|
+
total: relation<S.Codec.Encoded<typeof baseSchema>>("items").sumExpr(
|
|
854
|
+
expr.mul(expr.field("weight"), expr.field("tradeUnit.amount"))
|
|
855
|
+
)
|
|
856
|
+
})
|
|
857
|
+
)
|
|
858
|
+
)
|
|
859
|
+
const interpreted = toFilter(query, baseSchema)
|
|
860
|
+
const ir = interpreted.computed?.["total"]
|
|
861
|
+
expect(ir?._tag).toBe("relation-sum-expr")
|
|
862
|
+
expect((ir as { expression: unknown } | undefined)?.expression).toEqual({
|
|
863
|
+
_tag: "mul",
|
|
864
|
+
left: { _tag: "field", field: "weight" },
|
|
865
|
+
right: { _tag: "field", field: "tradeUnit.amount" }
|
|
866
|
+
})
|
|
867
|
+
})
|
|
868
|
+
|
|
869
|
+
it("projectComputed.sumExprBy emits relation-sum-expr-by IR", () => {
|
|
870
|
+
const baseSchema = S.Struct({
|
|
871
|
+
id: S.String,
|
|
872
|
+
items: S.Array(S.Struct({
|
|
873
|
+
weight: S.Number,
|
|
874
|
+
tradeUnit: S.Struct({ amount: S.Number, unit: S.String })
|
|
875
|
+
}))
|
|
876
|
+
})
|
|
877
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
878
|
+
projectComputed(
|
|
879
|
+
S.Struct({
|
|
880
|
+
totals: S.Array(S.Struct({ unit: S.String, total: S.Number }))
|
|
881
|
+
}),
|
|
882
|
+
computed({
|
|
883
|
+
totals: relation<S.Codec.Encoded<typeof baseSchema>>("items").sumExprBy(
|
|
884
|
+
expr.mul(expr.field("weight"), expr.field("tradeUnit.amount")),
|
|
885
|
+
{ unit: "tradeUnit.unit" }
|
|
886
|
+
)
|
|
887
|
+
})
|
|
888
|
+
)
|
|
889
|
+
)
|
|
890
|
+
const interpreted = toFilter(query, baseSchema)
|
|
891
|
+
const ir = interpreted.computed?.["totals"]
|
|
892
|
+
expect(ir?._tag).toBe("relation-sum-expr-by")
|
|
893
|
+
expect((ir as { unit: string } | undefined)?.unit).toBe("tradeUnit.unit")
|
|
894
|
+
})
|
|
895
|
+
|
|
549
896
|
it(
|
|
550
897
|
"doesn't mess when refining fields",
|
|
551
898
|
() =>
|
|
@@ -553,7 +900,7 @@ it(
|
|
|
553
900
|
.gen(function*() {
|
|
554
901
|
const schema = S.Struct({
|
|
555
902
|
id: S.String,
|
|
556
|
-
literals: S.
|
|
903
|
+
literals: S.Literals(["a", "b", "c"])
|
|
557
904
|
})
|
|
558
905
|
|
|
559
906
|
type Schema = typeof schema.Type
|
|
@@ -573,7 +920,7 @@ it(
|
|
|
573
920
|
|
|
574
921
|
expect(result).toEqual([])
|
|
575
922
|
})
|
|
576
|
-
.pipe(Effect.provide(
|
|
923
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
577
924
|
)
|
|
578
925
|
|
|
579
926
|
it(
|
|
@@ -583,7 +930,7 @@ it(
|
|
|
583
930
|
.gen(function*() {
|
|
584
931
|
const schema = S.Struct({
|
|
585
932
|
id: S.String,
|
|
586
|
-
literals: S.Union([S.
|
|
933
|
+
literals: S.Union([S.Literals(["a", "b", "c"]), S.Null])
|
|
587
934
|
})
|
|
588
935
|
|
|
589
936
|
type Schema = typeof schema.Type
|
|
@@ -617,7 +964,7 @@ it(
|
|
|
617
964
|
|
|
618
965
|
expect(result).toEqual([])
|
|
619
966
|
})
|
|
620
|
-
.pipe(Effect.provide(
|
|
967
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
621
968
|
)
|
|
622
969
|
|
|
623
970
|
it(
|
|
@@ -661,7 +1008,7 @@ it(
|
|
|
661
1008
|
|
|
662
1009
|
expect(result).toEqual([])
|
|
663
1010
|
})
|
|
664
|
-
.pipe(Effect.provide(
|
|
1011
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
665
1012
|
)
|
|
666
1013
|
|
|
667
1014
|
it("remove null from one constituent of a tagged union", () =>
|
|
@@ -674,7 +1021,7 @@ it("remove null from one constituent of a tagged union", () =>
|
|
|
674
1021
|
|
|
675
1022
|
class BB extends S.Class<BB>("BB")({
|
|
676
1023
|
id: S.Literal("BB"),
|
|
677
|
-
b: S.NullOr(S.
|
|
1024
|
+
b: S.NullOr(S.Finite)
|
|
678
1025
|
}) {}
|
|
679
1026
|
|
|
680
1027
|
type Union = AA | BB
|
|
@@ -710,7 +1057,7 @@ it("remove null from one constituent of a tagged union", () =>
|
|
|
710
1057
|
})[]
|
|
711
1058
|
>()
|
|
712
1059
|
})
|
|
713
|
-
.pipe(Effect.provide(
|
|
1060
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
714
1061
|
|
|
715
1062
|
it("refine 3", () =>
|
|
716
1063
|
Effect
|
|
@@ -748,7 +1095,7 @@ it("refine 3", () =>
|
|
|
748
1095
|
const resQuer1 = yield* repo.query(where("id", "AA"))
|
|
749
1096
|
expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
|
|
750
1097
|
})
|
|
751
|
-
.pipe(Effect.provide(
|
|
1098
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
752
1099
|
|
|
753
1100
|
it("my test", () =>
|
|
754
1101
|
Effect
|
|
@@ -766,7 +1113,7 @@ it("my test", () =>
|
|
|
766
1113
|
)
|
|
767
1114
|
expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
|
|
768
1115
|
})
|
|
769
|
-
.pipe(Effect.provide(
|
|
1116
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
770
1117
|
|
|
771
1118
|
it("refine inner without imposing a projection", () =>
|
|
772
1119
|
Effect
|
|
@@ -810,7 +1157,7 @@ it("refine inner without imposing a projection", () =>
|
|
|
810
1157
|
where("union._tag", "AA"),
|
|
811
1158
|
// But if I wanna the whole Data as output ignoring the inner refinement
|
|
812
1159
|
// I wanna be able to do so
|
|
813
|
-
project(
|
|
1160
|
+
project(Data.mapFields(Struct.pick(["union"])))
|
|
814
1161
|
)
|
|
815
1162
|
|
|
816
1163
|
expectTypeOf(query2).toEqualTypeOf<
|
|
@@ -841,7 +1188,7 @@ it("refine inner without imposing a projection", () =>
|
|
|
841
1188
|
}[]
|
|
842
1189
|
>()
|
|
843
1190
|
})
|
|
844
|
-
.pipe(Effect.provide(
|
|
1191
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
845
1192
|
|
|
846
1193
|
it("does not allow string queries on arrays", () =>
|
|
847
1194
|
Effect
|
|
@@ -876,7 +1223,7 @@ it("does not allow string queries on arrays", () =>
|
|
|
876
1223
|
expectTypeOf(good3).toEqualTypeOf<QueryWhere<Some, Some>>()
|
|
877
1224
|
expectTypeOf(good4).toEqualTypeOf<QueryWhere<Some, Some>>()
|
|
878
1225
|
})
|
|
879
|
-
.pipe(Effect.provide(
|
|
1226
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
880
1227
|
|
|
881
1228
|
it("test array.length", () =>
|
|
882
1229
|
Effect
|
|
@@ -917,7 +1264,7 @@ it("test array.length", () =>
|
|
|
917
1264
|
QueryWhere<Something, Something>
|
|
918
1265
|
>()
|
|
919
1266
|
})
|
|
920
|
-
.pipe(Effect.provide(
|
|
1267
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
921
1268
|
|
|
922
1269
|
it("distribution over union", () =>
|
|
923
1270
|
Effect
|
|
@@ -941,7 +1288,7 @@ it("distribution over union", () =>
|
|
|
941
1288
|
})[]
|
|
942
1289
|
>()
|
|
943
1290
|
})
|
|
944
|
-
.pipe(Effect.provide(
|
|
1291
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
945
1292
|
|
|
946
1293
|
it("refine nested union", () =>
|
|
947
1294
|
Effect
|
|
@@ -982,7 +1329,158 @@ it("refine nested union", () =>
|
|
|
982
1329
|
}[]
|
|
983
1330
|
>()
|
|
984
1331
|
})
|
|
985
|
-
.pipe(Effect.provide(
|
|
1332
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
1333
|
+
|
|
1334
|
+
it("find with transformed id", () =>
|
|
1335
|
+
Effect
|
|
1336
|
+
.gen(function*() {
|
|
1337
|
+
const ConfiguratorId = S.NonEmptyString255
|
|
1338
|
+
|
|
1339
|
+
class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
|
|
1340
|
+
configuratorId: ConfiguratorId,
|
|
1341
|
+
label: S.NonEmptyString50
|
|
1342
|
+
}) {}
|
|
1343
|
+
|
|
1344
|
+
const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
|
|
1345
|
+
S.decodeTo(
|
|
1346
|
+
S.toType(PreconfigurationId),
|
|
1347
|
+
SchemaTransformation.transformOrFail({
|
|
1348
|
+
decode: Effect.fnUntraced(function*(value) {
|
|
1349
|
+
const values = value.split("_")
|
|
1350
|
+
const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
|
|
1351
|
+
const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(
|
|
1352
|
+
values.join("_")
|
|
1353
|
+
)
|
|
1354
|
+
return new PreconfigurationId({ configuratorId, label })
|
|
1355
|
+
}),
|
|
1356
|
+
encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
|
|
1357
|
+
})
|
|
1358
|
+
),
|
|
1359
|
+
S.revealCodec
|
|
1360
|
+
)
|
|
1361
|
+
|
|
1362
|
+
const Preconfiguration = S.Struct({
|
|
1363
|
+
id: PreconfigurationIdFromString,
|
|
1364
|
+
name: S.String
|
|
1365
|
+
})
|
|
1366
|
+
|
|
1367
|
+
const repo = yield* makeRepo("Preconfiguration", Preconfiguration, { idKey: "id" as const })
|
|
1368
|
+
|
|
1369
|
+
const id = new PreconfigurationId({
|
|
1370
|
+
configuratorId: S.NonEmptyString255("myConfigurator"),
|
|
1371
|
+
label: S.NonEmptyString50("myLabel")
|
|
1372
|
+
})
|
|
1373
|
+
const item = { id, name: "test preconfig" }
|
|
1374
|
+
|
|
1375
|
+
yield* repo.saveAndPublish([item])
|
|
1376
|
+
|
|
1377
|
+
const found = yield* repo.find(id)
|
|
1378
|
+
expect(Option.isSome(found)).toBe(true)
|
|
1379
|
+
expect(Option.getOrThrow(found).name).toBe("test preconfig")
|
|
1380
|
+
expect(Option.getOrThrow(found).id).toEqual(id)
|
|
1381
|
+
|
|
1382
|
+
const notFound = yield* repo.find(
|
|
1383
|
+
new PreconfigurationId({
|
|
1384
|
+
configuratorId: S.NonEmptyString255("other"),
|
|
1385
|
+
label: S.NonEmptyString50("nope")
|
|
1386
|
+
})
|
|
1387
|
+
)
|
|
1388
|
+
expect(Option.isNone(notFound)).toBe(true)
|
|
1389
|
+
})
|
|
1390
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
1391
|
+
|
|
1392
|
+
it("find with transformed id in tagged union", () =>
|
|
1393
|
+
Effect
|
|
1394
|
+
.gen(function*() {
|
|
1395
|
+
const ConfiguratorId = S.NonEmptyString255
|
|
1396
|
+
|
|
1397
|
+
class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
|
|
1398
|
+
configuratorId: ConfiguratorId,
|
|
1399
|
+
label: S.NonEmptyString50
|
|
1400
|
+
}) {}
|
|
1401
|
+
|
|
1402
|
+
const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
|
|
1403
|
+
S.decodeTo(
|
|
1404
|
+
S.toType(PreconfigurationId),
|
|
1405
|
+
SchemaTransformation.transformOrFail({
|
|
1406
|
+
decode: Effect.fnUntraced(function*(value) {
|
|
1407
|
+
const values = value.split("_")
|
|
1408
|
+
const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
|
|
1409
|
+
const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(
|
|
1410
|
+
values.join("_")
|
|
1411
|
+
)
|
|
1412
|
+
return new PreconfigurationId({ configuratorId, label })
|
|
1413
|
+
}),
|
|
1414
|
+
encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
|
|
1415
|
+
})
|
|
1416
|
+
),
|
|
1417
|
+
S.revealCodec
|
|
1418
|
+
)
|
|
1419
|
+
|
|
1420
|
+
class Draft extends S.TaggedClass<Draft>()("Draft", {
|
|
1421
|
+
id: PreconfigurationIdFromString,
|
|
1422
|
+
name: S.String
|
|
1423
|
+
}) {}
|
|
1424
|
+
|
|
1425
|
+
class Published extends S.TaggedClass<Published>()("Published", {
|
|
1426
|
+
id: PreconfigurationIdFromString,
|
|
1427
|
+
name: S.String,
|
|
1428
|
+
publishedAt: S.String
|
|
1429
|
+
}) {}
|
|
1430
|
+
|
|
1431
|
+
class Archived extends S.TaggedClass<Archived>()("Archived", {
|
|
1432
|
+
id: PreconfigurationIdFromString,
|
|
1433
|
+
name: S.String,
|
|
1434
|
+
archivedAt: S.String
|
|
1435
|
+
}) {}
|
|
1436
|
+
|
|
1437
|
+
const Preconfiguration = S.Union([Draft, Published, Archived])
|
|
1438
|
+
|
|
1439
|
+
const repo = yield* makeRepo("Preconfiguration", Preconfiguration, {})
|
|
1440
|
+
|
|
1441
|
+
const id1 = new PreconfigurationId({
|
|
1442
|
+
configuratorId: S.NonEmptyString255("conf1"),
|
|
1443
|
+
label: S.NonEmptyString50("draft1")
|
|
1444
|
+
})
|
|
1445
|
+
const id2 = new PreconfigurationId({
|
|
1446
|
+
configuratorId: S.NonEmptyString255("conf2"),
|
|
1447
|
+
label: S.NonEmptyString50("pub1")
|
|
1448
|
+
})
|
|
1449
|
+
const id3 = new PreconfigurationId({
|
|
1450
|
+
configuratorId: S.NonEmptyString255("conf3"),
|
|
1451
|
+
label: S.NonEmptyString50("arch1")
|
|
1452
|
+
})
|
|
1453
|
+
|
|
1454
|
+
const draft = new Draft({ id: id1, name: "my draft" })
|
|
1455
|
+
const published = new Published({ id: id2, name: "my published", publishedAt: "2024-01-01" })
|
|
1456
|
+
const archived = new Archived({ id: id3, name: "my archived", archivedAt: "2024-06-01" })
|
|
1457
|
+
|
|
1458
|
+
yield* repo.saveAndPublish([draft, published, archived])
|
|
1459
|
+
|
|
1460
|
+
// find each by their PreconfigurationId instance
|
|
1461
|
+
const foundDraft = yield* repo.find(id1)
|
|
1462
|
+
expect(Option.isSome(foundDraft)).toBe(true)
|
|
1463
|
+
expect(Option.getOrThrow(foundDraft)._tag).toBe("Draft")
|
|
1464
|
+
expect(Option.getOrThrow(foundDraft).name).toBe("my draft")
|
|
1465
|
+
|
|
1466
|
+
const foundPublished = yield* repo.find(id2)
|
|
1467
|
+
expect(Option.isSome(foundPublished)).toBe(true)
|
|
1468
|
+
expect(Option.getOrThrow(foundPublished)._tag).toBe("Published")
|
|
1469
|
+
|
|
1470
|
+
const foundArchived = yield* repo.find(id3)
|
|
1471
|
+
expect(Option.isSome(foundArchived)).toBe(true)
|
|
1472
|
+
expect(Option.getOrThrow(foundArchived)._tag).toBe("Archived")
|
|
1473
|
+
|
|
1474
|
+
// not found
|
|
1475
|
+
const notFound = yield* repo.find(
|
|
1476
|
+
new PreconfigurationId({
|
|
1477
|
+
configuratorId: S.NonEmptyString255("nope"),
|
|
1478
|
+
label: S.NonEmptyString50("nope")
|
|
1479
|
+
})
|
|
1480
|
+
)
|
|
1481
|
+
expect(Option.isNone(notFound)).toBe(true)
|
|
1482
|
+
})
|
|
1483
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
986
1484
|
|
|
987
1485
|
it("refine union with nested union", () =>
|
|
988
1486
|
Effect
|
|
@@ -1097,4 +1595,470 @@ it("refine union with nested union", () =>
|
|
|
1097
1595
|
})[]
|
|
1098
1596
|
>()
|
|
1099
1597
|
})
|
|
1100
|
-
.pipe(Effect.provide(
|
|
1598
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
1599
|
+
|
|
1600
|
+
// ---------------------------------------------------------------------------
|
|
1601
|
+
// memFilter: computed projection execution (in-memory) and code filter coverage
|
|
1602
|
+
// ---------------------------------------------------------------------------
|
|
1603
|
+
|
|
1604
|
+
const computedBaseSchema = S.Struct({
|
|
1605
|
+
id: S.String,
|
|
1606
|
+
status: S.Literals(["active", "archived"]),
|
|
1607
|
+
items: S.Array(S.Struct({
|
|
1608
|
+
id: S.String,
|
|
1609
|
+
tag: S.Literals(["a", "b", "c"]),
|
|
1610
|
+
qty: S.Finite,
|
|
1611
|
+
note: S.String
|
|
1612
|
+
}))
|
|
1613
|
+
})
|
|
1614
|
+
type ComputedBase = S.Codec.Encoded<typeof computedBaseSchema>
|
|
1615
|
+
|
|
1616
|
+
const computedRows: ComputedBase[] = [
|
|
1617
|
+
{
|
|
1618
|
+
id: "r1",
|
|
1619
|
+
status: "active",
|
|
1620
|
+
items: [
|
|
1621
|
+
{ id: "i1", tag: "a", qty: 10, note: "alpha" },
|
|
1622
|
+
{ id: "i2", tag: "a", qty: 20, note: "alpha" },
|
|
1623
|
+
{ id: "i3", tag: "b", qty: 5, note: "beta" }
|
|
1624
|
+
]
|
|
1625
|
+
},
|
|
1626
|
+
{ id: "r2", status: "active", items: [] },
|
|
1627
|
+
{
|
|
1628
|
+
id: "r3",
|
|
1629
|
+
status: "archived",
|
|
1630
|
+
items: [
|
|
1631
|
+
{ id: "i4", tag: "b", qty: 7, note: "gamma" },
|
|
1632
|
+
{ id: "i5", tag: "b", qty: 7, note: "gamma" },
|
|
1633
|
+
{ id: "i6", tag: "c", qty: 3, note: "delta" }
|
|
1634
|
+
]
|
|
1635
|
+
}
|
|
1636
|
+
]
|
|
1637
|
+
|
|
1638
|
+
it("memFilter: relation-count with filter", () => {
|
|
1639
|
+
const q = make<ComputedBase>().pipe(
|
|
1640
|
+
projectComputed(
|
|
1641
|
+
S.Struct({ id: S.String, aCount: S.NonNegativeInt }),
|
|
1642
|
+
computed({
|
|
1643
|
+
aCount: relation<ComputedBase>("items").count(where("tag", "a"))
|
|
1644
|
+
})
|
|
1645
|
+
)
|
|
1646
|
+
)
|
|
1647
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1648
|
+
{ id: "r1", aCount: 2 },
|
|
1649
|
+
{ id: "r2", aCount: 0 },
|
|
1650
|
+
{ id: "r3", aCount: 0 }
|
|
1651
|
+
])
|
|
1652
|
+
})
|
|
1653
|
+
|
|
1654
|
+
it("memFilter: relation-any / every with filter", () => {
|
|
1655
|
+
const q = make<ComputedBase>().pipe(
|
|
1656
|
+
projectComputed(
|
|
1657
|
+
S.Struct({
|
|
1658
|
+
id: S.String,
|
|
1659
|
+
hasA: S.Boolean,
|
|
1660
|
+
allB: S.Boolean
|
|
1661
|
+
}),
|
|
1662
|
+
computed({
|
|
1663
|
+
hasA: relation<ComputedBase>("items").any(where("tag", "a")),
|
|
1664
|
+
allB: relation<ComputedBase>("items").every(where("tag", "b"))
|
|
1665
|
+
})
|
|
1666
|
+
)
|
|
1667
|
+
)
|
|
1668
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1669
|
+
{ id: "r1", hasA: true, allB: false },
|
|
1670
|
+
// empty array: any → false, every → true (JS Array.every on [] is true)
|
|
1671
|
+
{ id: "r2", hasA: false, allB: true },
|
|
1672
|
+
{ id: "r3", hasA: false, allB: false }
|
|
1673
|
+
])
|
|
1674
|
+
})
|
|
1675
|
+
|
|
1676
|
+
it("memFilter: relation-distinct-count with filter", () => {
|
|
1677
|
+
const q = make<ComputedBase>().pipe(
|
|
1678
|
+
projectComputed(
|
|
1679
|
+
S.Struct({ id: S.String, distinctNotes: S.NonNegativeInt }),
|
|
1680
|
+
computed({
|
|
1681
|
+
distinctNotes: relation<ComputedBase>("items").distinctCount("note", where("tag", "neq", "c"))
|
|
1682
|
+
})
|
|
1683
|
+
)
|
|
1684
|
+
)
|
|
1685
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1686
|
+
{ id: "r1", distinctNotes: 2 }, // alpha, beta
|
|
1687
|
+
{ id: "r2", distinctNotes: 0 },
|
|
1688
|
+
{ id: "r3", distinctNotes: 1 } // gamma (delta filtered out)
|
|
1689
|
+
])
|
|
1690
|
+
})
|
|
1691
|
+
|
|
1692
|
+
it("memFilter: relation-sum with filter", () => {
|
|
1693
|
+
const q = make<ComputedBase>().pipe(
|
|
1694
|
+
projectComputed(
|
|
1695
|
+
S.Struct({ id: S.String, totalQty: S.Finite }),
|
|
1696
|
+
computed({
|
|
1697
|
+
totalQty: relation<ComputedBase>("items").sum("qty", where("tag", "neq", "c"))
|
|
1698
|
+
})
|
|
1699
|
+
)
|
|
1700
|
+
)
|
|
1701
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1702
|
+
{ id: "r1", totalQty: 35 },
|
|
1703
|
+
{ id: "r2", totalQty: 0 },
|
|
1704
|
+
{ id: "r3", totalQty: 14 }
|
|
1705
|
+
])
|
|
1706
|
+
})
|
|
1707
|
+
|
|
1708
|
+
it("memFilter: relation-collect / collectDistinct with filter", () => {
|
|
1709
|
+
const q = make<ComputedBase>().pipe(
|
|
1710
|
+
projectComputed(
|
|
1711
|
+
S.Struct({
|
|
1712
|
+
id: S.String,
|
|
1713
|
+
notes: S.Array(S.String),
|
|
1714
|
+
distinctNotes: S.Array(S.String)
|
|
1715
|
+
}),
|
|
1716
|
+
computed({
|
|
1717
|
+
notes: relation<ComputedBase>("items").collect("note", where("tag", "neq", "c")),
|
|
1718
|
+
distinctNotes: relation<ComputedBase>("items").collectDistinct("note", where("tag", "neq", "c"))
|
|
1719
|
+
})
|
|
1720
|
+
)
|
|
1721
|
+
)
|
|
1722
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1723
|
+
{ id: "r1", notes: ["alpha", "alpha", "beta"], distinctNotes: ["alpha", "beta"] },
|
|
1724
|
+
{ id: "r2", notes: [], distinctNotes: [] },
|
|
1725
|
+
{ id: "r3", notes: ["gamma", "gamma"], distinctNotes: ["gamma"] }
|
|
1726
|
+
])
|
|
1727
|
+
})
|
|
1728
|
+
|
|
1729
|
+
it("memFilter: computed projection with multi-statement relation filter", () => {
|
|
1730
|
+
const q = make<ComputedBase>().pipe(
|
|
1731
|
+
projectComputed(
|
|
1732
|
+
S.Struct({ id: S.String, hits: S.NonNegativeInt }),
|
|
1733
|
+
computed({
|
|
1734
|
+
hits: relation<ComputedBase>("items").count(
|
|
1735
|
+
flow(
|
|
1736
|
+
where("tag", "a"),
|
|
1737
|
+
and("qty", "gt", 10)
|
|
1738
|
+
)
|
|
1739
|
+
)
|
|
1740
|
+
})
|
|
1741
|
+
)
|
|
1742
|
+
)
|
|
1743
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1744
|
+
{ id: "r1", hits: 1 }, // only i2 (a, qty 20)
|
|
1745
|
+
{ id: "r2", hits: 0 },
|
|
1746
|
+
{ id: "r3", hits: 0 }
|
|
1747
|
+
])
|
|
1748
|
+
})
|
|
1749
|
+
|
|
1750
|
+
it("memFilter: relation-sum-expr / sum-expr-by / sum-expr-normalized", () => {
|
|
1751
|
+
const schema = S.Struct({
|
|
1752
|
+
id: S.String,
|
|
1753
|
+
items: S.Array(S.Struct({
|
|
1754
|
+
weight: S.Finite,
|
|
1755
|
+
tradeUnit: S.Struct({ amount: S.Finite, unit: S.String })
|
|
1756
|
+
}))
|
|
1757
|
+
})
|
|
1758
|
+
type Row = S.Codec.Encoded<typeof schema>
|
|
1759
|
+
const rows: Row[] = [
|
|
1760
|
+
{
|
|
1761
|
+
id: "r1",
|
|
1762
|
+
items: [
|
|
1763
|
+
{ weight: 2, tradeUnit: { amount: 5, unit: "kg" } },
|
|
1764
|
+
{ weight: 4, tradeUnit: { amount: 1000, unit: "g" } },
|
|
1765
|
+
{ weight: 3, tradeUnit: { amount: 1, unit: "kg" } }
|
|
1766
|
+
]
|
|
1767
|
+
},
|
|
1768
|
+
{ id: "r2", items: [] }
|
|
1769
|
+
]
|
|
1770
|
+
const weighted = expr.mul(expr.field("weight"), expr.field("tradeUnit.amount"))
|
|
1771
|
+
const q = make<Row>().pipe(
|
|
1772
|
+
projectComputed(
|
|
1773
|
+
S.Struct({
|
|
1774
|
+
id: S.String,
|
|
1775
|
+
totalRaw: S.Finite,
|
|
1776
|
+
totalsByUnit: S.Array(S.Struct({ unit: S.String, total: S.Finite })),
|
|
1777
|
+
totalKg: S.Finite
|
|
1778
|
+
}),
|
|
1779
|
+
computed({
|
|
1780
|
+
totalRaw: relation<Row>("items").sumExpr(weighted, where("weight", "gte", 0)),
|
|
1781
|
+
totalsByUnit: relation<Row>("items").sumExprBy(weighted, { unit: "tradeUnit.unit" }, where("weight", "gte", 0)),
|
|
1782
|
+
totalKg: relation<Row>("items").sumExprNormalized(weighted, {
|
|
1783
|
+
unit: "tradeUnit.unit",
|
|
1784
|
+
toBase: "kg",
|
|
1785
|
+
factors: { g: 0.001 }
|
|
1786
|
+
}, where("weight", "gte", 0))
|
|
1787
|
+
})
|
|
1788
|
+
)
|
|
1789
|
+
)
|
|
1790
|
+
expect(memFilter(toFilter(q, schema))(rows)).toEqual([
|
|
1791
|
+
{
|
|
1792
|
+
id: "r1",
|
|
1793
|
+
totalRaw: 4013,
|
|
1794
|
+
totalsByUnit: [{ unit: "kg", total: 13 }, { unit: "g", total: 4000 }],
|
|
1795
|
+
totalKg: 17
|
|
1796
|
+
},
|
|
1797
|
+
{ id: "r2", totalRaw: 0, totalsByUnit: [], totalKg: 0 }
|
|
1798
|
+
])
|
|
1799
|
+
})
|
|
1800
|
+
|
|
1801
|
+
it("memFilter: computed projection combined with root where filter", () => {
|
|
1802
|
+
const q = make<ComputedBase>().pipe(
|
|
1803
|
+
where("id", "neq", "r3"),
|
|
1804
|
+
projectComputed(
|
|
1805
|
+
S.Struct({ id: S.String, totalQty: S.Finite }),
|
|
1806
|
+
computed({
|
|
1807
|
+
totalQty: relation<ComputedBase>("items").sum("qty", where("tag", "neq", "c"))
|
|
1808
|
+
})
|
|
1809
|
+
)
|
|
1810
|
+
)
|
|
1811
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1812
|
+
{ id: "r1", totalQty: 35 },
|
|
1813
|
+
{ id: "r2", totalQty: 0 }
|
|
1814
|
+
])
|
|
1815
|
+
})
|
|
1816
|
+
|
|
1817
|
+
it("memFilter: computed projection with order/limit/skip applied to base rows", () => {
|
|
1818
|
+
const q = make<ComputedBase>().pipe(
|
|
1819
|
+
order("id", "DESC"),
|
|
1820
|
+
page({ skip: 1, take: 1 }),
|
|
1821
|
+
projectComputed(
|
|
1822
|
+
S.Struct({ id: S.String, total: S.NonNegativeInt }),
|
|
1823
|
+
computed({
|
|
1824
|
+
total: relation<ComputedBase>("items").count(where("qty", "gte", 0))
|
|
1825
|
+
})
|
|
1826
|
+
)
|
|
1827
|
+
)
|
|
1828
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1829
|
+
{ id: "r2", total: 0 }
|
|
1830
|
+
])
|
|
1831
|
+
})
|
|
1832
|
+
|
|
1833
|
+
it("memFilter: computed projection - relation missing on row returns empty value", () => {
|
|
1834
|
+
const partial: ComputedBase[] = [
|
|
1835
|
+
{ id: "x1" } as ComputedBase,
|
|
1836
|
+
{ id: "x2", status: "active", items: undefined as unknown as ComputedBase["items"] }
|
|
1837
|
+
]
|
|
1838
|
+
const q = make<ComputedBase>().pipe(
|
|
1839
|
+
projectComputed(
|
|
1840
|
+
S.Struct({
|
|
1841
|
+
id: S.String,
|
|
1842
|
+
c: S.NonNegativeInt,
|
|
1843
|
+
s: S.Finite,
|
|
1844
|
+
any_: S.Boolean,
|
|
1845
|
+
every_: S.Boolean,
|
|
1846
|
+
coll: S.Array(S.String)
|
|
1847
|
+
}),
|
|
1848
|
+
computed({
|
|
1849
|
+
c: relation<ComputedBase>("items").count(where("tag", "a")),
|
|
1850
|
+
s: relation<ComputedBase>("items").sum("qty", where("tag", "a")),
|
|
1851
|
+
any_: relation<ComputedBase>("items").any(where("tag", "a")),
|
|
1852
|
+
every_: relation<ComputedBase>("items").every(where("tag", "a")),
|
|
1853
|
+
coll: relation<ComputedBase>("items").collect("note", where("tag", "a"))
|
|
1854
|
+
})
|
|
1855
|
+
)
|
|
1856
|
+
)
|
|
1857
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(partial)).toEqual([
|
|
1858
|
+
{ id: "x1", c: 0, s: 0, any_: false, every_: true, coll: [] },
|
|
1859
|
+
{ id: "x2", c: 0, s: 0, any_: false, every_: true, coll: [] }
|
|
1860
|
+
])
|
|
1861
|
+
})
|
|
1862
|
+
|
|
1863
|
+
it("memFilter: rejects extra computed keys not in projection schema", () => {
|
|
1864
|
+
const q = make<ComputedBase>().pipe(
|
|
1865
|
+
projectComputed(
|
|
1866
|
+
S.Struct({ id: S.String }),
|
|
1867
|
+
// @ts-expect-error extra computed keys are rejected statically; keep runtime guard covered
|
|
1868
|
+
computed({
|
|
1869
|
+
bogus: relation<ComputedBase>("items").count(where("tag", "a"))
|
|
1870
|
+
})
|
|
1871
|
+
)
|
|
1872
|
+
)
|
|
1873
|
+
expect(() => toFilter(q, computedBaseSchema)).toThrowError(
|
|
1874
|
+
"Computed projection keys must exist in projection schema"
|
|
1875
|
+
)
|
|
1876
|
+
})
|
|
1877
|
+
|
|
1878
|
+
// ---------------------------------------------------------------------------
|
|
1879
|
+
// memFilter: code filter (where/and/or/scopes) execution coverage
|
|
1880
|
+
// ---------------------------------------------------------------------------
|
|
1881
|
+
|
|
1882
|
+
type CFRow = {
|
|
1883
|
+
readonly id: string
|
|
1884
|
+
readonly tag: "x" | "y" | "z"
|
|
1885
|
+
readonly qty: number
|
|
1886
|
+
readonly desc: string
|
|
1887
|
+
readonly tags: ReadonlyArray<string>
|
|
1888
|
+
readonly nested: { readonly kind: "k1" | "k2"; readonly v: number }
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
const cfRows: CFRow[] = [
|
|
1892
|
+
{ id: "1", tag: "x", qty: 10, desc: "Hello World", tags: ["red", "green"], nested: { kind: "k1", v: 1 } },
|
|
1893
|
+
{ id: "2", tag: "y", qty: 20, desc: "Goodbye", tags: ["blue"], nested: { kind: "k2", v: 5 } },
|
|
1894
|
+
{ id: "3", tag: "z", qty: 0, desc: "Hello again", tags: ["red", "blue", "green"], nested: { kind: "k1", v: 9 } },
|
|
1895
|
+
{ id: "4", tag: "y", qty: 30, desc: "World cup", tags: [], nested: { kind: "k2", v: 0 } }
|
|
1896
|
+
]
|
|
1897
|
+
|
|
1898
|
+
const runCF = (q: any) => (memFilter(toFilter(q))(cfRows) as unknown as readonly CFRow[]).map((_) => _.id)
|
|
1899
|
+
|
|
1900
|
+
it("codeFilter: where + and chain", () => {
|
|
1901
|
+
const q = make<CFRow>().pipe(
|
|
1902
|
+
where("tag", "y"),
|
|
1903
|
+
and("qty", "gt", 25)
|
|
1904
|
+
)
|
|
1905
|
+
expect(runCF(q)).toEqual(["4"])
|
|
1906
|
+
})
|
|
1907
|
+
|
|
1908
|
+
it("codeFilter: where + or chain", () => {
|
|
1909
|
+
const q = make<CFRow>().pipe(
|
|
1910
|
+
where("tag", "x"),
|
|
1911
|
+
or("tag", "z")
|
|
1912
|
+
)
|
|
1913
|
+
expect(runCF(q).sort()).toEqual(["1", "3"])
|
|
1914
|
+
})
|
|
1915
|
+
|
|
1916
|
+
it("codeFilter: nested scope precedence (a AND (b OR c))", () => {
|
|
1917
|
+
const q = make<CFRow>().pipe(
|
|
1918
|
+
where("tag", "y"),
|
|
1919
|
+
and(
|
|
1920
|
+
where("qty", "gt", 25),
|
|
1921
|
+
or("desc", "contains", "good")
|
|
1922
|
+
)
|
|
1923
|
+
)
|
|
1924
|
+
// tag=y AND (qty>25 OR desc contains "good") → row 2 (Goodbye) and row 4 (qty 30)
|
|
1925
|
+
expect(runCF(q).sort()).toEqual(["2", "4"])
|
|
1926
|
+
})
|
|
1927
|
+
|
|
1928
|
+
it("codeFilter: contains/startsWith/endsWith are case-insensitive", () => {
|
|
1929
|
+
expect(runCF(make<CFRow>().pipe(where("desc", "contains", "WORLD"))).sort()).toEqual(["1", "4"])
|
|
1930
|
+
expect(runCF(make<CFRow>().pipe(where("desc", "startsWith", "hello"))).sort()).toEqual(["1", "3"])
|
|
1931
|
+
expect(runCF(make<CFRow>().pipe(where("desc", "endsWith", "AGAIN")))).toEqual(["3"])
|
|
1932
|
+
})
|
|
1933
|
+
|
|
1934
|
+
it("codeFilter: array includes / includes-any / includes-all", () => {
|
|
1935
|
+
expect(runCF(make<CFRow>().pipe(where("tags", "includes", "red"))).sort()).toEqual(["1", "3"])
|
|
1936
|
+
expect(runCF(make<CFRow>().pipe(where("tags", "includes-any", ["blue", "green"]))).sort()).toEqual([
|
|
1937
|
+
"1",
|
|
1938
|
+
"2",
|
|
1939
|
+
"3"
|
|
1940
|
+
])
|
|
1941
|
+
expect(runCF(make<CFRow>().pipe(where("tags", "includes-all", ["red", "blue"])))).toEqual(["3"])
|
|
1942
|
+
})
|
|
1943
|
+
|
|
1944
|
+
it("codeFilter: in / notIn", () => {
|
|
1945
|
+
expect(runCF(make<CFRow>().pipe(where("tag", "in", ["x", "z"]))).sort()).toEqual(["1", "3"])
|
|
1946
|
+
expect(runCF(make<CFRow>().pipe(where("tag", "notIn", ["x", "z"]))).sort()).toEqual(["2", "4"])
|
|
1947
|
+
})
|
|
1948
|
+
|
|
1949
|
+
it("codeFilter: gt / gte / lt / lte / neq", () => {
|
|
1950
|
+
expect(runCF(make<CFRow>().pipe(where("qty", "gt", 10))).sort()).toEqual(["2", "4"])
|
|
1951
|
+
expect(runCF(make<CFRow>().pipe(where("qty", "gte", 10))).sort()).toEqual(["1", "2", "4"])
|
|
1952
|
+
expect(runCF(make<CFRow>().pipe(where("qty", "lt", 10)))).toEqual(["3"])
|
|
1953
|
+
expect(runCF(make<CFRow>().pipe(where("qty", "lte", 10))).sort()).toEqual(["1", "3"])
|
|
1954
|
+
expect(runCF(make<CFRow>().pipe(where("qty", "neq", 0))).sort()).toEqual(["1", "2", "4"])
|
|
1955
|
+
})
|
|
1956
|
+
|
|
1957
|
+
it("codeFilter: nested path access through dot notation", () => {
|
|
1958
|
+
expect(runCF(make<CFRow>().pipe(where("nested.kind", "k1"))).sort()).toEqual(["1", "3"])
|
|
1959
|
+
expect(
|
|
1960
|
+
runCF(
|
|
1961
|
+
make<CFRow>().pipe(
|
|
1962
|
+
where("nested.kind", "k2"),
|
|
1963
|
+
and("nested.v", "gt", 0)
|
|
1964
|
+
)
|
|
1965
|
+
)
|
|
1966
|
+
)
|
|
1967
|
+
.toEqual(["2"])
|
|
1968
|
+
})
|
|
1969
|
+
|
|
1970
|
+
it("codeFilter: array length predicates", () => {
|
|
1971
|
+
expect(runCF(make<CFRow>().pipe(where("tags.length", 0)))).toEqual(["4"])
|
|
1972
|
+
expect(runCF(make<CFRow>().pipe(where("tags.length", "gte", 2))).sort()).toEqual(["1", "3"])
|
|
1973
|
+
})
|
|
1974
|
+
|
|
1975
|
+
it("codeFilter: order + skip + limit applied after filter", () => {
|
|
1976
|
+
const q = make<CFRow>().pipe(
|
|
1977
|
+
where("tag", "neq", "z"),
|
|
1978
|
+
order("qty", "DESC"),
|
|
1979
|
+
page({ skip: 1, take: 2 })
|
|
1980
|
+
)
|
|
1981
|
+
expect(runCF(q)).toEqual(["2", "1"])
|
|
1982
|
+
})
|
|
1983
|
+
|
|
1984
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1985
|
+
// memFilter: aggregate (GROUP BY) support
|
|
1986
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1987
|
+
|
|
1988
|
+
it("memFilter: agg-count-when groups rows and counts conditionally", () => {
|
|
1989
|
+
type Row = { city: string; status: string }
|
|
1990
|
+
const rows: Row[] = [
|
|
1991
|
+
{ city: "NYC", status: "active" },
|
|
1992
|
+
{ city: "NYC", status: "inactive" },
|
|
1993
|
+
{ city: "NYC", status: "active" },
|
|
1994
|
+
{ city: "LA", status: "active" }
|
|
1995
|
+
]
|
|
1996
|
+
const result = memFilter<Row>({
|
|
1997
|
+
t: {} as Row,
|
|
1998
|
+
select: [
|
|
1999
|
+
{ key: "city", path: "city" },
|
|
2000
|
+
{
|
|
2001
|
+
key: "activeCount",
|
|
2002
|
+
aggregate: {
|
|
2003
|
+
_tag: "agg-count-when",
|
|
2004
|
+
filter: [{ t: "where", path: "status", op: "eq", value: "active" }]
|
|
2005
|
+
}
|
|
2006
|
+
},
|
|
2007
|
+
{ key: "total", aggregate: { _tag: "agg-count" } }
|
|
2008
|
+
] as any
|
|
2009
|
+
})(rows as any) as any[]
|
|
2010
|
+
|
|
2011
|
+
expect(result.length).toBe(2)
|
|
2012
|
+
const nyc = result.find((r: any) => r.city === "NYC")!
|
|
2013
|
+
expect(nyc.activeCount).toBe(2)
|
|
2014
|
+
expect(nyc.total).toBe(3)
|
|
2015
|
+
const la = result.find((r: any) => r.city === "LA")!
|
|
2016
|
+
expect(la.activeCount).toBe(1)
|
|
2017
|
+
expect(la.total).toBe(1)
|
|
2018
|
+
})
|
|
2019
|
+
|
|
2020
|
+
it("memFilter: agg-sum / agg-min / agg-max aggregate numerics", () => {
|
|
2021
|
+
type Row = { dept: string; salary: number }
|
|
2022
|
+
const rows: Row[] = [
|
|
2023
|
+
{ dept: "eng", salary: 100 },
|
|
2024
|
+
{ dept: "eng", salary: 200 },
|
|
2025
|
+
{ dept: "hr", salary: 50 }
|
|
2026
|
+
]
|
|
2027
|
+
const result = memFilter<Row>({
|
|
2028
|
+
t: {} as Row,
|
|
2029
|
+
select: [
|
|
2030
|
+
{ key: "dept", path: "dept" },
|
|
2031
|
+
{ key: "total", aggregate: { _tag: "agg-sum", field: "salary" } },
|
|
2032
|
+
{ key: "min", aggregate: { _tag: "agg-min", field: "salary" } },
|
|
2033
|
+
{ key: "max", aggregate: { _tag: "agg-max", field: "salary" } }
|
|
2034
|
+
] as any
|
|
2035
|
+
})(rows as any) as any[]
|
|
2036
|
+
|
|
2037
|
+
expect(result.length).toBe(2)
|
|
2038
|
+
const eng = result.find((r: any) => r.dept === "eng")!
|
|
2039
|
+
expect(eng.total).toBe(300)
|
|
2040
|
+
expect(eng.min).toBe(100)
|
|
2041
|
+
expect(eng.max).toBe(200)
|
|
2042
|
+
const hr = result.find((r: any) => r.dept === "hr")!
|
|
2043
|
+
expect(hr.total).toBe(50)
|
|
2044
|
+
})
|
|
2045
|
+
|
|
2046
|
+
it("memFilter: aggregate with nested path grouping", () => {
|
|
2047
|
+
type Row = { address: { city: string }; value: number }
|
|
2048
|
+
const rows: Row[] = [
|
|
2049
|
+
{ address: { city: "NYC" }, value: 10 },
|
|
2050
|
+
{ address: { city: "NYC" }, value: 20 },
|
|
2051
|
+
{ address: { city: "LA" }, value: 5 }
|
|
2052
|
+
]
|
|
2053
|
+
const result = memFilter<Row>({
|
|
2054
|
+
t: {} as Row,
|
|
2055
|
+
select: [
|
|
2056
|
+
{ key: "city", path: "address.city" },
|
|
2057
|
+
{ key: "count", aggregate: { _tag: "agg-count" } }
|
|
2058
|
+
] as any
|
|
2059
|
+
})(rows as any) as any[]
|
|
2060
|
+
|
|
2061
|
+
expect(result.length).toBe(2)
|
|
2062
|
+
expect(result.find((r: any) => r.city === "NYC")!.count).toBe(2)
|
|
2063
|
+
expect(result.find((r: any) => r.city === "LA")!.count).toBe(1)
|
|
2064
|
+
})
|