@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
3
|
-
import { Array, identity, Match, Option, pipe, S } from "effect-app"
|
|
3
|
+
import { Array, identity, Match, Option, pipe, S, SchemaAST } from "effect-app"
|
|
4
4
|
import { toNonEmptyArray } from "effect-app/Array"
|
|
5
5
|
import { dropUndefinedT } from "effect-app/utils"
|
|
6
6
|
import type { FilterResult } from "../filter/filterApi.js"
|
|
@@ -8,6 +8,82 @@ import type { FieldValues } from "../filter/types.js"
|
|
|
8
8
|
import type { FieldPath } from "../filter/types/path/eager.js"
|
|
9
9
|
import { make, type Q, type QAll } from "../query/dsl.js"
|
|
10
10
|
|
|
11
|
+
export type ComputedProjectionMathIrExpression =
|
|
12
|
+
| {
|
|
13
|
+
readonly _tag: "field"
|
|
14
|
+
readonly field: string
|
|
15
|
+
}
|
|
16
|
+
| {
|
|
17
|
+
readonly _tag: "mul"
|
|
18
|
+
readonly left: ComputedProjectionMathIrExpression
|
|
19
|
+
readonly right: ComputedProjectionMathIrExpression
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type ComputedProjectionIrExpression =
|
|
23
|
+
| {
|
|
24
|
+
readonly _tag: "relation-count"
|
|
25
|
+
readonly path: string
|
|
26
|
+
readonly filter: readonly FilterResult[]
|
|
27
|
+
}
|
|
28
|
+
| {
|
|
29
|
+
readonly _tag: "relation-any"
|
|
30
|
+
readonly path: string
|
|
31
|
+
readonly filter: readonly FilterResult[]
|
|
32
|
+
}
|
|
33
|
+
| {
|
|
34
|
+
readonly _tag: "relation-every"
|
|
35
|
+
readonly path: string
|
|
36
|
+
readonly filter: readonly FilterResult[]
|
|
37
|
+
}
|
|
38
|
+
| {
|
|
39
|
+
readonly _tag: "relation-distinct-count"
|
|
40
|
+
readonly path: string
|
|
41
|
+
readonly field: string
|
|
42
|
+
readonly filter: readonly FilterResult[]
|
|
43
|
+
}
|
|
44
|
+
| {
|
|
45
|
+
readonly _tag: "relation-sum"
|
|
46
|
+
readonly path: string
|
|
47
|
+
readonly field: string
|
|
48
|
+
readonly filter: readonly FilterResult[]
|
|
49
|
+
}
|
|
50
|
+
| {
|
|
51
|
+
readonly _tag: "relation-sum-expr"
|
|
52
|
+
readonly path: string
|
|
53
|
+
readonly expression: ComputedProjectionMathIrExpression
|
|
54
|
+
readonly filter: readonly FilterResult[]
|
|
55
|
+
}
|
|
56
|
+
| {
|
|
57
|
+
readonly _tag: "relation-sum-expr-by"
|
|
58
|
+
readonly path: string
|
|
59
|
+
readonly expression: ComputedProjectionMathIrExpression
|
|
60
|
+
readonly unit: string
|
|
61
|
+
readonly filter: readonly FilterResult[]
|
|
62
|
+
}
|
|
63
|
+
| {
|
|
64
|
+
readonly _tag: "relation-sum-expr-normalized"
|
|
65
|
+
readonly path: string
|
|
66
|
+
readonly expression: ComputedProjectionMathIrExpression
|
|
67
|
+
readonly unit: string
|
|
68
|
+
readonly toBase: string
|
|
69
|
+
readonly factors: Readonly<Record<string, number>>
|
|
70
|
+
readonly filter: readonly FilterResult[]
|
|
71
|
+
}
|
|
72
|
+
| {
|
|
73
|
+
readonly _tag: "relation-collect"
|
|
74
|
+
readonly path: string
|
|
75
|
+
readonly field: string
|
|
76
|
+
readonly distinct: boolean
|
|
77
|
+
readonly filter: readonly FilterResult[]
|
|
78
|
+
}
|
|
79
|
+
| {
|
|
80
|
+
readonly _tag: "relation-collect-fields"
|
|
81
|
+
readonly path: string
|
|
82
|
+
readonly fields: readonly string[]
|
|
83
|
+
readonly distinct: boolean
|
|
84
|
+
readonly filter: readonly FilterResult[]
|
|
85
|
+
}
|
|
86
|
+
|
|
11
87
|
type Result<TFieldValues extends FieldValues, A = TFieldValues, R = never> = {
|
|
12
88
|
filter: FilterResult[]
|
|
13
89
|
schema: S.Codec<A, TFieldValues, R> | undefined
|
|
@@ -16,6 +92,7 @@ type Result<TFieldValues extends FieldValues, A = TFieldValues, R = never> = {
|
|
|
16
92
|
order: { key: FieldPath<TFieldValues>; direction: "ASC" | "DESC" }[]
|
|
17
93
|
ttype: "one" | "many" | "count" | undefined
|
|
18
94
|
mode: "collect" | "project" | "transform" | undefined
|
|
95
|
+
computed: Record<string, ComputedProjectionIrExpression> | undefined
|
|
19
96
|
}
|
|
20
97
|
|
|
21
98
|
const interpret = <
|
|
@@ -33,7 +110,8 @@ const interpret = <
|
|
|
33
110
|
skip: undefined,
|
|
34
111
|
order: [],
|
|
35
112
|
ttype: undefined,
|
|
36
|
-
mode: undefined
|
|
113
|
+
mode: undefined,
|
|
114
|
+
computed: undefined
|
|
37
115
|
}
|
|
38
116
|
|
|
39
117
|
const upd = (
|
|
@@ -46,6 +124,7 @@ const interpret = <
|
|
|
46
124
|
if (v.ttype !== undefined) data.ttype = v.ttype
|
|
47
125
|
if (v.schema !== undefined) data.schema = v.schema
|
|
48
126
|
if (v.mode !== undefined) data.mode = v.mode
|
|
127
|
+
if (v.computed !== undefined) data.computed = v.computed
|
|
49
128
|
}
|
|
50
129
|
|
|
51
130
|
const applyPath = (path: string) => (_: FilterResult): FilterResult =>
|
|
@@ -135,8 +214,84 @@ const interpret = <
|
|
|
135
214
|
},
|
|
136
215
|
project: (v) => {
|
|
137
216
|
upd(interpret(v.current))
|
|
217
|
+
if (v.computed && v.mode === "transform") {
|
|
218
|
+
throw new Error("Computed projections require mode 'project' or 'collect', not 'transform'")
|
|
219
|
+
}
|
|
138
220
|
data.schema = v.schema
|
|
139
|
-
data.mode = v.
|
|
221
|
+
data.mode = v.computed
|
|
222
|
+
? v.mode === "collect" ? "collect" : "project"
|
|
223
|
+
: v.mode
|
|
224
|
+
data.computed = v.computed
|
|
225
|
+
? Object.fromEntries(
|
|
226
|
+
Object.entries(v.computed).map(([key, expression]) => {
|
|
227
|
+
const e = expression
|
|
228
|
+
const filter = e.operation ? interpret(e.operation(make())).filter.map(applyPath(e.path)) : []
|
|
229
|
+
switch (e._tag) {
|
|
230
|
+
case "relation-count":
|
|
231
|
+
case "relation-any":
|
|
232
|
+
case "relation-every":
|
|
233
|
+
return [key, { _tag: e._tag, path: e.path, filter } as ComputedProjectionIrExpression]
|
|
234
|
+
case "relation-distinct-count":
|
|
235
|
+
case "relation-sum":
|
|
236
|
+
return [
|
|
237
|
+
key,
|
|
238
|
+
{ _tag: e._tag, path: e.path, field: e.field, filter } as ComputedProjectionIrExpression
|
|
239
|
+
]
|
|
240
|
+
case "relation-sum-expr":
|
|
241
|
+
return [
|
|
242
|
+
key,
|
|
243
|
+
{ _tag: e._tag, path: e.path, expression: e.expression, filter } as ComputedProjectionIrExpression
|
|
244
|
+
]
|
|
245
|
+
case "relation-sum-expr-by":
|
|
246
|
+
return [
|
|
247
|
+
key,
|
|
248
|
+
{
|
|
249
|
+
_tag: e._tag,
|
|
250
|
+
path: e.path,
|
|
251
|
+
expression: e.expression,
|
|
252
|
+
unit: e.unit,
|
|
253
|
+
filter
|
|
254
|
+
} as ComputedProjectionIrExpression
|
|
255
|
+
]
|
|
256
|
+
case "relation-sum-expr-normalized":
|
|
257
|
+
return [
|
|
258
|
+
key,
|
|
259
|
+
{
|
|
260
|
+
_tag: e._tag,
|
|
261
|
+
path: e.path,
|
|
262
|
+
expression: e.expression,
|
|
263
|
+
unit: e.unit,
|
|
264
|
+
toBase: e.toBase,
|
|
265
|
+
factors: e.factors,
|
|
266
|
+
filter
|
|
267
|
+
} as ComputedProjectionIrExpression
|
|
268
|
+
]
|
|
269
|
+
case "relation-collect":
|
|
270
|
+
return [
|
|
271
|
+
key,
|
|
272
|
+
{
|
|
273
|
+
_tag: e._tag,
|
|
274
|
+
path: e.path,
|
|
275
|
+
field: e.field,
|
|
276
|
+
distinct: e.distinct,
|
|
277
|
+
filter
|
|
278
|
+
} as ComputedProjectionIrExpression
|
|
279
|
+
]
|
|
280
|
+
case "relation-collect-fields":
|
|
281
|
+
return [
|
|
282
|
+
key,
|
|
283
|
+
{
|
|
284
|
+
_tag: e._tag,
|
|
285
|
+
path: e.path,
|
|
286
|
+
fields: e.fields,
|
|
287
|
+
distinct: e.distinct,
|
|
288
|
+
filter
|
|
289
|
+
} as ComputedProjectionIrExpression
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
)
|
|
294
|
+
: undefined
|
|
140
295
|
}
|
|
141
296
|
})
|
|
142
297
|
)
|
|
@@ -157,15 +312,19 @@ export const toFilter = <
|
|
|
157
312
|
R,
|
|
158
313
|
TFieldValuesRefined extends TFieldValues = TFieldValues
|
|
159
314
|
>(
|
|
160
|
-
q: QAll<TFieldValues, TFieldValuesRefined, A, R
|
|
315
|
+
q: QAll<TFieldValues, TFieldValuesRefined, A, R>,
|
|
316
|
+
baseSchema?: S.Schema<unknown>
|
|
161
317
|
) => {
|
|
162
318
|
// TODO: Native interpreter for each db adapter, instead of the intermediate "new-kid" format
|
|
163
319
|
const a = interpret(q)
|
|
164
320
|
const schema = a.schema
|
|
165
|
-
let select: (keyof TFieldValues | { key: string; subKeys: string[] }
|
|
321
|
+
let select: (keyof TFieldValues | { key: string; subKeys: string[] } | {
|
|
322
|
+
key: string
|
|
323
|
+
computed: ComputedProjectionIrExpression
|
|
324
|
+
})[] = []
|
|
166
325
|
// TODO: support more complex (nested) schemas?
|
|
167
326
|
if (schema) {
|
|
168
|
-
const t = walkTransformation(schema.ast)
|
|
327
|
+
const t = walkTransformation(SchemaAST.toEncoded(schema.ast))
|
|
169
328
|
if (S.AST.isObjects(t)) {
|
|
170
329
|
select = t.propertySignatures.map((_) => _.name as string)
|
|
171
330
|
for (const prop of t.propertySignatures) {
|
|
@@ -194,12 +353,53 @@ export const toFilter = <
|
|
|
194
353
|
}
|
|
195
354
|
}
|
|
196
355
|
}
|
|
356
|
+
const computed = a.computed
|
|
357
|
+
const getSelectKey = (_: (typeof select)[number]) => {
|
|
358
|
+
if (typeof _ === "string") {
|
|
359
|
+
return _
|
|
360
|
+
}
|
|
361
|
+
if (typeof _ === "object" && _ !== null && "key" in _) {
|
|
362
|
+
return _.key
|
|
363
|
+
}
|
|
364
|
+
return String(_)
|
|
365
|
+
}
|
|
366
|
+
const schemaKeys = select.map(getSelectKey)
|
|
367
|
+
const nonEncodedSchemaKeys = (() => {
|
|
368
|
+
if (!baseSchema) {
|
|
369
|
+
return [] as string[]
|
|
370
|
+
}
|
|
371
|
+
const encoded = walkTransformation(SchemaAST.toEncoded(baseSchema.ast))
|
|
372
|
+
if (!S.AST.isObjects(encoded)) {
|
|
373
|
+
return [] as string[]
|
|
374
|
+
}
|
|
375
|
+
const encodedKeys = encoded.propertySignatures.map((_) => _.name as string)
|
|
376
|
+
return schemaKeys.filter((key) => !encodedKeys.includes(key))
|
|
377
|
+
})()
|
|
378
|
+
const missingComputedKeys = nonEncodedSchemaKeys.filter((key) => !(computed && key in computed))
|
|
379
|
+
|
|
380
|
+
if (Array.isArrayNonEmpty(missingComputedKeys)) {
|
|
381
|
+
throw new Error(`Missing computed projections for schema keys: ${missingComputedKeys.join(", ")}`)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (computed) {
|
|
385
|
+
const computedKeys = Object.keys(computed)
|
|
386
|
+
const extraComputedKeys = computedKeys.filter((key) => !schemaKeys.includes(key))
|
|
387
|
+
if (Array.isArrayNonEmpty(extraComputedKeys)) {
|
|
388
|
+
throw new Error(`Computed projection keys must exist in projection schema: ${extraComputedKeys.join(", ")}`)
|
|
389
|
+
}
|
|
390
|
+
select = select.filter((_) => {
|
|
391
|
+
const key = getSelectKey(_)
|
|
392
|
+
return !(key in computed)
|
|
393
|
+
})
|
|
394
|
+
select.push(...Object.entries(computed).map(([key, expression]) => ({ key, computed: expression })))
|
|
395
|
+
}
|
|
197
396
|
return dropUndefinedT({
|
|
198
397
|
t: null as unknown as TFieldValues,
|
|
199
398
|
limit: a.limit,
|
|
200
399
|
skip: a.skip,
|
|
201
400
|
select: Option.getOrUndefined(toNonEmptyArray(select)),
|
|
202
401
|
schema,
|
|
402
|
+
computed,
|
|
203
403
|
order: Option.getOrUndefined(toNonEmptyArray(a.order)),
|
|
204
404
|
ttype: a.ttype,
|
|
205
405
|
mode: a.mode ?? "transform",
|
package/src/Model.ts
CHANGED
|
@@ -3,17 +3,18 @@ import { reportNonInterruptedFailure } from "@effect-app/infra/QueueMaker/errors
|
|
|
3
3
|
import { type QueueBase, QueueMeta } from "@effect-app/infra/QueueMaker/service"
|
|
4
4
|
import { subMinutes } from "date-fns"
|
|
5
5
|
import { Effect, Fiber, type NonEmptyReadonlyArray, Option, S, Tracer } from "effect-app"
|
|
6
|
-
import type
|
|
6
|
+
import { type NonEmptyString255 } from "effect-app/Schema"
|
|
7
7
|
import { pretty } from "effect-app/utils"
|
|
8
8
|
import { SqlClient } from "effect/unstable/sql"
|
|
9
9
|
import { SQLModel } from "../adapters/SQL.js"
|
|
10
10
|
import { InfraLogger } from "../logger.js"
|
|
11
|
+
import { messagingSpanArgs } from "../otel.js"
|
|
11
12
|
|
|
12
|
-
export const QueueId = S.
|
|
13
|
+
export const QueueId = S.Finite.pipe(S.brand("QueueId"))
|
|
13
14
|
export type QueueId = typeof QueueId.Type
|
|
14
15
|
|
|
15
16
|
// TODO: let the model track and Auto Generate versionColumn on every update instead
|
|
16
|
-
export
|
|
17
|
+
export const makeSQLQueue = Effect.fnUntraced(function*<
|
|
17
18
|
Evt extends { id: S.StringId; _tag: string },
|
|
18
19
|
DrainEvt extends { id: S.StringId; _tag: string },
|
|
19
20
|
EvtE,
|
|
@@ -24,166 +25,157 @@ export function makeSQLQueue<
|
|
|
24
25
|
schema: S.Codec<Evt, EvtE>,
|
|
25
26
|
drainSchema: S.Codec<DrainEvt, DrainEvtE>
|
|
26
27
|
) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const sql = yield* SqlClient.SqlClient
|
|
28
|
+
const base = {
|
|
29
|
+
id: SQLModel.Generated(QueueId),
|
|
30
|
+
meta: SQLModel.JsonFromString(QueueMeta),
|
|
31
|
+
name: S.NonEmptyString255,
|
|
32
|
+
createdAt: SQLModel.DateTimeInsert,
|
|
33
|
+
updatedAt: SQLModel.DateTimeUpdate,
|
|
34
|
+
// TODO: at+owner
|
|
35
|
+
processingAt: SQLModel.FieldOption(S.Date),
|
|
36
|
+
finishedAt: SQLModel.FieldOption(S.Date),
|
|
37
|
+
etag: S.String // TODO: use a SQLModel thing that auto updates it?
|
|
38
|
+
// TODO: record locking.. / optimistic locking
|
|
39
|
+
// rowVersion: SQLModel.DateTimeFromNumberWithNow
|
|
40
|
+
}
|
|
41
|
+
class Queue extends SQLModel.Class<Queue>("Queue")({
|
|
42
|
+
body: SQLModel.JsonFromString(schema),
|
|
43
|
+
...base
|
|
44
|
+
}) {}
|
|
45
|
+
class Drain extends SQLModel.Class<Drain>("Drain")({
|
|
46
|
+
body: SQLModel.JsonFromString(drainSchema),
|
|
47
|
+
...base
|
|
48
|
+
}) {}
|
|
49
|
+
const sql = yield* SqlClient.SqlClient
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
const queueRepo = yield* SQLModel.makeRepository(Queue, {
|
|
52
|
+
tableName: "queue",
|
|
53
|
+
spanPrefix: "QueueRepo",
|
|
54
|
+
idColumn: "id",
|
|
55
|
+
versionColumn: "etag"
|
|
56
|
+
})
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
const drainRepo = yield* SQLModel.makeRepository(Drain, {
|
|
59
|
+
tableName: "queue",
|
|
60
|
+
spanPrefix: "DrainRepo",
|
|
61
|
+
idColumn: "id",
|
|
62
|
+
versionColumn: "etag"
|
|
63
|
+
})
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
const decodeDrain = S.decodeEffectConcurrently(Drain)
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
Effect
|
|
71
|
-
.andThen((limit) =>
|
|
72
|
-
sql<typeof Drain.Encoded>`SELECT *
|
|
67
|
+
const drain = Effect.gen(function*() {
|
|
68
|
+
const limit = subMinutes(new Date(), 15)
|
|
69
|
+
return yield* sql<typeof Drain.Encoded>`SELECT *
|
|
73
70
|
FROM queue
|
|
74
71
|
WHERE name = ${queueDrainName} AND finishedAt IS NULL AND (processingAt IS NULL OR processingAt < ${limit.getTime()})
|
|
75
72
|
LIMIT 1`
|
|
76
|
-
|
|
77
|
-
)
|
|
73
|
+
})
|
|
78
74
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
if (first) return first
|
|
101
|
-
yield* Effect.sleep(250)
|
|
75
|
+
const q = {
|
|
76
|
+
offer: Effect.fnUntraced(function*(body: Evt, meta: typeof QueueMeta.Type) {
|
|
77
|
+
yield* queueRepo.insertVoid(Queue.insert.make({
|
|
78
|
+
body,
|
|
79
|
+
meta,
|
|
80
|
+
name: queueName,
|
|
81
|
+
processingAt: Option.none(),
|
|
82
|
+
finishedAt: Option.none(),
|
|
83
|
+
etag: crypto.randomUUID()
|
|
84
|
+
}))
|
|
85
|
+
}),
|
|
86
|
+
take: Effect.gen(function*() {
|
|
87
|
+
while (true) {
|
|
88
|
+
const [first] = yield* drain.pipe(Effect.withTracerEnabled(false)) // disable sql tracer otherwise we spam it..
|
|
89
|
+
if (first) {
|
|
90
|
+
const dec = yield* decodeDrain(first)
|
|
91
|
+
const { createdAt, updatedAt, ...rest } = dec
|
|
92
|
+
return yield* drainRepo.update(
|
|
93
|
+
Drain.update.make({ ...rest, processingAt: Option.some(new Date()) }) // auto in lib , etag: crypto.randomUUID()
|
|
94
|
+
)
|
|
102
95
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
96
|
+
if (first) return first
|
|
97
|
+
yield* Effect.sleep(250)
|
|
98
|
+
}
|
|
99
|
+
}),
|
|
100
|
+
finish: Effect.fn(function*({ createdAt, updatedAt, ...q }: Drain) {
|
|
101
|
+
return yield* drainRepo.updateVoid(Drain.update.make({ ...q, finishedAt: Option.some(new Date()) })) // auto in lib , etag: crypto.randomUUID()
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
const queue = {
|
|
105
|
+
publish: Effect.fn(`publish ${queueName}`, {
|
|
106
|
+
kind: "producer",
|
|
107
|
+
attributes: {
|
|
108
|
+
"messaging.system": "sql",
|
|
109
|
+
"messaging.operation.name": "publish",
|
|
110
|
+
"messaging.destination.name": queueName
|
|
111
|
+
}
|
|
112
|
+
})(function*(
|
|
113
|
+
...messages: NonEmptyReadonlyArray<Evt>
|
|
114
|
+
) {
|
|
115
|
+
yield* Effect.annotateCurrentSpan({
|
|
116
|
+
"messaging.batch.message_count": messages.length,
|
|
117
|
+
"messaging.message.types": messages.map((_) => _._tag)
|
|
118
|
+
})
|
|
119
|
+
const requestContext = yield* getRequestContext
|
|
120
|
+
yield* Effect.forEach(messages, (m) => q.offer(m, requestContext), { discard: true })
|
|
121
|
+
}),
|
|
122
|
+
drain: <DrainE, DrainR>(
|
|
123
|
+
handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
|
|
124
|
+
sessionId?: string
|
|
125
|
+
) => {
|
|
126
|
+
const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
|
|
127
|
+
const processMessage = Effect.fnUntraced(function*({ body, meta }: Drain) {
|
|
128
|
+
let effect = InfraLogger
|
|
129
|
+
.logDebug(`[${queueDrainName}] Processing incoming message`)
|
|
110
130
|
.pipe(
|
|
111
|
-
Effect.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const processMessage = (msg: Drain) =>
|
|
132
|
-
Effect
|
|
133
|
-
.succeed(msg)
|
|
134
|
-
.pipe(Effect
|
|
135
|
-
.flatMap(({ body, meta }) => {
|
|
136
|
-
let effect = InfraLogger
|
|
137
|
-
.logDebug(`[${queueDrainName}] Processing incoming message`)
|
|
138
|
-
.pipe(
|
|
139
|
-
Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
|
|
140
|
-
Effect.andThen(handleEvent(body)),
|
|
141
|
-
silenceAndReportError,
|
|
142
|
-
(_) =>
|
|
143
|
-
setupRequestContextWithCustomSpan(
|
|
144
|
-
_,
|
|
145
|
-
meta,
|
|
146
|
-
`queue.drain: ${queueDrainName}.${body._tag}`,
|
|
147
|
-
{
|
|
148
|
-
captureStackTrace: false,
|
|
149
|
-
kind: "consumer",
|
|
150
|
-
attributes: {
|
|
151
|
-
"queue.name": queueDrainName,
|
|
152
|
-
"queue.sessionId": sessionId,
|
|
153
|
-
"queue.input": body
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
)
|
|
157
|
-
)
|
|
158
|
-
if (meta.span) {
|
|
159
|
-
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
131
|
+
Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
|
|
132
|
+
Effect.andThen(handleEvent(body)),
|
|
133
|
+
silenceAndReportError,
|
|
134
|
+
(_) => {
|
|
135
|
+
const args = messagingSpanArgs({
|
|
136
|
+
operation: "process",
|
|
137
|
+
system: "sql",
|
|
138
|
+
destination: queueDrainName,
|
|
139
|
+
messageId: body.id,
|
|
140
|
+
conversationId: sessionId,
|
|
141
|
+
extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
|
|
142
|
+
}, "consumer")
|
|
143
|
+
return setupRequestContextWithCustomSpan(
|
|
144
|
+
_,
|
|
145
|
+
meta,
|
|
146
|
+
args.name,
|
|
147
|
+
{
|
|
148
|
+
captureStackTrace: false,
|
|
149
|
+
kind: args.kind,
|
|
150
|
+
attributes: args.attributes
|
|
160
151
|
}
|
|
161
|
-
return effect
|
|
162
|
-
}))
|
|
163
|
-
|
|
164
|
-
return q
|
|
165
|
-
.take
|
|
166
|
-
.pipe(
|
|
167
|
-
Effect.flatMap((x) =>
|
|
168
|
-
processMessage(x).pipe(
|
|
169
|
-
Effect.uninterruptible,
|
|
170
|
-
Effect.forkChild,
|
|
171
|
-
Effect.flatMap(Fiber.join),
|
|
172
|
-
Effect.tap(q.finish(x))
|
|
173
152
|
)
|
|
174
|
-
|
|
175
|
-
silenceAndReportError,
|
|
176
|
-
Effect.withSpan(`queue.drain: ${queueDrainName}`, {
|
|
177
|
-
attributes: {
|
|
178
|
-
"queue.type": "sql",
|
|
179
|
-
"queue.name": queueDrainName,
|
|
180
|
-
"queue.sessionId": sessionId
|
|
181
|
-
}
|
|
182
|
-
}),
|
|
183
|
-
Effect.forever
|
|
153
|
+
}
|
|
184
154
|
)
|
|
185
|
-
|
|
155
|
+
if (meta.span) {
|
|
156
|
+
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
157
|
+
}
|
|
158
|
+
return yield* effect
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
return Effect.fn(`receive ${queueDrainName}`, {
|
|
162
|
+
kind: "consumer",
|
|
163
|
+
attributes: {
|
|
164
|
+
"messaging.system": "sql",
|
|
165
|
+
"messaging.operation.name": "receive",
|
|
166
|
+
"messaging.destination.name": queueDrainName,
|
|
167
|
+
...(sessionId !== undefined && { "messaging.message.conversation_id": sessionId })
|
|
168
|
+
}
|
|
169
|
+
})(function*() {
|
|
170
|
+
const x = yield* q.take
|
|
171
|
+
yield* processMessage(x).pipe(
|
|
172
|
+
Effect.uninterruptible,
|
|
173
|
+
Effect.forkChild,
|
|
174
|
+
Effect.flatMap(Fiber.join),
|
|
175
|
+
Effect.tap(q.finish(x))
|
|
176
|
+
)
|
|
177
|
+
}, (effect) => effect.pipe(silenceAndReportError, Effect.forever))()
|
|
186
178
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
179
|
+
}
|
|
180
|
+
return queue as QueueBase<Evt, DrainEvt>
|
|
181
|
+
})
|