@effect-app/infra 4.0.0-beta.26 → 4.0.0-beta.260
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 +1966 -0
- package/_check.sh +1 -1
- package/dist/CUPS.d.ts +30 -11
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +35 -14
- package/dist/ClusterCosmos.d.ts +64 -0
- package/dist/ClusterCosmos.d.ts.map +1 -0
- package/dist/ClusterCosmos.js +487 -0
- package/dist/ClusterServiceBus.d.ts +67 -0
- package/dist/ClusterServiceBus.d.ts.map +1 -0
- package/dist/ClusterServiceBus.js +82 -0
- package/dist/ContextProvider.d.ts +34 -0
- package/dist/ContextProvider.d.ts.map +1 -0
- package/dist/ContextProvider.js +40 -0
- package/dist/Emailer/Sendgrid.d.ts +111 -147
- package/dist/Emailer/Sendgrid.d.ts.map +1 -1
- package/dist/Emailer/Sendgrid.js +24 -19
- package/dist/Emailer/fake.d.ts +2 -2
- package/dist/Emailer/fake.d.ts.map +1 -1
- package/dist/Emailer/fake.js +4 -4
- package/dist/MainFiberSet.d.ts +12 -9
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +10 -6
- package/dist/QueueMaker/SQLQueue.d.ts +8 -9
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +138 -120
- package/dist/QueueMaker/errors.d.ts +5 -3
- package/dist/QueueMaker/errors.d.ts.map +1 -1
- package/dist/QueueMaker/errors.js +4 -2
- package/dist/QueueMaker/memQueue.d.ts +10 -6
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +84 -68
- package/dist/QueueMaker/sbqueue.d.ts +9 -5
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +60 -58
- package/dist/RequestFiberSet.d.ts +10 -7
- package/dist/RequestFiberSet.d.ts.map +1 -1
- package/dist/RequestFiberSet.js +13 -8
- package/dist/SQL/Model.d.ts +468 -0
- package/dist/SQL/Model.d.ts.map +1 -0
- package/dist/SQL/Model.js +469 -0
- package/dist/SQL.d.ts +2 -0
- package/dist/SQL.d.ts.map +1 -0
- package/dist/{adapters/SQL.js → SQL.js} +1 -1
- package/dist/ServiceBus.d.ts +61 -0
- package/dist/ServiceBus.d.ts.map +1 -0
- package/dist/ServiceBus.js +108 -0
- package/dist/Store/Cosmos/query.d.ts +15 -4
- package/dist/Store/Cosmos/query.d.ts.map +1 -1
- package/dist/Store/Cosmos/query.js +179 -41
- package/dist/Store/Cosmos.d.ts +3 -3
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +344 -246
- package/dist/Store/Disk.d.ts +5 -5
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +78 -38
- package/dist/Store/Memory.d.ts +7 -10
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +326 -66
- package/dist/Store/SQL/Pg.d.ts +4 -0
- package/dist/Store/SQL/Pg.d.ts.map +1 -0
- package/dist/Store/SQL/Pg.js +232 -0
- package/dist/Store/SQL/query.d.ts +49 -0
- package/dist/Store/SQL/query.d.ts.map +1 -0
- package/dist/Store/SQL/query.js +527 -0
- package/dist/Store/SQL.d.ts +21 -0
- package/dist/Store/SQL.d.ts.map +1 -0
- package/dist/Store/SQL.js +449 -0
- package/dist/Store/codeFilter.d.ts +5 -5
- package/dist/Store/codeFilter.d.ts.map +1 -1
- package/dist/Store/codeFilter.js +6 -3
- package/dist/Store/index.d.ts +7 -5
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +18 -5
- package/dist/Store/utils.d.ts +4 -3
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +5 -5
- package/dist/WorkflowEngineCosmos.d.ts +29 -0
- package/dist/WorkflowEngineCosmos.d.ts.map +1 -0
- package/dist/WorkflowEngineCosmos.js +521 -0
- package/dist/WorkflowEngineSqlite.d.ts +24 -0
- package/dist/WorkflowEngineSqlite.d.ts.map +1 -0
- package/dist/WorkflowEngineSqlite.js +550 -0
- package/dist/arbs.d.ts +2 -2
- package/dist/arbs.d.ts.map +1 -1
- package/dist/arbs.js +5 -3
- package/dist/codec.d.ts +5 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +5 -0
- package/dist/cosmos-client.d.ts +16 -0
- package/dist/cosmos-client.d.ts.map +1 -0
- package/dist/cosmos-client.js +11 -0
- package/dist/errorReporter.d.ts +7 -5
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +23 -27
- package/dist/errors.d.ts +1 -1
- package/dist/fileUtil.d.ts +2 -2
- package/dist/fileUtil.d.ts.map +1 -1
- package/dist/fileUtil.js +2 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/internal/RequestContextMiddleware.d.ts +5 -0
- package/dist/internal/RequestContextMiddleware.d.ts.map +1 -0
- package/dist/internal/RequestContextMiddleware.js +45 -0
- package/dist/internal/auth.d.ts +53 -0
- package/dist/internal/auth.d.ts.map +1 -0
- package/dist/internal/auth.js +180 -0
- package/dist/internal/events.d.ts +11 -0
- package/dist/internal/events.d.ts.map +1 -0
- package/dist/internal/events.js +49 -0
- package/dist/internal/health.d.ts +3 -0
- package/dist/internal/health.d.ts.map +1 -0
- package/dist/internal/health.js +5 -0
- package/dist/layerUtils.d.ts +32 -0
- package/dist/layerUtils.d.ts.map +1 -0
- package/dist/layerUtils.js +17 -0
- package/dist/logger/jsonLogger.d.ts +2 -2
- package/dist/logger/jsonLogger.d.ts.map +1 -1
- package/dist/logger/jsonLogger.js +5 -3
- package/dist/logger/logFmtLogger.d.ts +2 -2
- package/dist/logger/logFmtLogger.d.ts.map +1 -1
- package/dist/logger/logFmtLogger.js +3 -3
- package/dist/logger/shared.d.ts +3 -3
- package/dist/logger/shared.d.ts.map +1 -1
- package/dist/logger/shared.js +5 -5
- package/dist/logger.d.ts +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/memQueue.d.ts +15 -0
- package/dist/memQueue.d.ts.map +1 -0
- package/dist/memQueue.js +21 -0
- package/dist/middlewares.d.ts +10 -0
- package/dist/middlewares.d.ts.map +1 -0
- package/dist/{api/middlewares.js → middlewares.js} +1 -1
- package/dist/mongo-client.d.ts +11 -0
- package/dist/mongo-client.d.ts.map +1 -0
- package/dist/mongo-client.js +15 -0
- package/dist/otel.d.ts +75 -0
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +65 -0
- package/dist/rateLimit.d.ts +12 -4
- package/dist/rateLimit.d.ts.map +1 -1
- package/dist/rateLimit.js +7 -12
- package/dist/redis-client.d.ts +42 -0
- package/dist/redis-client.d.ts.map +1 -0
- package/dist/redis-client.js +98 -0
- package/dist/reportError.d.ts +4 -0
- package/dist/reportError.d.ts.map +1 -0
- package/dist/reportError.js +28 -0
- package/dist/routing/middleware/RouterMiddleware.d.ts +16 -0
- package/dist/routing/middleware/RouterMiddleware.d.ts.map +1 -0
- package/dist/{api/routing → routing}/middleware/RouterMiddleware.js +1 -1
- package/dist/routing/middleware/middleware.d.ts +48 -0
- package/dist/routing/middleware/middleware.d.ts.map +1 -0
- package/dist/routing/middleware/middleware.js +128 -0
- package/dist/routing/middleware.d.ts +3 -0
- package/dist/routing/middleware.d.ts.map +1 -0
- package/dist/{api/routing → routing}/middleware.js +1 -2
- package/dist/routing/schema/jwt.d.ts +4 -0
- package/dist/routing/schema/jwt.d.ts.map +1 -0
- package/dist/routing/schema/jwt.js +13 -0
- package/dist/routing/tsort.d.ts +8 -0
- package/dist/routing/tsort.d.ts.map +1 -0
- package/dist/routing/tsort.js +51 -0
- package/dist/routing/utils.d.ts +19 -0
- package/dist/routing/utils.d.ts.map +1 -0
- package/dist/routing/utils.js +45 -0
- package/dist/routing.d.ts +184 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +236 -0
- package/dist/test.d.ts +3 -3
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +2 -2
- package/dist/util.d.ts +3 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +14 -0
- package/dist/vitest.d.ts +1 -1
- package/docs/cluster-storage.md +26 -0
- package/docs/workflow-engine.md +262 -0
- package/examples/query.ts +47 -39
- package/package.json +31 -345
- package/run.sh +1 -0
- package/src/CUPS.ts +52 -13
- package/src/ClusterCosmos.ts +954 -0
- package/src/ClusterServiceBus.ts +242 -0
- package/src/{api/ContextProvider.ts → ContextProvider.ts} +19 -16
- package/src/Emailer/Sendgrid.ts +82 -59
- package/src/Emailer/fake.ts +3 -3
- package/src/MainFiberSet.ts +12 -10
- package/src/QueueMaker/SQLQueue.ts +153 -156
- package/src/QueueMaker/errors.ts +3 -1
- package/src/QueueMaker/memQueue.ts +113 -107
- package/src/QueueMaker/sbqueue.ts +78 -90
- package/src/RequestFiberSet.ts +13 -8
- package/src/{adapters/SQL → SQL}/Model.ts +42 -41
- package/src/ServiceBus.ts +219 -0
- package/src/Store/Cosmos/query.ts +216 -52
- package/src/Store/Cosmos.ts +493 -353
- package/src/Store/Disk.ts +109 -69
- package/src/Store/Memory.ts +365 -96
- package/src/Store/SQL/Pg.ts +363 -0
- package/src/Store/SQL/query.ts +603 -0
- package/src/Store/SQL.ts +735 -0
- package/src/Store/codeFilter.ts +8 -5
- package/src/Store/index.ts +21 -6
- package/src/Store/utils.ts +26 -24
- package/src/WorkflowEngineCosmos.ts +719 -0
- package/src/WorkflowEngineSqlite.ts +813 -0
- package/src/arbs.ts +5 -3
- package/src/{adapters/cosmos-client.ts → cosmos-client.ts} +5 -3
- package/src/errorReporter.ts +66 -76
- package/src/fileUtil.ts +1 -1
- package/src/index.ts +2 -1
- package/src/{api/internal → internal}/RequestContextMiddleware.ts +23 -6
- package/src/internal/auth.ts +272 -0
- package/src/{api/internal → internal}/events.ts +22 -13
- package/src/{api/layerUtils.ts → layerUtils.ts} +14 -10
- package/src/logger/jsonLogger.ts +4 -2
- package/src/logger/logFmtLogger.ts +2 -2
- package/src/logger/shared.ts +5 -4
- package/src/{adapters/memQueue.ts → memQueue.ts} +5 -4
- package/src/{adapters/mongo-client.ts → mongo-client.ts} +4 -2
- package/src/otel.ts +152 -0
- package/src/rateLimit.ts +34 -23
- package/src/{adapters/redis-client.ts → redis-client.ts} +7 -3
- package/src/{api/reportError.ts → reportError.ts} +3 -2
- package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +5 -4
- package/src/{api/routing → routing}/middleware/middleware.ts +62 -17
- package/src/routing/middleware.ts +4 -0
- package/src/{api/routing → routing}/schema/jwt.ts +2 -1
- package/src/{api/routing → routing}/utils.ts +2 -1
- package/src/routing.ts +768 -0
- package/src/test.ts +2 -2
- package/test/auth.test.ts +101 -0
- package/test/cluster-cosmos.test.ts +503 -0
- package/test/cluster-servicebus.test.ts +180 -0
- package/test/contextProvider.test.ts +15 -12
- package/test/controller.test.ts +28 -32
- package/test/cosmos-query.test.ts +159 -0
- package/test/dist/_check-agg-infer.test-d.d.ts +2 -0
- package/test/dist/_check-agg-infer.test-d.d.ts.map +1 -0
- package/test/dist/_check-agg-infer.test-d.js +19 -0
- package/test/dist/_check-proj-infer.test-d.d.ts +2 -0
- package/test/dist/_check-proj-infer.test-d.d.ts.map +1 -0
- package/test/dist/_check-proj-infer.test-d.js +16 -0
- package/test/dist/_check-tighten.test-d.d.ts +2 -0
- package/test/dist/_check-tighten.test-d.d.ts.map +1 -0
- package/test/dist/_check-tighten.test-d.js +21 -0
- package/test/dist/auth.test.d.ts.map +1 -0
- package/test/dist/cluster-cosmos.test.d.ts.map +1 -0
- package/test/dist/cluster-servicebus.test.d.ts.map +1 -0
- package/test/dist/contextProvider.test.d.ts.map +1 -1
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/cosmos-query.test.d.ts.map +1 -0
- package/test/dist/date-query.test.d.ts.map +1 -0
- package/test/dist/fixtures.d.ts +30 -12
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +17 -10
- package/test/dist/query.test.d.ts.map +1 -1
- package/test/dist/rawQuery.test.d.ts.map +1 -1
- package/test/dist/repository-ext.test.d.ts.map +1 -0
- package/test/dist/requires.test.d.ts.map +1 -1
- package/test/dist/router-generator.test.d.ts.map +1 -0
- package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
- package/test/dist/rpc-context-map-streaming.test.d.ts.map +1 -0
- package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
- package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
- package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
- package/test/dist/sql-store.test.d.ts.map +1 -0
- package/test/dist/workflow-engine-cosmos.test.d.ts.map +1 -0
- package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
- package/test/fixtures.ts +16 -9
- package/test/layerUtils.test.ts +2 -2
- package/test/query.test.ts +905 -40
- package/test/rawQuery.test.ts +340 -22
- package/test/repository-ext.test.ts +62 -0
- package/test/requires.test.ts +10 -5
- package/test/router-generator.test.ts +187 -0
- package/test/routing-interruptibility.test.ts +66 -0
- package/test/rpc-context-map-streaming.test.ts +262 -0
- package/test/rpc-e2e-invalidation.test.ts +256 -0
- package/test/rpc-multi-middleware.test.ts +85 -10
- package/test/rpc-stream-fullstack.test.ts +304 -0
- package/test/sql-store.test.ts +1711 -0
- package/test/validateSample.test.ts +26 -14
- package/test/workflow-engine-cosmos.test.ts +354 -0
- package/test/workflow-engine-sqlite.test.ts +299 -0
- package/tsconfig.examples.json +1 -1
- package/tsconfig.json +2 -1
- package/tsconfig.json.bak +2 -2
- package/tsconfig.src.json +35 -35
- package/tsconfig.test.json +2 -2
- package/dist/Emailer/service.d.ts +0 -55
- package/dist/Emailer/service.d.ts.map +0 -1
- package/dist/Emailer/service.js +0 -6
- package/dist/Emailer.d.ts +0 -2
- package/dist/Emailer.d.ts.map +0 -1
- package/dist/Emailer.js +0 -2
- package/dist/Model/Repository/ext.d.ts +0 -41
- package/dist/Model/Repository/ext.d.ts.map +0 -1
- package/dist/Model/Repository/ext.js +0 -65
- package/dist/Model/Repository/internal/internal.d.ts +0 -59
- package/dist/Model/Repository/internal/internal.d.ts.map +0 -1
- package/dist/Model/Repository/internal/internal.js +0 -316
- package/dist/Model/Repository/legacy.d.ts +0 -19
- package/dist/Model/Repository/legacy.d.ts.map +0 -1
- package/dist/Model/Repository/legacy.js +0 -2
- package/dist/Model/Repository/makeRepo.d.ts +0 -49
- package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
- package/dist/Model/Repository/makeRepo.js +0 -24
- package/dist/Model/Repository/service.d.ts +0 -89
- package/dist/Model/Repository/service.d.ts.map +0 -1
- package/dist/Model/Repository/service.js +0 -2
- package/dist/Model/Repository/validation.d.ts +0 -42
- package/dist/Model/Repository/validation.d.ts.map +0 -1
- package/dist/Model/Repository/validation.js +0 -32
- package/dist/Model/Repository.d.ts +0 -6
- package/dist/Model/Repository.d.ts.map +0 -1
- package/dist/Model/Repository.js +0 -6
- package/dist/Model/dsl.d.ts +0 -32
- package/dist/Model/dsl.d.ts.map +0 -1
- package/dist/Model/dsl.js +0 -44
- package/dist/Model/filter/filterApi.d.ts +0 -30
- package/dist/Model/filter/filterApi.d.ts.map +0 -1
- package/dist/Model/filter/filterApi.js +0 -2
- package/dist/Model/filter/types/errors.d.ts +0 -29
- package/dist/Model/filter/types/errors.d.ts.map +0 -1
- package/dist/Model/filter/types/errors.js +0 -2
- package/dist/Model/filter/types/fields.d.ts +0 -15
- package/dist/Model/filter/types/fields.d.ts.map +0 -1
- package/dist/Model/filter/types/fields.js +0 -2
- package/dist/Model/filter/types/path/common.d.ts +0 -316
- package/dist/Model/filter/types/path/common.d.ts.map +0 -1
- package/dist/Model/filter/types/path/common.js +0 -2
- package/dist/Model/filter/types/path/eager.d.ts +0 -95
- package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
- package/dist/Model/filter/types/path/eager.js +0 -31
- package/dist/Model/filter/types/path/index.d.ts +0 -4
- package/dist/Model/filter/types/path/index.d.ts.map +0 -1
- package/dist/Model/filter/types/path/index.js +0 -3
- package/dist/Model/filter/types/utils.d.ts +0 -79
- package/dist/Model/filter/types/utils.d.ts.map +0 -1
- package/dist/Model/filter/types/utils.js +0 -2
- package/dist/Model/filter/types/validator.d.ts +0 -30
- package/dist/Model/filter/types/validator.d.ts.map +0 -1
- package/dist/Model/filter/types/validator.js +0 -2
- package/dist/Model/filter/types.d.ts +0 -5
- package/dist/Model/filter/types.d.ts.map +0 -1
- package/dist/Model/filter/types.js +0 -7
- package/dist/Model/query/dsl.d.ts +0 -248
- package/dist/Model/query/dsl.d.ts.map +0 -1
- package/dist/Model/query/dsl.js +0 -104
- package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
- package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
- package/dist/Model/query/new-kid-interpreter.js +0 -165
- package/dist/Model/query.d.ts +0 -15
- package/dist/Model/query.d.ts.map +0 -1
- package/dist/Model/query.js +0 -3
- package/dist/Model.d.ts +0 -4
- package/dist/Model.d.ts.map +0 -1
- package/dist/Model.js +0 -4
- package/dist/Operations.d.ts +0 -55
- package/dist/Operations.d.ts.map +0 -1
- package/dist/Operations.js +0 -102
- package/dist/OperationsRepo.d.ts +0 -41
- package/dist/OperationsRepo.d.ts.map +0 -1
- package/dist/OperationsRepo.js +0 -14
- package/dist/QueueMaker/service.d.ts +0 -11
- package/dist/QueueMaker/service.d.ts.map +0 -1
- package/dist/QueueMaker/service.js +0 -4
- package/dist/RequestContext.d.ts +0 -63
- package/dist/RequestContext.d.ts.map +0 -1
- package/dist/RequestContext.js +0 -49
- package/dist/Store/ContextMapContainer.d.ts +0 -14
- package/dist/Store/ContextMapContainer.d.ts.map +0 -1
- package/dist/Store/ContextMapContainer.js +0 -16
- package/dist/Store/service.d.ts +0 -108
- package/dist/Store/service.d.ts.map +0 -1
- package/dist/Store/service.js +0 -71
- package/dist/Store.d.ts +0 -2
- package/dist/Store.d.ts.map +0 -1
- package/dist/Store.js +0 -2
- package/dist/adapters/SQL/Model.d.ts +0 -479
- package/dist/adapters/SQL/Model.d.ts.map +0 -1
- package/dist/adapters/SQL/Model.js +0 -478
- package/dist/adapters/SQL.d.ts +0 -2
- package/dist/adapters/SQL.d.ts.map +0 -1
- package/dist/adapters/ServiceBus.d.ts +0 -58
- package/dist/adapters/ServiceBus.d.ts.map +0 -1
- package/dist/adapters/ServiceBus.js +0 -99
- package/dist/adapters/cosmos-client.d.ts +0 -14
- package/dist/adapters/cosmos-client.d.ts.map +0 -1
- package/dist/adapters/cosmos-client.js +0 -9
- package/dist/adapters/index.d.ts +0 -2
- package/dist/adapters/index.d.ts.map +0 -1
- package/dist/adapters/index.js +0 -2
- package/dist/adapters/logger.d.ts +0 -9
- package/dist/adapters/logger.d.ts.map +0 -1
- package/dist/adapters/logger.js +0 -3
- package/dist/adapters/memQueue.d.ts +0 -13
- package/dist/adapters/memQueue.d.ts.map +0 -1
- package/dist/adapters/memQueue.js +0 -20
- package/dist/adapters/mongo-client.d.ts +0 -10
- package/dist/adapters/mongo-client.d.ts.map +0 -1
- package/dist/adapters/mongo-client.js +0 -13
- package/dist/adapters/redis-client.d.ts +0 -39
- package/dist/adapters/redis-client.d.ts.map +0 -1
- package/dist/adapters/redis-client.js +0 -94
- package/dist/api/ContextProvider.d.ts +0 -31
- package/dist/api/ContextProvider.d.ts.map +0 -1
- package/dist/api/ContextProvider.js +0 -38
- package/dist/api/codec.d.ts +0 -5
- package/dist/api/codec.d.ts.map +0 -1
- package/dist/api/codec.js +0 -5
- package/dist/api/internal/RequestContextMiddleware.d.ts +0 -5
- package/dist/api/internal/RequestContextMiddleware.d.ts.map +0 -1
- package/dist/api/internal/RequestContextMiddleware.js +0 -35
- package/dist/api/internal/auth.d.ts +0 -15
- package/dist/api/internal/auth.d.ts.map +0 -1
- package/dist/api/internal/auth.js +0 -47
- package/dist/api/internal/events.d.ts +0 -9
- package/dist/api/internal/events.d.ts.map +0 -1
- package/dist/api/internal/events.js +0 -42
- package/dist/api/internal/health.d.ts +0 -3
- package/dist/api/internal/health.d.ts.map +0 -1
- package/dist/api/internal/health.js +0 -5
- package/dist/api/layerUtils.d.ts +0 -24
- package/dist/api/layerUtils.d.ts.map +0 -1
- package/dist/api/layerUtils.js +0 -16
- package/dist/api/middlewares.d.ts +0 -10
- package/dist/api/middlewares.d.ts.map +0 -1
- package/dist/api/reportError.d.ts +0 -4
- package/dist/api/reportError.d.ts.map +0 -1
- package/dist/api/reportError.js +0 -27
- package/dist/api/routing/middleware/RouterMiddleware.d.ts +0 -15
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
- package/dist/api/routing/middleware/middleware.d.ts +0 -9
- package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
- package/dist/api/routing/middleware/middleware.js +0 -92
- package/dist/api/routing/middleware.d.ts +0 -4
- package/dist/api/routing/middleware.d.ts.map +0 -1
- package/dist/api/routing/schema/jwt.d.ts +0 -4
- package/dist/api/routing/schema/jwt.d.ts.map +0 -1
- package/dist/api/routing/schema/jwt.js +0 -12
- package/dist/api/routing/tsort.d.ts +0 -8
- package/dist/api/routing/tsort.d.ts.map +0 -1
- package/dist/api/routing/tsort.js +0 -51
- package/dist/api/routing/utils.d.ts +0 -19
- package/dist/api/routing/utils.d.ts.map +0 -1
- package/dist/api/routing/utils.js +0 -44
- package/dist/api/routing.d.ts +0 -138
- package/dist/api/routing.d.ts.map +0 -1
- package/dist/api/routing.js +0 -166
- package/dist/api/setupRequest.d.ts +0 -12
- package/dist/api/setupRequest.d.ts.map +0 -1
- package/dist/api/setupRequest.js +0 -44
- package/dist/api/util.d.ts +0 -3
- package/dist/api/util.d.ts.map +0 -1
- package/dist/api/util.js +0 -14
- package/eslint.config.mjs +0 -24
- package/src/Emailer/service.ts +0 -52
- package/src/Emailer.ts +0 -1
- package/src/Model/Repository/ext.ts +0 -283
- package/src/Model/Repository/internal/internal.ts +0 -577
- package/src/Model/Repository/legacy.ts +0 -27
- package/src/Model/Repository/makeRepo.ts +0 -139
- package/src/Model/Repository/service.ts +0 -627
- package/src/Model/Repository/validation.ts +0 -31
- package/src/Model/Repository.ts +0 -5
- package/src/Model/dsl.ts +0 -128
- package/src/Model/filter/filterApi.ts +0 -60
- package/src/Model/filter/types/errors.ts +0 -47
- package/src/Model/filter/types/fields.ts +0 -50
- package/src/Model/filter/types/path/common.ts +0 -404
- package/src/Model/filter/types/path/eager.ts +0 -298
- package/src/Model/filter/types/path/index.ts +0 -4
- package/src/Model/filter/types/utils.ts +0 -128
- package/src/Model/filter/types/validator.ts +0 -46
- package/src/Model/filter/types.ts +0 -6
- package/src/Model/query/dsl.ts +0 -2110
- package/src/Model/query/new-kid-interpreter.ts +0 -210
- package/src/Model/query.ts +0 -13
- package/src/Model.ts +0 -3
- package/src/Operations.ts +0 -235
- package/src/OperationsRepo.ts +0 -16
- package/src/QueueMaker/service.ts +0 -17
- package/src/RequestContext.ts +0 -63
- package/src/Store/ContextMapContainer.ts +0 -20
- package/src/Store/service.ts +0 -184
- package/src/Store.ts +0 -1
- package/src/adapters/ServiceBus.ts +0 -209
- package/src/adapters/index.ts +0 -0
- package/src/adapters/logger.ts +0 -3
- package/src/api/internal/auth.ts +0 -68
- package/src/api/routing/middleware.ts +0 -6
- package/src/api/routing.ts +0 -598
- package/src/api/setupRequest.ts +0 -84
- /package/src/{adapters/SQL.ts → SQL.ts} +0 -0
- /package/src/{api/codec.ts → codec.ts} +0 -0
- /package/src/{api/internal → internal}/health.ts +0 -0
- /package/src/{api/middlewares.ts → middlewares.ts} +0 -0
- /package/src/{api/routing → routing}/tsort.ts +0 -0
- /package/src/{api/util.ts → util.ts} +0 -0
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
|
|
|
@@ -162,6 +172,7 @@ it("works with repo", () =>
|
|
|
162
172
|
.pipe(
|
|
163
173
|
Effect.provide(Layer.mergeAll(SomethingRepo.Test, SomeService.Default)),
|
|
164
174
|
setupRequestContextFromCurrent(),
|
|
175
|
+
Effect.scoped,
|
|
165
176
|
Effect.runPromise
|
|
166
177
|
))
|
|
167
178
|
|
|
@@ -233,18 +244,19 @@ it("collect", () =>
|
|
|
233
244
|
.pipe(
|
|
234
245
|
Effect.provide(Layer.mergeAll(SomethingRepo.Test, SomeService.Default)),
|
|
235
246
|
setupRequestContextFromCurrent(),
|
|
247
|
+
Effect.scoped,
|
|
236
248
|
Effect.runPromise
|
|
237
249
|
))
|
|
238
250
|
|
|
239
|
-
class Person extends S.
|
|
251
|
+
class Person extends S.TaggedClass<Person, Person.Encoded>()("person", {
|
|
240
252
|
id: S.String,
|
|
241
253
|
surname: S.String
|
|
242
254
|
}) {}
|
|
243
|
-
class Animal extends S.
|
|
255
|
+
class Animal extends S.TaggedClass<Animal, Animal.Encoded>()("animal", {
|
|
244
256
|
id: S.String,
|
|
245
257
|
surname: S.String
|
|
246
258
|
}) {}
|
|
247
|
-
class Test extends S.
|
|
259
|
+
class Test extends S.TaggedClass<Test, Test.Encoded>()("test", {
|
|
248
260
|
id: S.String
|
|
249
261
|
}) {}
|
|
250
262
|
|
|
@@ -279,7 +291,7 @@ it(
|
|
|
279
291
|
expect(result).toEqual([])
|
|
280
292
|
expect(result2).toEqual([])
|
|
281
293
|
})
|
|
282
|
-
.pipe(Effect.provide(
|
|
294
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
283
295
|
)
|
|
284
296
|
|
|
285
297
|
it(
|
|
@@ -465,7 +477,7 @@ it(
|
|
|
465
477
|
|
|
466
478
|
expect([]).toEqual([])
|
|
467
479
|
})
|
|
468
|
-
.pipe(Effect.provide(
|
|
480
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
469
481
|
)
|
|
470
482
|
|
|
471
483
|
it(
|
|
@@ -508,7 +520,7 @@ it(
|
|
|
508
520
|
|
|
509
521
|
expect([]).toEqual([])
|
|
510
522
|
})
|
|
511
|
-
.pipe(Effect.provide(
|
|
523
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
512
524
|
)
|
|
513
525
|
|
|
514
526
|
it(
|
|
@@ -519,8 +531,8 @@ it(
|
|
|
519
531
|
const schema = S.Struct({
|
|
520
532
|
id: S.String,
|
|
521
533
|
createdAt: S.Date.pipe(
|
|
522
|
-
S.withDecodingDefault(() => new Date().toISOString()),
|
|
523
|
-
S.withConstructorDefault(() =>
|
|
534
|
+
S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
|
|
535
|
+
S.withConstructorDefault(Effect.sync(() => new Date()))
|
|
524
536
|
)
|
|
525
537
|
})
|
|
526
538
|
const repo = yield* makeRepo(
|
|
@@ -532,8 +544,8 @@ it(
|
|
|
532
544
|
const outputSchema = S.Struct({
|
|
533
545
|
id: S.Literal("123"),
|
|
534
546
|
createdAt: S.Date.pipe(
|
|
535
|
-
S.withDecodingDefault(() => new Date().toISOString()),
|
|
536
|
-
S.withConstructorDefault(() =>
|
|
547
|
+
S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
|
|
548
|
+
S.withConstructorDefault(Effect.sync(() => new Date()))
|
|
537
549
|
)
|
|
538
550
|
})
|
|
539
551
|
|
|
@@ -541,9 +553,246 @@ it(
|
|
|
541
553
|
|
|
542
554
|
expect(result).toEqual([])
|
|
543
555
|
})
|
|
544
|
-
.pipe(Effect.provide(
|
|
556
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
545
557
|
)
|
|
546
558
|
|
|
559
|
+
it(
|
|
560
|
+
"project with encodeKeys in projection maps encoded keys",
|
|
561
|
+
() =>
|
|
562
|
+
Effect
|
|
563
|
+
.gen(function*() {
|
|
564
|
+
const schema = S.Struct({
|
|
565
|
+
id: S.String,
|
|
566
|
+
a: S.Number
|
|
567
|
+
})
|
|
568
|
+
|
|
569
|
+
const repo = yield* makeRepo(
|
|
570
|
+
"test",
|
|
571
|
+
schema,
|
|
572
|
+
{
|
|
573
|
+
makeInitial: Effect.sync(() => [{ id: "1", a: 1 }])
|
|
574
|
+
}
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
const outputSchema = S.Struct({ b: S.Number }).pipe(S.encodeKeys({ b: "a" }))
|
|
578
|
+
|
|
579
|
+
const result = yield* repo.query(project(outputSchema))
|
|
580
|
+
|
|
581
|
+
expect(result).toStrictEqual([{ b: 1 }])
|
|
582
|
+
})
|
|
583
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
it("projectComputed sets computed IR and forces project mode", () => {
|
|
587
|
+
const baseSchema = S.Struct({
|
|
588
|
+
id: S.String,
|
|
589
|
+
items: S.Array(S.Struct({
|
|
590
|
+
state: S.Struct({
|
|
591
|
+
_tag: S.String
|
|
592
|
+
})
|
|
593
|
+
}))
|
|
594
|
+
})
|
|
595
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
596
|
+
projectComputed(
|
|
597
|
+
S.Struct({
|
|
598
|
+
pickedCount: S.NonNegativeInt
|
|
599
|
+
}),
|
|
600
|
+
computed({
|
|
601
|
+
pickedCount: relation<S.Codec.Encoded<typeof baseSchema>>("items").count(where("state._tag", "Picked"))
|
|
602
|
+
})
|
|
603
|
+
)
|
|
604
|
+
)
|
|
605
|
+
const interpreted = toFilter(query, baseSchema)
|
|
606
|
+
expect(interpreted.mode).toBe("project")
|
|
607
|
+
expect(interpreted.select).toEqual([
|
|
608
|
+
{
|
|
609
|
+
key: "pickedCount",
|
|
610
|
+
computed: {
|
|
611
|
+
_tag: "relation-count",
|
|
612
|
+
path: "items",
|
|
613
|
+
filter: [{ t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }]
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
])
|
|
617
|
+
expect(interpreted.computed?.["pickedCount"]?._tag).toBe("relation-count")
|
|
618
|
+
expect(interpreted.computed?.["pickedCount"]?.path).toBe("items")
|
|
619
|
+
expect((interpreted.computed?.["pickedCount"] as { filter: unknown } | undefined)?.filter).toEqual([
|
|
620
|
+
{ t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }
|
|
621
|
+
])
|
|
622
|
+
})
|
|
623
|
+
|
|
624
|
+
it("projectComputed validates extra computed keys", () => {
|
|
625
|
+
const baseSchema = S.Struct({
|
|
626
|
+
id: S.String,
|
|
627
|
+
items: S.Array(S.Struct({ value: S.Number }))
|
|
628
|
+
})
|
|
629
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
630
|
+
projectComputed(
|
|
631
|
+
S.Struct({ id: S.String }),
|
|
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("projection schema with computed fields fails without computed map", () => {
|
|
641
|
+
const baseSchema = S.Struct({
|
|
642
|
+
id: S.String,
|
|
643
|
+
items: S.Array(S.Struct({ value: S.Number }))
|
|
644
|
+
})
|
|
645
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
646
|
+
projectComputed(S.Struct({ pickedCount: S.NonNegativeInt }), computed({}))
|
|
647
|
+
)
|
|
648
|
+
expect(() => toFilter(query, baseSchema)).toThrowError("Missing computed projections for schema keys")
|
|
649
|
+
})
|
|
650
|
+
|
|
651
|
+
it("projectComputed.every emits relation-every IR", () => {
|
|
652
|
+
const baseSchema = S.Struct({
|
|
653
|
+
id: S.String,
|
|
654
|
+
items: S.Array(S.Struct({ state: S.Struct({ _tag: S.String }) }))
|
|
655
|
+
})
|
|
656
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
657
|
+
projectComputed(
|
|
658
|
+
S.Struct({ allPicked: S.Boolean }),
|
|
659
|
+
computed({
|
|
660
|
+
allPicked: relation<S.Codec.Encoded<typeof baseSchema>>("items").every(where("state._tag", "Picked"))
|
|
661
|
+
})
|
|
662
|
+
)
|
|
663
|
+
)
|
|
664
|
+
const interpreted = toFilter(query, baseSchema)
|
|
665
|
+
expect(interpreted.computed?.["allPicked"]?._tag).toBe("relation-every")
|
|
666
|
+
expect(interpreted.computed?.["allPicked"]?.path).toBe("items")
|
|
667
|
+
expect((interpreted.computed?.["allPicked"] as { filter: unknown } | undefined)?.filter).toEqual([
|
|
668
|
+
{ t: "where", path: "items.-1.state._tag", op: "eq", value: "Picked" }
|
|
669
|
+
])
|
|
670
|
+
})
|
|
671
|
+
|
|
672
|
+
it("projectComputed.distinctCount emits relation-distinct-count IR with field", () => {
|
|
673
|
+
const baseSchema = S.Struct({
|
|
674
|
+
id: S.String,
|
|
675
|
+
items: S.Array(S.Struct({ rowId: S.String, state: S.Struct({ _tag: S.String }) }))
|
|
676
|
+
})
|
|
677
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
678
|
+
projectComputed(
|
|
679
|
+
S.Struct({ positionCount: S.NonNegativeInt }),
|
|
680
|
+
computed({
|
|
681
|
+
positionCount: relation<S.Codec.Encoded<typeof baseSchema>>("items").distinctCount(
|
|
682
|
+
"rowId",
|
|
683
|
+
where("state._tag", "neq", "cancelled")
|
|
684
|
+
)
|
|
685
|
+
})
|
|
686
|
+
)
|
|
687
|
+
)
|
|
688
|
+
const interpreted = toFilter(query, baseSchema)
|
|
689
|
+
const ir = interpreted.computed?.["positionCount"]
|
|
690
|
+
expect(ir?._tag).toBe("relation-distinct-count")
|
|
691
|
+
expect((ir as { field: string } | undefined)?.field).toBe("rowId")
|
|
692
|
+
expect((ir as { filter: unknown } | undefined)?.filter).toEqual([
|
|
693
|
+
{ t: "where", path: "items.-1.state._tag", op: "neq", value: "cancelled" }
|
|
694
|
+
])
|
|
695
|
+
})
|
|
696
|
+
|
|
697
|
+
it("projectComputed.sum emits relation-sum IR with field", () => {
|
|
698
|
+
const baseSchema = S.Struct({
|
|
699
|
+
id: S.String,
|
|
700
|
+
items: S.Array(S.Struct({ weight: S.Number }))
|
|
701
|
+
})
|
|
702
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
703
|
+
projectComputed(
|
|
704
|
+
S.Struct({ totalWeight: S.Number }),
|
|
705
|
+
computed({ totalWeight: relation<S.Codec.Encoded<typeof baseSchema>>("items").sum("weight") })
|
|
706
|
+
)
|
|
707
|
+
)
|
|
708
|
+
const interpreted = toFilter(query, baseSchema)
|
|
709
|
+
const ir = interpreted.computed?.["totalWeight"]
|
|
710
|
+
expect(ir?._tag).toBe("relation-sum")
|
|
711
|
+
expect((ir as { field: string } | undefined)?.field).toBe("weight")
|
|
712
|
+
expect((ir as { filter: unknown } | undefined)?.filter).toEqual([])
|
|
713
|
+
})
|
|
714
|
+
|
|
715
|
+
it("projectComputed.collect / collectDistinct emit relation-collect IR", () => {
|
|
716
|
+
const baseSchema = S.Struct({
|
|
717
|
+
id: S.String,
|
|
718
|
+
items: S.Array(S.Struct({ articleId: S.String }))
|
|
719
|
+
})
|
|
720
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
721
|
+
projectComputed(
|
|
722
|
+
S.Struct({
|
|
723
|
+
all: S.Array(S.String),
|
|
724
|
+
distinct: S.Array(S.String)
|
|
725
|
+
}),
|
|
726
|
+
computed({
|
|
727
|
+
all: relation<S.Codec.Encoded<typeof baseSchema>>("items").collect("articleId"),
|
|
728
|
+
distinct: relation<S.Codec.Encoded<typeof baseSchema>>("items").collectDistinct("articleId")
|
|
729
|
+
})
|
|
730
|
+
)
|
|
731
|
+
)
|
|
732
|
+
const interpreted = toFilter(query, baseSchema)
|
|
733
|
+
const all = interpreted.computed?.["all"]
|
|
734
|
+
const distinct = interpreted.computed?.["distinct"]
|
|
735
|
+
expect(all?._tag).toBe("relation-collect")
|
|
736
|
+
expect((all as { distinct: boolean } | undefined)?.distinct).toBe(false)
|
|
737
|
+
expect(distinct?._tag).toBe("relation-collect")
|
|
738
|
+
expect((distinct as { distinct: boolean } | undefined)?.distinct).toBe(true)
|
|
739
|
+
})
|
|
740
|
+
|
|
741
|
+
it("projectComputed.sumExpr emits relation-sum-expr IR", () => {
|
|
742
|
+
const baseSchema = S.Struct({
|
|
743
|
+
id: S.String,
|
|
744
|
+
items: S.Array(S.Struct({
|
|
745
|
+
weight: S.Number,
|
|
746
|
+
tradeUnit: S.Struct({ amount: S.Number, unit: S.String })
|
|
747
|
+
}))
|
|
748
|
+
})
|
|
749
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
750
|
+
projectComputed(
|
|
751
|
+
S.Struct({ total: S.Number }),
|
|
752
|
+
computed({
|
|
753
|
+
total: relation<S.Codec.Encoded<typeof baseSchema>>("items").sumExpr(
|
|
754
|
+
expr.mul(expr.field("weight"), expr.field("tradeUnit.amount"))
|
|
755
|
+
)
|
|
756
|
+
})
|
|
757
|
+
)
|
|
758
|
+
)
|
|
759
|
+
const interpreted = toFilter(query, baseSchema)
|
|
760
|
+
const ir = interpreted.computed?.["total"]
|
|
761
|
+
expect(ir?._tag).toBe("relation-sum-expr")
|
|
762
|
+
expect((ir as { expression: unknown } | undefined)?.expression).toEqual({
|
|
763
|
+
_tag: "mul",
|
|
764
|
+
left: { _tag: "field", field: "weight" },
|
|
765
|
+
right: { _tag: "field", field: "tradeUnit.amount" }
|
|
766
|
+
})
|
|
767
|
+
})
|
|
768
|
+
|
|
769
|
+
it("projectComputed.sumExprBy emits relation-sum-expr-by IR", () => {
|
|
770
|
+
const baseSchema = S.Struct({
|
|
771
|
+
id: S.String,
|
|
772
|
+
items: S.Array(S.Struct({
|
|
773
|
+
weight: S.Number,
|
|
774
|
+
tradeUnit: S.Struct({ amount: S.Number, unit: S.String })
|
|
775
|
+
}))
|
|
776
|
+
})
|
|
777
|
+
const query = make<S.Codec.Encoded<typeof baseSchema>>().pipe(
|
|
778
|
+
projectComputed(
|
|
779
|
+
S.Struct({
|
|
780
|
+
totals: S.Array(S.Struct({ unit: S.String, total: S.Number }))
|
|
781
|
+
}),
|
|
782
|
+
computed({
|
|
783
|
+
totals: relation<S.Codec.Encoded<typeof baseSchema>>("items").sumExprBy(
|
|
784
|
+
expr.mul(expr.field("weight"), expr.field("tradeUnit.amount")),
|
|
785
|
+
{ unit: "tradeUnit.unit" }
|
|
786
|
+
)
|
|
787
|
+
})
|
|
788
|
+
)
|
|
789
|
+
)
|
|
790
|
+
const interpreted = toFilter(query, baseSchema)
|
|
791
|
+
const ir = interpreted.computed?.["totals"]
|
|
792
|
+
expect(ir?._tag).toBe("relation-sum-expr-by")
|
|
793
|
+
expect((ir as { unit: string } | undefined)?.unit).toBe("tradeUnit.unit")
|
|
794
|
+
})
|
|
795
|
+
|
|
547
796
|
it(
|
|
548
797
|
"doesn't mess when refining fields",
|
|
549
798
|
() =>
|
|
@@ -551,7 +800,7 @@ it(
|
|
|
551
800
|
.gen(function*() {
|
|
552
801
|
const schema = S.Struct({
|
|
553
802
|
id: S.String,
|
|
554
|
-
literals: S.
|
|
803
|
+
literals: S.Literals(["a", "b", "c"])
|
|
555
804
|
})
|
|
556
805
|
|
|
557
806
|
type Schema = typeof schema.Type
|
|
@@ -571,7 +820,7 @@ it(
|
|
|
571
820
|
|
|
572
821
|
expect(result).toEqual([])
|
|
573
822
|
})
|
|
574
|
-
.pipe(Effect.provide(
|
|
823
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
575
824
|
)
|
|
576
825
|
|
|
577
826
|
it(
|
|
@@ -581,7 +830,7 @@ it(
|
|
|
581
830
|
.gen(function*() {
|
|
582
831
|
const schema = S.Struct({
|
|
583
832
|
id: S.String,
|
|
584
|
-
literals: S.Union([S.
|
|
833
|
+
literals: S.Union([S.Literals(["a", "b", "c"]), S.Null])
|
|
585
834
|
})
|
|
586
835
|
|
|
587
836
|
type Schema = typeof schema.Type
|
|
@@ -615,7 +864,7 @@ it(
|
|
|
615
864
|
|
|
616
865
|
expect(result).toEqual([])
|
|
617
866
|
})
|
|
618
|
-
.pipe(Effect.provide(
|
|
867
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
619
868
|
)
|
|
620
869
|
|
|
621
870
|
it(
|
|
@@ -659,7 +908,7 @@ it(
|
|
|
659
908
|
|
|
660
909
|
expect(result).toEqual([])
|
|
661
910
|
})
|
|
662
|
-
.pipe(Effect.provide(
|
|
911
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise)
|
|
663
912
|
)
|
|
664
913
|
|
|
665
914
|
it("remove null from one constituent of a tagged union", () =>
|
|
@@ -672,7 +921,7 @@ it("remove null from one constituent of a tagged union", () =>
|
|
|
672
921
|
|
|
673
922
|
class BB extends S.Class<BB>("BB")({
|
|
674
923
|
id: S.Literal("BB"),
|
|
675
|
-
b: S.NullOr(S.
|
|
924
|
+
b: S.NullOr(S.Finite)
|
|
676
925
|
}) {}
|
|
677
926
|
|
|
678
927
|
type Union = AA | BB
|
|
@@ -708,7 +957,7 @@ it("remove null from one constituent of a tagged union", () =>
|
|
|
708
957
|
})[]
|
|
709
958
|
>()
|
|
710
959
|
})
|
|
711
|
-
.pipe(Effect.provide(
|
|
960
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
712
961
|
|
|
713
962
|
it("refine 3", () =>
|
|
714
963
|
Effect
|
|
@@ -746,7 +995,7 @@ it("refine 3", () =>
|
|
|
746
995
|
const resQuer1 = yield* repo.query(where("id", "AA"))
|
|
747
996
|
expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
|
|
748
997
|
})
|
|
749
|
-
.pipe(Effect.provide(
|
|
998
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
750
999
|
|
|
751
1000
|
it("my test", () =>
|
|
752
1001
|
Effect
|
|
@@ -764,7 +1013,7 @@ it("my test", () =>
|
|
|
764
1013
|
)
|
|
765
1014
|
expectTypeOf(resQuer1).toEqualTypeOf<readonly AA[]>()
|
|
766
1015
|
})
|
|
767
|
-
.pipe(Effect.provide(
|
|
1016
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
768
1017
|
|
|
769
1018
|
it("refine inner without imposing a projection", () =>
|
|
770
1019
|
Effect
|
|
@@ -808,7 +1057,7 @@ it("refine inner without imposing a projection", () =>
|
|
|
808
1057
|
where("union._tag", "AA"),
|
|
809
1058
|
// But if I wanna the whole Data as output ignoring the inner refinement
|
|
810
1059
|
// I wanna be able to do so
|
|
811
|
-
project(
|
|
1060
|
+
project(Data.mapFields(Struct.pick(["union"])))
|
|
812
1061
|
)
|
|
813
1062
|
|
|
814
1063
|
expectTypeOf(query2).toEqualTypeOf<
|
|
@@ -839,7 +1088,7 @@ it("refine inner without imposing a projection", () =>
|
|
|
839
1088
|
}[]
|
|
840
1089
|
>()
|
|
841
1090
|
})
|
|
842
|
-
.pipe(Effect.provide(
|
|
1091
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
843
1092
|
|
|
844
1093
|
it("does not allow string queries on arrays", () =>
|
|
845
1094
|
Effect
|
|
@@ -874,7 +1123,7 @@ it("does not allow string queries on arrays", () =>
|
|
|
874
1123
|
expectTypeOf(good3).toEqualTypeOf<QueryWhere<Some, Some>>()
|
|
875
1124
|
expectTypeOf(good4).toEqualTypeOf<QueryWhere<Some, Some>>()
|
|
876
1125
|
})
|
|
877
|
-
.pipe(Effect.provide(
|
|
1126
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
878
1127
|
|
|
879
1128
|
it("test array.length", () =>
|
|
880
1129
|
Effect
|
|
@@ -915,7 +1164,7 @@ it("test array.length", () =>
|
|
|
915
1164
|
QueryWhere<Something, Something>
|
|
916
1165
|
>()
|
|
917
1166
|
})
|
|
918
|
-
.pipe(Effect.provide(
|
|
1167
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
919
1168
|
|
|
920
1169
|
it("distribution over union", () =>
|
|
921
1170
|
Effect
|
|
@@ -939,7 +1188,7 @@ it("distribution over union", () =>
|
|
|
939
1188
|
})[]
|
|
940
1189
|
>()
|
|
941
1190
|
})
|
|
942
|
-
.pipe(Effect.provide(
|
|
1191
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
943
1192
|
|
|
944
1193
|
it("refine nested union", () =>
|
|
945
1194
|
Effect
|
|
@@ -980,7 +1229,158 @@ it("refine nested union", () =>
|
|
|
980
1229
|
}[]
|
|
981
1230
|
>()
|
|
982
1231
|
})
|
|
983
|
-
.pipe(Effect.provide(
|
|
1232
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
1233
|
+
|
|
1234
|
+
it("find with transformed id", () =>
|
|
1235
|
+
Effect
|
|
1236
|
+
.gen(function*() {
|
|
1237
|
+
const ConfiguratorId = S.NonEmptyString255
|
|
1238
|
+
|
|
1239
|
+
class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
|
|
1240
|
+
configuratorId: ConfiguratorId,
|
|
1241
|
+
label: S.NonEmptyString50
|
|
1242
|
+
}) {}
|
|
1243
|
+
|
|
1244
|
+
const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
|
|
1245
|
+
S.decodeTo(
|
|
1246
|
+
S.toType(PreconfigurationId),
|
|
1247
|
+
SchemaTransformation.transformOrFail({
|
|
1248
|
+
decode: Effect.fnUntraced(function*(value) {
|
|
1249
|
+
const values = value.split("_")
|
|
1250
|
+
const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
|
|
1251
|
+
const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(
|
|
1252
|
+
values.join("_")
|
|
1253
|
+
)
|
|
1254
|
+
return new PreconfigurationId({ configuratorId, label })
|
|
1255
|
+
}),
|
|
1256
|
+
encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
|
|
1257
|
+
})
|
|
1258
|
+
),
|
|
1259
|
+
S.revealCodec
|
|
1260
|
+
)
|
|
1261
|
+
|
|
1262
|
+
const Preconfiguration = S.Struct({
|
|
1263
|
+
id: PreconfigurationIdFromString,
|
|
1264
|
+
name: S.String
|
|
1265
|
+
})
|
|
1266
|
+
|
|
1267
|
+
const repo = yield* makeRepo("Preconfiguration", Preconfiguration, { idKey: "id" as const })
|
|
1268
|
+
|
|
1269
|
+
const id = new PreconfigurationId({
|
|
1270
|
+
configuratorId: S.NonEmptyString255("myConfigurator"),
|
|
1271
|
+
label: S.NonEmptyString50("myLabel")
|
|
1272
|
+
})
|
|
1273
|
+
const item = { id, name: "test preconfig" }
|
|
1274
|
+
|
|
1275
|
+
yield* repo.saveAndPublish([item])
|
|
1276
|
+
|
|
1277
|
+
const found = yield* repo.find(id)
|
|
1278
|
+
expect(Option.isSome(found)).toBe(true)
|
|
1279
|
+
expect(Option.getOrThrow(found).name).toBe("test preconfig")
|
|
1280
|
+
expect(Option.getOrThrow(found).id).toEqual(id)
|
|
1281
|
+
|
|
1282
|
+
const notFound = yield* repo.find(
|
|
1283
|
+
new PreconfigurationId({
|
|
1284
|
+
configuratorId: S.NonEmptyString255("other"),
|
|
1285
|
+
label: S.NonEmptyString50("nope")
|
|
1286
|
+
})
|
|
1287
|
+
)
|
|
1288
|
+
expect(Option.isNone(notFound)).toBe(true)
|
|
1289
|
+
})
|
|
1290
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
1291
|
+
|
|
1292
|
+
it("find with transformed id in tagged union", () =>
|
|
1293
|
+
Effect
|
|
1294
|
+
.gen(function*() {
|
|
1295
|
+
const ConfiguratorId = S.NonEmptyString255
|
|
1296
|
+
|
|
1297
|
+
class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
|
|
1298
|
+
configuratorId: ConfiguratorId,
|
|
1299
|
+
label: S.NonEmptyString50
|
|
1300
|
+
}) {}
|
|
1301
|
+
|
|
1302
|
+
const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
|
|
1303
|
+
S.decodeTo(
|
|
1304
|
+
S.toType(PreconfigurationId),
|
|
1305
|
+
SchemaTransformation.transformOrFail({
|
|
1306
|
+
decode: Effect.fnUntraced(function*(value) {
|
|
1307
|
+
const values = value.split("_")
|
|
1308
|
+
const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
|
|
1309
|
+
const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(
|
|
1310
|
+
values.join("_")
|
|
1311
|
+
)
|
|
1312
|
+
return new PreconfigurationId({ configuratorId, label })
|
|
1313
|
+
}),
|
|
1314
|
+
encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
|
|
1315
|
+
})
|
|
1316
|
+
),
|
|
1317
|
+
S.revealCodec
|
|
1318
|
+
)
|
|
1319
|
+
|
|
1320
|
+
class Draft extends S.TaggedClass<Draft>()("Draft", {
|
|
1321
|
+
id: PreconfigurationIdFromString,
|
|
1322
|
+
name: S.String
|
|
1323
|
+
}) {}
|
|
1324
|
+
|
|
1325
|
+
class Published extends S.TaggedClass<Published>()("Published", {
|
|
1326
|
+
id: PreconfigurationIdFromString,
|
|
1327
|
+
name: S.String,
|
|
1328
|
+
publishedAt: S.String
|
|
1329
|
+
}) {}
|
|
1330
|
+
|
|
1331
|
+
class Archived extends S.TaggedClass<Archived>()("Archived", {
|
|
1332
|
+
id: PreconfigurationIdFromString,
|
|
1333
|
+
name: S.String,
|
|
1334
|
+
archivedAt: S.String
|
|
1335
|
+
}) {}
|
|
1336
|
+
|
|
1337
|
+
const Preconfiguration = S.Union([Draft, Published, Archived])
|
|
1338
|
+
|
|
1339
|
+
const repo = yield* makeRepo("Preconfiguration", Preconfiguration, {})
|
|
1340
|
+
|
|
1341
|
+
const id1 = new PreconfigurationId({
|
|
1342
|
+
configuratorId: S.NonEmptyString255("conf1"),
|
|
1343
|
+
label: S.NonEmptyString50("draft1")
|
|
1344
|
+
})
|
|
1345
|
+
const id2 = new PreconfigurationId({
|
|
1346
|
+
configuratorId: S.NonEmptyString255("conf2"),
|
|
1347
|
+
label: S.NonEmptyString50("pub1")
|
|
1348
|
+
})
|
|
1349
|
+
const id3 = new PreconfigurationId({
|
|
1350
|
+
configuratorId: S.NonEmptyString255("conf3"),
|
|
1351
|
+
label: S.NonEmptyString50("arch1")
|
|
1352
|
+
})
|
|
1353
|
+
|
|
1354
|
+
const draft = new Draft({ id: id1, name: "my draft" })
|
|
1355
|
+
const published = new Published({ id: id2, name: "my published", publishedAt: "2024-01-01" })
|
|
1356
|
+
const archived = new Archived({ id: id3, name: "my archived", archivedAt: "2024-06-01" })
|
|
1357
|
+
|
|
1358
|
+
yield* repo.saveAndPublish([draft, published, archived])
|
|
1359
|
+
|
|
1360
|
+
// find each by their PreconfigurationId instance
|
|
1361
|
+
const foundDraft = yield* repo.find(id1)
|
|
1362
|
+
expect(Option.isSome(foundDraft)).toBe(true)
|
|
1363
|
+
expect(Option.getOrThrow(foundDraft)._tag).toBe("Draft")
|
|
1364
|
+
expect(Option.getOrThrow(foundDraft).name).toBe("my draft")
|
|
1365
|
+
|
|
1366
|
+
const foundPublished = yield* repo.find(id2)
|
|
1367
|
+
expect(Option.isSome(foundPublished)).toBe(true)
|
|
1368
|
+
expect(Option.getOrThrow(foundPublished)._tag).toBe("Published")
|
|
1369
|
+
|
|
1370
|
+
const foundArchived = yield* repo.find(id3)
|
|
1371
|
+
expect(Option.isSome(foundArchived)).toBe(true)
|
|
1372
|
+
expect(Option.getOrThrow(foundArchived)._tag).toBe("Archived")
|
|
1373
|
+
|
|
1374
|
+
// not found
|
|
1375
|
+
const notFound = yield* repo.find(
|
|
1376
|
+
new PreconfigurationId({
|
|
1377
|
+
configuratorId: S.NonEmptyString255("nope"),
|
|
1378
|
+
label: S.NonEmptyString50("nope")
|
|
1379
|
+
})
|
|
1380
|
+
)
|
|
1381
|
+
expect(Option.isNone(notFound)).toBe(true)
|
|
1382
|
+
})
|
|
1383
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
984
1384
|
|
|
985
1385
|
it("refine union with nested union", () =>
|
|
986
1386
|
Effect
|
|
@@ -1095,4 +1495,469 @@ it("refine union with nested union", () =>
|
|
|
1095
1495
|
})[]
|
|
1096
1496
|
>()
|
|
1097
1497
|
})
|
|
1098
|
-
.pipe(Effect.provide(
|
|
1498
|
+
.pipe(Effect.provide(TestStoreLive), setupRequestContextFromCurrent(), Effect.scoped, Effect.runPromise))
|
|
1499
|
+
|
|
1500
|
+
// ---------------------------------------------------------------------------
|
|
1501
|
+
// memFilter: computed projection execution (in-memory) and code filter coverage
|
|
1502
|
+
// ---------------------------------------------------------------------------
|
|
1503
|
+
|
|
1504
|
+
const computedBaseSchema = S.Struct({
|
|
1505
|
+
id: S.String,
|
|
1506
|
+
status: S.Literals(["active", "archived"]),
|
|
1507
|
+
items: S.Array(S.Struct({
|
|
1508
|
+
id: S.String,
|
|
1509
|
+
tag: S.Literals(["a", "b", "c"]),
|
|
1510
|
+
qty: S.Finite,
|
|
1511
|
+
note: S.String
|
|
1512
|
+
}))
|
|
1513
|
+
})
|
|
1514
|
+
type ComputedBase = S.Codec.Encoded<typeof computedBaseSchema>
|
|
1515
|
+
|
|
1516
|
+
const computedRows: ComputedBase[] = [
|
|
1517
|
+
{
|
|
1518
|
+
id: "r1",
|
|
1519
|
+
status: "active",
|
|
1520
|
+
items: [
|
|
1521
|
+
{ id: "i1", tag: "a", qty: 10, note: "alpha" },
|
|
1522
|
+
{ id: "i2", tag: "a", qty: 20, note: "alpha" },
|
|
1523
|
+
{ id: "i3", tag: "b", qty: 5, note: "beta" }
|
|
1524
|
+
]
|
|
1525
|
+
},
|
|
1526
|
+
{ id: "r2", status: "active", items: [] },
|
|
1527
|
+
{
|
|
1528
|
+
id: "r3",
|
|
1529
|
+
status: "archived",
|
|
1530
|
+
items: [
|
|
1531
|
+
{ id: "i4", tag: "b", qty: 7, note: "gamma" },
|
|
1532
|
+
{ id: "i5", tag: "b", qty: 7, note: "gamma" },
|
|
1533
|
+
{ id: "i6", tag: "c", qty: 3, note: "delta" }
|
|
1534
|
+
]
|
|
1535
|
+
}
|
|
1536
|
+
]
|
|
1537
|
+
|
|
1538
|
+
it("memFilter: relation-count with filter", () => {
|
|
1539
|
+
const q = make<ComputedBase>().pipe(
|
|
1540
|
+
projectComputed(
|
|
1541
|
+
S.Struct({ id: S.String, aCount: S.NonNegativeInt }),
|
|
1542
|
+
computed({
|
|
1543
|
+
aCount: relation<ComputedBase>("items").count(where("tag", "a"))
|
|
1544
|
+
})
|
|
1545
|
+
)
|
|
1546
|
+
)
|
|
1547
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1548
|
+
{ id: "r1", aCount: 2 },
|
|
1549
|
+
{ id: "r2", aCount: 0 },
|
|
1550
|
+
{ id: "r3", aCount: 0 }
|
|
1551
|
+
])
|
|
1552
|
+
})
|
|
1553
|
+
|
|
1554
|
+
it("memFilter: relation-any / every with filter", () => {
|
|
1555
|
+
const q = make<ComputedBase>().pipe(
|
|
1556
|
+
projectComputed(
|
|
1557
|
+
S.Struct({
|
|
1558
|
+
id: S.String,
|
|
1559
|
+
hasA: S.Boolean,
|
|
1560
|
+
allB: S.Boolean
|
|
1561
|
+
}),
|
|
1562
|
+
computed({
|
|
1563
|
+
hasA: relation<ComputedBase>("items").any(where("tag", "a")),
|
|
1564
|
+
allB: relation<ComputedBase>("items").every(where("tag", "b"))
|
|
1565
|
+
})
|
|
1566
|
+
)
|
|
1567
|
+
)
|
|
1568
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1569
|
+
{ id: "r1", hasA: true, allB: false },
|
|
1570
|
+
// empty array: any → false, every → true (JS Array.every on [] is true)
|
|
1571
|
+
{ id: "r2", hasA: false, allB: true },
|
|
1572
|
+
{ id: "r3", hasA: false, allB: false }
|
|
1573
|
+
])
|
|
1574
|
+
})
|
|
1575
|
+
|
|
1576
|
+
it("memFilter: relation-distinct-count with filter", () => {
|
|
1577
|
+
const q = make<ComputedBase>().pipe(
|
|
1578
|
+
projectComputed(
|
|
1579
|
+
S.Struct({ id: S.String, distinctNotes: S.NonNegativeInt }),
|
|
1580
|
+
computed({
|
|
1581
|
+
distinctNotes: relation<ComputedBase>("items").distinctCount("note", where("tag", "neq", "c"))
|
|
1582
|
+
})
|
|
1583
|
+
)
|
|
1584
|
+
)
|
|
1585
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1586
|
+
{ id: "r1", distinctNotes: 2 }, // alpha, beta
|
|
1587
|
+
{ id: "r2", distinctNotes: 0 },
|
|
1588
|
+
{ id: "r3", distinctNotes: 1 } // gamma (delta filtered out)
|
|
1589
|
+
])
|
|
1590
|
+
})
|
|
1591
|
+
|
|
1592
|
+
it("memFilter: relation-sum with filter", () => {
|
|
1593
|
+
const q = make<ComputedBase>().pipe(
|
|
1594
|
+
projectComputed(
|
|
1595
|
+
S.Struct({ id: S.String, totalQty: S.Finite }),
|
|
1596
|
+
computed({
|
|
1597
|
+
totalQty: relation<ComputedBase>("items").sum("qty", where("tag", "neq", "c"))
|
|
1598
|
+
})
|
|
1599
|
+
)
|
|
1600
|
+
)
|
|
1601
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1602
|
+
{ id: "r1", totalQty: 35 },
|
|
1603
|
+
{ id: "r2", totalQty: 0 },
|
|
1604
|
+
{ id: "r3", totalQty: 14 }
|
|
1605
|
+
])
|
|
1606
|
+
})
|
|
1607
|
+
|
|
1608
|
+
it("memFilter: relation-collect / collectDistinct with filter", () => {
|
|
1609
|
+
const q = make<ComputedBase>().pipe(
|
|
1610
|
+
projectComputed(
|
|
1611
|
+
S.Struct({
|
|
1612
|
+
id: S.String,
|
|
1613
|
+
notes: S.Array(S.String),
|
|
1614
|
+
distinctNotes: S.Array(S.String)
|
|
1615
|
+
}),
|
|
1616
|
+
computed({
|
|
1617
|
+
notes: relation<ComputedBase>("items").collect("note", where("tag", "neq", "c")),
|
|
1618
|
+
distinctNotes: relation<ComputedBase>("items").collectDistinct("note", where("tag", "neq", "c"))
|
|
1619
|
+
})
|
|
1620
|
+
)
|
|
1621
|
+
)
|
|
1622
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1623
|
+
{ id: "r1", notes: ["alpha", "alpha", "beta"], distinctNotes: ["alpha", "beta"] },
|
|
1624
|
+
{ id: "r2", notes: [], distinctNotes: [] },
|
|
1625
|
+
{ id: "r3", notes: ["gamma", "gamma"], distinctNotes: ["gamma"] }
|
|
1626
|
+
])
|
|
1627
|
+
})
|
|
1628
|
+
|
|
1629
|
+
it("memFilter: computed projection with multi-statement relation filter", () => {
|
|
1630
|
+
const q = make<ComputedBase>().pipe(
|
|
1631
|
+
projectComputed(
|
|
1632
|
+
S.Struct({ id: S.String, hits: S.NonNegativeInt }),
|
|
1633
|
+
computed({
|
|
1634
|
+
hits: relation<ComputedBase>("items").count(
|
|
1635
|
+
flow(
|
|
1636
|
+
where("tag", "a"),
|
|
1637
|
+
and("qty", "gt", 10)
|
|
1638
|
+
)
|
|
1639
|
+
)
|
|
1640
|
+
})
|
|
1641
|
+
)
|
|
1642
|
+
)
|
|
1643
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1644
|
+
{ id: "r1", hits: 1 }, // only i2 (a, qty 20)
|
|
1645
|
+
{ id: "r2", hits: 0 },
|
|
1646
|
+
{ id: "r3", hits: 0 }
|
|
1647
|
+
])
|
|
1648
|
+
})
|
|
1649
|
+
|
|
1650
|
+
it("memFilter: relation-sum-expr / sum-expr-by / sum-expr-normalized", () => {
|
|
1651
|
+
const schema = S.Struct({
|
|
1652
|
+
id: S.String,
|
|
1653
|
+
items: S.Array(S.Struct({
|
|
1654
|
+
weight: S.Finite,
|
|
1655
|
+
tradeUnit: S.Struct({ amount: S.Finite, unit: S.String })
|
|
1656
|
+
}))
|
|
1657
|
+
})
|
|
1658
|
+
type Row = S.Codec.Encoded<typeof schema>
|
|
1659
|
+
const rows: Row[] = [
|
|
1660
|
+
{
|
|
1661
|
+
id: "r1",
|
|
1662
|
+
items: [
|
|
1663
|
+
{ weight: 2, tradeUnit: { amount: 5, unit: "kg" } },
|
|
1664
|
+
{ weight: 4, tradeUnit: { amount: 1000, unit: "g" } },
|
|
1665
|
+
{ weight: 3, tradeUnit: { amount: 1, unit: "kg" } }
|
|
1666
|
+
]
|
|
1667
|
+
},
|
|
1668
|
+
{ id: "r2", items: [] }
|
|
1669
|
+
]
|
|
1670
|
+
const weighted = expr.mul(expr.field("weight"), expr.field("tradeUnit.amount"))
|
|
1671
|
+
const q = make<Row>().pipe(
|
|
1672
|
+
projectComputed(
|
|
1673
|
+
S.Struct({
|
|
1674
|
+
id: S.String,
|
|
1675
|
+
totalRaw: S.Finite,
|
|
1676
|
+
totalsByUnit: S.Array(S.Struct({ unit: S.String, total: S.Finite })),
|
|
1677
|
+
totalKg: S.Finite
|
|
1678
|
+
}),
|
|
1679
|
+
computed({
|
|
1680
|
+
totalRaw: relation<Row>("items").sumExpr(weighted, where("weight", "gte", 0)),
|
|
1681
|
+
totalsByUnit: relation<Row>("items").sumExprBy(weighted, { unit: "tradeUnit.unit" }, where("weight", "gte", 0)),
|
|
1682
|
+
totalKg: relation<Row>("items").sumExprNormalized(weighted, {
|
|
1683
|
+
unit: "tradeUnit.unit",
|
|
1684
|
+
toBase: "kg",
|
|
1685
|
+
factors: { g: 0.001 }
|
|
1686
|
+
}, where("weight", "gte", 0))
|
|
1687
|
+
})
|
|
1688
|
+
)
|
|
1689
|
+
)
|
|
1690
|
+
expect(memFilter(toFilter(q, schema))(rows)).toEqual([
|
|
1691
|
+
{
|
|
1692
|
+
id: "r1",
|
|
1693
|
+
totalRaw: 4013,
|
|
1694
|
+
totalsByUnit: [{ unit: "kg", total: 13 }, { unit: "g", total: 4000 }],
|
|
1695
|
+
totalKg: 17
|
|
1696
|
+
},
|
|
1697
|
+
{ id: "r2", totalRaw: 0, totalsByUnit: [], totalKg: 0 }
|
|
1698
|
+
])
|
|
1699
|
+
})
|
|
1700
|
+
|
|
1701
|
+
it("memFilter: computed projection combined with root where filter", () => {
|
|
1702
|
+
const q = make<ComputedBase>().pipe(
|
|
1703
|
+
where("id", "neq", "r3"),
|
|
1704
|
+
projectComputed(
|
|
1705
|
+
S.Struct({ id: S.String, totalQty: S.Finite }),
|
|
1706
|
+
computed({
|
|
1707
|
+
totalQty: relation<ComputedBase>("items").sum("qty", where("tag", "neq", "c"))
|
|
1708
|
+
})
|
|
1709
|
+
)
|
|
1710
|
+
)
|
|
1711
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1712
|
+
{ id: "r1", totalQty: 35 },
|
|
1713
|
+
{ id: "r2", totalQty: 0 }
|
|
1714
|
+
])
|
|
1715
|
+
})
|
|
1716
|
+
|
|
1717
|
+
it("memFilter: computed projection with order/limit/skip applied to base rows", () => {
|
|
1718
|
+
const q = make<ComputedBase>().pipe(
|
|
1719
|
+
order("id", "DESC"),
|
|
1720
|
+
page({ skip: 1, take: 1 }),
|
|
1721
|
+
projectComputed(
|
|
1722
|
+
S.Struct({ id: S.String, total: S.NonNegativeInt }),
|
|
1723
|
+
computed({
|
|
1724
|
+
total: relation<ComputedBase>("items").count(where("qty", "gte", 0))
|
|
1725
|
+
})
|
|
1726
|
+
)
|
|
1727
|
+
)
|
|
1728
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(computedRows)).toEqual([
|
|
1729
|
+
{ id: "r2", total: 0 }
|
|
1730
|
+
])
|
|
1731
|
+
})
|
|
1732
|
+
|
|
1733
|
+
it("memFilter: computed projection - relation missing on row returns empty value", () => {
|
|
1734
|
+
const partial: ComputedBase[] = [
|
|
1735
|
+
{ id: "x1" } as ComputedBase,
|
|
1736
|
+
{ id: "x2", status: "active", items: undefined as unknown as ComputedBase["items"] }
|
|
1737
|
+
]
|
|
1738
|
+
const q = make<ComputedBase>().pipe(
|
|
1739
|
+
projectComputed(
|
|
1740
|
+
S.Struct({
|
|
1741
|
+
id: S.String,
|
|
1742
|
+
c: S.NonNegativeInt,
|
|
1743
|
+
s: S.Finite,
|
|
1744
|
+
any_: S.Boolean,
|
|
1745
|
+
every_: S.Boolean,
|
|
1746
|
+
coll: S.Array(S.String)
|
|
1747
|
+
}),
|
|
1748
|
+
computed({
|
|
1749
|
+
c: relation<ComputedBase>("items").count(where("tag", "a")),
|
|
1750
|
+
s: relation<ComputedBase>("items").sum("qty", where("tag", "a")),
|
|
1751
|
+
any_: relation<ComputedBase>("items").any(where("tag", "a")),
|
|
1752
|
+
every_: relation<ComputedBase>("items").every(where("tag", "a")),
|
|
1753
|
+
coll: relation<ComputedBase>("items").collect("note", where("tag", "a"))
|
|
1754
|
+
})
|
|
1755
|
+
)
|
|
1756
|
+
)
|
|
1757
|
+
expect(memFilter(toFilter(q, computedBaseSchema))(partial)).toEqual([
|
|
1758
|
+
{ id: "x1", c: 0, s: 0, any_: false, every_: true, coll: [] },
|
|
1759
|
+
{ id: "x2", c: 0, s: 0, any_: false, every_: true, coll: [] }
|
|
1760
|
+
])
|
|
1761
|
+
})
|
|
1762
|
+
|
|
1763
|
+
it("memFilter: rejects extra computed keys not in projection schema", () => {
|
|
1764
|
+
const q = make<ComputedBase>().pipe(
|
|
1765
|
+
projectComputed(
|
|
1766
|
+
S.Struct({ id: S.String }),
|
|
1767
|
+
computed({
|
|
1768
|
+
bogus: relation<ComputedBase>("items").count(where("tag", "a"))
|
|
1769
|
+
})
|
|
1770
|
+
)
|
|
1771
|
+
)
|
|
1772
|
+
expect(() => toFilter(q, computedBaseSchema)).toThrowError(
|
|
1773
|
+
"Computed projection keys must exist in projection schema"
|
|
1774
|
+
)
|
|
1775
|
+
})
|
|
1776
|
+
|
|
1777
|
+
// ---------------------------------------------------------------------------
|
|
1778
|
+
// memFilter: code filter (where/and/or/scopes) execution coverage
|
|
1779
|
+
// ---------------------------------------------------------------------------
|
|
1780
|
+
|
|
1781
|
+
type CFRow = {
|
|
1782
|
+
readonly id: string
|
|
1783
|
+
readonly tag: "x" | "y" | "z"
|
|
1784
|
+
readonly qty: number
|
|
1785
|
+
readonly desc: string
|
|
1786
|
+
readonly tags: ReadonlyArray<string>
|
|
1787
|
+
readonly nested: { readonly kind: "k1" | "k2"; readonly v: number }
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
const cfRows: CFRow[] = [
|
|
1791
|
+
{ id: "1", tag: "x", qty: 10, desc: "Hello World", tags: ["red", "green"], nested: { kind: "k1", v: 1 } },
|
|
1792
|
+
{ id: "2", tag: "y", qty: 20, desc: "Goodbye", tags: ["blue"], nested: { kind: "k2", v: 5 } },
|
|
1793
|
+
{ id: "3", tag: "z", qty: 0, desc: "Hello again", tags: ["red", "blue", "green"], nested: { kind: "k1", v: 9 } },
|
|
1794
|
+
{ id: "4", tag: "y", qty: 30, desc: "World cup", tags: [], nested: { kind: "k2", v: 0 } }
|
|
1795
|
+
]
|
|
1796
|
+
|
|
1797
|
+
const runCF = (q: any) => (memFilter(toFilter(q))(cfRows) as unknown as readonly CFRow[]).map((_) => _.id)
|
|
1798
|
+
|
|
1799
|
+
it("codeFilter: where + and chain", () => {
|
|
1800
|
+
const q = make<CFRow>().pipe(
|
|
1801
|
+
where("tag", "y"),
|
|
1802
|
+
and("qty", "gt", 25)
|
|
1803
|
+
)
|
|
1804
|
+
expect(runCF(q)).toEqual(["4"])
|
|
1805
|
+
})
|
|
1806
|
+
|
|
1807
|
+
it("codeFilter: where + or chain", () => {
|
|
1808
|
+
const q = make<CFRow>().pipe(
|
|
1809
|
+
where("tag", "x"),
|
|
1810
|
+
or("tag", "z")
|
|
1811
|
+
)
|
|
1812
|
+
expect(runCF(q).sort()).toEqual(["1", "3"])
|
|
1813
|
+
})
|
|
1814
|
+
|
|
1815
|
+
it("codeFilter: nested scope precedence (a AND (b OR c))", () => {
|
|
1816
|
+
const q = make<CFRow>().pipe(
|
|
1817
|
+
where("tag", "y"),
|
|
1818
|
+
and(
|
|
1819
|
+
where("qty", "gt", 25),
|
|
1820
|
+
or("desc", "contains", "good")
|
|
1821
|
+
)
|
|
1822
|
+
)
|
|
1823
|
+
// tag=y AND (qty>25 OR desc contains "good") → row 2 (Goodbye) and row 4 (qty 30)
|
|
1824
|
+
expect(runCF(q).sort()).toEqual(["2", "4"])
|
|
1825
|
+
})
|
|
1826
|
+
|
|
1827
|
+
it("codeFilter: contains/startsWith/endsWith are case-insensitive", () => {
|
|
1828
|
+
expect(runCF(make<CFRow>().pipe(where("desc", "contains", "WORLD"))).sort()).toEqual(["1", "4"])
|
|
1829
|
+
expect(runCF(make<CFRow>().pipe(where("desc", "startsWith", "hello"))).sort()).toEqual(["1", "3"])
|
|
1830
|
+
expect(runCF(make<CFRow>().pipe(where("desc", "endsWith", "AGAIN")))).toEqual(["3"])
|
|
1831
|
+
})
|
|
1832
|
+
|
|
1833
|
+
it("codeFilter: array includes / includes-any / includes-all", () => {
|
|
1834
|
+
expect(runCF(make<CFRow>().pipe(where("tags", "includes", "red"))).sort()).toEqual(["1", "3"])
|
|
1835
|
+
expect(runCF(make<CFRow>().pipe(where("tags", "includes-any", ["blue", "green"]))).sort()).toEqual([
|
|
1836
|
+
"1",
|
|
1837
|
+
"2",
|
|
1838
|
+
"3"
|
|
1839
|
+
])
|
|
1840
|
+
expect(runCF(make<CFRow>().pipe(where("tags", "includes-all", ["red", "blue"])))).toEqual(["3"])
|
|
1841
|
+
})
|
|
1842
|
+
|
|
1843
|
+
it("codeFilter: in / notIn", () => {
|
|
1844
|
+
expect(runCF(make<CFRow>().pipe(where("tag", "in", ["x", "z"]))).sort()).toEqual(["1", "3"])
|
|
1845
|
+
expect(runCF(make<CFRow>().pipe(where("tag", "notIn", ["x", "z"]))).sort()).toEqual(["2", "4"])
|
|
1846
|
+
})
|
|
1847
|
+
|
|
1848
|
+
it("codeFilter: gt / gte / lt / lte / neq", () => {
|
|
1849
|
+
expect(runCF(make<CFRow>().pipe(where("qty", "gt", 10))).sort()).toEqual(["2", "4"])
|
|
1850
|
+
expect(runCF(make<CFRow>().pipe(where("qty", "gte", 10))).sort()).toEqual(["1", "2", "4"])
|
|
1851
|
+
expect(runCF(make<CFRow>().pipe(where("qty", "lt", 10)))).toEqual(["3"])
|
|
1852
|
+
expect(runCF(make<CFRow>().pipe(where("qty", "lte", 10))).sort()).toEqual(["1", "3"])
|
|
1853
|
+
expect(runCF(make<CFRow>().pipe(where("qty", "neq", 0))).sort()).toEqual(["1", "2", "4"])
|
|
1854
|
+
})
|
|
1855
|
+
|
|
1856
|
+
it("codeFilter: nested path access through dot notation", () => {
|
|
1857
|
+
expect(runCF(make<CFRow>().pipe(where("nested.kind", "k1"))).sort()).toEqual(["1", "3"])
|
|
1858
|
+
expect(
|
|
1859
|
+
runCF(
|
|
1860
|
+
make<CFRow>().pipe(
|
|
1861
|
+
where("nested.kind", "k2"),
|
|
1862
|
+
and("nested.v", "gt", 0)
|
|
1863
|
+
)
|
|
1864
|
+
)
|
|
1865
|
+
)
|
|
1866
|
+
.toEqual(["2"])
|
|
1867
|
+
})
|
|
1868
|
+
|
|
1869
|
+
it("codeFilter: array length predicates", () => {
|
|
1870
|
+
expect(runCF(make<CFRow>().pipe(where("tags.length", 0)))).toEqual(["4"])
|
|
1871
|
+
expect(runCF(make<CFRow>().pipe(where("tags.length", "gte", 2))).sort()).toEqual(["1", "3"])
|
|
1872
|
+
})
|
|
1873
|
+
|
|
1874
|
+
it("codeFilter: order + skip + limit applied after filter", () => {
|
|
1875
|
+
const q = make<CFRow>().pipe(
|
|
1876
|
+
where("tag", "neq", "z"),
|
|
1877
|
+
order("qty", "DESC"),
|
|
1878
|
+
page({ skip: 1, take: 2 })
|
|
1879
|
+
)
|
|
1880
|
+
expect(runCF(q)).toEqual(["2", "1"])
|
|
1881
|
+
})
|
|
1882
|
+
|
|
1883
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1884
|
+
// memFilter: aggregate (GROUP BY) support
|
|
1885
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1886
|
+
|
|
1887
|
+
it("memFilter: agg-count-when groups rows and counts conditionally", () => {
|
|
1888
|
+
type Row = { city: string; status: string }
|
|
1889
|
+
const rows: Row[] = [
|
|
1890
|
+
{ city: "NYC", status: "active" },
|
|
1891
|
+
{ city: "NYC", status: "inactive" },
|
|
1892
|
+
{ city: "NYC", status: "active" },
|
|
1893
|
+
{ city: "LA", status: "active" }
|
|
1894
|
+
]
|
|
1895
|
+
const result = memFilter<Row>({
|
|
1896
|
+
t: {} as Row,
|
|
1897
|
+
select: [
|
|
1898
|
+
{ key: "city", path: "city" },
|
|
1899
|
+
{
|
|
1900
|
+
key: "activeCount",
|
|
1901
|
+
aggregate: {
|
|
1902
|
+
_tag: "agg-count-when",
|
|
1903
|
+
filter: [{ t: "where", path: "status", op: "eq", value: "active" }]
|
|
1904
|
+
}
|
|
1905
|
+
},
|
|
1906
|
+
{ key: "total", aggregate: { _tag: "agg-count" } }
|
|
1907
|
+
] as any
|
|
1908
|
+
})(rows as any) as any[]
|
|
1909
|
+
|
|
1910
|
+
expect(result.length).toBe(2)
|
|
1911
|
+
const nyc = result.find((r: any) => r.city === "NYC")!
|
|
1912
|
+
expect(nyc.activeCount).toBe(2)
|
|
1913
|
+
expect(nyc.total).toBe(3)
|
|
1914
|
+
const la = result.find((r: any) => r.city === "LA")!
|
|
1915
|
+
expect(la.activeCount).toBe(1)
|
|
1916
|
+
expect(la.total).toBe(1)
|
|
1917
|
+
})
|
|
1918
|
+
|
|
1919
|
+
it("memFilter: agg-sum / agg-min / agg-max aggregate numerics", () => {
|
|
1920
|
+
type Row = { dept: string; salary: number }
|
|
1921
|
+
const rows: Row[] = [
|
|
1922
|
+
{ dept: "eng", salary: 100 },
|
|
1923
|
+
{ dept: "eng", salary: 200 },
|
|
1924
|
+
{ dept: "hr", salary: 50 }
|
|
1925
|
+
]
|
|
1926
|
+
const result = memFilter<Row>({
|
|
1927
|
+
t: {} as Row,
|
|
1928
|
+
select: [
|
|
1929
|
+
{ key: "dept", path: "dept" },
|
|
1930
|
+
{ key: "total", aggregate: { _tag: "agg-sum", field: "salary" } },
|
|
1931
|
+
{ key: "min", aggregate: { _tag: "agg-min", field: "salary" } },
|
|
1932
|
+
{ key: "max", aggregate: { _tag: "agg-max", field: "salary" } }
|
|
1933
|
+
] as any
|
|
1934
|
+
})(rows as any) as any[]
|
|
1935
|
+
|
|
1936
|
+
expect(result.length).toBe(2)
|
|
1937
|
+
const eng = result.find((r: any) => r.dept === "eng")!
|
|
1938
|
+
expect(eng.total).toBe(300)
|
|
1939
|
+
expect(eng.min).toBe(100)
|
|
1940
|
+
expect(eng.max).toBe(200)
|
|
1941
|
+
const hr = result.find((r: any) => r.dept === "hr")!
|
|
1942
|
+
expect(hr.total).toBe(50)
|
|
1943
|
+
})
|
|
1944
|
+
|
|
1945
|
+
it("memFilter: aggregate with nested path grouping", () => {
|
|
1946
|
+
type Row = { address: { city: string }; value: number }
|
|
1947
|
+
const rows: Row[] = [
|
|
1948
|
+
{ address: { city: "NYC" }, value: 10 },
|
|
1949
|
+
{ address: { city: "NYC" }, value: 20 },
|
|
1950
|
+
{ address: { city: "LA" }, value: 5 }
|
|
1951
|
+
]
|
|
1952
|
+
const result = memFilter<Row>({
|
|
1953
|
+
t: {} as Row,
|
|
1954
|
+
select: [
|
|
1955
|
+
{ key: "city", path: "address.city" },
|
|
1956
|
+
{ key: "count", aggregate: { _tag: "agg-count" } }
|
|
1957
|
+
] as any
|
|
1958
|
+
})(rows as any) as any[]
|
|
1959
|
+
|
|
1960
|
+
expect(result.length).toBe(2)
|
|
1961
|
+
expect(result.find((r: any) => r.city === "NYC")!.count).toBe(2)
|
|
1962
|
+
expect(result.find((r: any) => r.city === "LA")!.count).toBe(1)
|
|
1963
|
+
})
|