@effect-app/infra 3.10.0 → 4.0.0-beta.1
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 +19 -0
- package/_check.sh +3 -0
- package/dist/CUPS.d.ts +22 -12
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +28 -29
- package/dist/Emailer/Sendgrid.js +13 -12
- package/dist/Emailer/service.d.ts +3 -13
- package/dist/Emailer/service.d.ts.map +1 -1
- package/dist/Emailer/service.js +3 -3
- package/dist/MainFiberSet.d.ts +18 -41
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +10 -10
- package/dist/Model/Repository/ext.d.ts.map +1 -1
- package/dist/Model/Repository/ext.js +13 -10
- package/dist/Model/Repository/internal/internal.d.ts +5 -5
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +52 -42
- package/dist/Model/Repository/legacy.d.ts +9 -9
- package/dist/Model/Repository/legacy.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.d.ts +4 -4
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +1 -1
- package/dist/Model/Repository/service.d.ts +11 -11
- package/dist/Model/Repository/service.d.ts.map +1 -1
- package/dist/Model/Repository/validation.d.ts +17 -47
- package/dist/Model/Repository/validation.d.ts.map +1 -1
- package/dist/Model/Repository/validation.js +2 -2
- package/dist/Model/query/dsl.d.ts +22 -22
- package/dist/Model/query/dsl.d.ts.map +1 -1
- package/dist/Model/query/dsl.js +1 -1
- package/dist/Model/query/new-kid-interpreter.d.ts +1 -1
- package/dist/Model/query/new-kid-interpreter.js +7 -7
- package/dist/Operations.d.ts +22 -63
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Operations.js +14 -14
- package/dist/OperationsRepo.d.ts +23 -7
- package/dist/OperationsRepo.d.ts.map +1 -1
- package/dist/OperationsRepo.js +4 -5
- package/dist/QueueMaker/SQLQueue.d.ts +6 -8
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +20 -24
- package/dist/QueueMaker/errors.js +1 -1
- package/dist/QueueMaker/memQueue.d.ts +2 -5
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +22 -26
- package/dist/QueueMaker/sbqueue.d.ts +2 -5
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +24 -28
- package/dist/RequestContext.d.ts +28 -41
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +4 -4
- package/dist/RequestFiberSet.d.ts +23 -50
- package/dist/RequestFiberSet.d.ts.map +1 -1
- package/dist/RequestFiberSet.js +14 -14
- package/dist/Store/ContextMapContainer.d.ts +4 -4
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +5 -5
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +21 -28
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +12 -16
- package/dist/Store/Memory.d.ts +2 -2
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +25 -33
- package/dist/Store/index.js +2 -2
- package/dist/Store/service.d.ts +9 -34
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +4 -4
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +10 -2
- package/dist/adapters/SQL/Model.d.ts +106 -162
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +92 -130
- package/dist/adapters/ServiceBus.d.ts +13 -44
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +13 -15
- package/dist/adapters/cosmos-client.d.ts +7 -3
- package/dist/adapters/cosmos-client.d.ts.map +1 -1
- package/dist/adapters/cosmos-client.js +5 -4
- package/dist/adapters/logger.d.ts +1 -1
- package/dist/adapters/logger.d.ts.map +1 -1
- package/dist/adapters/memQueue.d.ts +8 -21
- package/dist/adapters/memQueue.d.ts.map +1 -1
- package/dist/adapters/memQueue.js +4 -4
- package/dist/adapters/mongo-client.d.ts +6 -6
- package/dist/adapters/mongo-client.d.ts.map +1 -1
- package/dist/adapters/mongo-client.js +5 -4
- package/dist/adapters/redis-client.d.ts +14 -4
- package/dist/adapters/redis-client.d.ts.map +1 -1
- package/dist/adapters/redis-client.js +19 -18
- package/dist/api/ContextProvider.d.ts +10 -15
- package/dist/api/ContextProvider.d.ts.map +1 -1
- package/dist/api/ContextProvider.js +8 -8
- package/dist/api/codec.d.ts +1 -1
- package/dist/api/codec.d.ts.map +1 -1
- package/dist/api/codec.js +1 -1
- package/dist/api/internal/RequestContextMiddleware.d.ts +1 -1
- package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
- package/dist/api/internal/auth.d.ts +3 -3
- package/dist/api/internal/auth.d.ts.map +1 -1
- package/dist/api/internal/auth.js +8 -8
- package/dist/api/internal/events.d.ts +2 -2
- package/dist/api/internal/events.d.ts.map +1 -1
- package/dist/api/internal/events.js +9 -9
- package/dist/api/internal/health.d.ts +1 -1
- package/dist/api/internal/health.d.ts.map +1 -1
- package/dist/api/internal/health.js +2 -2
- package/dist/api/layerUtils.d.ts +14 -14
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +5 -5
- package/dist/api/middlewares.d.ts +0 -75
- package/dist/api/middlewares.d.ts.map +1 -1
- package/dist/api/middlewares.js +6 -51
- package/dist/api/reportError.js +4 -4
- 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 +6 -7
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +9 -13
- package/dist/api/routing/schema/jwt.d.ts +1 -1
- package/dist/api/routing/schema/jwt.d.ts.map +1 -1
- package/dist/api/routing/schema/jwt.js +5 -4
- package/dist/api/routing/utils.d.ts +2 -2
- package/dist/api/routing/utils.d.ts.map +1 -1
- package/dist/api/routing/utils.js +10 -8
- package/dist/api/routing.d.ts +39 -37
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +17 -21
- package/dist/api/setupRequest.d.ts +4 -6
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +10 -9
- package/dist/arbs.d.ts +3 -3
- package/dist/arbs.d.ts.map +1 -1
- package/dist/arbs.js +2 -2
- package/dist/errorReporter.d.ts +1 -1
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +12 -12
- package/dist/fileUtil.d.ts +6 -6
- package/dist/fileUtil.d.ts.map +1 -1
- package/dist/logger/jsonLogger.d.ts.map +1 -1
- package/dist/logger/jsonLogger.js +19 -18
- package/dist/logger/logFmtLogger.d.ts.map +1 -1
- package/dist/logger/logFmtLogger.js +11 -13
- package/dist/logger/shared.d.ts +2 -2
- package/dist/logger/shared.d.ts.map +1 -1
- package/dist/logger/shared.js +7 -9
- package/dist/logger.d.ts +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/rateLimit.d.ts +2 -2
- package/dist/rateLimit.d.ts.map +1 -1
- package/dist/rateLimit.js +5 -5
- package/dist/test.d.ts +2 -2
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +6 -24
- package/package.json +19 -22
- package/src/CUPS.ts +15 -14
- package/src/Emailer/Sendgrid.ts +15 -13
- package/src/Emailer/service.ts +3 -3
- package/src/MainFiberSet.ts +16 -12
- package/src/Model/Repository/ext.ts +18 -16
- package/src/Model/Repository/internal/internal.ts +80 -69
- package/src/Model/Repository/legacy.ts +9 -9
- package/src/Model/Repository/makeRepo.ts +5 -5
- package/src/Model/Repository/service.ts +12 -12
- package/src/Model/Repository/validation.ts +1 -1
- package/src/Model/query/dsl.ts +13 -13
- package/src/Model/query/new-kid-interpreter.ts +8 -8
- package/src/Operations.ts +17 -14
- package/src/OperationsRepo.ts +3 -4
- package/src/QueueMaker/SQLQueue.ts +86 -89
- package/src/QueueMaker/errors.ts +1 -1
- package/src/QueueMaker/memQueue.ts +90 -91
- package/src/QueueMaker/sbqueue.ts +90 -92
- package/src/RequestContext.ts +3 -3
- package/src/RequestFiberSet.ts +17 -15
- package/src/Store/ContextMapContainer.ts +4 -4
- package/src/Store/Cosmos.ts +20 -27
- package/src/Store/Disk.ts +13 -17
- package/src/Store/Memory.ts +28 -34
- package/src/Store/index.ts +1 -1
- package/src/Store/service.ts +4 -4
- package/src/Store/utils.ts +9 -5
- package/src/adapters/SQL/Model.ts +255 -268
- package/src/adapters/ServiceBus.ts +17 -20
- package/src/adapters/cosmos-client.ts +5 -5
- package/src/adapters/memQueue.ts +3 -3
- package/src/adapters/mongo-client.ts +5 -5
- package/src/adapters/redis-client.ts +25 -19
- package/src/api/ContextProvider.ts +24 -34
- package/src/api/codec.ts +1 -1
- package/src/api/internal/auth.ts +11 -13
- package/src/api/internal/events.ts +11 -11
- package/src/api/internal/health.ts +1 -1
- package/src/api/layerUtils.ts +20 -20
- package/src/api/middlewares.ts +0 -97
- package/src/api/reportError.ts +3 -3
- package/src/api/routing/middleware/RouterMiddleware.ts +5 -6
- package/src/api/routing/middleware/middleware.ts +13 -25
- package/src/api/routing/schema/jwt.ts +9 -7
- package/src/api/routing/utils.ts +12 -10
- package/src/api/routing.ts +77 -79
- package/src/api/setupRequest.ts +9 -8
- package/src/arbs.ts +3 -3
- package/src/errorReporter.ts +12 -12
- package/src/logger/jsonLogger.ts +18 -17
- package/src/logger/logFmtLogger.ts +10 -12
- package/src/logger/shared.ts +6 -8
- package/src/rateLimit.ts +7 -7
- package/src/test.ts +7 -29
- package/test/contextProvider.test.ts +77 -70
- package/test/controller.test.ts +51 -39
- package/test/dist/contextProvider.test.d.ts.map +1 -1
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/fixtures.d.ts +33 -81
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +9 -8
- package/test/dist/query.test.d.ts.map +1 -1
- package/test/dist/rawQuery.test.d.ts.map +1 -1
- package/test/dist/requires.test.d.ts.map +1 -1
- package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
- package/test/fixtures.ts +9 -7
- package/test/query.test.ts +49 -41
- package/test/rawQuery.test.ts +44 -40
- package/test/requires.test.ts +40 -31
- package/test/rpc-multi-middleware.test.ts +13 -14
- package/test/validateSample.test.ts +2 -2
- package/tsconfig.json +1 -27
- package/dist/api/internal/middlewares.d.ts +0 -15
- package/dist/api/internal/middlewares.d.ts.map +0 -1
- package/dist/api/internal/middlewares.js +0 -168
- package/src/api/internal/middlewares.ts +0 -279
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/prefer-promise-reject-errors */
|
|
2
2
|
import { type OperationOptionsBase, type ProcessErrorArgs, ServiceBusClient, type ServiceBusMessage, type ServiceBusMessageBatch, type ServiceBusReceivedMessage, type ServiceBusReceiver } from "@azure/service-bus"
|
|
3
|
-
import { Cause,
|
|
3
|
+
import { Cause, Effect, Exit, FiberSet, Layer, type Scope, ServiceMap } from "effect-app"
|
|
4
4
|
import { InfraLogger } from "../logger.js"
|
|
5
5
|
|
|
6
6
|
const withSpanAndLog = (name: string) => <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
7
7
|
Effect.logInfo(name).pipe(
|
|
8
|
-
Effect.
|
|
8
|
+
Effect.andThen(self),
|
|
9
9
|
Effect.tap(Effect.logInfo(name + " done")),
|
|
10
10
|
Effect.withLogSpan(name),
|
|
11
11
|
Effect.withSpan(name)
|
|
@@ -18,9 +18,10 @@ function makeClient(url: string) {
|
|
|
18
18
|
)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export class ServiceBusClientTag
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
export class ServiceBusClientTag
|
|
22
|
+
extends ServiceMap.Opaque<ServiceBusClientTag, ServiceBusClient>()("@services/Client", { make: makeClient })
|
|
23
|
+
{
|
|
24
|
+
static readonly layer = (url: string) => this.toLayer(this.make(url))
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
function makeSender_(queueName: string) {
|
|
@@ -49,27 +50,23 @@ const makeSender = (name: string) =>
|
|
|
49
50
|
return { name, sendMessages }
|
|
50
51
|
})
|
|
51
52
|
|
|
52
|
-
export class Sender extends
|
|
53
|
+
export class Sender extends ServiceMap.Opaque<Sender, {
|
|
53
54
|
name: string
|
|
54
55
|
sendMessages: (
|
|
55
56
|
messages: ServiceBusMessage | ServiceBusMessage[] | ServiceBusMessageBatch,
|
|
56
57
|
options?: Omit<OperationOptionsBase, "abortSignal">
|
|
57
58
|
) => Effect.Effect<void, never, never>
|
|
58
|
-
}>() {
|
|
59
|
-
static readonly
|
|
60
|
-
static readonly layer = (name: string) => this.toLayerScoped(makeSender(name))
|
|
59
|
+
}>()("Sender", { make: makeSender }) {
|
|
60
|
+
static readonly layer = (name: string) => this.toLayer(this.make(name))
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export const SenderTag = <Id>() => <Key extends string>(queueName: Key) => {
|
|
64
|
-
const tag =
|
|
65
|
-
Id,
|
|
66
|
-
Sender
|
|
67
|
-
>()
|
|
64
|
+
const tag = ServiceMap.Service<Id, Sender>(`ServiceBus.Sender.${queueName}`)
|
|
68
65
|
|
|
69
66
|
return Object.assign(tag, {
|
|
70
|
-
layer: Layer.
|
|
67
|
+
layer: Layer.effect(
|
|
71
68
|
tag,
|
|
72
|
-
|
|
69
|
+
Sender.make(queueName).pipe(Effect.map((_) => Sender.of(_)))
|
|
73
70
|
)
|
|
74
71
|
})
|
|
75
72
|
}
|
|
@@ -133,7 +130,7 @@ const makeReceiver = (name: string) =>
|
|
|
133
130
|
resolve(exit.value)
|
|
134
131
|
} else {
|
|
135
132
|
// disable @typescript-eslint/prefer-promise-reject-errors
|
|
136
|
-
reject(Cause.pretty(exit.cause
|
|
133
|
+
reject(Cause.pretty(exit.cause))
|
|
137
134
|
}
|
|
138
135
|
})
|
|
139
136
|
)
|
|
@@ -148,7 +145,7 @@ const makeReceiver = (name: string) =>
|
|
|
148
145
|
hndlr
|
|
149
146
|
.processError(err)
|
|
150
147
|
.pipe(
|
|
151
|
-
Effect.
|
|
148
|
+
Effect.catchCause((cause) => Effect.logError(`ServiceBus Error ${sessionId}`, cause))
|
|
152
149
|
)
|
|
153
150
|
),
|
|
154
151
|
processMessage: (msg) => runEffect(hndlr.processMessage(msg))
|
|
@@ -166,7 +163,7 @@ const makeReceiver = (name: string) =>
|
|
|
166
163
|
}
|
|
167
164
|
})
|
|
168
165
|
|
|
169
|
-
export class Receiver extends
|
|
166
|
+
export class Receiver extends ServiceMap.Opaque<Receiver, {
|
|
170
167
|
name: string
|
|
171
168
|
make: (waitTillEmpty: Effect.Effect<void>) => Effect.Effect<ServiceBusReceiver, never, Scope.Scope>
|
|
172
169
|
makeSession: (
|
|
@@ -177,13 +174,13 @@ export class Receiver extends Context.TagId("Receiver")<Receiver, {
|
|
|
177
174
|
hndlr: MessageHandlers<RMsg, RErr>,
|
|
178
175
|
sessionId?: string
|
|
179
176
|
): Effect.Effect<void, never, Scope.Scope | RMsg | RErr>
|
|
180
|
-
}>() {
|
|
177
|
+
}>()("Receiver") {
|
|
181
178
|
static readonly make = makeReceiver
|
|
182
179
|
static readonly layer = (name: string) => this.toLayer(makeReceiver(name))
|
|
183
180
|
}
|
|
184
181
|
|
|
185
182
|
export const ReceiverTag = <Id>() => <Key extends string>(queueName: Key) => {
|
|
186
|
-
const tag =
|
|
183
|
+
const tag = ServiceMap.Service<Id, Receiver>(`ServiceBus.Receiver.${queueName}`)
|
|
187
184
|
|
|
188
185
|
return Object.assign(tag, {
|
|
189
186
|
layer: Layer.effect(
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { CosmosClient as ComosClient_ } from "@azure/cosmos"
|
|
2
|
-
import {
|
|
2
|
+
import { Effect, Layer, ServiceMap } from "effect-app"
|
|
3
3
|
|
|
4
4
|
const withClient = (url: string) => Effect.sync(() => new ComosClient_(url))
|
|
5
5
|
|
|
6
6
|
export const makeCosmosClient = (url: string, dbName: string) =>
|
|
7
7
|
Effect.map(withClient(url), (x) => ({ db: x.database(dbName) }))
|
|
8
8
|
|
|
9
|
-
export
|
|
9
|
+
export class CosmosClient extends ServiceMap.Service<CosmosClient, {
|
|
10
|
+
readonly db: ReturnType<InstanceType<typeof ComosClient_>["database"]>
|
|
11
|
+
}>()("@services/CosmosClient") {}
|
|
10
12
|
|
|
11
|
-
export const
|
|
12
|
-
|
|
13
|
-
export const db = Effect.map(CosmosClient, (_) => _.db)
|
|
13
|
+
export const db = CosmosClient.asEffect().pipe(Effect.map((_) => _.db))
|
|
14
14
|
|
|
15
15
|
export const CosmosClientLayer = (cosmosUrl: string, dbName: string) =>
|
|
16
16
|
Layer.effect(CosmosClient, makeCosmosClient(cosmosUrl, dbName))
|
package/src/adapters/memQueue.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Effect, type Queue, ServiceMap } from "effect-app"
|
|
2
2
|
import * as Q from "effect/Queue"
|
|
3
3
|
|
|
4
4
|
const make = Effect
|
|
@@ -16,6 +16,6 @@ const make = Effect
|
|
|
16
16
|
}
|
|
17
17
|
})
|
|
18
18
|
|
|
19
|
-
export class MemQueue extends
|
|
20
|
-
static readonly Live = this.toLayer()
|
|
19
|
+
export class MemQueue extends ServiceMap.Opaque<MemQueue>()("effect-app/MemQueue", { make }) {
|
|
20
|
+
static readonly Live = this.toLayer(this.make)
|
|
21
21
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Effect, Layer, ServiceMap } from "effect-app"
|
|
2
2
|
import { MongoClient as MongoClient_ } from "mongodb"
|
|
3
3
|
|
|
4
4
|
// TODO: we should probably share a single client...
|
|
@@ -15,9 +15,9 @@ const withClient = (url: string) =>
|
|
|
15
15
|
|
|
16
16
|
const makeMongoClient = (url: string, dbName?: string) => Effect.map(withClient(url), (x) => ({ db: x.db(dbName) }))
|
|
17
17
|
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
export class MongoClient extends ServiceMap.Service<MongoClient, {
|
|
19
|
+
readonly db: ReturnType<InstanceType<typeof MongoClient_>["db"]>
|
|
20
|
+
}>()("@services/MongoClient") {}
|
|
21
21
|
|
|
22
22
|
export const MongoClientLive = (mongoUrl: string, dbName?: string) =>
|
|
23
|
-
Layer.
|
|
23
|
+
Layer.effect(MongoClient, makeMongoClient(mongoUrl, dbName))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Data, Effect, Layer, Option, ServiceMap } from "effect-app"
|
|
2
2
|
import type { RedisClient as Client } from "redis"
|
|
3
3
|
import Redlock from "redlock"
|
|
4
4
|
|
|
@@ -17,21 +17,21 @@ export const makeRedisClient = (makeClient: () => Client) =>
|
|
|
17
17
|
|
|
18
18
|
function get(key: string) {
|
|
19
19
|
return Effect
|
|
20
|
-
.
|
|
20
|
+
.callback<Option.Option<string>, ConnectionException>((res) => {
|
|
21
21
|
client.get(key, (err, v) =>
|
|
22
22
|
err
|
|
23
|
-
? res(new ConnectionException(err))
|
|
24
|
-
: res(Effect.sync(() => Option.
|
|
23
|
+
? res(Effect.fail(new ConnectionException(err)))
|
|
24
|
+
: res(Effect.sync(() => Option.fromNullishOr(v))))
|
|
25
25
|
})
|
|
26
26
|
.pipe(Effect.uninterruptible)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function set(key: string, val: string) {
|
|
30
30
|
return Effect
|
|
31
|
-
.
|
|
31
|
+
.callback<void, ConnectionException>((res) => {
|
|
32
32
|
client.set(key, val, (err) =>
|
|
33
33
|
err
|
|
34
|
-
? res(new ConnectionException(err))
|
|
34
|
+
? res(Effect.fail(new ConnectionException(err)))
|
|
35
35
|
: res(Effect.sync(() => void 0)))
|
|
36
36
|
})
|
|
37
37
|
.pipe(Effect.uninterruptible)
|
|
@@ -39,10 +39,10 @@ export const makeRedisClient = (makeClient: () => Client) =>
|
|
|
39
39
|
|
|
40
40
|
function hset(key: string, field: string, value: string) {
|
|
41
41
|
return Effect
|
|
42
|
-
.
|
|
42
|
+
.callback<void, ConnectionException>((res) => {
|
|
43
43
|
client.hset(key, field, value, (err) =>
|
|
44
44
|
err
|
|
45
|
-
? res(new ConnectionException(err))
|
|
45
|
+
? res(Effect.fail(new ConnectionException(err)))
|
|
46
46
|
: res(Effect.sync(() => void 0)))
|
|
47
47
|
})
|
|
48
48
|
.pipe(Effect.uninterruptible)
|
|
@@ -50,22 +50,22 @@ export const makeRedisClient = (makeClient: () => Client) =>
|
|
|
50
50
|
|
|
51
51
|
function hget(key: string, field: string) {
|
|
52
52
|
return Effect
|
|
53
|
-
.
|
|
53
|
+
.callback<Option.Option<string>, ConnectionException>((res) => {
|
|
54
54
|
client.hget(key, field, (err, v) =>
|
|
55
55
|
err
|
|
56
|
-
? res(new ConnectionException(err))
|
|
57
|
-
: res(Effect.sync(() => Option.
|
|
56
|
+
? res(Effect.fail(new ConnectionException(err)))
|
|
57
|
+
: res(Effect.sync(() => Option.fromNullishOr(v))))
|
|
58
58
|
})
|
|
59
59
|
.pipe(Effect.uninterruptible)
|
|
60
60
|
}
|
|
61
61
|
function hmgetAll(key: string) {
|
|
62
62
|
return Effect
|
|
63
|
-
.
|
|
63
|
+
.callback<Option.Option<{ [key: string]: string }>, ConnectionException>(
|
|
64
64
|
(res) => {
|
|
65
65
|
client.hgetall(key, (err, v) =>
|
|
66
66
|
err
|
|
67
|
-
? res(new ConnectionException(err))
|
|
68
|
-
: res(Effect.sync(() => Option.
|
|
67
|
+
? res(Effect.fail(new ConnectionException(err)))
|
|
68
|
+
: res(Effect.sync(() => Option.fromNullishOr(v))))
|
|
69
69
|
}
|
|
70
70
|
)
|
|
71
71
|
.pipe(Effect.uninterruptible)
|
|
@@ -84,18 +84,24 @@ export const makeRedisClient = (makeClient: () => Client) =>
|
|
|
84
84
|
}),
|
|
85
85
|
(cl) =>
|
|
86
86
|
Effect
|
|
87
|
-
.
|
|
87
|
+
.callback<void, Error>((res) => {
|
|
88
88
|
cl.client.quit((err) => res(err ? Effect.fail(err) : Effect.void))
|
|
89
89
|
})
|
|
90
90
|
.pipe(Effect.uninterruptible, Effect.orDie)
|
|
91
91
|
)
|
|
92
92
|
|
|
93
|
-
export
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
export class RedisClient extends ServiceMap.Service<RedisClient, {
|
|
94
|
+
readonly client: Client
|
|
95
|
+
readonly lock: Redlock
|
|
96
|
+
readonly get: (key: string) => Effect.Effect<Option.Option<string>, ConnectionException>
|
|
97
|
+
readonly hget: (key: string, field: string) => Effect.Effect<Option.Option<string>, ConnectionException>
|
|
98
|
+
readonly hset: (key: string, field: string, value: string) => Effect.Effect<void, ConnectionException>
|
|
99
|
+
readonly hmgetAll: (key: string) => Effect.Effect<Option.Option<{ [key: string]: string }>, ConnectionException>
|
|
100
|
+
readonly set: (key: string, val: string) => Effect.Effect<void, ConnectionException>
|
|
101
|
+
}>()("@services/RedisClient") {}
|
|
96
102
|
|
|
97
103
|
export const RedisClientLayer = (storageUrl: string) =>
|
|
98
|
-
Layer.
|
|
104
|
+
Layer.effect(RedisClient, makeRedisClient(makeRedis(storageUrl)))
|
|
99
105
|
|
|
100
106
|
function createClient(makeClient: () => Client) {
|
|
101
107
|
const client = makeClient()
|
|
@@ -1,25 +1,15 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import {
|
|
2
|
+
import { Effect, Layer, type NonEmptyReadonlyArray, pipe, type Scope, ServiceMap } from "effect-app"
|
|
3
3
|
|
|
4
|
-
import { type
|
|
4
|
+
import { type HttpRouter } from "effect-app/http"
|
|
5
5
|
import { type EffectGenUtils } from "effect-app/utils/gen"
|
|
6
|
-
import { type
|
|
7
|
-
import { type YieldWrap } from "effect/Utils"
|
|
6
|
+
import { type Yieldable } from "effect/Effect"
|
|
8
7
|
import { type ContextTagWithDefault, type GetContext, type LayerUtils, mergeContexts } from "./layerUtils.js"
|
|
9
8
|
|
|
10
|
-
// // the context provider provides additional stuff
|
|
11
|
-
// export type ContextProviderShape<ContextProviderA, ContextProviderR> = Effect.Effect<
|
|
12
|
-
// Context.Context<ContextProviderA>,
|
|
13
|
-
// never, // no errors are allowed
|
|
14
|
-
// ContextProviderR
|
|
15
|
-
// >
|
|
16
|
-
|
|
17
9
|
export interface ContextProviderId {
|
|
18
10
|
_tag: "ContextProvider"
|
|
19
11
|
}
|
|
20
12
|
|
|
21
|
-
// ContextTagWithDefault.Base<Effect.Effect<Context.Context<infer _1>, never, infer _R> & { _tag: infer _2 }>
|
|
22
|
-
|
|
23
13
|
/**
|
|
24
14
|
* TDeps is an array of services with Default implementation
|
|
25
15
|
* each service is an effect which builds some context for each request
|
|
@@ -33,25 +23,22 @@ type TDepsArr<TDeps extends ReadonlyArray<any>> = {
|
|
|
33
23
|
// E = never => the context provided cannot trigger errors
|
|
34
24
|
// TODO: remove HttpLayerRouter.Provided - it's not even relevant outside of Http context, while ContextProviders are for anywhere. Only support Scope.Scope?
|
|
35
25
|
// _R extends HttpLayerRouter.Provided => the context provided can only have what HttpLayerRouter.Provided provides as requirements
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
) ? [_R] extends [HttpLayerRouter.Provided] ? TDeps[K]
|
|
26
|
+
ContextTagWithDefault.Base<Effect.Effect<ServiceMap.ServiceMap<infer _1>, never, infer _R>> // & { _tag: infer _2 }>
|
|
27
|
+
? [_R] extends [HttpRouter.Provided] ? TDeps[K]
|
|
39
28
|
: `HttpLayerRouter.Provided is the only requirement ${TDeps[K]["Service"][
|
|
40
29
|
"_tag"
|
|
41
30
|
]}'s returned effect can have`
|
|
42
31
|
: TDeps[K] extends (
|
|
43
32
|
ContextTagWithDefault.Base<
|
|
44
|
-
|
|
33
|
+
(() => Generator<
|
|
45
34
|
infer _YW,
|
|
46
35
|
infer _1,
|
|
47
36
|
infer _2
|
|
48
37
|
>)
|
|
49
|
-
|
|
50
|
-
>
|
|
38
|
+
> // & { _tag: infer _3 }
|
|
51
39
|
) // [_YW] extends [never] if no yield* is used and just some context is returned
|
|
52
40
|
? [_YW] extends [never] ? TDeps[K]
|
|
53
|
-
: [_YW] extends [
|
|
54
|
-
? [_R] extends [HttpLayerRouter.Provided] ? TDeps[K]
|
|
41
|
+
: [_YW] extends [Yieldable<any, infer _2, never, infer _R>] ? [_R] extends [HttpRouter.Provided] ? TDeps[K]
|
|
55
42
|
: `HttpLayerRouter.Provided is the only requirement ${TDeps[K]["Service"][
|
|
56
43
|
"_tag"
|
|
57
44
|
]}'s returned effect can have`
|
|
@@ -70,9 +57,10 @@ export const mergeContextProviders = <
|
|
|
70
57
|
effect: Effect.Effect<
|
|
71
58
|
Effect.Effect<
|
|
72
59
|
// we need to merge all contexts into one
|
|
73
|
-
|
|
60
|
+
// v4: Service.Shape extracts the service value type (v3's Tag.Identifier)
|
|
61
|
+
ServiceMap.ServiceMap<GetContext<EffectGenUtils.Success<ServiceMap.Service.Shape<TDeps[number]>>>>,
|
|
74
62
|
never,
|
|
75
|
-
EffectGenUtils.
|
|
63
|
+
EffectGenUtils.ServiceMap<ServiceMap.Service.Shape<TDeps[number]>>
|
|
76
64
|
>,
|
|
77
65
|
LayerUtils.GetLayersError<{ [K in keyof TDeps]: TDeps[K]["Default"] }>,
|
|
78
66
|
LayerUtils.GetLayersSuccess<{ [K in keyof TDeps]: TDeps[K]["Default"] }>
|
|
@@ -90,26 +78,26 @@ export const mergeContextProviders = <
|
|
|
90
78
|
handle: handle[Symbol.toStringTag] === "GeneratorFunction" ? Effect.fnUntraced(handle)() : handle
|
|
91
79
|
}
|
|
92
80
|
))
|
|
93
|
-
// services are effects which return some
|
|
81
|
+
// services are effects which return some ServiceMap.ServiceMap<...>
|
|
94
82
|
const context = yield* mergeContexts(services as any)
|
|
95
83
|
return context
|
|
96
84
|
})
|
|
97
85
|
}) as any
|
|
98
86
|
})
|
|
99
87
|
|
|
100
|
-
// Effect Rpc Middleware: for single tag providing, we could use Provides, for providing
|
|
88
|
+
// Effect Rpc Middleware: for single tag providing, we could use Provides, for providing ServiceMap or Layer (bad boy) we could use Wrap..
|
|
101
89
|
export const ContextProvider = <
|
|
102
90
|
ContextProviderA,
|
|
103
91
|
MakeContextProviderE,
|
|
104
92
|
MakeContextProviderR,
|
|
105
93
|
ContextProviderR extends Scope.Scope,
|
|
106
|
-
Dependencies extends NonEmptyReadonlyArray<Layer.
|
|
94
|
+
Dependencies extends NonEmptyReadonlyArray<Layer.Any>
|
|
107
95
|
>(
|
|
108
96
|
input: {
|
|
109
97
|
effect: Effect.Effect<
|
|
110
98
|
| Effect.Effect<ContextProviderA, never, ContextProviderR>
|
|
111
99
|
| (() => Generator<
|
|
112
|
-
|
|
100
|
+
Yieldable<any, any, never, ContextProviderR>,
|
|
113
101
|
ContextProviderA,
|
|
114
102
|
any
|
|
115
103
|
>),
|
|
@@ -119,7 +107,7 @@ export const ContextProvider = <
|
|
|
119
107
|
dependencies?: Dependencies
|
|
120
108
|
}
|
|
121
109
|
) => {
|
|
122
|
-
const ctx =
|
|
110
|
+
const ctx = ServiceMap.Service<
|
|
123
111
|
ContextProviderId,
|
|
124
112
|
Effect.Effect<ContextProviderA, never, ContextProviderR>
|
|
125
113
|
>(
|
|
@@ -128,10 +116,10 @@ export const ContextProvider = <
|
|
|
128
116
|
const e = input.effect.pipe(
|
|
129
117
|
Effect.map((eg) => (eg as any)[Symbol.toStringTag] === "GeneratorFunction" ? Effect.fnUntraced(eg as any)() : eg)
|
|
130
118
|
)
|
|
131
|
-
const l = Layer.
|
|
119
|
+
const l = Layer.effect(ctx, e as any)
|
|
132
120
|
return Object.assign(ctx, {
|
|
133
121
|
Default: l.pipe(
|
|
134
|
-
input.dependencies ? Layer.provide(input.dependencies) as any : (_) => _
|
|
122
|
+
input.dependencies ? Layer.provide([...input.dependencies] as [Layer.Any, ...Layer.Any[]]) as any : (_: any) => _
|
|
135
123
|
) satisfies Layer.Layer<
|
|
136
124
|
ContextProviderId,
|
|
137
125
|
| MakeContextProviderE
|
|
@@ -157,16 +145,18 @@ export const MergedContextProvider = <
|
|
|
157
145
|
ContextProviderId,
|
|
158
146
|
Effect.Effect<
|
|
159
147
|
// we need to merge all contexts into one
|
|
160
|
-
|
|
148
|
+
// v4: Service.Shape extracts the service value type (v3's Tag.Identifier)
|
|
149
|
+
ServiceMap.ServiceMap<GetContext<EffectGenUtils.Success<ServiceMap.Service.Shape<TDeps[number]>>>>,
|
|
161
150
|
never,
|
|
162
|
-
EffectGenUtils.
|
|
151
|
+
EffectGenUtils.ServiceMap<ServiceMap.Service.Shape<TDeps[number]>>
|
|
163
152
|
>,
|
|
164
153
|
LayerUtils.GetLayersError<{ [K in keyof TDeps]: TDeps[K]["Default"] }>,
|
|
154
|
+
// v4: Identifier here is correct — it's the nominal service identity for layer provide/exclude
|
|
165
155
|
| Exclude<
|
|
166
|
-
|
|
156
|
+
ServiceMap.Service.Identifier<TDeps[number]>,
|
|
167
157
|
LayerUtils.GetLayersSuccess<{ [K in keyof TDeps]: TDeps[K]["Default"] }>
|
|
168
158
|
>
|
|
169
159
|
| LayerUtils.GetLayersContext<{ [K in keyof TDeps]: TDeps[K]["Default"] }>
|
|
170
160
|
>
|
|
171
161
|
|
|
172
|
-
export const EmptyContextProvider = ContextProvider({ effect: Effect.succeed(Effect.succeed(
|
|
162
|
+
export const EmptyContextProvider = ContextProvider({ effect: Effect.succeed(Effect.succeed(ServiceMap.empty())) })
|
package/src/api/codec.ts
CHANGED
package/src/api/internal/auth.ts
CHANGED
|
@@ -12,28 +12,28 @@ type Config = Parameters<typeof auth>[0]
|
|
|
12
12
|
export const checkJWTI = (config: Config) => {
|
|
13
13
|
const mw = auth(config)
|
|
14
14
|
return Effect.fnUntraced(function*(headers: HttpHeaders.Headers) {
|
|
15
|
-
return yield* Effect.
|
|
15
|
+
return yield* Effect.callback<
|
|
16
16
|
void,
|
|
17
17
|
InsufficientScopeError | InvalidRequestError | InvalidTokenError | UnauthorizedError
|
|
18
18
|
>(
|
|
19
|
-
(
|
|
19
|
+
(resume) => {
|
|
20
20
|
const next = (err?: unknown) => {
|
|
21
|
-
if (!err) return
|
|
21
|
+
if (!err) return resume(Effect.void)
|
|
22
22
|
if (
|
|
23
23
|
err instanceof InsufficientScopeError
|
|
24
24
|
|| err instanceof InvalidRequestError
|
|
25
25
|
|| err instanceof InvalidTokenError
|
|
26
26
|
|| err instanceof UnauthorizedError
|
|
27
27
|
) {
|
|
28
|
-
return
|
|
28
|
+
return resume(Effect.fail(err))
|
|
29
29
|
}
|
|
30
|
-
return
|
|
30
|
+
return resume(Effect.die(err))
|
|
31
31
|
}
|
|
32
32
|
const r = { headers, query: {}, body: {}, is: () => false, method: "POST" } // is("urlencoded")
|
|
33
33
|
try {
|
|
34
34
|
mw(r as any, {} as any, next)
|
|
35
35
|
} catch (e) {
|
|
36
|
-
return
|
|
36
|
+
return resume(Effect.die(e))
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
)
|
|
@@ -45,13 +45,11 @@ export const checkJwt = (config: Config) => {
|
|
|
45
45
|
return HttpMiddleware.make((app) =>
|
|
46
46
|
Effect.gen(function*() {
|
|
47
47
|
const req = yield* HttpServerRequest.HttpServerRequest
|
|
48
|
-
const response = yield* check(req.headers).pipe(Effect.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
})
|
|
54
|
-
)
|
|
48
|
+
const response = yield* check(req.headers).pipe(Effect.catch((e) =>
|
|
49
|
+
HttpServerResponse.json({ message: e.message }, {
|
|
50
|
+
status: e.status,
|
|
51
|
+
headers: HttpHeaders.fromInput(e.headers)
|
|
52
|
+
})
|
|
55
53
|
))
|
|
56
54
|
if (response) {
|
|
57
55
|
return response
|
|
@@ -5,18 +5,18 @@ import { setupRequestContextFromCurrent } from "../setupRequest.js"
|
|
|
5
5
|
|
|
6
6
|
// Tell the client to retry every 10 seconds if connectivity is lost
|
|
7
7
|
const setRetry = Stream.succeed("retry: 10000")
|
|
8
|
-
const keepAlive = Stream.
|
|
8
|
+
const keepAlive = Stream.fromEffectSchedule(Effect.succeed(":keep-alive"), Schedule.fixed(Duration.seconds(15)))
|
|
9
9
|
|
|
10
10
|
let connId = BigInt(0)
|
|
11
11
|
|
|
12
12
|
export const makeSSE = <A extends { id: any }, SI, SR>(
|
|
13
|
-
schema: S.
|
|
13
|
+
schema: S.Codec<A, SI, SR>
|
|
14
14
|
) =>
|
|
15
15
|
<E, R>(events: Stream.Stream<{ evt: A; namespace: string }, E, R>) =>
|
|
16
16
|
Effect
|
|
17
17
|
.gen(function*() {
|
|
18
18
|
const id = connId++
|
|
19
|
-
const ctx = yield* Effect.
|
|
19
|
+
const ctx = yield* Effect.services<R | SR>()
|
|
20
20
|
const res = HttpServerResponse.stream(
|
|
21
21
|
// workaround for different scoped behaviour for streams in Bun
|
|
22
22
|
// https://discord.com/channels/795981131316985866/1098177242598756412/1389646879675125861
|
|
@@ -28,29 +28,29 @@ export const makeSSE = <A extends { id: any }, SI, SR>(
|
|
|
28
28
|
|
|
29
29
|
const enc = new TextEncoder()
|
|
30
30
|
|
|
31
|
-
const encode = S.
|
|
31
|
+
const encode = S.encodeEffect(schema)
|
|
32
32
|
|
|
33
|
-
const eventStream = Stream.
|
|
33
|
+
const eventStream = Stream.mapEffect(
|
|
34
34
|
events,
|
|
35
35
|
(_) =>
|
|
36
36
|
encode(_.evt)
|
|
37
|
-
.pipe(Effect.
|
|
37
|
+
.pipe(Effect.map((evt) => `id: ${_.evt.id}\ndata: ${JSON.stringify(evt)}`))
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
const stream = pipe(
|
|
41
41
|
setRetry,
|
|
42
42
|
Stream.merge(keepAlive),
|
|
43
43
|
Stream.merge(eventStream, { haltStrategy: "either" }),
|
|
44
|
-
Stream.
|
|
44
|
+
Stream.tapCause((cause) => Effect.logError("SSE error", cause)),
|
|
45
45
|
Stream.map((_) => enc.encode(_ + "\n\n"))
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
return stream
|
|
49
49
|
})
|
|
50
50
|
.pipe(
|
|
51
|
-
Stream.
|
|
52
|
-
Stream.
|
|
53
|
-
Stream.
|
|
51
|
+
Stream.unwrap,
|
|
52
|
+
Stream.tapCause(reportError("Request")),
|
|
53
|
+
Stream.provide(ctx)
|
|
54
54
|
),
|
|
55
55
|
{
|
|
56
56
|
contentType: "text/event-stream",
|
|
@@ -64,4 +64,4 @@ export const makeSSE = <A extends { id: any }, SI, SR>(
|
|
|
64
64
|
)
|
|
65
65
|
return res
|
|
66
66
|
})
|
|
67
|
-
.pipe(Effect.
|
|
67
|
+
.pipe(Effect.tapCause(reportError("Request")), setupRequestContextFromCurrent("events"))
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HttpMiddleware, HttpServerResponse } from "effect-app/http"
|
|
2
2
|
|
|
3
3
|
export function serverHealth(version: string) {
|
|
4
|
-
return HttpServerResponse.
|
|
4
|
+
return HttpServerResponse.json({ version }).pipe(HttpMiddleware.withLoggerDisabled)
|
|
5
5
|
}
|
package/src/api/layerUtils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import {
|
|
2
|
+
import { Effect, type Layer, type NonEmptyReadonlyArray, Option, ServiceMap } from "effect-app"
|
|
3
3
|
import { InfraLogger } from "../logger.js"
|
|
4
4
|
|
|
5
5
|
// TODO: These LayerUtils are flaky, like in dependencies as a readonly array, it breaks when there are two entries
|
|
@@ -7,27 +7,27 @@ import { InfraLogger } from "../logger.js"
|
|
|
7
7
|
// and in general make sure `dependencies` are NonEmptyReadonlyArrays, so they infer to consts.
|
|
8
8
|
|
|
9
9
|
export namespace LayerUtils {
|
|
10
|
-
export type GetLayersSuccess<Layers extends ReadonlyArray<Layer.
|
|
11
|
-
NonEmptyReadonlyArray<Layer.
|
|
12
|
-
[k in keyof Layers]: Layer.
|
|
10
|
+
export type GetLayersSuccess<Layers extends ReadonlyArray<Layer.Any>> = Layers extends
|
|
11
|
+
NonEmptyReadonlyArray<Layer.Any> ? {
|
|
12
|
+
[k in keyof Layers]: Layer.Success<Layers[k]>
|
|
13
13
|
}[number]
|
|
14
|
-
: Layer.
|
|
14
|
+
: Layer.Success<Layers[number]>
|
|
15
15
|
|
|
16
|
-
export type GetLayersContext<Layers extends ReadonlyArray<Layer.
|
|
17
|
-
NonEmptyReadonlyArray<Layer.
|
|
18
|
-
[k in keyof Layers]: Layer.
|
|
16
|
+
export type GetLayersContext<Layers extends ReadonlyArray<Layer.Any>> = Layers extends
|
|
17
|
+
NonEmptyReadonlyArray<Layer.Any> ? {
|
|
18
|
+
[k in keyof Layers]: Layer.Services<Layers[k]>
|
|
19
19
|
}[number]
|
|
20
|
-
: Layer.
|
|
20
|
+
: Layer.Services<Layers[number]>
|
|
21
21
|
|
|
22
|
-
export type GetLayersError<Layers extends ReadonlyArray<Layer.
|
|
23
|
-
|
|
24
|
-
[k in keyof Layers]: Layer.
|
|
22
|
+
export type GetLayersError<Layers extends ReadonlyArray<Layer.Any>> = Layers extends NonEmptyReadonlyArray<Layer.Any>
|
|
23
|
+
? {
|
|
24
|
+
[k in keyof Layers]: Layer.Error<Layers[k]>
|
|
25
25
|
}[number]
|
|
26
|
-
: Layer.
|
|
26
|
+
: Layer.Error<Layers[number]>
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export type ContextTagWithDefault<Id, A, LayerE, LayerR> =
|
|
30
|
-
&
|
|
30
|
+
& ServiceMap.Service<Id, A>
|
|
31
31
|
& {
|
|
32
32
|
Default: Layer.Layer<Id, LayerE, LayerR>
|
|
33
33
|
}
|
|
@@ -36,29 +36,29 @@ export namespace ContextTagWithDefault {
|
|
|
36
36
|
export type Base<A> = ContextTagWithDefault<any, A, any, any>
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export type GetContext<T> = T extends
|
|
39
|
+
export type GetContext<T> = T extends ServiceMap.ServiceMap<infer Y> ? Y : never
|
|
40
40
|
|
|
41
41
|
export const mergeContexts = Effect.fnUntraced(
|
|
42
42
|
function*<
|
|
43
43
|
T extends readonly {
|
|
44
44
|
maker: any
|
|
45
|
-
handle: Effect.Effect<
|
|
45
|
+
handle: Effect.Effect<ServiceMap.ServiceMap<any> | Option.Option<ServiceMap.ServiceMap<any>>>
|
|
46
46
|
}[]
|
|
47
47
|
>(
|
|
48
48
|
makers: T
|
|
49
49
|
) {
|
|
50
|
-
let context =
|
|
50
|
+
let context = ServiceMap.empty()
|
|
51
51
|
for (const mw of makers) {
|
|
52
52
|
const ctx = yield* mw.handle.pipe(Effect.provide(context))
|
|
53
|
-
const moreContext =
|
|
53
|
+
const moreContext = ServiceMap.isServiceMap(ctx) ? Option.some(ctx) : ctx
|
|
54
54
|
yield* InfraLogger.logDebug(
|
|
55
55
|
"Built dynamic context for middleware" + (mw.maker.key ?? mw.maker),
|
|
56
56
|
Option.map(moreContext, (c) => (c as any).toJSON().services)
|
|
57
57
|
)
|
|
58
58
|
if (moreContext.value) {
|
|
59
|
-
context =
|
|
59
|
+
context = ServiceMap.merge(context, moreContext.value)
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
return context as
|
|
62
|
+
return context as ServiceMap.ServiceMap<Effect.Success<T[number]["handle"]>>
|
|
63
63
|
}
|
|
64
64
|
)
|