@effect-app/infra 4.0.0-beta.22 → 4.0.0-beta.220
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 +1640 -0
- package/_check.sh +1 -1
- package/dist/CUPS.d.ts +7 -7
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +10 -12
- package/dist/Emailer/Sendgrid.d.ts +14 -14
- package/dist/Emailer/Sendgrid.d.ts.map +1 -1
- package/dist/Emailer/Sendgrid.js +16 -15
- package/dist/Emailer/fake.d.ts +1 -1
- package/dist/Emailer/service.d.ts +10 -4
- package/dist/Emailer/service.d.ts.map +1 -1
- package/dist/Emailer/service.js +3 -3
- package/dist/Emailer.d.ts +1 -1
- package/dist/MainFiberSet.d.ts +9 -9
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +3 -3
- package/dist/Model/Repository/Registry.d.ts +20 -0
- package/dist/Model/Repository/Registry.d.ts.map +1 -0
- package/dist/Model/Repository/Registry.js +17 -0
- package/dist/Model/Repository/ext.d.ts +33 -15
- package/dist/Model/Repository/ext.d.ts.map +1 -1
- package/dist/Model/Repository/ext.js +54 -2
- package/dist/Model/Repository/internal/internal.d.ts +6 -6
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +103 -51
- package/dist/Model/Repository/legacy.d.ts +1 -1
- package/dist/Model/Repository/makeRepo.d.ts +7 -6
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +5 -1
- package/dist/Model/Repository/service.d.ts +28 -23
- package/dist/Model/Repository/service.d.ts.map +1 -1
- package/dist/Model/Repository/validation.d.ts +46 -17
- package/dist/Model/Repository/validation.d.ts.map +1 -1
- package/dist/Model/Repository/validation.js +5 -5
- 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 +4 -4
- package/dist/Model/dsl.d.ts.map +1 -1
- 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 +139 -16
- package/dist/Model/query/dsl.d.ts.map +1 -1
- package/dist/Model/query/dsl.js +187 -1
- package/dist/Model/query/new-kid-interpreter.d.ts +76 -7
- package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
- package/dist/Model/query/new-kid-interpreter.js +122 -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 +5 -7
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +130 -116
- package/dist/QueueMaker/errors.d.ts +2 -2
- package/dist/QueueMaker/errors.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.d.ts +7 -4
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +75 -63
- package/dist/QueueMaker/sbqueue.d.ts +6 -3
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +52 -53
- package/dist/QueueMaker/service.d.ts +1 -1
- package/dist/RequestContext.d.ts +74 -35
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +13 -14
- package/dist/RequestFiberSet.d.ts +7 -7
- package/dist/RequestFiberSet.d.ts.map +1 -1
- package/dist/RequestFiberSet.js +3 -3
- package/dist/Store/ContextMapContainer.d.ts +19 -3
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +13 -3
- package/dist/Store/Cosmos/query.d.ts +5 -1
- package/dist/Store/Cosmos/query.d.ts.map +1 -1
- package/dist/Store/Cosmos/query.js +113 -34
- package/dist/Store/Cosmos.d.ts +1 -1
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +335 -243
- package/dist/Store/Disk.d.ts +2 -2
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +72 -35
- package/dist/Store/Memory.d.ts +6 -4
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +242 -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 +231 -0
- package/dist/Store/SQL/query.d.ts +42 -0
- package/dist/Store/SQL/query.d.ts.map +1 -0
- package/dist/Store/SQL/query.js +479 -0
- package/dist/Store/SQL.d.ts +20 -0
- package/dist/Store/SQL.d.ts.map +1 -0
- package/dist/Store/SQL.js +446 -0
- package/dist/Store/codeFilter.d.ts +1 -1
- package/dist/Store/codeFilter.d.ts.map +1 -1
- package/dist/Store/codeFilter.js +4 -2
- package/dist/Store/index.d.ts +5 -2
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +15 -3
- package/dist/Store/service.d.ts +22 -8
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +24 -6
- package/dist/Store/utils.d.ts +1 -1
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +3 -4
- package/dist/Store.d.ts +1 -1
- package/dist/adapters/SQL/Model.d.ts +31 -42
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +29 -38
- package/dist/adapters/SQL.d.ts +1 -1
- package/dist/adapters/ServiceBus.d.ts +11 -11
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +25 -21
- package/dist/adapters/cosmos-client.d.ts +3 -3
- package/dist/adapters/cosmos-client.d.ts.map +1 -1
- package/dist/adapters/cosmos-client.js +3 -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 +1 -1
- package/dist/adapters/logger.d.ts.map +1 -1
- package/dist/adapters/memQueue.d.ts +3 -3
- package/dist/adapters/memQueue.d.ts.map +1 -1
- package/dist/adapters/memQueue.js +3 -3
- package/dist/adapters/mongo-client.d.ts +3 -3
- package/dist/adapters/mongo-client.d.ts.map +1 -1
- package/dist/adapters/mongo-client.js +3 -3
- package/dist/adapters/redis-client.d.ts +3 -3
- package/dist/adapters/redis-client.d.ts.map +1 -1
- package/dist/adapters/redis-client.js +3 -3
- package/dist/api/ContextProvider.d.ts +8 -8
- package/dist/api/ContextProvider.d.ts.map +1 -1
- package/dist/api/ContextProvider.js +6 -6
- package/dist/api/codec.d.ts +1 -1
- package/dist/api/internal/RequestContextMiddleware.d.ts +2 -2
- package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
- package/dist/api/internal/RequestContextMiddleware.js +9 -6
- package/dist/api/internal/auth.d.ts +44 -6
- package/dist/api/internal/auth.d.ts.map +1 -1
- package/dist/api/internal/auth.js +160 -29
- package/dist/api/internal/events.d.ts +3 -3
- package/dist/api/internal/events.d.ts.map +1 -1
- package/dist/api/internal/events.js +10 -8
- package/dist/api/internal/health.d.ts +1 -1
- package/dist/api/layerUtils.d.ts +6 -6
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +5 -5
- package/dist/api/middlewares.d.ts +1 -1
- package/dist/api/reportError.d.ts +1 -1
- package/dist/api/routing/middleware/RouterMiddleware.d.ts +4 -4
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.d.ts +39 -3
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +48 -16
- 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/tsort.d.ts +1 -1
- package/dist/api/routing/tsort.d.ts.map +1 -1
- package/dist/api/routing/utils.d.ts +3 -3
- package/dist/api/routing/utils.d.ts.map +1 -1
- package/dist/api/routing.d.ts +80 -37
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +109 -41
- package/dist/api/setupRequest.d.ts +8 -5
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +12 -7
- package/dist/api/util.d.ts +1 -1
- package/dist/arbs.d.ts +1 -1
- package/dist/arbs.d.ts.map +1 -1
- package/dist/arbs.js +5 -3
- package/dist/errorReporter.d.ts +4 -4
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +20 -25
- package/dist/errors.d.ts +1 -1
- package/dist/fileUtil.d.ts +1 -1
- package/dist/fileUtil.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/logger/jsonLogger.d.ts +1 -1
- package/dist/logger/logFmtLogger.d.ts +1 -1
- package/dist/logger/shared.d.ts +1 -1
- package/dist/logger/shared.js +2 -2
- 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 +9 -3
- package/dist/rateLimit.d.ts.map +1 -1
- package/dist/rateLimit.js +5 -11
- package/dist/test.d.ts +2 -2
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +1 -1
- package/dist/vitest.d.ts +1 -1
- package/examples/query.ts +42 -38
- package/package.json +46 -37
- package/src/CUPS.ts +9 -11
- package/src/Emailer/Sendgrid.ts +17 -14
- package/src/Emailer/service.ts +9 -3
- package/src/MainFiberSet.ts +5 -6
- package/src/Model/Repository/Registry.ts +33 -0
- package/src/Model/Repository/ext.ts +96 -10
- package/src/Model/Repository/internal/internal.ts +218 -149
- package/src/Model/Repository/makeRepo.ts +12 -10
- package/src/Model/Repository/service.ts +31 -22
- package/src/Model/Repository/validation.ts +4 -4
- package/src/Model/Repository.ts +1 -0
- package/src/Model/dsl.ts +3 -3
- package/src/Model/filter/types/path/eager.ts +1 -2
- package/src/Model/query/dsl.ts +348 -18
- package/src/Model/query/new-kid-interpreter.ts +206 -6
- package/src/Model.ts +1 -0
- package/src/QueueMaker/SQLQueue.ts +144 -152
- package/src/QueueMaker/memQueue.ts +104 -103
- package/src/QueueMaker/sbqueue.ts +70 -86
- package/src/RequestContext.ts +14 -16
- package/src/RequestFiberSet.ts +2 -2
- package/src/Store/ContextMapContainer.ts +41 -2
- package/src/Store/Cosmos/query.ts +140 -43
- package/src/Store/Cosmos.ts +482 -349
- package/src/Store/Disk.ts +102 -65
- package/src/Store/Memory.ts +275 -87
- package/src/Store/SQL/Pg.ts +361 -0
- package/src/Store/SQL/query.ts +539 -0
- package/src/Store/SQL.ts +731 -0
- package/src/Store/codeFilter.ts +3 -1
- package/src/Store/index.ts +17 -2
- package/src/Store/service.ts +41 -10
- package/src/Store/utils.ts +23 -22
- package/src/adapters/SQL/Model.ts +41 -40
- package/src/adapters/ServiceBus.ts +125 -121
- package/src/adapters/cosmos-client.ts +2 -2
- package/src/adapters/index.ts +7 -0
- package/src/adapters/memQueue.ts +2 -2
- package/src/adapters/mongo-client.ts +2 -2
- package/src/adapters/redis-client.ts +2 -2
- package/src/api/ContextProvider.ts +12 -13
- package/src/api/internal/RequestContextMiddleware.ts +15 -5
- package/src/api/internal/auth.ts +246 -44
- package/src/api/internal/events.ts +13 -9
- package/src/api/layerUtils.ts +8 -8
- package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
- package/src/api/routing/middleware/middleware.ts +55 -14
- package/src/api/routing/middleware.ts +0 -2
- package/src/api/routing.ts +296 -131
- package/src/api/setupRequest.ts +28 -8
- package/src/arbs.ts +4 -2
- package/src/errorReporter.ts +62 -74
- package/src/logger/shared.ts +1 -1
- package/src/otel.ts +152 -0
- package/src/rateLimit.ts +30 -22
- package/src/test.ts +1 -1
- package/test/auth.test.ts +101 -0
- package/test/contextProvider.test.ts +11 -11
- package/test/controller.test.ts +21 -30
- 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 +26 -12
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +12 -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 +11 -9
- package/test/query.test.ts +813 -38
- package/test/rawQuery.test.ts +301 -20
- package/test/repository-ext.test.ts +60 -0
- package/test/requires.test.ts +6 -6
- package/test/router-generator.test.ts +183 -0
- package/test/routing-interruptibility.test.ts +63 -0
- package/test/rpc-e2e-invalidation.test.ts +251 -0
- package/test/rpc-multi-middleware.test.ts +78 -9
- package/test/rpc-stream-fullstack.test.ts +300 -0
- package/test/sql-store.test.ts +1592 -0
- package/test/validateSample.test.ts +15 -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,164 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
|
|
3
|
-
import { Array, Effect, flow, type NonEmptyReadonlyArray, Option, Order, pipe, Ref, Result, Semaphore,
|
|
3
|
+
import { Array, Context, Effect, flow, type NonEmptyReadonlyArray, Option, Order, pipe, Ref, Result, Semaphore, Struct } from "effect-app"
|
|
4
4
|
import { NonEmptyString255 } from "effect-app/Schema"
|
|
5
|
-
import {
|
|
5
|
+
import { assertUnreachable } from "effect-app/utils"
|
|
6
6
|
import { InfraLogger } from "../logger.js"
|
|
7
|
+
import type { FilterResult } from "../Model/filter/filterApi.js"
|
|
7
8
|
import type { FieldValues } from "../Model/filter/types.js"
|
|
9
|
+
import type { ComputedProjectionIrExpression, ComputedProjectionMathIrExpression } from "../Model/query.js"
|
|
10
|
+
import { annotateDb } from "../otel.js"
|
|
8
11
|
import { codeFilter, codeFilter3_ } from "./codeFilter.js"
|
|
9
12
|
import { type FilterArgs, type PersistenceModelType, type Store, type StoreConfig, StoreMaker } from "./service.js"
|
|
10
13
|
import { makeUpdateETag } from "./utils.js"
|
|
11
14
|
|
|
15
|
+
/** Traverse an object by a dot-separated path string, e.g. `"a.b.c"`. */
|
|
16
|
+
export function get(obj: any, path: string): any {
|
|
17
|
+
return path.split(".").reduce((res: any, key: string) => (res != null ? res[key] : res), obj)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const stripRelationFilterPaths = (state: readonly FilterResult[], relationPath: string): readonly FilterResult[] => {
|
|
21
|
+
const prefix = `${relationPath}.-1.`
|
|
22
|
+
return state.map((entry) =>
|
|
23
|
+
"path" in entry
|
|
24
|
+
? {
|
|
25
|
+
...entry,
|
|
26
|
+
path: entry.path.startsWith(prefix) ? entry.path.slice(prefix.length) : entry.path
|
|
27
|
+
}
|
|
28
|
+
: {
|
|
29
|
+
...entry,
|
|
30
|
+
result: stripRelationFilterPaths(entry.result, relationPath)
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const emptyValueFor = (tag: ComputedProjectionIrExpression["_tag"]) => {
|
|
36
|
+
switch (tag) {
|
|
37
|
+
case "relation-count":
|
|
38
|
+
case "relation-distinct-count":
|
|
39
|
+
case "relation-sum":
|
|
40
|
+
case "relation-sum-expr":
|
|
41
|
+
case "relation-sum-expr-normalized":
|
|
42
|
+
return 0
|
|
43
|
+
case "relation-sum-expr-by":
|
|
44
|
+
return [] as unknown[]
|
|
45
|
+
case "relation-any":
|
|
46
|
+
return false
|
|
47
|
+
case "relation-every":
|
|
48
|
+
return true
|
|
49
|
+
case "relation-collect":
|
|
50
|
+
return [] as unknown[]
|
|
51
|
+
case "relation-collect-fields":
|
|
52
|
+
return [] as unknown[]
|
|
53
|
+
default:
|
|
54
|
+
return assertUnreachable(tag)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const computeProjectionValue = (
|
|
59
|
+
row: FieldValues,
|
|
60
|
+
computed: ComputedProjectionIrExpression
|
|
61
|
+
) => {
|
|
62
|
+
const relation = get(row, computed.path)
|
|
63
|
+
if (!Array.isArray(relation)) {
|
|
64
|
+
return emptyValueFor(computed._tag)
|
|
65
|
+
}
|
|
66
|
+
const filter = stripRelationFilterPaths(computed.filter, computed.path)
|
|
67
|
+
// empty filter = unconditional match (codeFilter3_ uses eval on a built
|
|
68
|
+
// string and chokes on `( )`, so short-circuit before invoking it).
|
|
69
|
+
const matches = filter.length === 0
|
|
70
|
+
? (_value: unknown) => true
|
|
71
|
+
: (value: unknown) => codeFilter3_(filter, value)
|
|
72
|
+
const evalExpr = (value: unknown, expression: ComputedProjectionMathIrExpression): number => {
|
|
73
|
+
switch (expression._tag) {
|
|
74
|
+
case "field": {
|
|
75
|
+
const v = get(value, expression.field)
|
|
76
|
+
return typeof v === "number" ? v : Number(v) || 0
|
|
77
|
+
}
|
|
78
|
+
case "mul":
|
|
79
|
+
return evalExpr(value, expression.left) * evalExpr(value, expression.right)
|
|
80
|
+
default:
|
|
81
|
+
return assertUnreachable(expression)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
switch (computed._tag) {
|
|
85
|
+
case "relation-count":
|
|
86
|
+
return relation.reduce<number>((acc, value) => matches(value) ? acc + 1 : acc, 0)
|
|
87
|
+
case "relation-any":
|
|
88
|
+
return relation.some(matches)
|
|
89
|
+
case "relation-every":
|
|
90
|
+
return relation.every(matches)
|
|
91
|
+
case "relation-distinct-count": {
|
|
92
|
+
const seen = new Set<unknown>()
|
|
93
|
+
for (const value of relation) {
|
|
94
|
+
if (matches(value)) seen.add(get(value, computed.field))
|
|
95
|
+
}
|
|
96
|
+
return seen.size
|
|
97
|
+
}
|
|
98
|
+
case "relation-sum":
|
|
99
|
+
return relation.reduce<number>((acc, value) => {
|
|
100
|
+
if (!matches(value)) return acc
|
|
101
|
+
const v = get(value, computed.field)
|
|
102
|
+
return acc + (typeof v === "number" ? v : Number(v) || 0)
|
|
103
|
+
}, 0)
|
|
104
|
+
case "relation-sum-expr":
|
|
105
|
+
return relation.reduce<number>((acc, value) => {
|
|
106
|
+
if (!matches(value)) return acc
|
|
107
|
+
return acc + evalExpr(value, computed.expression)
|
|
108
|
+
}, 0)
|
|
109
|
+
case "relation-sum-expr-by": {
|
|
110
|
+
const totals = new Map<unknown, number>()
|
|
111
|
+
for (const value of relation) {
|
|
112
|
+
if (!matches(value)) continue
|
|
113
|
+
const unit = get(value, computed.unit)
|
|
114
|
+
const current = totals.get(unit) ?? 0
|
|
115
|
+
totals.set(unit, current + evalExpr(value, computed.expression))
|
|
116
|
+
}
|
|
117
|
+
return [...totals.entries()].map(([unit, total]) => ({ unit, total }))
|
|
118
|
+
}
|
|
119
|
+
case "relation-sum-expr-normalized":
|
|
120
|
+
return relation.reduce<number>((acc, value) => {
|
|
121
|
+
if (!matches(value)) return acc
|
|
122
|
+
const unit = get(value, computed.unit)
|
|
123
|
+
const factor = unit === computed.toBase ? 1 : computed.factors[String(unit)]
|
|
124
|
+
if (factor === undefined || !Number.isFinite(factor)) return acc
|
|
125
|
+
return acc + evalExpr(value, computed.expression) * factor
|
|
126
|
+
}, 0)
|
|
127
|
+
case "relation-collect": {
|
|
128
|
+
const out: unknown[] = []
|
|
129
|
+
const seen = computed.distinct ? new Set<unknown>() : undefined
|
|
130
|
+
for (const value of relation) {
|
|
131
|
+
if (!matches(value)) continue
|
|
132
|
+
const v = get(value, computed.field)
|
|
133
|
+
if (seen) {
|
|
134
|
+
if (seen.has(v)) continue
|
|
135
|
+
seen.add(v)
|
|
136
|
+
}
|
|
137
|
+
out.push(v)
|
|
138
|
+
}
|
|
139
|
+
return out
|
|
140
|
+
}
|
|
141
|
+
case "relation-collect-fields": {
|
|
142
|
+
const out: unknown[] = []
|
|
143
|
+
const seen = computed.distinct ? new Set<unknown>() : undefined
|
|
144
|
+
for (const value of relation) {
|
|
145
|
+
if (!matches(value)) continue
|
|
146
|
+
for (const field of computed.fields) {
|
|
147
|
+
const v = get(value, field)
|
|
148
|
+
if (seen) {
|
|
149
|
+
if (seen.has(v)) continue
|
|
150
|
+
seen.add(v)
|
|
151
|
+
}
|
|
152
|
+
out.push(v)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return out
|
|
156
|
+
}
|
|
157
|
+
default:
|
|
158
|
+
return assertUnreachable(computed)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
12
162
|
export function memFilter<T extends FieldValues, U extends keyof T = never>(f: FilterArgs<T, U>) {
|
|
13
163
|
type M = U extends undefined ? T : Pick<T, U>
|
|
14
164
|
return ((c: T[]): M[] => {
|
|
@@ -16,18 +166,26 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
16
166
|
const sel = f.select
|
|
17
167
|
if (!sel) return r as M[]
|
|
18
168
|
return r.map((i) => {
|
|
19
|
-
const [keys,
|
|
169
|
+
const [keys, entries] = pipe(
|
|
20
170
|
sel,
|
|
21
|
-
Array.partition((
|
|
22
|
-
|
|
23
|
-
|
|
171
|
+
Array.partition((entry) => typeof entry === "string" ? Result.fail(String(entry)) : Result.succeed(entry))
|
|
172
|
+
)
|
|
173
|
+
const subKeys = entries.filter((entry): entry is { key: string; subKeys: readonly string[] } =>
|
|
174
|
+
typeof entry === "object" && entry !== null && "subKeys" in entry
|
|
24
175
|
)
|
|
176
|
+
const computedKeys = entries.filter((entry): entry is {
|
|
177
|
+
key: string
|
|
178
|
+
computed: ComputedProjectionIrExpression
|
|
179
|
+
} => typeof entry === "object" && entry !== null && "computed" in entry)
|
|
25
180
|
const n = Struct.pick(i, keys)
|
|
26
181
|
subKeys.forEach((subKey) => {
|
|
27
182
|
n[subKey.key] = i[subKey.key]!.map(Struct.pick(subKey.subKeys as never[]))
|
|
28
183
|
})
|
|
184
|
+
computedKeys.forEach((entry) => {
|
|
185
|
+
;(n as Record<string, unknown>)[entry.key] = computeProjectionValue(i, entry.computed)
|
|
186
|
+
})
|
|
29
187
|
return n as M
|
|
30
|
-
})
|
|
188
|
+
})
|
|
31
189
|
}
|
|
32
190
|
const skip = f?.skip
|
|
33
191
|
const limit = f?.limit
|
|
@@ -72,7 +230,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
72
230
|
}
|
|
73
231
|
|
|
74
232
|
const defaultNs: NonEmptyString255 = NonEmptyString255("primary")
|
|
75
|
-
export class storeId extends
|
|
233
|
+
export class storeId extends Context.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs }) {}
|
|
76
234
|
|
|
77
235
|
function logQuery(f: FilterArgs<any, any>, defaultValues?: any) {
|
|
78
236
|
return InfraLogger
|
|
@@ -151,43 +309,55 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
151
309
|
withPermit
|
|
152
310
|
)
|
|
153
311
|
const s: Store<IdKey, Encoded> = {
|
|
312
|
+
seedNamespace: () => Effect.void,
|
|
313
|
+
|
|
154
314
|
queryRaw: (query) =>
|
|
155
315
|
all
|
|
156
316
|
.pipe(
|
|
157
317
|
// Effect.tap(() => logQuery(query, defaultValues)),
|
|
158
318
|
Effect.map(query.memory),
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
319
|
+
annotateDb({
|
|
320
|
+
operation: "queryRaw",
|
|
321
|
+
system: "memory",
|
|
322
|
+
collection: modelName,
|
|
323
|
+
namespace,
|
|
324
|
+
entity: modelName
|
|
325
|
+
})
|
|
162
326
|
),
|
|
163
327
|
|
|
164
|
-
all: all.pipe(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
328
|
+
all: all.pipe(annotateDb({
|
|
329
|
+
operation: "all",
|
|
330
|
+
system: "memory",
|
|
331
|
+
collection: modelName,
|
|
332
|
+
namespace,
|
|
333
|
+
entity: modelName
|
|
334
|
+
})),
|
|
170
335
|
find: (id) =>
|
|
171
336
|
Ref
|
|
172
337
|
.get(store)
|
|
173
338
|
.pipe(
|
|
174
339
|
Effect.map((_) => Option.fromNullishOr(_.get(id))),
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
340
|
+
annotateDb({
|
|
341
|
+
operation: "find",
|
|
342
|
+
system: "memory",
|
|
343
|
+
collection: modelName,
|
|
344
|
+
namespace,
|
|
345
|
+
entity: modelName,
|
|
346
|
+
extra: { "app.entity.id": id as unknown }
|
|
347
|
+
})
|
|
182
348
|
),
|
|
183
349
|
filter: (f) =>
|
|
184
350
|
all
|
|
185
351
|
.pipe(
|
|
186
352
|
Effect.tap(() => logQuery(f, defaultValues)),
|
|
187
353
|
Effect.map(memFilter(f)),
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
354
|
+
annotateDb({
|
|
355
|
+
operation: "filter",
|
|
356
|
+
system: "memory",
|
|
357
|
+
collection: modelName,
|
|
358
|
+
namespace,
|
|
359
|
+
entity: modelName
|
|
360
|
+
})
|
|
191
361
|
),
|
|
192
362
|
set: (e) =>
|
|
193
363
|
s
|
|
@@ -202,10 +372,14 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
202
372
|
)
|
|
203
373
|
),
|
|
204
374
|
withPermit,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
375
|
+
annotateDb({
|
|
376
|
+
operation: "set",
|
|
377
|
+
system: "memory",
|
|
378
|
+
collection: modelName,
|
|
379
|
+
namespace,
|
|
380
|
+
entity: modelName,
|
|
381
|
+
extra: { "app.entity.id": e[idKey] as unknown }
|
|
382
|
+
})
|
|
209
383
|
),
|
|
210
384
|
batchRemove: (items: NonEmptyReadonlyArray<Encoded[IdKey]>) =>
|
|
211
385
|
pipe(
|
|
@@ -216,10 +390,13 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
216
390
|
Effect.filterOrFail((_) => _.length <= 100, () => "BatchRemove: a batch may not exceed 100 items"),
|
|
217
391
|
Effect.orDie,
|
|
218
392
|
Effect.andThen(batchRemove),
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
393
|
+
annotateDb({
|
|
394
|
+
operation: "batchRemove",
|
|
395
|
+
system: "memory",
|
|
396
|
+
collection: modelName,
|
|
397
|
+
namespace,
|
|
398
|
+
entity: modelName
|
|
399
|
+
})
|
|
223
400
|
)
|
|
224
401
|
),
|
|
225
402
|
batchSet: (items: readonly [PM, ...PM[]]) =>
|
|
@@ -231,18 +408,25 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
231
408
|
Effect.filterOrFail((_) => _.length <= 100, () => "BatchSet: a batch may not exceed 100 items"),
|
|
232
409
|
Effect.orDie,
|
|
233
410
|
Effect.andThen(batchSet),
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
411
|
+
annotateDb({
|
|
412
|
+
operation: "batchSet",
|
|
413
|
+
system: "memory",
|
|
414
|
+
collection: modelName,
|
|
415
|
+
namespace,
|
|
416
|
+
entity: modelName
|
|
417
|
+
})
|
|
238
418
|
)
|
|
239
419
|
),
|
|
240
420
|
bulkSet: flow(
|
|
241
421
|
batchSet,
|
|
242
422
|
(_) =>
|
|
243
|
-
_.pipe(
|
|
244
|
-
|
|
245
|
-
|
|
423
|
+
_.pipe(annotateDb({
|
|
424
|
+
operation: "bulkSet",
|
|
425
|
+
system: "memory",
|
|
426
|
+
collection: modelName,
|
|
427
|
+
namespace,
|
|
428
|
+
entity: modelName
|
|
429
|
+
}))
|
|
246
430
|
)
|
|
247
431
|
}
|
|
248
432
|
return s
|
|
@@ -250,56 +434,60 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
250
434
|
}
|
|
251
435
|
|
|
252
436
|
export const makeMemoryStore = () => ({
|
|
253
|
-
make:
|
|
437
|
+
make: Effect.fnUntraced(function*<IdKey extends keyof Encoded, Encoded extends FieldValues, R, E>(
|
|
254
438
|
modelName: string,
|
|
255
439
|
idKey: IdKey,
|
|
256
440
|
seed?: Effect.Effect<Iterable<Encoded>, E, R>,
|
|
257
441
|
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))
|
|
442
|
+
) {
|
|
443
|
+
const primary = yield* makeMemoryStoreInt<IdKey, Encoded, R, E>(
|
|
444
|
+
modelName,
|
|
445
|
+
idKey,
|
|
446
|
+
"primary",
|
|
447
|
+
seed,
|
|
448
|
+
config?.defaultValues
|
|
449
|
+
)
|
|
450
|
+
const ctx = yield* Effect.context<R>()
|
|
451
|
+
const stores = new Map([["primary", primary]])
|
|
452
|
+
const semaphores = new Map<string, Semaphore.Semaphore>()
|
|
453
|
+
const getSem = (ns: string) => {
|
|
454
|
+
let sem = semaphores.get(ns)
|
|
455
|
+
if (!sem) {
|
|
456
|
+
sem = Semaphore.makeUnsafe(1)
|
|
457
|
+
semaphores.set(ns, sem)
|
|
300
458
|
}
|
|
301
|
-
return
|
|
302
|
-
}
|
|
459
|
+
return sem
|
|
460
|
+
}
|
|
461
|
+
const ensureStore = (namespace: string) =>
|
|
462
|
+
getSem(namespace).withPermits(1)(Effect.suspend(() => {
|
|
463
|
+
const store = stores.get(namespace)
|
|
464
|
+
if (store) return Effect.succeed(store)
|
|
465
|
+
if (config?.allowNamespace && !config.allowNamespace(namespace)) {
|
|
466
|
+
throw new Error(`Namespace ${namespace} not allowed!`)
|
|
467
|
+
}
|
|
468
|
+
return makeMemoryStoreInt(modelName, idKey, namespace, seed, config?.defaultValues)
|
|
469
|
+
.pipe(
|
|
470
|
+
Effect.orDie,
|
|
471
|
+
Effect.provide(ctx),
|
|
472
|
+
Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
|
|
473
|
+
)
|
|
474
|
+
}))
|
|
475
|
+
const getStore = !config?.allowNamespace
|
|
476
|
+
? Effect.succeed(primary)
|
|
477
|
+
: storeId.asEffect().pipe(Effect.flatMap((namespace) => ensureStore(namespace)))
|
|
478
|
+
const s: Store<IdKey, Encoded> = {
|
|
479
|
+
seedNamespace: (namespace) => ensureStore(namespace).pipe(Effect.asVoid),
|
|
480
|
+
all: Effect.flatMap(getStore, (_) => _.all),
|
|
481
|
+
queryRaw: (...args) => Effect.flatMap(getStore, (_) => _.queryRaw(...args)),
|
|
482
|
+
find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
|
|
483
|
+
filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
|
|
484
|
+
set: (...args) => Effect.flatMap(getStore, (_) => _.set(...args)),
|
|
485
|
+
batchSet: (...args) => Effect.flatMap(getStore, (_) => _.batchSet(...args)),
|
|
486
|
+
bulkSet: (...args) => Effect.flatMap(getStore, (_) => _.bulkSet(...args)),
|
|
487
|
+
batchRemove: (...args) => Effect.flatMap(getStore, (_) => _.batchRemove(...args))
|
|
488
|
+
}
|
|
489
|
+
return s
|
|
490
|
+
})
|
|
303
491
|
})
|
|
304
492
|
|
|
305
493
|
export const MemoryStoreLive = StoreMaker.toLayer(Effect.sync(() => makeMemoryStore()))
|