@effect-app/infra 4.0.0-beta.23 → 4.0.0-beta.231
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 +1726 -0
- package/_check.sh +1 -1
- package/dist/CUPS.d.ts +12 -7
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +16 -12
- package/dist/Emailer/Sendgrid.d.ts +15 -15
- package/dist/Emailer/Sendgrid.d.ts.map +1 -1
- package/dist/Emailer/Sendgrid.js +21 -17
- package/dist/Emailer/fake.d.ts +1 -1
- package/dist/Emailer/fake.js +3 -3
- package/dist/Emailer/service.d.ts +13 -4
- package/dist/Emailer/service.d.ts.map +1 -1
- package/dist/Emailer/service.js +4 -3
- package/dist/Emailer.d.ts +1 -1
- package/dist/MainFiberSet.d.ts +12 -9
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +10 -6
- package/dist/Model/Repository/Registry.d.ts +21 -0
- package/dist/Model/Repository/Registry.d.ts.map +1 -0
- package/dist/Model/Repository/Registry.js +18 -0
- package/dist/Model/Repository/ext.d.ts +35 -16
- package/dist/Model/Repository/ext.d.ts.map +1 -1
- package/dist/Model/Repository/ext.js +60 -3
- package/dist/Model/Repository/internal/internal.d.ts +9 -6
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +141 -59
- package/dist/Model/Repository/legacy.d.ts +4 -2
- package/dist/Model/Repository/legacy.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.d.ts +10 -6
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +5 -2
- package/dist/Model/Repository/service.d.ts +32 -24
- package/dist/Model/Repository/service.d.ts.map +1 -1
- package/dist/Model/Repository/validation.d.ts +47 -18
- package/dist/Model/Repository/validation.d.ts.map +1 -1
- package/dist/Model/Repository/validation.js +6 -6
- package/dist/Model/Repository.d.ts +2 -1
- package/dist/Model/Repository.d.ts.map +1 -1
- package/dist/Model/Repository.js +2 -1
- package/dist/Model/dsl.d.ts +6 -5
- package/dist/Model/dsl.d.ts.map +1 -1
- package/dist/Model/dsl.js +2 -3
- package/dist/Model/filter/filterApi.d.ts +5 -5
- package/dist/Model/filter/filterApi.d.ts.map +1 -1
- package/dist/Model/filter/types/errors.d.ts +1 -1
- package/dist/Model/filter/types/fields.d.ts +1 -1
- package/dist/Model/filter/types/path/common.d.ts +1 -1
- package/dist/Model/filter/types/path/eager.d.ts +1 -1
- package/dist/Model/filter/types/path/eager.d.ts.map +1 -1
- package/dist/Model/filter/types/path/eager.js +1 -1
- package/dist/Model/filter/types/path/index.d.ts +1 -1
- package/dist/Model/filter/types/utils.d.ts +1 -1
- package/dist/Model/filter/types/validator.d.ts +1 -1
- package/dist/Model/filter/types.d.ts +1 -1
- package/dist/Model/query/dsl.d.ts +216 -18
- package/dist/Model/query/dsl.d.ts.map +1 -1
- package/dist/Model/query/dsl.js +240 -5
- package/dist/Model/query/new-kid-interpreter.d.ts +116 -8
- package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
- package/dist/Model/query/new-kid-interpreter.js +177 -6
- package/dist/Model/query.d.ts +1 -1
- package/dist/Model.d.ts +2 -1
- package/dist/Model.d.ts.map +1 -1
- package/dist/Model.js +2 -1
- package/dist/QueueMaker/SQLQueue.d.ts +7 -8
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +135 -117
- 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 +9 -5
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +81 -65
- package/dist/QueueMaker/sbqueue.d.ts +8 -4
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +57 -55
- package/dist/QueueMaker/service.d.ts +4 -2
- package/dist/QueueMaker/service.d.ts.map +1 -1
- package/dist/QueueMaker/service.js +1 -1
- package/dist/RequestContext.d.ts +75 -35
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +14 -14
- package/dist/RequestFiberSet.d.ts +10 -7
- package/dist/RequestFiberSet.d.ts.map +1 -1
- package/dist/RequestFiberSet.js +12 -7
- package/dist/Store/ContextMapContainer.d.ts +22 -3
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +18 -4
- package/dist/Store/Cosmos/query.d.ts +13 -2
- 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 +343 -244
- package/dist/Store/Disk.d.ts +3 -3
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +76 -36
- package/dist/Store/Memory.d.ts +7 -4
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +327 -62
- 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 +233 -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 +450 -0
- package/dist/Store/codeFilter.d.ts +2 -2
- package/dist/Store/codeFilter.d.ts.map +1 -1
- package/dist/Store/codeFilter.js +6 -3
- package/dist/Store/index.d.ts +6 -3
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +18 -4
- package/dist/Store/service.d.ts +32 -8
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +25 -6
- package/dist/Store/utils.d.ts +3 -2
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +5 -5
- package/dist/Store.d.ts +1 -1
- package/dist/adapters/SQL/Model.d.ts +32 -43
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +30 -39
- package/dist/adapters/SQL.d.ts +1 -1
- package/dist/adapters/ServiceBus.d.ts +14 -11
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +30 -21
- package/dist/adapters/cosmos-client.d.ts +5 -3
- package/dist/adapters/cosmos-client.d.ts.map +1 -1
- package/dist/adapters/cosmos-client.js +6 -4
- package/dist/adapters/index.d.ts +8 -2
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +8 -2
- package/dist/adapters/logger.d.ts +2 -2
- package/dist/adapters/logger.d.ts.map +1 -1
- package/dist/adapters/memQueue.d.ts +5 -3
- package/dist/adapters/memQueue.d.ts.map +1 -1
- package/dist/adapters/memQueue.js +6 -5
- package/dist/adapters/mongo-client.d.ts +4 -3
- package/dist/adapters/mongo-client.d.ts.map +1 -1
- package/dist/adapters/mongo-client.js +5 -3
- package/dist/adapters/redis-client.d.ts +6 -3
- package/dist/adapters/redis-client.d.ts.map +1 -1
- package/dist/adapters/redis-client.js +7 -3
- package/dist/api/ContextProvider.d.ts +12 -9
- package/dist/api/ContextProvider.d.ts.map +1 -1
- package/dist/api/ContextProvider.js +9 -7
- package/dist/api/codec.d.ts +1 -1
- package/dist/api/internal/RequestContextMiddleware.d.ts +3 -3
- package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
- package/dist/api/internal/RequestContextMiddleware.js +11 -7
- package/dist/api/internal/auth.d.ts +45 -7
- package/dist/api/internal/auth.d.ts.map +1 -1
- package/dist/api/internal/auth.js +162 -29
- package/dist/api/internal/events.d.ts +6 -4
- package/dist/api/internal/events.d.ts.map +1 -1
- package/dist/api/internal/events.js +16 -9
- package/dist/api/internal/health.d.ts +1 -1
- package/dist/api/layerUtils.d.ts +15 -7
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +7 -6
- package/dist/api/middlewares.d.ts +1 -1
- package/dist/api/reportError.d.ts +2 -2
- package/dist/api/reportError.d.ts.map +1 -1
- package/dist/api/reportError.js +3 -2
- package/dist/api/routing/middleware/RouterMiddleware.d.ts +5 -4
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.d.ts +42 -3
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +53 -17
- package/dist/api/routing/middleware.d.ts +1 -2
- package/dist/api/routing/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware.js +1 -2
- package/dist/api/routing/schema/jwt.d.ts +1 -1
- package/dist/api/routing/schema/jwt.d.ts.map +1 -1
- package/dist/api/routing/schema/jwt.js +3 -2
- package/dist/api/routing/tsort.d.ts +1 -1
- package/dist/api/routing/tsort.d.ts.map +1 -1
- package/dist/api/routing/utils.d.ts +4 -4
- package/dist/api/routing/utils.d.ts.map +1 -1
- package/dist/api/routing/utils.js +3 -2
- package/dist/api/routing.d.ts +84 -38
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +115 -45
- package/dist/api/setupRequest.d.ts +10 -6
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +19 -11
- package/dist/api/util.d.ts +1 -1
- package/dist/arbs.d.ts +2 -2
- package/dist/arbs.d.ts.map +1 -1
- package/dist/arbs.js +5 -3
- package/dist/errorReporter.d.ts +7 -5
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +22 -26
- 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 +1 -1
- package/dist/logger/jsonLogger.d.ts +2 -2
- package/dist/logger/jsonLogger.d.ts.map +1 -1
- package/dist/logger/jsonLogger.js +4 -2
- package/dist/logger/logFmtLogger.d.ts +2 -2
- package/dist/logger/logFmtLogger.d.ts.map +1 -1
- package/dist/logger/logFmtLogger.js +2 -2
- package/dist/logger/shared.d.ts +2 -2
- package/dist/logger/shared.d.ts.map +1 -1
- package/dist/logger/shared.js +3 -3
- package/dist/logger.d.ts +1 -1
- package/dist/logger.d.ts.map +1 -1
- 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/test.d.ts +3 -3
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +2 -2
- package/dist/vitest.d.ts +1 -1
- package/examples/query.ts +46 -38
- package/package.json +46 -37
- package/src/CUPS.ts +15 -11
- package/src/Emailer/Sendgrid.ts +65 -57
- package/src/Emailer/fake.ts +2 -2
- package/src/Emailer/service.ts +13 -3
- package/src/MainFiberSet.ts +12 -10
- package/src/Model/Repository/Registry.ts +34 -0
- package/src/Model/Repository/ext.ts +103 -11
- package/src/Model/Repository/internal/internal.ts +266 -151
- package/src/Model/Repository/legacy.ts +3 -1
- package/src/Model/Repository/makeRepo.ts +15 -10
- package/src/Model/Repository/service.ts +35 -23
- package/src/Model/Repository/validation.ts +5 -5
- package/src/Model/Repository.ts +1 -0
- package/src/Model/dsl.ts +5 -4
- package/src/Model/filter/types/path/eager.ts +1 -2
- package/src/Model/query/dsl.ts +456 -20
- package/src/Model/query/new-kid-interpreter.ts +281 -7
- package/src/Model.ts +1 -0
- package/src/QueueMaker/SQLQueue.ts +150 -153
- package/src/QueueMaker/errors.ts +3 -1
- package/src/QueueMaker/memQueue.ts +111 -105
- package/src/QueueMaker/sbqueue.ts +76 -88
- package/src/QueueMaker/service.ts +3 -1
- package/src/RequestContext.ts +15 -16
- package/src/RequestFiberSet.ts +12 -7
- package/src/Store/ContextMapContainer.ts +46 -3
- package/src/Store/Cosmos/query.ts +214 -50
- package/src/Store/Cosmos.ts +491 -350
- package/src/Store/Disk.ts +106 -66
- package/src/Store/Memory.ts +365 -91
- package/src/Store/SQL/Pg.ts +364 -0
- package/src/Store/SQL/query.ts +603 -0
- package/src/Store/SQL.ts +736 -0
- package/src/Store/codeFilter.ts +5 -2
- package/src/Store/index.ts +20 -3
- package/src/Store/service.ts +51 -10
- package/src/Store/utils.ts +25 -23
- package/src/adapters/SQL/Model.ts +42 -41
- package/src/adapters/ServiceBus.ts +131 -121
- package/src/adapters/cosmos-client.ts +5 -3
- package/src/adapters/index.ts +7 -0
- package/src/adapters/memQueue.ts +5 -4
- package/src/adapters/mongo-client.ts +4 -2
- package/src/adapters/redis-client.ts +6 -2
- package/src/api/ContextProvider.ts +19 -16
- package/src/api/internal/RequestContextMiddleware.ts +17 -6
- package/src/api/internal/auth.ts +248 -44
- package/src/api/internal/events.ts +19 -10
- package/src/api/layerUtils.ts +13 -9
- package/src/api/reportError.ts +2 -1
- package/src/api/routing/middleware/RouterMiddleware.ts +5 -4
- package/src/api/routing/middleware/middleware.ts +60 -15
- package/src/api/routing/middleware.ts +0 -2
- package/src/api/routing/schema/jwt.ts +2 -1
- package/src/api/routing/utils.ts +2 -1
- package/src/api/routing.ts +309 -139
- package/src/api/setupRequest.ts +35 -12
- package/src/arbs.ts +5 -3
- package/src/errorReporter.ts +65 -75
- package/src/fileUtil.ts +1 -1
- package/src/logger/jsonLogger.ts +3 -1
- package/src/logger/logFmtLogger.ts +1 -1
- package/src/logger/shared.ts +3 -2
- package/src/otel.ts +152 -0
- package/src/rateLimit.ts +34 -23
- package/src/test.ts +2 -2
- package/test/auth.test.ts +101 -0
- package/test/contextProvider.test.ts +14 -11
- package/test/controller.test.ts +25 -29
- package/test/cosmos-query.test.ts +159 -0
- package/test/dist/auth.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-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/fixtures.ts +16 -9
- package/test/layerUtils.test.ts +1 -1
- package/test/query.test.ts +901 -38
- package/test/rawQuery.test.ts +338 -20
- 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-e2e-invalidation.test.ts +256 -0
- package/test/rpc-multi-middleware.test.ts +84 -9
- package/test/rpc-stream-fullstack.test.ts +304 -0
- package/test/sql-store.test.ts +1711 -0
- package/test/validateSample.test.ts +17 -12
- package/tsconfig.examples.json +1 -1
- package/tsconfig.json +0 -1
- package/tsconfig.json.bak +2 -2
- package/tsconfig.src.json +35 -35
- package/tsconfig.test.json +2 -2
- 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/eslint.config.mjs +0 -24
- package/src/Operations.ts +0 -235
- package/src/OperationsRepo.ts +0 -16
package/src/Store/Memory.ts
CHANGED
|
@@ -1,34 +1,278 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import * as Array from "effect-app/Array"
|
|
4
|
+
import type { NonEmptyReadonlyArray } from "effect-app/Array"
|
|
5
|
+
import * as Context from "effect-app/Context"
|
|
6
|
+
import * as Effect from "effect-app/Effect"
|
|
7
|
+
import * as Option from "effect-app/Option"
|
|
4
8
|
import { NonEmptyString255 } from "effect-app/Schema"
|
|
5
|
-
import {
|
|
9
|
+
import { assertUnreachable } from "effect-app/utils"
|
|
10
|
+
import { flow, pipe } from "effect/Function"
|
|
11
|
+
import * as Order from "effect/Order"
|
|
12
|
+
import * as Ref from "effect/Ref"
|
|
13
|
+
import * as Result from "effect/Result"
|
|
14
|
+
import * as Semaphore from "effect/Semaphore"
|
|
15
|
+
import * as Struct from "effect/Struct"
|
|
6
16
|
import { InfraLogger } from "../logger.js"
|
|
17
|
+
import type { FilterResult } from "../Model/filter/filterApi.js"
|
|
7
18
|
import type { FieldValues } from "../Model/filter/types.js"
|
|
19
|
+
import type { AggregateIrExpression, ComputedProjectionIrExpression, ComputedProjectionMathIrExpression } from "../Model/query.js"
|
|
20
|
+
import { annotateDb } from "../otel.js"
|
|
8
21
|
import { codeFilter, codeFilter3_ } from "./codeFilter.js"
|
|
9
22
|
import { type FilterArgs, type PersistenceModelType, type Store, type StoreConfig, StoreMaker } from "./service.js"
|
|
10
23
|
import { makeUpdateETag } from "./utils.js"
|
|
11
24
|
|
|
25
|
+
/** Traverse an object by a dot-separated path string, e.g. `"a.b.c"`. */
|
|
26
|
+
export function get(obj: any, path: string): any {
|
|
27
|
+
return path.split(".").reduce((res: any, key: string) => (res != null ? res[key] : res), obj)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const stripRelationFilterPaths = (state: readonly FilterResult[], relationPath: string): readonly FilterResult[] => {
|
|
31
|
+
const prefix = `${relationPath}.-1.`
|
|
32
|
+
return state.map((entry) =>
|
|
33
|
+
"path" in entry
|
|
34
|
+
? {
|
|
35
|
+
...entry,
|
|
36
|
+
path: entry.path.startsWith(prefix) ? entry.path.slice(prefix.length) : entry.path
|
|
37
|
+
}
|
|
38
|
+
: {
|
|
39
|
+
...entry,
|
|
40
|
+
result: stripRelationFilterPaths(entry.result, relationPath)
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const emptyValueFor = (tag: ComputedProjectionIrExpression["_tag"]) => {
|
|
46
|
+
switch (tag) {
|
|
47
|
+
case "relation-count":
|
|
48
|
+
case "relation-distinct-count":
|
|
49
|
+
case "relation-sum":
|
|
50
|
+
case "relation-sum-expr":
|
|
51
|
+
case "relation-sum-expr-normalized":
|
|
52
|
+
return 0
|
|
53
|
+
case "relation-sum-expr-by":
|
|
54
|
+
return [] as unknown[]
|
|
55
|
+
case "relation-any":
|
|
56
|
+
return false
|
|
57
|
+
case "relation-every":
|
|
58
|
+
return true
|
|
59
|
+
case "relation-collect":
|
|
60
|
+
return [] as unknown[]
|
|
61
|
+
case "relation-collect-fields":
|
|
62
|
+
return [] as unknown[]
|
|
63
|
+
case "relation-length":
|
|
64
|
+
return 0
|
|
65
|
+
default:
|
|
66
|
+
return assertUnreachable(tag)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const computeProjectionValue = (
|
|
71
|
+
row: FieldValues,
|
|
72
|
+
computed: ComputedProjectionIrExpression
|
|
73
|
+
) => {
|
|
74
|
+
const relation = get(row, computed.path)
|
|
75
|
+
if (!Array.isArray(relation)) {
|
|
76
|
+
return emptyValueFor(computed._tag)
|
|
77
|
+
}
|
|
78
|
+
if (computed._tag === "relation-length") {
|
|
79
|
+
return relation.length
|
|
80
|
+
}
|
|
81
|
+
const filter = stripRelationFilterPaths(computed.filter, computed.path)
|
|
82
|
+
// empty filter = unconditional match (codeFilter3_ uses eval on a built
|
|
83
|
+
// string and chokes on `( )`, so short-circuit before invoking it).
|
|
84
|
+
const matches = filter.length === 0
|
|
85
|
+
? (_value: unknown) => true
|
|
86
|
+
: (value: unknown) => codeFilter3_(filter, value)
|
|
87
|
+
const evalExpr = (value: unknown, expression: ComputedProjectionMathIrExpression): number => {
|
|
88
|
+
switch (expression._tag) {
|
|
89
|
+
case "field": {
|
|
90
|
+
const v = get(value, expression.field)
|
|
91
|
+
return typeof v === "number" ? v : Number(v) || 0
|
|
92
|
+
}
|
|
93
|
+
case "mul":
|
|
94
|
+
return evalExpr(value, expression.left) * evalExpr(value, expression.right)
|
|
95
|
+
default:
|
|
96
|
+
return assertUnreachable(expression)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
switch (computed._tag) {
|
|
100
|
+
case "relation-count":
|
|
101
|
+
return relation.reduce<number>((acc, value) => matches(value) ? acc + 1 : acc, 0)
|
|
102
|
+
case "relation-any":
|
|
103
|
+
return relation.some(matches)
|
|
104
|
+
case "relation-every":
|
|
105
|
+
return relation.every(matches)
|
|
106
|
+
case "relation-distinct-count": {
|
|
107
|
+
const seen = new Set<unknown>()
|
|
108
|
+
for (const value of relation) {
|
|
109
|
+
if (matches(value)) seen.add(get(value, computed.field))
|
|
110
|
+
}
|
|
111
|
+
return seen.size
|
|
112
|
+
}
|
|
113
|
+
case "relation-sum":
|
|
114
|
+
return relation.reduce<number>((acc, value) => {
|
|
115
|
+
if (!matches(value)) return acc
|
|
116
|
+
const v = get(value, computed.field)
|
|
117
|
+
return acc + (typeof v === "number" ? v : Number(v) || 0)
|
|
118
|
+
}, 0)
|
|
119
|
+
case "relation-sum-expr":
|
|
120
|
+
return relation.reduce<number>((acc, value) => {
|
|
121
|
+
if (!matches(value)) return acc
|
|
122
|
+
return acc + evalExpr(value, computed.expression)
|
|
123
|
+
}, 0)
|
|
124
|
+
case "relation-sum-expr-by": {
|
|
125
|
+
const totals = new Map<unknown, number>()
|
|
126
|
+
for (const value of relation) {
|
|
127
|
+
if (!matches(value)) continue
|
|
128
|
+
const unit = get(value, computed.unit)
|
|
129
|
+
const current = totals.get(unit) ?? 0
|
|
130
|
+
totals.set(unit, current + evalExpr(value, computed.expression))
|
|
131
|
+
}
|
|
132
|
+
return [...totals.entries()].map(([unit, total]) => ({ unit, total }))
|
|
133
|
+
}
|
|
134
|
+
case "relation-sum-expr-normalized":
|
|
135
|
+
return relation.reduce<number>((acc, value) => {
|
|
136
|
+
if (!matches(value)) return acc
|
|
137
|
+
const unit = get(value, computed.unit)
|
|
138
|
+
const factor = unit === computed.toBase ? 1 : computed.factors[String(unit)]
|
|
139
|
+
if (factor === undefined || !Number.isFinite(factor)) return acc
|
|
140
|
+
return acc + evalExpr(value, computed.expression) * factor
|
|
141
|
+
}, 0)
|
|
142
|
+
case "relation-collect": {
|
|
143
|
+
const out: unknown[] = []
|
|
144
|
+
const seen = computed.distinct ? new Set<unknown>() : undefined
|
|
145
|
+
for (const value of relation) {
|
|
146
|
+
if (!matches(value)) continue
|
|
147
|
+
const v = get(value, computed.field)
|
|
148
|
+
if (seen) {
|
|
149
|
+
if (seen.has(v)) continue
|
|
150
|
+
seen.add(v)
|
|
151
|
+
}
|
|
152
|
+
out.push(v)
|
|
153
|
+
}
|
|
154
|
+
return out
|
|
155
|
+
}
|
|
156
|
+
case "relation-collect-fields": {
|
|
157
|
+
const out: unknown[] = []
|
|
158
|
+
const seen = computed.distinct ? new Set<unknown>() : undefined
|
|
159
|
+
for (const value of relation) {
|
|
160
|
+
if (!matches(value)) continue
|
|
161
|
+
for (const field of computed.fields) {
|
|
162
|
+
const v = get(value, field)
|
|
163
|
+
if (seen) {
|
|
164
|
+
if (seen.has(v)) continue
|
|
165
|
+
seen.add(v)
|
|
166
|
+
}
|
|
167
|
+
out.push(v)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return out
|
|
171
|
+
}
|
|
172
|
+
default:
|
|
173
|
+
return assertUnreachable(computed)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const computeAggregateValue = <T extends FieldValues>(rows: readonly T[], agg: AggregateIrExpression): unknown => {
|
|
178
|
+
switch (agg._tag) {
|
|
179
|
+
case "agg-count":
|
|
180
|
+
return rows.length
|
|
181
|
+
case "agg-count-when": {
|
|
182
|
+
const filter = agg.filter
|
|
183
|
+
const matches = filter.length === 0 ? () => true : (row: unknown) => codeFilter3_(filter, row)
|
|
184
|
+
return rows.filter((row) => matches(row)).length
|
|
185
|
+
}
|
|
186
|
+
case "agg-sum":
|
|
187
|
+
return rows.reduce<number>((acc, row) => {
|
|
188
|
+
const v = get(row, agg.field)
|
|
189
|
+
return acc + (typeof v === "number" ? v : Number(v) || 0)
|
|
190
|
+
}, 0)
|
|
191
|
+
case "agg-min": {
|
|
192
|
+
let min: unknown = undefined
|
|
193
|
+
for (const row of rows) {
|
|
194
|
+
const v = get(row, agg.field)
|
|
195
|
+
if (v == null) continue
|
|
196
|
+
if (min === undefined || v < (min as any)) min = v
|
|
197
|
+
}
|
|
198
|
+
return min ?? null
|
|
199
|
+
}
|
|
200
|
+
case "agg-max": {
|
|
201
|
+
let max: unknown = undefined
|
|
202
|
+
for (const row of rows) {
|
|
203
|
+
const v = get(row, agg.field)
|
|
204
|
+
if (v == null) continue
|
|
205
|
+
if (max === undefined || v > (max as any)) max = v
|
|
206
|
+
}
|
|
207
|
+
return max ?? null
|
|
208
|
+
}
|
|
209
|
+
default:
|
|
210
|
+
return assertUnreachable(agg)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
12
214
|
export function memFilter<T extends FieldValues, U extends keyof T = never>(f: FilterArgs<T, U>) {
|
|
13
215
|
type M = U extends undefined ? T : Pick<T, U>
|
|
14
216
|
return ((c: T[]): M[] => {
|
|
15
|
-
const
|
|
16
|
-
|
|
217
|
+
const sel = f.select
|
|
218
|
+
|
|
219
|
+
const selectPerRow = (r: T[]): M[] => {
|
|
17
220
|
if (!sel) return r as M[]
|
|
221
|
+
|
|
222
|
+
// Detect aggregate mode: any select item has `aggregate` key
|
|
223
|
+
const hasAggregates = sel.some((s) => typeof s === "object" && s !== null && "aggregate" in s)
|
|
224
|
+
if (hasAggregates) {
|
|
225
|
+
// GROUP BY + aggregate
|
|
226
|
+
const fieldItems = sel.filter((s): s is { key: string; path: string } =>
|
|
227
|
+
typeof s === "object" && s !== null && "path" in s && !("aggregate" in s)
|
|
228
|
+
)
|
|
229
|
+
const aggregateItems = sel.filter((s): s is { key: string; aggregate: AggregateIrExpression } =>
|
|
230
|
+
typeof s === "object" && s !== null && "aggregate" in s
|
|
231
|
+
)
|
|
232
|
+
const groups = new Map<string, T[]>()
|
|
233
|
+
for (const row of r) {
|
|
234
|
+
const key = fieldItems.map((fi) => JSON.stringify(get(row, fi.path))).join("\0")
|
|
235
|
+
const existing = groups.get(key) ?? []
|
|
236
|
+
existing.push(row)
|
|
237
|
+
groups.set(key, existing)
|
|
238
|
+
}
|
|
239
|
+
return [...groups.values()].map((rows) => {
|
|
240
|
+
const result: Record<string, unknown> = {}
|
|
241
|
+
for (const fi of fieldItems) result[fi.key] = get(rows[0]!, fi.path)
|
|
242
|
+
for (const ai of aggregateItems) result[ai.key] = computeAggregateValue(rows, ai.aggregate)
|
|
243
|
+
return result as M
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
|
|
18
247
|
return r.map((i) => {
|
|
19
|
-
const [keys,
|
|
248
|
+
const [keys, entries] = pipe(
|
|
20
249
|
sel,
|
|
21
|
-
Array.partition((
|
|
22
|
-
|
|
23
|
-
|
|
250
|
+
Array.partition((entry) => typeof entry === "string" ? Result.fail(String(entry)) : Result.succeed(entry))
|
|
251
|
+
)
|
|
252
|
+
const subKeys = entries.filter((entry): entry is { key: string; subKeys: readonly string[] } =>
|
|
253
|
+
typeof entry === "object" && entry !== null && "subKeys" in entry
|
|
254
|
+
)
|
|
255
|
+
const computedKeys = entries.filter((entry): entry is {
|
|
256
|
+
key: string
|
|
257
|
+
computed: ComputedProjectionIrExpression
|
|
258
|
+
} => typeof entry === "object" && entry !== null && "computed" in entry)
|
|
259
|
+
const pathKeys = entries.filter((entry): entry is { key: string; path: string } =>
|
|
260
|
+
typeof entry === "object" && entry !== null && "path" in entry && !("aggregate" in entry)
|
|
24
261
|
)
|
|
25
262
|
const n = Struct.pick(i, keys)
|
|
26
263
|
subKeys.forEach((subKey) => {
|
|
27
264
|
n[subKey.key] = i[subKey.key]!.map(Struct.pick(subKey.subKeys as never[]))
|
|
28
265
|
})
|
|
266
|
+
computedKeys.forEach((entry) => {
|
|
267
|
+
;(n as Record<string, unknown>)[entry.key] = computeProjectionValue(i, entry.computed)
|
|
268
|
+
})
|
|
269
|
+
pathKeys.forEach((entry) => {
|
|
270
|
+
;(n as Record<string, unknown>)[entry.key] = get(i, entry.path)
|
|
271
|
+
})
|
|
29
272
|
return n as M
|
|
30
|
-
})
|
|
273
|
+
})
|
|
31
274
|
}
|
|
275
|
+
|
|
32
276
|
const skip = f?.skip
|
|
33
277
|
const limit = f?.limit
|
|
34
278
|
const ords = Option.map(Option.fromNullishOr(f.order), (_) =>
|
|
@@ -50,7 +294,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
50
294
|
c = Array.sortBy(...ords.value)(c)
|
|
51
295
|
}
|
|
52
296
|
if (!skip && limit === 1) {
|
|
53
|
-
return
|
|
297
|
+
return selectPerRow(
|
|
54
298
|
Array.findFirst(c, f.filter ? codeFilter(f.filter) : (_) => Option.some(_)).pipe(
|
|
55
299
|
Option.map(Array.make),
|
|
56
300
|
Option.getOrElse(
|
|
@@ -67,12 +311,12 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
67
311
|
r = Array.take(r, limit)
|
|
68
312
|
}
|
|
69
313
|
|
|
70
|
-
return
|
|
314
|
+
return selectPerRow(r)
|
|
71
315
|
})
|
|
72
316
|
}
|
|
73
317
|
|
|
74
318
|
const defaultNs: NonEmptyString255 = NonEmptyString255("primary")
|
|
75
|
-
export class storeId extends
|
|
319
|
+
export class storeId extends Context.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs }) {}
|
|
76
320
|
|
|
77
321
|
function logQuery(f: FilterArgs<any, any>, defaultValues?: any) {
|
|
78
322
|
return InfraLogger
|
|
@@ -151,43 +395,55 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
151
395
|
withPermit
|
|
152
396
|
)
|
|
153
397
|
const s: Store<IdKey, Encoded> = {
|
|
398
|
+
seedNamespace: () => Effect.void,
|
|
399
|
+
|
|
154
400
|
queryRaw: (query) =>
|
|
155
401
|
all
|
|
156
402
|
.pipe(
|
|
157
403
|
// Effect.tap(() => logQuery(query, defaultValues)),
|
|
158
404
|
Effect.map(query.memory),
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
405
|
+
annotateDb({
|
|
406
|
+
operation: "queryRaw",
|
|
407
|
+
system: "memory",
|
|
408
|
+
collection: modelName,
|
|
409
|
+
namespace,
|
|
410
|
+
entity: modelName
|
|
411
|
+
})
|
|
162
412
|
),
|
|
163
413
|
|
|
164
|
-
all: all.pipe(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
414
|
+
all: all.pipe(annotateDb({
|
|
415
|
+
operation: "all",
|
|
416
|
+
system: "memory",
|
|
417
|
+
collection: modelName,
|
|
418
|
+
namespace,
|
|
419
|
+
entity: modelName
|
|
420
|
+
})),
|
|
170
421
|
find: (id) =>
|
|
171
422
|
Ref
|
|
172
423
|
.get(store)
|
|
173
424
|
.pipe(
|
|
174
425
|
Effect.map((_) => Option.fromNullishOr(_.get(id))),
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
426
|
+
annotateDb({
|
|
427
|
+
operation: "find",
|
|
428
|
+
system: "memory",
|
|
429
|
+
collection: modelName,
|
|
430
|
+
namespace,
|
|
431
|
+
entity: modelName,
|
|
432
|
+
extra: { "app.entity.id": id as unknown }
|
|
433
|
+
})
|
|
182
434
|
),
|
|
183
435
|
filter: (f) =>
|
|
184
436
|
all
|
|
185
437
|
.pipe(
|
|
186
438
|
Effect.tap(() => logQuery(f, defaultValues)),
|
|
187
439
|
Effect.map(memFilter(f)),
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
440
|
+
annotateDb({
|
|
441
|
+
operation: "filter",
|
|
442
|
+
system: "memory",
|
|
443
|
+
collection: modelName,
|
|
444
|
+
namespace,
|
|
445
|
+
entity: modelName
|
|
446
|
+
})
|
|
191
447
|
),
|
|
192
448
|
set: (e) =>
|
|
193
449
|
s
|
|
@@ -202,10 +458,14 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
202
458
|
)
|
|
203
459
|
),
|
|
204
460
|
withPermit,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
461
|
+
annotateDb({
|
|
462
|
+
operation: "set",
|
|
463
|
+
system: "memory",
|
|
464
|
+
collection: modelName,
|
|
465
|
+
namespace,
|
|
466
|
+
entity: modelName,
|
|
467
|
+
extra: { "app.entity.id": e[idKey] as unknown }
|
|
468
|
+
})
|
|
209
469
|
),
|
|
210
470
|
batchRemove: (items: NonEmptyReadonlyArray<Encoded[IdKey]>) =>
|
|
211
471
|
pipe(
|
|
@@ -216,10 +476,13 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
216
476
|
Effect.filterOrFail((_) => _.length <= 100, () => "BatchRemove: a batch may not exceed 100 items"),
|
|
217
477
|
Effect.orDie,
|
|
218
478
|
Effect.andThen(batchRemove),
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
479
|
+
annotateDb({
|
|
480
|
+
operation: "batchRemove",
|
|
481
|
+
system: "memory",
|
|
482
|
+
collection: modelName,
|
|
483
|
+
namespace,
|
|
484
|
+
entity: modelName
|
|
485
|
+
})
|
|
223
486
|
)
|
|
224
487
|
),
|
|
225
488
|
batchSet: (items: readonly [PM, ...PM[]]) =>
|
|
@@ -231,18 +494,25 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
231
494
|
Effect.filterOrFail((_) => _.length <= 100, () => "BatchSet: a batch may not exceed 100 items"),
|
|
232
495
|
Effect.orDie,
|
|
233
496
|
Effect.andThen(batchSet),
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
497
|
+
annotateDb({
|
|
498
|
+
operation: "batchSet",
|
|
499
|
+
system: "memory",
|
|
500
|
+
collection: modelName,
|
|
501
|
+
namespace,
|
|
502
|
+
entity: modelName
|
|
503
|
+
})
|
|
238
504
|
)
|
|
239
505
|
),
|
|
240
506
|
bulkSet: flow(
|
|
241
507
|
batchSet,
|
|
242
508
|
(_) =>
|
|
243
|
-
_.pipe(
|
|
244
|
-
|
|
245
|
-
|
|
509
|
+
_.pipe(annotateDb({
|
|
510
|
+
operation: "bulkSet",
|
|
511
|
+
system: "memory",
|
|
512
|
+
collection: modelName,
|
|
513
|
+
namespace,
|
|
514
|
+
entity: modelName
|
|
515
|
+
}))
|
|
246
516
|
)
|
|
247
517
|
}
|
|
248
518
|
return s
|
|
@@ -250,56 +520,60 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
250
520
|
}
|
|
251
521
|
|
|
252
522
|
export const makeMemoryStore = () => ({
|
|
253
|
-
make:
|
|
523
|
+
make: Effect.fnUntraced(function*<IdKey extends keyof Encoded, Encoded extends FieldValues, R, E>(
|
|
254
524
|
modelName: string,
|
|
255
525
|
idKey: IdKey,
|
|
256
526
|
seed?: Effect.Effect<Iterable<Encoded>, E, R>,
|
|
257
527
|
config?: StoreConfig<Encoded>
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (store) {
|
|
275
|
-
return Effect.succeed(store)
|
|
276
|
-
}
|
|
277
|
-
if (!config.allowNamespace!(namespace)) {
|
|
278
|
-
throw new Error(`Namespace ${namespace} not allowed!`)
|
|
279
|
-
}
|
|
280
|
-
return storesSem.withPermits(1)(Effect.suspend(() => {
|
|
281
|
-
const store = stores.get(namespace)
|
|
282
|
-
if (store) return Effect.sync(() => store)
|
|
283
|
-
return makeMemoryStoreInt(modelName, idKey, namespace, seed, config?.defaultValues)
|
|
284
|
-
.pipe(
|
|
285
|
-
Effect.orDie,
|
|
286
|
-
Effect.provide(ctx),
|
|
287
|
-
Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
|
|
288
|
-
)
|
|
289
|
-
}))
|
|
290
|
-
}))
|
|
291
|
-
const s: Store<IdKey, Encoded> = {
|
|
292
|
-
all: Effect.flatMap(getStore, (_) => _.all),
|
|
293
|
-
queryRaw: (...args) => Effect.flatMap(getStore, (_) => _.queryRaw(...args)),
|
|
294
|
-
find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
|
|
295
|
-
filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
|
|
296
|
-
set: (...args) => Effect.flatMap(getStore, (_) => _.set(...args)),
|
|
297
|
-
batchSet: (...args) => Effect.flatMap(getStore, (_) => _.batchSet(...args)),
|
|
298
|
-
bulkSet: (...args) => Effect.flatMap(getStore, (_) => _.bulkSet(...args)),
|
|
299
|
-
batchRemove: (...args) => Effect.flatMap(getStore, (_) => _.batchRemove(...args))
|
|
528
|
+
) {
|
|
529
|
+
const primary = yield* makeMemoryStoreInt<IdKey, Encoded, R, E>(
|
|
530
|
+
modelName,
|
|
531
|
+
idKey,
|
|
532
|
+
"primary",
|
|
533
|
+
seed,
|
|
534
|
+
config?.defaultValues
|
|
535
|
+
)
|
|
536
|
+
const ctx = yield* Effect.context<R>()
|
|
537
|
+
const stores = new Map([["primary", primary]])
|
|
538
|
+
const semaphores = new Map<string, Semaphore.Semaphore>()
|
|
539
|
+
const getSem = (ns: string) => {
|
|
540
|
+
let sem = semaphores.get(ns)
|
|
541
|
+
if (!sem) {
|
|
542
|
+
sem = Semaphore.makeUnsafe(1)
|
|
543
|
+
semaphores.set(ns, sem)
|
|
300
544
|
}
|
|
301
|
-
return
|
|
302
|
-
}
|
|
545
|
+
return sem
|
|
546
|
+
}
|
|
547
|
+
const ensureStore = (namespace: string) =>
|
|
548
|
+
getSem(namespace).withPermits(1)(Effect.suspend(() => {
|
|
549
|
+
const store = stores.get(namespace)
|
|
550
|
+
if (store) return Effect.succeed(store)
|
|
551
|
+
if (config?.allowNamespace && !config.allowNamespace(namespace)) {
|
|
552
|
+
throw new Error(`Namespace ${namespace} not allowed!`)
|
|
553
|
+
}
|
|
554
|
+
return makeMemoryStoreInt(modelName, idKey, namespace, seed, config?.defaultValues)
|
|
555
|
+
.pipe(
|
|
556
|
+
Effect.orDie,
|
|
557
|
+
Effect.provide(ctx),
|
|
558
|
+
Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
|
|
559
|
+
)
|
|
560
|
+
}))
|
|
561
|
+
const getStore = !config?.allowNamespace
|
|
562
|
+
? Effect.succeed(primary)
|
|
563
|
+
: storeId.pipe(Effect.flatMap((namespace) => ensureStore(namespace)))
|
|
564
|
+
const s: Store<IdKey, Encoded> = {
|
|
565
|
+
seedNamespace: (namespace) => ensureStore(namespace).pipe(Effect.asVoid),
|
|
566
|
+
all: Effect.flatMap(getStore, (_) => _.all),
|
|
567
|
+
queryRaw: (...args) => Effect.flatMap(getStore, (_) => _.queryRaw(...args)),
|
|
568
|
+
find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
|
|
569
|
+
filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
|
|
570
|
+
set: (...args) => Effect.flatMap(getStore, (_) => _.set(...args)),
|
|
571
|
+
batchSet: (...args) => Effect.flatMap(getStore, (_) => _.batchSet(...args)),
|
|
572
|
+
bulkSet: (...args) => Effect.flatMap(getStore, (_) => _.bulkSet(...args)),
|
|
573
|
+
batchRemove: (...args) => Effect.flatMap(getStore, (_) => _.batchRemove(...args))
|
|
574
|
+
}
|
|
575
|
+
return s
|
|
576
|
+
})
|
|
303
577
|
})
|
|
304
578
|
|
|
305
579
|
export const MemoryStoreLive = StoreMaker.toLayer(Effect.sync(() => makeMemoryStore()))
|