@effect-app/infra 4.0.0-beta.22 → 4.0.0-beta.221
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 +1648 -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 +20 -16
- package/dist/Emailer/fake.d.ts +1 -1
- package/dist/Emailer/fake.js +2 -2
- 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 +7 -3
- 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 +115 -51
- 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 +142 -17
- package/dist/Model/query/dsl.d.ts.map +1 -1
- package/dist/Model/query/dsl.js +190 -5
- package/dist/Model/query/new-kid-interpreter.d.ts +77 -8
- package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
- package/dist/Model/query/new-kid-interpreter.js +127 -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 +8 -3
- package/dist/Store/ContextMapContainer.d.ts +22 -3
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +17 -3
- package/dist/Store/Cosmos/query.d.ts +7 -2
- package/dist/Store/Cosmos/query.d.ts.map +1 -1
- package/dist/Store/Cosmos/query.js +115 -35
- 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 +251 -58
- 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 +43 -0
- package/dist/Store/SQL/query.d.ts.map +1 -0
- package/dist/Store/SQL/query.js +478 -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 +26 -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 +5 -3
- 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 -8
- 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 +10 -6
- 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 +10 -6
- 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 -37
- 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 +15 -7
- 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 +21 -15
- package/src/Emailer/fake.ts +1 -1
- package/src/Emailer/service.ts +13 -3
- package/src/MainFiberSet.ts +9 -6
- package/src/Model/Repository/Registry.ts +34 -0
- package/src/Model/Repository/ext.ts +103 -11
- package/src/Model/Repository/internal/internal.ts +231 -149
- 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 +353 -19
- package/src/Model/query/new-kid-interpreter.ts +211 -6
- 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 +8 -2
- package/src/Store/ContextMapContainer.ts +45 -2
- package/src/Store/Cosmos/query.ts +143 -44
- package/src/Store/Cosmos.ts +491 -350
- package/src/Store/Disk.ts +106 -66
- package/src/Store/Memory.ts +285 -87
- package/src/Store/SQL/Pg.ts +364 -0
- package/src/Store/SQL/query.ts +540 -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 +45 -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 +4 -2
- 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 +17 -13
- package/src/api/internal/RequestContextMiddleware.ts +16 -5
- package/src/api/internal/auth.ts +248 -44
- package/src/api/internal/events.ts +19 -10
- package/src/api/layerUtils.ts +12 -8
- 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 +304 -131
- package/src/api/setupRequest.ts +31 -8
- 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/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/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 +819 -38
- package/test/rawQuery.test.ts +312 -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 +1592 -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,14 +1,174 @@
|
|
|
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 { 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
|
+
default:
|
|
64
|
+
return assertUnreachable(tag)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const computeProjectionValue = (
|
|
69
|
+
row: FieldValues,
|
|
70
|
+
computed: ComputedProjectionIrExpression
|
|
71
|
+
) => {
|
|
72
|
+
const relation = get(row, computed.path)
|
|
73
|
+
if (!Array.isArray(relation)) {
|
|
74
|
+
return emptyValueFor(computed._tag)
|
|
75
|
+
}
|
|
76
|
+
const filter = stripRelationFilterPaths(computed.filter, computed.path)
|
|
77
|
+
// empty filter = unconditional match (codeFilter3_ uses eval on a built
|
|
78
|
+
// string and chokes on `( )`, so short-circuit before invoking it).
|
|
79
|
+
const matches = filter.length === 0
|
|
80
|
+
? (_value: unknown) => true
|
|
81
|
+
: (value: unknown) => codeFilter3_(filter, value)
|
|
82
|
+
const evalExpr = (value: unknown, expression: ComputedProjectionMathIrExpression): number => {
|
|
83
|
+
switch (expression._tag) {
|
|
84
|
+
case "field": {
|
|
85
|
+
const v = get(value, expression.field)
|
|
86
|
+
return typeof v === "number" ? v : Number(v) || 0
|
|
87
|
+
}
|
|
88
|
+
case "mul":
|
|
89
|
+
return evalExpr(value, expression.left) * evalExpr(value, expression.right)
|
|
90
|
+
default:
|
|
91
|
+
return assertUnreachable(expression)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
switch (computed._tag) {
|
|
95
|
+
case "relation-count":
|
|
96
|
+
return relation.reduce<number>((acc, value) => matches(value) ? acc + 1 : acc, 0)
|
|
97
|
+
case "relation-any":
|
|
98
|
+
return relation.some(matches)
|
|
99
|
+
case "relation-every":
|
|
100
|
+
return relation.every(matches)
|
|
101
|
+
case "relation-distinct-count": {
|
|
102
|
+
const seen = new Set<unknown>()
|
|
103
|
+
for (const value of relation) {
|
|
104
|
+
if (matches(value)) seen.add(get(value, computed.field))
|
|
105
|
+
}
|
|
106
|
+
return seen.size
|
|
107
|
+
}
|
|
108
|
+
case "relation-sum":
|
|
109
|
+
return relation.reduce<number>((acc, value) => {
|
|
110
|
+
if (!matches(value)) return acc
|
|
111
|
+
const v = get(value, computed.field)
|
|
112
|
+
return acc + (typeof v === "number" ? v : Number(v) || 0)
|
|
113
|
+
}, 0)
|
|
114
|
+
case "relation-sum-expr":
|
|
115
|
+
return relation.reduce<number>((acc, value) => {
|
|
116
|
+
if (!matches(value)) return acc
|
|
117
|
+
return acc + evalExpr(value, computed.expression)
|
|
118
|
+
}, 0)
|
|
119
|
+
case "relation-sum-expr-by": {
|
|
120
|
+
const totals = new Map<unknown, number>()
|
|
121
|
+
for (const value of relation) {
|
|
122
|
+
if (!matches(value)) continue
|
|
123
|
+
const unit = get(value, computed.unit)
|
|
124
|
+
const current = totals.get(unit) ?? 0
|
|
125
|
+
totals.set(unit, current + evalExpr(value, computed.expression))
|
|
126
|
+
}
|
|
127
|
+
return [...totals.entries()].map(([unit, total]) => ({ unit, total }))
|
|
128
|
+
}
|
|
129
|
+
case "relation-sum-expr-normalized":
|
|
130
|
+
return relation.reduce<number>((acc, value) => {
|
|
131
|
+
if (!matches(value)) return acc
|
|
132
|
+
const unit = get(value, computed.unit)
|
|
133
|
+
const factor = unit === computed.toBase ? 1 : computed.factors[String(unit)]
|
|
134
|
+
if (factor === undefined || !Number.isFinite(factor)) return acc
|
|
135
|
+
return acc + evalExpr(value, computed.expression) * factor
|
|
136
|
+
}, 0)
|
|
137
|
+
case "relation-collect": {
|
|
138
|
+
const out: unknown[] = []
|
|
139
|
+
const seen = computed.distinct ? new Set<unknown>() : undefined
|
|
140
|
+
for (const value of relation) {
|
|
141
|
+
if (!matches(value)) continue
|
|
142
|
+
const v = get(value, computed.field)
|
|
143
|
+
if (seen) {
|
|
144
|
+
if (seen.has(v)) continue
|
|
145
|
+
seen.add(v)
|
|
146
|
+
}
|
|
147
|
+
out.push(v)
|
|
148
|
+
}
|
|
149
|
+
return out
|
|
150
|
+
}
|
|
151
|
+
case "relation-collect-fields": {
|
|
152
|
+
const out: unknown[] = []
|
|
153
|
+
const seen = computed.distinct ? new Set<unknown>() : undefined
|
|
154
|
+
for (const value of relation) {
|
|
155
|
+
if (!matches(value)) continue
|
|
156
|
+
for (const field of computed.fields) {
|
|
157
|
+
const v = get(value, field)
|
|
158
|
+
if (seen) {
|
|
159
|
+
if (seen.has(v)) continue
|
|
160
|
+
seen.add(v)
|
|
161
|
+
}
|
|
162
|
+
out.push(v)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return out
|
|
166
|
+
}
|
|
167
|
+
default:
|
|
168
|
+
return assertUnreachable(computed)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
12
172
|
export function memFilter<T extends FieldValues, U extends keyof T = never>(f: FilterArgs<T, U>) {
|
|
13
173
|
type M = U extends undefined ? T : Pick<T, U>
|
|
14
174
|
return ((c: T[]): M[] => {
|
|
@@ -16,18 +176,26 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
16
176
|
const sel = f.select
|
|
17
177
|
if (!sel) return r as M[]
|
|
18
178
|
return r.map((i) => {
|
|
19
|
-
const [keys,
|
|
179
|
+
const [keys, entries] = pipe(
|
|
20
180
|
sel,
|
|
21
|
-
Array.partition((
|
|
22
|
-
|
|
23
|
-
|
|
181
|
+
Array.partition((entry) => typeof entry === "string" ? Result.fail(String(entry)) : Result.succeed(entry))
|
|
182
|
+
)
|
|
183
|
+
const subKeys = entries.filter((entry): entry is { key: string; subKeys: readonly string[] } =>
|
|
184
|
+
typeof entry === "object" && entry !== null && "subKeys" in entry
|
|
24
185
|
)
|
|
186
|
+
const computedKeys = entries.filter((entry): entry is {
|
|
187
|
+
key: string
|
|
188
|
+
computed: ComputedProjectionIrExpression
|
|
189
|
+
} => typeof entry === "object" && entry !== null && "computed" in entry)
|
|
25
190
|
const n = Struct.pick(i, keys)
|
|
26
191
|
subKeys.forEach((subKey) => {
|
|
27
192
|
n[subKey.key] = i[subKey.key]!.map(Struct.pick(subKey.subKeys as never[]))
|
|
28
193
|
})
|
|
194
|
+
computedKeys.forEach((entry) => {
|
|
195
|
+
;(n as Record<string, unknown>)[entry.key] = computeProjectionValue(i, entry.computed)
|
|
196
|
+
})
|
|
29
197
|
return n as M
|
|
30
|
-
})
|
|
198
|
+
})
|
|
31
199
|
}
|
|
32
200
|
const skip = f?.skip
|
|
33
201
|
const limit = f?.limit
|
|
@@ -72,7 +240,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
72
240
|
}
|
|
73
241
|
|
|
74
242
|
const defaultNs: NonEmptyString255 = NonEmptyString255("primary")
|
|
75
|
-
export class storeId extends
|
|
243
|
+
export class storeId extends Context.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs }) {}
|
|
76
244
|
|
|
77
245
|
function logQuery(f: FilterArgs<any, any>, defaultValues?: any) {
|
|
78
246
|
return InfraLogger
|
|
@@ -151,43 +319,55 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
151
319
|
withPermit
|
|
152
320
|
)
|
|
153
321
|
const s: Store<IdKey, Encoded> = {
|
|
322
|
+
seedNamespace: () => Effect.void,
|
|
323
|
+
|
|
154
324
|
queryRaw: (query) =>
|
|
155
325
|
all
|
|
156
326
|
.pipe(
|
|
157
327
|
// Effect.tap(() => logQuery(query, defaultValues)),
|
|
158
328
|
Effect.map(query.memory),
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
329
|
+
annotateDb({
|
|
330
|
+
operation: "queryRaw",
|
|
331
|
+
system: "memory",
|
|
332
|
+
collection: modelName,
|
|
333
|
+
namespace,
|
|
334
|
+
entity: modelName
|
|
335
|
+
})
|
|
162
336
|
),
|
|
163
337
|
|
|
164
|
-
all: all.pipe(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
338
|
+
all: all.pipe(annotateDb({
|
|
339
|
+
operation: "all",
|
|
340
|
+
system: "memory",
|
|
341
|
+
collection: modelName,
|
|
342
|
+
namespace,
|
|
343
|
+
entity: modelName
|
|
344
|
+
})),
|
|
170
345
|
find: (id) =>
|
|
171
346
|
Ref
|
|
172
347
|
.get(store)
|
|
173
348
|
.pipe(
|
|
174
349
|
Effect.map((_) => Option.fromNullishOr(_.get(id))),
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
350
|
+
annotateDb({
|
|
351
|
+
operation: "find",
|
|
352
|
+
system: "memory",
|
|
353
|
+
collection: modelName,
|
|
354
|
+
namespace,
|
|
355
|
+
entity: modelName,
|
|
356
|
+
extra: { "app.entity.id": id as unknown }
|
|
357
|
+
})
|
|
182
358
|
),
|
|
183
359
|
filter: (f) =>
|
|
184
360
|
all
|
|
185
361
|
.pipe(
|
|
186
362
|
Effect.tap(() => logQuery(f, defaultValues)),
|
|
187
363
|
Effect.map(memFilter(f)),
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
364
|
+
annotateDb({
|
|
365
|
+
operation: "filter",
|
|
366
|
+
system: "memory",
|
|
367
|
+
collection: modelName,
|
|
368
|
+
namespace,
|
|
369
|
+
entity: modelName
|
|
370
|
+
})
|
|
191
371
|
),
|
|
192
372
|
set: (e) =>
|
|
193
373
|
s
|
|
@@ -202,10 +382,14 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
202
382
|
)
|
|
203
383
|
),
|
|
204
384
|
withPermit,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
385
|
+
annotateDb({
|
|
386
|
+
operation: "set",
|
|
387
|
+
system: "memory",
|
|
388
|
+
collection: modelName,
|
|
389
|
+
namespace,
|
|
390
|
+
entity: modelName,
|
|
391
|
+
extra: { "app.entity.id": e[idKey] as unknown }
|
|
392
|
+
})
|
|
209
393
|
),
|
|
210
394
|
batchRemove: (items: NonEmptyReadonlyArray<Encoded[IdKey]>) =>
|
|
211
395
|
pipe(
|
|
@@ -216,10 +400,13 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
216
400
|
Effect.filterOrFail((_) => _.length <= 100, () => "BatchRemove: a batch may not exceed 100 items"),
|
|
217
401
|
Effect.orDie,
|
|
218
402
|
Effect.andThen(batchRemove),
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
403
|
+
annotateDb({
|
|
404
|
+
operation: "batchRemove",
|
|
405
|
+
system: "memory",
|
|
406
|
+
collection: modelName,
|
|
407
|
+
namespace,
|
|
408
|
+
entity: modelName
|
|
409
|
+
})
|
|
223
410
|
)
|
|
224
411
|
),
|
|
225
412
|
batchSet: (items: readonly [PM, ...PM[]]) =>
|
|
@@ -231,18 +418,25 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
231
418
|
Effect.filterOrFail((_) => _.length <= 100, () => "BatchSet: a batch may not exceed 100 items"),
|
|
232
419
|
Effect.orDie,
|
|
233
420
|
Effect.andThen(batchSet),
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
421
|
+
annotateDb({
|
|
422
|
+
operation: "batchSet",
|
|
423
|
+
system: "memory",
|
|
424
|
+
collection: modelName,
|
|
425
|
+
namespace,
|
|
426
|
+
entity: modelName
|
|
427
|
+
})
|
|
238
428
|
)
|
|
239
429
|
),
|
|
240
430
|
bulkSet: flow(
|
|
241
431
|
batchSet,
|
|
242
432
|
(_) =>
|
|
243
|
-
_.pipe(
|
|
244
|
-
|
|
245
|
-
|
|
433
|
+
_.pipe(annotateDb({
|
|
434
|
+
operation: "bulkSet",
|
|
435
|
+
system: "memory",
|
|
436
|
+
collection: modelName,
|
|
437
|
+
namespace,
|
|
438
|
+
entity: modelName
|
|
439
|
+
}))
|
|
246
440
|
)
|
|
247
441
|
}
|
|
248
442
|
return s
|
|
@@ -250,56 +444,60 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
250
444
|
}
|
|
251
445
|
|
|
252
446
|
export const makeMemoryStore = () => ({
|
|
253
|
-
make:
|
|
447
|
+
make: Effect.fnUntraced(function*<IdKey extends keyof Encoded, Encoded extends FieldValues, R, E>(
|
|
254
448
|
modelName: string,
|
|
255
449
|
idKey: IdKey,
|
|
256
450
|
seed?: Effect.Effect<Iterable<Encoded>, E, R>,
|
|
257
451
|
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))
|
|
452
|
+
) {
|
|
453
|
+
const primary = yield* makeMemoryStoreInt<IdKey, Encoded, R, E>(
|
|
454
|
+
modelName,
|
|
455
|
+
idKey,
|
|
456
|
+
"primary",
|
|
457
|
+
seed,
|
|
458
|
+
config?.defaultValues
|
|
459
|
+
)
|
|
460
|
+
const ctx = yield* Effect.context<R>()
|
|
461
|
+
const stores = new Map([["primary", primary]])
|
|
462
|
+
const semaphores = new Map<string, Semaphore.Semaphore>()
|
|
463
|
+
const getSem = (ns: string) => {
|
|
464
|
+
let sem = semaphores.get(ns)
|
|
465
|
+
if (!sem) {
|
|
466
|
+
sem = Semaphore.makeUnsafe(1)
|
|
467
|
+
semaphores.set(ns, sem)
|
|
300
468
|
}
|
|
301
|
-
return
|
|
302
|
-
}
|
|
469
|
+
return sem
|
|
470
|
+
}
|
|
471
|
+
const ensureStore = (namespace: string) =>
|
|
472
|
+
getSem(namespace).withPermits(1)(Effect.suspend(() => {
|
|
473
|
+
const store = stores.get(namespace)
|
|
474
|
+
if (store) return Effect.succeed(store)
|
|
475
|
+
if (config?.allowNamespace && !config.allowNamespace(namespace)) {
|
|
476
|
+
throw new Error(`Namespace ${namespace} not allowed!`)
|
|
477
|
+
}
|
|
478
|
+
return makeMemoryStoreInt(modelName, idKey, namespace, seed, config?.defaultValues)
|
|
479
|
+
.pipe(
|
|
480
|
+
Effect.orDie,
|
|
481
|
+
Effect.provide(ctx),
|
|
482
|
+
Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
|
|
483
|
+
)
|
|
484
|
+
}))
|
|
485
|
+
const getStore = !config?.allowNamespace
|
|
486
|
+
? Effect.succeed(primary)
|
|
487
|
+
: storeId.asEffect().pipe(Effect.flatMap((namespace) => ensureStore(namespace)))
|
|
488
|
+
const s: Store<IdKey, Encoded> = {
|
|
489
|
+
seedNamespace: (namespace) => ensureStore(namespace).pipe(Effect.asVoid),
|
|
490
|
+
all: Effect.flatMap(getStore, (_) => _.all),
|
|
491
|
+
queryRaw: (...args) => Effect.flatMap(getStore, (_) => _.queryRaw(...args)),
|
|
492
|
+
find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
|
|
493
|
+
filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
|
|
494
|
+
set: (...args) => Effect.flatMap(getStore, (_) => _.set(...args)),
|
|
495
|
+
batchSet: (...args) => Effect.flatMap(getStore, (_) => _.batchSet(...args)),
|
|
496
|
+
bulkSet: (...args) => Effect.flatMap(getStore, (_) => _.bulkSet(...args)),
|
|
497
|
+
batchRemove: (...args) => Effect.flatMap(getStore, (_) => _.batchRemove(...args))
|
|
498
|
+
}
|
|
499
|
+
return s
|
|
500
|
+
})
|
|
303
501
|
})
|
|
304
502
|
|
|
305
503
|
export const MemoryStoreLive = StoreMaker.toLayer(Effect.sync(() => makeMemoryStore()))
|