@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
|
@@ -5,10 +5,11 @@ import * as Q from "effect/Queue"
|
|
|
5
5
|
import { MemQueue } from "../adapters/memQueue.js"
|
|
6
6
|
import { getRequestContext, setupRequestContextWithCustomSpan } from "../api/setupRequest.js"
|
|
7
7
|
import { InfraLogger } from "../logger.js"
|
|
8
|
+
import { messagingSpanArgs } from "../otel.js"
|
|
8
9
|
import { reportNonInterruptedFailure, reportNonInterruptedFailureCause } from "./errors.js"
|
|
9
|
-
import {
|
|
10
|
+
import { QueueMeta } from "./service.js"
|
|
10
11
|
|
|
11
|
-
export
|
|
12
|
+
export const makeMemQueue = Effect.fnUntraced(function*<
|
|
12
13
|
Evt extends { id: S.StringId; _tag: string },
|
|
13
14
|
DrainEvt extends { id: S.StringId; _tag: string },
|
|
14
15
|
EvtE,
|
|
@@ -19,112 +20,112 @@ export function makeMemQueue<
|
|
|
19
20
|
schema: S.Codec<Evt, EvtE>,
|
|
20
21
|
drainSchema: S.Codec<DrainEvt, DrainEvtE>
|
|
21
22
|
) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const qDrain = yield* mem.getOrCreateQueue(queueDrainName)
|
|
23
|
+
const mem = yield* MemQueue
|
|
24
|
+
const q = yield* mem.getOrCreateQueue(queueName)
|
|
25
|
+
const qDrain = yield* mem.getOrCreateQueue(queueDrainName)
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
const wireSchema = S.Struct({ body: schema, meta: QueueMeta })
|
|
28
|
+
const wireSchemaJson = S.fromJsonString(S.toCodecJson(wireSchema))
|
|
29
|
+
const encodePublish = S.encodeEffect(wireSchemaJson)
|
|
30
|
+
const drainW = S.Struct({ body: drainSchema, meta: QueueMeta })
|
|
31
|
+
const drainWJson = S.fromJsonString(S.toCodecJson(drainW))
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
const parseDrain = flow(S.decodeUnknownEffectConcurrently(drainWJson), Effect.orDie)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
sessionId?: string
|
|
57
|
-
) => {
|
|
58
|
-
const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
|
|
59
|
-
const reportError = reportNonInterruptedFailureCause({ name: "MemQueue.drain." + queueDrainName })
|
|
60
|
-
const processMessage = (msg: string) =>
|
|
61
|
-
// we JSON parse, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
|
|
62
|
-
parseDrain(msg).pipe(
|
|
35
|
+
const queue = {
|
|
36
|
+
publish: Effect.fn(`publish ${queueName}`, {
|
|
37
|
+
kind: "producer",
|
|
38
|
+
attributes: {
|
|
39
|
+
"messaging.system": "memory",
|
|
40
|
+
"messaging.operation.name": "publish",
|
|
41
|
+
"messaging.destination.name": queueName
|
|
42
|
+
}
|
|
43
|
+
})(function*(
|
|
44
|
+
...messages: NonEmptyReadonlyArray<Evt>
|
|
45
|
+
) {
|
|
46
|
+
yield* Effect.annotateCurrentSpan({
|
|
47
|
+
"messaging.batch.message_count": messages.length,
|
|
48
|
+
"messaging.message.types": messages.map((_) => _._tag)
|
|
49
|
+
})
|
|
50
|
+
const requestContext = yield* getRequestContext
|
|
51
|
+
// we JSON encode, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
|
|
52
|
+
yield* Effect.forEach(
|
|
53
|
+
messages,
|
|
54
|
+
(m) =>
|
|
55
|
+
encodePublish({ body: m, meta: requestContext }).pipe(
|
|
63
56
|
Effect.orDie,
|
|
64
|
-
Effect
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
attributes: {
|
|
81
|
-
"queue.name": queueDrainName,
|
|
82
|
-
"queue.sessionId": sessionId,
|
|
83
|
-
"queue.input": body
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
)
|
|
87
|
-
)
|
|
88
|
-
if (meta.span) {
|
|
89
|
-
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
90
|
-
}
|
|
91
|
-
return effect
|
|
92
|
-
})
|
|
93
|
-
)
|
|
94
|
-
return Q
|
|
95
|
-
.take(qDrain)
|
|
57
|
+
Effect.flatMap((_) => Q.offer(q, _))
|
|
58
|
+
),
|
|
59
|
+
{ discard: true }
|
|
60
|
+
)
|
|
61
|
+
}),
|
|
62
|
+
drain: <DrainE, DrainR>(
|
|
63
|
+
handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
|
|
64
|
+
sessionId?: string
|
|
65
|
+
) => {
|
|
66
|
+
const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
|
|
67
|
+
const reportError = reportNonInterruptedFailureCause({ name: "MemQueue.drain." + queueDrainName })
|
|
68
|
+
const processMessage = Effect.fnUntraced(function*(msg: string) {
|
|
69
|
+
// we JSON parse, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
|
|
70
|
+
const { body, meta } = yield* parseDrain(msg).pipe(Effect.orDie)
|
|
71
|
+
let effect = InfraLogger
|
|
72
|
+
.logDebug(`[${queueDrainName}] Processing incoming message`)
|
|
96
73
|
.pipe(
|
|
97
|
-
Effect
|
|
98
|
-
|
|
99
|
-
processMessage(x).pipe(
|
|
100
|
-
Effect.uninterruptible,
|
|
101
|
-
Effect.forkChild,
|
|
102
|
-
Effect.flatMap(Fiber.join),
|
|
103
|
-
// normally a failed item would be returned to the queue and retried up to X times.
|
|
104
|
-
Effect.flatMap((_) =>
|
|
105
|
-
_._tag === "Failure" && !Cause.hasInterruptsOnly(_.cause)
|
|
106
|
-
? Q.offer(qDrain, x).pipe(
|
|
107
|
-
// TODO: retry count tracking and max retries.
|
|
108
|
-
Effect.delay("5 seconds"),
|
|
109
|
-
Effect.tapCause(reportError),
|
|
110
|
-
Effect.forkDetach
|
|
111
|
-
)
|
|
112
|
-
: Effect.void
|
|
113
|
-
)
|
|
114
|
-
)
|
|
115
|
-
),
|
|
74
|
+
Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
|
|
75
|
+
Effect.andThen(handleEvent(body)),
|
|
116
76
|
silenceAndReportError,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
"
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
77
|
+
(_) => {
|
|
78
|
+
const args = messagingSpanArgs({
|
|
79
|
+
operation: "process",
|
|
80
|
+
system: "memory",
|
|
81
|
+
destination: queueDrainName,
|
|
82
|
+
messageId: body.id,
|
|
83
|
+
conversationId: sessionId,
|
|
84
|
+
extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
|
|
85
|
+
}, "consumer")
|
|
86
|
+
return setupRequestContextWithCustomSpan(
|
|
87
|
+
_,
|
|
88
|
+
meta,
|
|
89
|
+
args.name,
|
|
90
|
+
{
|
|
91
|
+
captureStackTrace: false,
|
|
92
|
+
kind: args.kind,
|
|
93
|
+
attributes: args.attributes
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
}
|
|
125
97
|
)
|
|
126
|
-
|
|
98
|
+
if (meta.span) {
|
|
99
|
+
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
100
|
+
}
|
|
101
|
+
return yield* effect
|
|
102
|
+
})
|
|
103
|
+
return Effect.fn(`receive ${queueDrainName}`, {
|
|
104
|
+
kind: "consumer",
|
|
105
|
+
attributes: {
|
|
106
|
+
"messaging.system": "memory",
|
|
107
|
+
"messaging.operation.name": "receive",
|
|
108
|
+
"messaging.destination.name": queueDrainName,
|
|
109
|
+
...(sessionId !== undefined && { "messaging.message.conversation_id": sessionId })
|
|
110
|
+
}
|
|
111
|
+
})(function*() {
|
|
112
|
+
const x = yield* Q.take(qDrain)
|
|
113
|
+
const exit = yield* processMessage(x).pipe(
|
|
114
|
+
Effect.uninterruptible,
|
|
115
|
+
Effect.forkChild,
|
|
116
|
+
Effect.flatMap(Fiber.join)
|
|
117
|
+
)
|
|
118
|
+
if (exit._tag === "Failure" && !Cause.hasInterruptsOnly(exit.cause)) {
|
|
119
|
+
// normally a failed item would be returned to the queue and retried up to X times.
|
|
120
|
+
yield* Q.offer(qDrain, x).pipe(
|
|
121
|
+
// TODO: retry count tracking and max retries.
|
|
122
|
+
Effect.delay("5 seconds"),
|
|
123
|
+
Effect.tapCause(reportError),
|
|
124
|
+
Effect.forkDetach
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
}, (effect) => effect.pipe(silenceAndReportError, Effect.forever))()
|
|
127
128
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
129
|
+
}
|
|
130
|
+
return queue
|
|
131
|
+
})
|
|
@@ -5,8 +5,9 @@ import { pretty } from "effect-app/utils"
|
|
|
5
5
|
import { Receiver, Sender } from "../adapters/ServiceBus.js"
|
|
6
6
|
import { getRequestContext, setupRequestContextWithCustomSpan } from "../api/setupRequest.js"
|
|
7
7
|
import { InfraLogger } from "../logger.js"
|
|
8
|
+
import { messagingSpanArgs } from "../otel.js"
|
|
8
9
|
import { reportNonInterruptedFailure, reportNonInterruptedFailureCause, reportQueueError } from "./errors.js"
|
|
9
|
-
import {
|
|
10
|
+
import { QueueMeta } from "./service.js"
|
|
10
11
|
|
|
11
12
|
export function makeServiceBusQueue<
|
|
12
13
|
Evt extends { id: StringId; _tag: string },
|
|
@@ -21,11 +22,11 @@ export function makeServiceBusQueue<
|
|
|
21
22
|
body: schema,
|
|
22
23
|
meta: QueueMeta
|
|
23
24
|
})
|
|
24
|
-
const wireSchemaJson = S.fromJsonString(wireSchema)
|
|
25
|
+
const wireSchemaJson = S.fromJsonString(S.toCodecJson(wireSchema))
|
|
25
26
|
const encodePublish = S.encodeEffect(wireSchemaJson)
|
|
26
27
|
const drainW = S.Struct({ body: drainSchema, meta: QueueMeta })
|
|
27
|
-
const drainWJson = S.fromJsonString(drainW)
|
|
28
|
-
const parseDrain = flow(S.
|
|
28
|
+
const drainWJson = S.fromJsonString(S.toCodecJson(drainW))
|
|
29
|
+
const parseDrain = flow(S.decodeUnknownEffectConcurrently(drainWJson), Effect.orDie)
|
|
29
30
|
|
|
30
31
|
return Effect.gen(function*() {
|
|
31
32
|
const sender = yield* Sender
|
|
@@ -42,96 +43,79 @@ export function makeServiceBusQueue<
|
|
|
42
43
|
handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
|
|
43
44
|
sessionId?: string
|
|
44
45
|
) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}),
|
|
84
|
-
Effect
|
|
85
|
-
// we reportError here, so that we report the error only, and keep flowing
|
|
86
|
-
.tapCause(reportError),
|
|
87
|
-
// we still need to flatten the Exit.
|
|
88
|
-
Effect.flatMap((_) => _)
|
|
89
|
-
)
|
|
90
|
-
}
|
|
46
|
+
const processMessage = Effect.fnUntraced(function*(messageBody: unknown) {
|
|
47
|
+
const { body, meta } = yield* parseDrain(messageBody).pipe(Effect.orDie)
|
|
48
|
+
let effect = InfraLogger
|
|
49
|
+
.logDebug(`[${receiver.name}] Processing incoming message`)
|
|
50
|
+
.pipe(
|
|
51
|
+
Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
|
|
52
|
+
Effect.andThen(handleEvent(body)),
|
|
53
|
+
Effect.orDie,
|
|
54
|
+
// we silenceAndReportError here, so that the error is reported, and moves into the Exit.
|
|
55
|
+
silenceAndReportError,
|
|
56
|
+
(_) => {
|
|
57
|
+
const args = messagingSpanArgs({
|
|
58
|
+
operation: "process",
|
|
59
|
+
system: "servicebus",
|
|
60
|
+
destination: receiver.name,
|
|
61
|
+
messageId: body.id,
|
|
62
|
+
conversationId: sessionId,
|
|
63
|
+
extra: { "messaging.message.type": body._tag, "messaging.message.body": body }
|
|
64
|
+
}, "consumer")
|
|
65
|
+
return setupRequestContextWithCustomSpan(
|
|
66
|
+
_,
|
|
67
|
+
meta,
|
|
68
|
+
args.name,
|
|
69
|
+
{
|
|
70
|
+
captureStackTrace: false,
|
|
71
|
+
kind: args.kind,
|
|
72
|
+
attributes: args.attributes
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
if (meta.span) {
|
|
78
|
+
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
79
|
+
}
|
|
80
|
+
// we reportError here, so that we report the error only, and keep flowing
|
|
81
|
+
const exit = yield* Effect.tapCause(effect, reportError)
|
|
82
|
+
return yield* exit
|
|
83
|
+
})
|
|
91
84
|
|
|
92
85
|
return receiver
|
|
93
86
|
.subscribe({
|
|
94
87
|
processMessage: (x) => processMessage(x.body).pipe(Effect.uninterruptible),
|
|
95
88
|
processError: (err) => reportQueueError(Cause.fail(err.error))
|
|
96
|
-
// Deferred.completeWith(
|
|
97
|
-
// deferred,
|
|
98
|
-
// reportFatalQueueError(Cause.fail(err.error))
|
|
99
|
-
// .pipe(Effect.andThen(Effect.fail(err.error)))
|
|
100
|
-
// )
|
|
101
89
|
}, sessionId)
|
|
102
|
-
|
|
103
|
-
.pipe(
|
|
104
|
-
Effect.andThen(Effect.never)
|
|
105
|
-
)
|
|
90
|
+
.pipe(Effect.andThen(Effect.never))
|
|
106
91
|
},
|
|
107
92
|
|
|
108
|
-
publish: (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
)
|
|
93
|
+
publish: Effect.fn(`publish ${sender.name}`, {
|
|
94
|
+
kind: "producer",
|
|
95
|
+
attributes: {
|
|
96
|
+
"messaging.system": "servicebus",
|
|
97
|
+
"messaging.operation.name": "publish",
|
|
98
|
+
"messaging.destination.name": sender.name
|
|
99
|
+
}
|
|
100
|
+
})(function*(...messages: NonEmptyReadonlyArray<Evt>) {
|
|
101
|
+
yield* Effect.annotateCurrentSpan({
|
|
102
|
+
"messaging.batch.message_count": messages.length,
|
|
103
|
+
"messaging.message.types": messages.map((_) => _._tag)
|
|
104
|
+
})
|
|
105
|
+
const requestContext = yield* getRequestContext
|
|
106
|
+
const msgs = yield* Effect.forEach(messages, (m) =>
|
|
107
|
+
encodePublish({ body: m, meta: requestContext }).pipe(
|
|
108
|
+
Effect.orDie,
|
|
109
|
+
Effect.map((body) => ({
|
|
110
|
+
body,
|
|
111
|
+
messageId: m.id, /* correllationid: requestId */
|
|
112
|
+
contentType: "application/json",
|
|
113
|
+
sessionId: "sessionId" in m ? m.sessionId as string : undefined as unknown as string // TODO: optional
|
|
114
|
+
}))
|
|
115
|
+
))
|
|
116
|
+
yield* sender.sendMessages(msgs)
|
|
117
|
+
})
|
|
134
118
|
}
|
|
135
|
-
return queue
|
|
119
|
+
return queue
|
|
136
120
|
})
|
|
137
121
|
}
|
package/src/RequestContext.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context, S } from "effect-app"
|
|
2
2
|
import { UserProfileId } from "effect-app/ids"
|
|
3
3
|
import { NonEmptyString255 } from "effect-app/Schema"
|
|
4
4
|
|
|
5
|
-
export const Locale = S.
|
|
5
|
+
export const Locale = S.Literals(["en", "de"])
|
|
6
6
|
export type Locale = typeof Locale.Type
|
|
7
7
|
|
|
8
|
-
export class LocaleRef extends
|
|
8
|
+
export class LocaleRef extends Context.Reference("Locale", { defaultValue: (): Locale => "en" }) {}
|
|
9
9
|
|
|
10
|
-
export class RequestContext extends S.
|
|
10
|
+
export class RequestContext extends S.Opaque<
|
|
11
11
|
RequestContext,
|
|
12
12
|
RequestContext.Encoded
|
|
13
|
-
>(
|
|
13
|
+
>()(S.Struct({
|
|
14
14
|
span: S.Struct({
|
|
15
15
|
traceId: S.String,
|
|
16
16
|
spanId: S.String,
|
|
@@ -22,8 +22,8 @@ export class RequestContext extends S.ExtendedClass<
|
|
|
22
22
|
namespace: NonEmptyString255,
|
|
23
23
|
/** @deprecated */
|
|
24
24
|
userProfile: S.optional(S.Struct({ sub: UserProfileId })) //
|
|
25
|
-
}) {
|
|
26
|
-
// static Tag =
|
|
25
|
+
})) {
|
|
26
|
+
// static Tag = Context.Tag<RequestContext>()
|
|
27
27
|
|
|
28
28
|
static toMonitoring(this: void, self: RequestContext) {
|
|
29
29
|
return {
|
|
@@ -34,16 +34,16 @@ export class RequestContext extends S.ExtendedClass<
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export const spanAttributes = (ctx: Pick<RequestContext, "locale" | "namespace"> & Partial<RequestContext>) => ({
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
...ctx.sourceId ? { "
|
|
37
|
+
"code.function.name": ctx.name,
|
|
38
|
+
"app.locale": ctx.locale,
|
|
39
|
+
"app.tenant.id": ctx.namespace,
|
|
40
|
+
...ctx.sourceId ? { "client.id": ctx.sourceId } : {},
|
|
41
41
|
...(ctx.userProfile?.sub
|
|
42
42
|
? {
|
|
43
|
-
"
|
|
43
|
+
"user.id": ctx
|
|
44
44
|
.userProfile
|
|
45
45
|
.sub,
|
|
46
|
-
"
|
|
46
|
+
"user.roles": "roles" in ctx
|
|
47
47
|
.userProfile
|
|
48
48
|
? ctx.userProfile.roles
|
|
49
49
|
: undefined
|
|
@@ -53,11 +53,9 @@ export const spanAttributes = (ctx: Pick<RequestContext, "locale" | "namespace">
|
|
|
53
53
|
|
|
54
54
|
// codegen:start {preset: model}
|
|
55
55
|
//
|
|
56
|
-
/* eslint-disable */
|
|
57
56
|
export namespace RequestContext {
|
|
58
|
-
export interface Encoded extends S.
|
|
57
|
+
export interface Encoded extends S.StructNestedEncoded<typeof RequestContext> {}
|
|
59
58
|
}
|
|
60
|
-
/* eslint-enable */
|
|
61
59
|
//
|
|
62
60
|
// codegen:end
|
|
63
61
|
//
|
package/src/RequestFiberSet.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { Effect, Fiber, FiberSet, Layer, Option,
|
|
2
|
+
import { Context, Effect, Fiber, FiberSet, Layer, Option, type Tracer } from "effect-app"
|
|
3
3
|
import { reportRequestError, reportUnknownRequestError } from "./api/reportError.js"
|
|
4
4
|
import { InfraLogger } from "./logger.js"
|
|
5
5
|
|
|
@@ -92,7 +92,7 @@ const make = Effect.gen(function*() {
|
|
|
92
92
|
* Whenever you fork a fiber for a Request, and you want to prevent dependent services to close prematurely on interruption,
|
|
93
93
|
* like the ServiceBus Sender, you should register these fibers in this FiberSet.
|
|
94
94
|
*/
|
|
95
|
-
export class RequestFiberSet extends
|
|
95
|
+
export class RequestFiberSet extends Context.Service<RequestFiberSet>()("RequestFiberSet", { make }) {
|
|
96
96
|
static readonly Live = Layer.effect(this, this.make)
|
|
97
97
|
static readonly register = <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
98
98
|
this.asEffect().pipe(Effect.andThen((_) => _.register(self)))
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { Data, Effect, Layer,
|
|
1
|
+
import { Context, Data, Effect, Layer, RequestResolver } from "effect-app"
|
|
2
|
+
import { dual } from "effect/Function"
|
|
3
|
+
import type * as Request from "effect/Request"
|
|
2
4
|
import { ContextMap } from "./service.js"
|
|
3
5
|
|
|
4
6
|
// TODO: we have to create a new contextmap on every request.
|
|
@@ -7,7 +9,7 @@ import { ContextMap } from "./service.js"
|
|
|
7
9
|
// we can call another start after startup. but it would be even better if we could Die on accessing rootmap
|
|
8
10
|
// we could also make the ContextMap optional, and when missing, issue a warning instead?
|
|
9
11
|
|
|
10
|
-
export class ContextMapContainer extends
|
|
12
|
+
export class ContextMapContainer extends Context.Reference("ContextMapContainer", {
|
|
11
13
|
defaultValue: (): ContextMap | "root" => "root"
|
|
12
14
|
}) {
|
|
13
15
|
static readonly layer = Layer.effect(this, ContextMap.make.pipe(Effect.map(ContextMap.of)))
|
|
@@ -18,3 +20,40 @@ export class ContextMapNotStartedError extends Data.TaggedError("ContextMapNotSt
|
|
|
18
20
|
export const getContextMap = ContextMapContainer.asEffect().pipe(
|
|
19
21
|
Effect.filterOrFail((_) => _ !== "root", () => new ContextMapNotStartedError())
|
|
20
22
|
)
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Uses the official `RequestResolver.withCache` internally,
|
|
26
|
+
* creating one cached resolver per ContextMap (i.e. per request).
|
|
27
|
+
* Uses a shared semaphore in the ContextMap to ensure safe single initialization.
|
|
28
|
+
*/
|
|
29
|
+
export const withRequestResolverCache: {
|
|
30
|
+
<A extends Request.Request<any, any>>(options: {
|
|
31
|
+
readonly capacity: number
|
|
32
|
+
readonly strategy?: "lru" | "fifo" | undefined
|
|
33
|
+
}): (
|
|
34
|
+
self: RequestResolver.RequestResolver<A>
|
|
35
|
+
) => Effect.Effect<RequestResolver.RequestResolver<A>, ContextMapNotStartedError>
|
|
36
|
+
<A extends Request.Request<any, any>>(
|
|
37
|
+
self: RequestResolver.RequestResolver<A>,
|
|
38
|
+
options: {
|
|
39
|
+
readonly capacity: number
|
|
40
|
+
readonly strategy?: "lru" | "fifo" | undefined
|
|
41
|
+
}
|
|
42
|
+
): Effect.Effect<RequestResolver.RequestResolver<A>, ContextMapNotStartedError>
|
|
43
|
+
} = dual(2, <A extends Request.Request<any, any>>(
|
|
44
|
+
self: RequestResolver.RequestResolver<A>,
|
|
45
|
+
options: {
|
|
46
|
+
readonly capacity: number
|
|
47
|
+
readonly strategy?: "lru" | "fifo" | undefined
|
|
48
|
+
}
|
|
49
|
+
): Effect.Effect<RequestResolver.RequestResolver<A>, ContextMapNotStartedError> => {
|
|
50
|
+
const cacheKey = Symbol()
|
|
51
|
+
return getContextMap.pipe(
|
|
52
|
+
Effect.flatMap((ctxMap) =>
|
|
53
|
+
ctxMap.getOrCreateStoreEffect(
|
|
54
|
+
cacheKey,
|
|
55
|
+
RequestResolver.withCache(self, options)
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
})
|