@effect-app/infra 2.76.0 → 2.78.0
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 +18 -0
- package/dist/Emailer/service.d.ts +1 -1
- package/dist/MainFiberSet.d.ts +1 -1
- package/dist/Operations.d.ts +1 -1
- package/dist/RequestFiberSet.d.ts +1 -1
- package/dist/Store/service.d.ts +2 -2
- package/dist/adapters/SQL/Model.d.ts +6 -6
- package/dist/adapters/ServiceBus.d.ts +1 -1
- package/dist/adapters/memQueue.d.ts +1 -1
- package/dist/api/layerUtils.d.ts +10 -2
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +25 -2
- package/dist/api/routing/middleware/ContextProvider.d.ts.map +1 -1
- package/dist/api/routing/middleware/ContextProvider.js +3 -2
- package/dist/api/routing/middleware/DynamicMiddleware.d.ts +4 -4
- package/dist/api/routing/middleware/DynamicMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/DynamicMiddleware.js +18 -12
- package/dist/api/routing/middleware/dynamic-middleware.d.ts +1 -9
- package/dist/api/routing/middleware/dynamic-middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/dynamic-middleware.js +4 -25
- package/dist/api/routing/middleware/generic-middleware.d.ts +10 -1
- package/dist/api/routing/middleware/generic-middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/generic-middleware.js +8 -9
- package/dist/api/routing/middleware/middleware.d.ts +4 -10
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +36 -48
- package/package.json +5 -5
- package/src/api/layerUtils.ts +39 -2
- package/src/api/routing/middleware/ContextProvider.ts +2 -2
- package/src/api/routing/middleware/DynamicMiddleware.ts +36 -30
- package/src/api/routing/middleware/dynamic-middleware.ts +3 -39
- package/src/api/routing/middleware/generic-middleware.ts +19 -12
- package/src/api/routing/middleware/middleware.ts +55 -86
- package/test/controller.test.ts +5 -9
- package/test/dist/controller.test.d.ts.map +1 -1
|
@@ -1,82 +1,71 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import { Cause, Context, Effect, ParseResult } from "effect-app";
|
|
2
|
-
import { HttpHeaders, HttpServerRequest } from "effect-app/http";
|
|
3
3
|
import { pretty } from "effect-app/utils";
|
|
4
4
|
import { logError, reportError } from "../../../errorReporter.js";
|
|
5
5
|
import { InfraLogger } from "../../../logger.js";
|
|
6
|
-
import { RequestCacheLayers } from "../../routing.js";
|
|
6
|
+
import { genericMiddleware, RequestCacheLayers } from "../../routing.js";
|
|
7
7
|
const logRequestError = logError("Request");
|
|
8
8
|
const reportRequestError = reportError("Request");
|
|
9
9
|
export class DevMode extends Context.Reference()("DevMode", { defaultValue: () => false }) {
|
|
10
10
|
}
|
|
11
|
+
// Effect Rpc Middleware: Wrap
|
|
11
12
|
export class RequestCacheMiddleware extends Effect.Service()("RequestCacheMiddleware", {
|
|
12
13
|
effect: Effect.gen(function* () {
|
|
13
|
-
return (
|
|
14
|
-
return yield*
|
|
15
|
-
});
|
|
14
|
+
return genericMiddleware(Effect.fnUntraced(function* (options) {
|
|
15
|
+
return yield* options.next.pipe(Effect.provide(RequestCacheLayers));
|
|
16
|
+
}));
|
|
16
17
|
})
|
|
17
18
|
}) {
|
|
18
19
|
}
|
|
20
|
+
// Effect Rpc Middleware: Wrap
|
|
19
21
|
export class ConfigureInterruptibility extends Effect.Service()("ConfigureInterruptibility", {
|
|
20
22
|
effect: Effect.gen(function* () {
|
|
21
|
-
return (
|
|
22
|
-
return yield*
|
|
23
|
+
return genericMiddleware(Effect.fnUntraced(function* (options) {
|
|
24
|
+
return yield* options.next.pipe(
|
|
23
25
|
// TODO: make this depend on query/command, and consider if middleware also should be affected. right now it's not.
|
|
24
26
|
Effect.uninterruptible);
|
|
25
|
-
});
|
|
26
|
-
})
|
|
27
|
-
}) {
|
|
28
|
-
}
|
|
29
|
-
export class CaptureHttpHeadersAsRpcHeaders extends Effect.Service()("CaptureHttpHeadersAsRpcHeaders", {
|
|
30
|
-
effect: Effect.gen(function* () {
|
|
31
|
-
return (handle, _moduleName) => Effect.fnUntraced(function* (input, rpcHeaders) {
|
|
32
|
-
// merge in the request headers
|
|
33
|
-
// we should consider if we should merge them into rpc headers on the Protocol layer instead.
|
|
34
|
-
const httpReq = yield* HttpServerRequest.HttpServerRequest;
|
|
35
|
-
const headers = HttpHeaders.merge(httpReq.headers, rpcHeaders);
|
|
36
|
-
return yield* handle(input, headers);
|
|
37
|
-
});
|
|
27
|
+
}));
|
|
38
28
|
})
|
|
39
29
|
}) {
|
|
40
30
|
}
|
|
31
|
+
// Effect Rpc Middleware: Wrap
|
|
41
32
|
export class MiddlewareLogger extends Effect.Service()("MiddlewareLogger", {
|
|
42
33
|
effect: Effect.gen(function* () {
|
|
43
|
-
return (
|
|
34
|
+
return genericMiddleware(Effect.fnUntraced(function* ({ headers, next, payload, rpc }) {
|
|
44
35
|
const devMode = yield* DevMode;
|
|
45
|
-
// merge in the request headers
|
|
46
|
-
// we should consider if we should merge them into rpc headers on the Protocol layer instead.
|
|
47
|
-
const httpReq = yield* HttpServerRequest.HttpServerRequest;
|
|
48
|
-
const headers = HttpHeaders.merge(httpReq.headers, rpcHeaders);
|
|
49
36
|
return yield* Effect
|
|
50
|
-
.annotateCurrentSpan("requestInput",
|
|
51
|
-
prev[key]
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
?
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
37
|
+
.annotateCurrentSpan("requestInput", typeof payload === "object" && payload !== null
|
|
38
|
+
? Object.entries(payload).reduce((prev, [key, value]) => {
|
|
39
|
+
prev[key] = key === "password"
|
|
40
|
+
? "<redacted>"
|
|
41
|
+
: typeof value === "string" || typeof value === "number" || typeof value === "boolean"
|
|
42
|
+
? typeof value === "string" && value.length > 256
|
|
43
|
+
? (value.substring(0, 253) + "...")
|
|
44
|
+
: value
|
|
45
|
+
: Array.isArray(value)
|
|
46
|
+
? `Array[${value.length}]`
|
|
47
|
+
: value === null || value === undefined
|
|
48
|
+
? `${value}`
|
|
49
|
+
: typeof value === "object" && value
|
|
50
|
+
? `Object[${Object.keys(value).length}]`
|
|
51
|
+
: typeof value;
|
|
52
|
+
return prev;
|
|
53
|
+
}, {})
|
|
54
|
+
: payload)
|
|
66
55
|
.pipe(
|
|
67
56
|
// can't use andThen due to some being a function and effect
|
|
68
|
-
Effect.zipRight(
|
|
57
|
+
Effect.zipRight(next),
|
|
69
58
|
// TODO: support ParseResult if the error channel of the request allows it.. but who would want that?
|
|
70
59
|
Effect.catchAll((_) => ParseResult.isParseError(_) ? Effect.die(_) : Effect.fail(_)), Effect.tapErrorCause((cause) => Cause.isFailure(cause) ? logRequestError(cause) : Effect.void), Effect.tapDefect((cause) => Effect
|
|
71
60
|
.all([
|
|
72
61
|
reportRequestError(cause, {
|
|
73
|
-
action:
|
|
62
|
+
action: rpc._tag
|
|
74
63
|
}),
|
|
75
64
|
InfraLogger
|
|
76
65
|
.logError("Finished request", cause)
|
|
77
66
|
.pipe(Effect.annotateLogs({
|
|
78
|
-
action:
|
|
79
|
-
req: pretty(
|
|
67
|
+
action: rpc._tag,
|
|
68
|
+
req: pretty(payload),
|
|
80
69
|
headers: pretty(headers)
|
|
81
70
|
// resHeaders: pretty(
|
|
82
71
|
// Object
|
|
@@ -88,14 +77,13 @@ export class MiddlewareLogger extends Effect.Service()("MiddlewareLogger", {
|
|
|
88
77
|
// )
|
|
89
78
|
}))
|
|
90
79
|
])), devMode ? (_) => _ : Effect.catchAllDefect(() => Effect.die("Internal Server Error")));
|
|
91
|
-
});
|
|
80
|
+
}));
|
|
92
81
|
})
|
|
93
82
|
}) {
|
|
94
83
|
}
|
|
95
84
|
export const DefaultGenericMiddlewares = [
|
|
96
85
|
RequestCacheMiddleware,
|
|
97
86
|
ConfigureInterruptibility,
|
|
98
|
-
CaptureHttpHeadersAsRpcHeaders,
|
|
99
87
|
MiddlewareLogger
|
|
100
88
|
];
|
|
101
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
89
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9hcGkvcm91dGluZy9taWRkbGV3YXJlL21pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsdURBQXVEO0FBQ3ZELE9BQU8sRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFDaEUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGtCQUFrQixDQUFBO0FBQ3pDLE9BQU8sRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLE1BQU0sMkJBQTJCLENBQUE7QUFDakUsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLG9CQUFvQixDQUFBO0FBQ2hELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGtCQUFrQixDQUFBO0FBRXhFLE1BQU0sZUFBZSxHQUFHLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQTtBQUMzQyxNQUFNLGtCQUFrQixHQUFHLFdBQVcsQ0FBQyxTQUFTLENBQUMsQ0FBQTtBQUVqRCxNQUFNLE9BQU8sT0FBUSxTQUFRLE9BQU8sQ0FBQyxTQUFTLEVBQVcsQ0FBQyxTQUFTLEVBQUUsRUFBRSxZQUFZLEVBQUUsR0FBRyxFQUFFLENBQUMsS0FBSyxFQUFFLENBQUM7Q0FBRztBQUN0Ryw4QkFBOEI7QUFDOUIsTUFBTSxPQUFPLHNCQUF1QixTQUFRLE1BQU0sQ0FBQyxPQUFPLEVBQTBCLENBQUMsd0JBQXdCLEVBQUU7SUFDN0csTUFBTSxFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDO1FBQzFCLE9BQU8saUJBQWlCLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsRUFBQyxPQUFPO1lBQzFELE9BQU8sS0FBSyxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsQ0FB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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-app/infra",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.78.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"proper-lockfile": "^4.1.2",
|
|
14
14
|
"pure-rand": "7.0.1",
|
|
15
15
|
"query-string": "^9.2.2",
|
|
16
|
-
"effect-app": "2.
|
|
16
|
+
"effect-app": "2.50.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@azure/cosmos": "^4.5.0",
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@azure/cosmos": "^4.5.0",
|
|
40
40
|
"@azure/service-bus": "^7.9.5",
|
|
41
|
-
"@effect/experimental": "^0.54.
|
|
41
|
+
"@effect/experimental": "^0.54.3",
|
|
42
42
|
"@effect/platform": "^0.90.0",
|
|
43
43
|
"@effect/rpc-http": "^0.52.4",
|
|
44
|
-
"@effect/rpc": "^0.68.
|
|
44
|
+
"@effect/rpc": "^0.68.1",
|
|
45
45
|
"@effect/sql": "^0.44.0",
|
|
46
46
|
"@effect/vitest": "^0.25.0",
|
|
47
47
|
"@sendgrid/helpers": "^8.0.0",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"jwt-decode": "^4.0.0",
|
|
52
52
|
"redis": "^3.1.2",
|
|
53
53
|
"redlock": "^4.2.0",
|
|
54
|
-
"effect": "^3.17.
|
|
54
|
+
"effect": "^3.17.4",
|
|
55
55
|
"express": "^5.1.0"
|
|
56
56
|
},
|
|
57
57
|
"typesVersions": {
|
package/src/api/layerUtils.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context, Effect, type Layer, type NonEmptyReadonlyArray, Option } from "effect-app"
|
|
2
|
+
import { InfraLogger } from "../logger.js"
|
|
2
3
|
|
|
3
4
|
export namespace LayerUtils {
|
|
4
5
|
export type GetLayersSuccess<Layers extends ReadonlyArray<Layer.Layer.Any>> = Layers extends
|
|
@@ -27,7 +28,43 @@ export type ContextTagWithDefault<Id, A, LayerE, LayerR, Tag = unknown> =
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export namespace ContextTagWithDefault {
|
|
30
|
-
export type Base<A> = ContextTagWithDefault<any,
|
|
31
|
+
export type Base<A> = ContextTagWithDefault<any, A, any, any, any>
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export type GetContext<T> = T extends Context.Context<infer Y> ? Y : never
|
|
35
|
+
|
|
36
|
+
export const mergeContexts = Effect.fnUntraced(
|
|
37
|
+
function*<T extends readonly { maker: any; handle: Effect<Context<any>> }[]>(makers: T) {
|
|
38
|
+
let context = Context.empty()
|
|
39
|
+
for (const mw of makers) {
|
|
40
|
+
yield* InfraLogger.logDebug("Building context for middleware", mw.maker.key ?? mw.maker)
|
|
41
|
+
const moreContext = yield* mw.handle.pipe(Effect.provide(context))
|
|
42
|
+
yield* InfraLogger.logDebug(
|
|
43
|
+
"Built context for middleware",
|
|
44
|
+
mw.maker.key ?? mw.maker,
|
|
45
|
+
(moreContext as any).toJSON().services
|
|
46
|
+
)
|
|
47
|
+
context = Context.merge(context, moreContext)
|
|
48
|
+
}
|
|
49
|
+
return context as Context.Context<Effect.Success<T[number]["handle"]>>
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
export const mergeOptionContexts = Effect.fnUntraced(
|
|
54
|
+
function*<T extends readonly { maker: any; handle: Effect<Option<Context<any>>> }[]>(makers: T) {
|
|
55
|
+
let context = Context.empty()
|
|
56
|
+
for (const mw of makers) {
|
|
57
|
+
yield* InfraLogger.logDebug("Building context for middleware", mw.maker.key ?? mw.maker)
|
|
58
|
+
const moreContext = yield* mw.handle.pipe(Effect.provide(context))
|
|
59
|
+
yield* InfraLogger.logDebug(
|
|
60
|
+
"Built context for middleware",
|
|
61
|
+
mw.maker.key ?? mw.maker,
|
|
62
|
+
Option.map(moreContext, (c) => (c as any).toJSON().services)
|
|
63
|
+
)
|
|
64
|
+
if (moreContext.value) {
|
|
65
|
+
context = Context.merge(context, moreContext.value)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return context
|
|
69
|
+
}
|
|
70
|
+
)
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type Array, Context, Effect, Layer, type NonEmptyArray, pipe, type Scope } from "effect-app"
|
|
2
2
|
import { type HttpRouter } from "effect-app/http"
|
|
3
3
|
import { type Tag } from "effect/Context"
|
|
4
|
-
import { type ContextTagWithDefault, type GetContext, type LayerUtils } from "../../layerUtils.js"
|
|
5
|
-
import { mergeContexts } from "./dynamic-middleware.js"
|
|
4
|
+
import { type ContextTagWithDefault, type GetContext, type LayerUtils, mergeContexts } from "../../layerUtils.js"
|
|
6
5
|
|
|
7
6
|
// the context provider provides additional stuff
|
|
8
7
|
export type ContextProviderShape<ContextProviderA, ContextProviderR extends HttpRouter.HttpRouter.Provided> = Effect<
|
|
@@ -71,6 +70,7 @@ export const mergeContextProviders = <
|
|
|
71
70
|
}) as any
|
|
72
71
|
})
|
|
73
72
|
|
|
73
|
+
// Effect Rpc Middleware: for single tag providing, we could use Provides, for providing Context or Layer (bad boy) we could use Wrap..
|
|
74
74
|
export const ContextProvider = <
|
|
75
75
|
ContextProviderA,
|
|
76
76
|
MakeContextProviderE,
|
|
@@ -23,7 +23,7 @@ export type MakeRPCHandlerFactory<
|
|
|
23
23
|
HandlerR
|
|
24
24
|
>(
|
|
25
25
|
schema: T & S.Schema<Req, any, never>,
|
|
26
|
-
|
|
26
|
+
next: (
|
|
27
27
|
request: Req,
|
|
28
28
|
headers: any
|
|
29
29
|
) => Effect.Effect<
|
|
@@ -56,7 +56,7 @@ export type RPCHandlerFactory<
|
|
|
56
56
|
HandlerR
|
|
57
57
|
>(
|
|
58
58
|
schema: T & S.Schema<Req, any, never>,
|
|
59
|
-
|
|
59
|
+
next: (
|
|
60
60
|
request: Req,
|
|
61
61
|
headers: any
|
|
62
62
|
) => Effect.Effect<
|
|
@@ -76,7 +76,7 @@ export type RPCHandlerFactory<
|
|
|
76
76
|
// the middleware will remove from HandlerR the dynamic context
|
|
77
77
|
// & S.Schema<Req, any, never> is useless here but useful when creating the middleware
|
|
78
78
|
Exclude<HandlerR, GetEffectContext<RequestContextMap, (T & S.Schema<Req, any, never>)["config"]>>,
|
|
79
|
-
// the context provider provides additional stuff both to the middleware and the
|
|
79
|
+
// the context provider provides additional stuff both to the middleware and the next
|
|
80
80
|
ContextProviderA
|
|
81
81
|
>
|
|
82
82
|
>
|
|
@@ -90,7 +90,7 @@ type RequestContextMapProvider<RequestContextMap extends Record<string, RPCConte
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
export interface MiddlewareMake<
|
|
93
|
-
RequestContextMap extends Record<string, RPCContextMap.Any>, // what services will the middleware provide dynamically to the
|
|
93
|
+
RequestContextMap extends Record<string, RPCContextMap.Any>, // what services will the middleware provide dynamically to the next, or raise errors.
|
|
94
94
|
//
|
|
95
95
|
// ContextProvider is a service that builds additional context for each request.
|
|
96
96
|
ContextProviderA, // what the context provider provides
|
|
@@ -117,9 +117,9 @@ export interface MiddlewareMake<
|
|
|
117
117
|
MakeContextProviderR
|
|
118
118
|
>
|
|
119
119
|
|
|
120
|
-
/* dependencies for the main middleware running just before the
|
|
120
|
+
/* dependencies for the main middleware running just before the next is called */
|
|
121
121
|
dependencies?: MiddlewareDependencies
|
|
122
|
-
// this actually builds "the middleware", i.e. returns the augmented
|
|
122
|
+
// this actually builds "the middleware", i.e. returns the augmented next factory when yielded...
|
|
123
123
|
execute?: (
|
|
124
124
|
maker: (
|
|
125
125
|
// MiddlewareR is set to ContextProviderA | HttpRouter.HttpRouter.Provided because that's what, at most
|
|
@@ -138,7 +138,7 @@ export interface MiddlewareMakerId {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
export type Middleware<
|
|
141
|
-
RequestContextMap extends Record<string, RPCContextMap.Any>, // what services will the middlware provide dynamically to the
|
|
141
|
+
RequestContextMap extends Record<string, RPCContextMap.Any>, // what services will the middlware provide dynamically to the next, or raise errors.
|
|
142
142
|
MakeMiddlewareE, // what the middleware construction can fail with
|
|
143
143
|
MakeMiddlewareR, // what the middlware requires to be constructed
|
|
144
144
|
ContextProviderA // what the context provider provides
|
|
@@ -190,7 +190,6 @@ export const makeMiddleware =
|
|
|
190
190
|
MiddlewareDependencies
|
|
191
191
|
>
|
|
192
192
|
) => {
|
|
193
|
-
// type Id = MiddlewareMakerId &
|
|
194
193
|
const MiddlewareMaker = Context.GenericTag<
|
|
195
194
|
MiddlewareMakerId,
|
|
196
195
|
{
|
|
@@ -216,35 +215,42 @@ export const makeMiddleware =
|
|
|
216
215
|
) => cb)
|
|
217
216
|
: Effect.succeed<
|
|
218
217
|
MakeRPCHandlerFactory<RequestContextMap, ContextProviderA | HttpRouter.HttpRouter.Provided>
|
|
219
|
-
>((_schema,
|
|
218
|
+
>((_schema, next) => (payload, headers) => next(payload, headers)),
|
|
220
219
|
contextProvider: make.contextProvider // uses the middleware.contextProvider tag to get the context provider service
|
|
221
220
|
})
|
|
222
221
|
.pipe(
|
|
223
222
|
Effect.map(({ contextProvider, dynamicMiddlewares, generic, middleware }) => ({
|
|
224
223
|
_tag: "MiddlewareMaker" as const,
|
|
225
224
|
effect: makeRpcEffect<RequestContextMap, ContextProviderA>()(
|
|
226
|
-
(schema,
|
|
227
|
-
const h = middleware(schema,
|
|
228
|
-
return
|
|
229
|
-
Effect.
|
|
230
|
-
yield*
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
225
|
+
(schema, next, moduleName) => {
|
|
226
|
+
const h = middleware(schema, next as any, moduleName)
|
|
227
|
+
return (payload, headers) =>
|
|
228
|
+
Effect.gen(function*() {
|
|
229
|
+
return yield* generic({
|
|
230
|
+
payload,
|
|
231
|
+
headers,
|
|
232
|
+
rpc: { _tag: `${moduleName}.${payload._tag}` }, // todo: make moduleName part of the tag on S.Req creation.
|
|
233
|
+
next: Effect.gen(function*() {
|
|
234
|
+
yield* Effect.annotateCurrentSpan(
|
|
235
|
+
"request.name",
|
|
236
|
+
moduleName ? `${moduleName}.${payload._tag}` : payload._tag
|
|
237
|
+
)
|
|
234
238
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
// the contextProvider is an Effect that builds the context for the request
|
|
240
|
+
return yield* contextProvider.pipe(
|
|
241
|
+
Effect.flatMap((contextProviderContext) =>
|
|
242
|
+
// the dynamicMiddlewares is an Effect that builds the dynamiuc context for the request
|
|
243
|
+
dynamicMiddlewares(schema.config ?? {}, headers).pipe(
|
|
244
|
+
Effect.flatMap((dynamicContext) =>
|
|
245
|
+
h(payload, headers).pipe(Effect.provide(dynamicContext))
|
|
246
|
+
),
|
|
247
|
+
Effect.provide(contextProviderContext)
|
|
248
|
+
)
|
|
249
|
+
)
|
|
242
250
|
)
|
|
243
|
-
)
|
|
244
|
-
)
|
|
245
|
-
}) as any
|
|
246
|
-
moduleName
|
|
247
|
-
)
|
|
251
|
+
}) as any
|
|
252
|
+
})
|
|
253
|
+
}) as any // why?
|
|
248
254
|
}
|
|
249
255
|
)
|
|
250
256
|
}))
|
|
@@ -291,7 +297,7 @@ function makeRpcEffect<
|
|
|
291
297
|
HandlerR
|
|
292
298
|
>(
|
|
293
299
|
schema: T & S.Schema<Req, any, never>,
|
|
294
|
-
|
|
300
|
+
next: (
|
|
295
301
|
request: Req,
|
|
296
302
|
headers: any
|
|
297
303
|
) => Effect.Effect<
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { Array, Context, Effect, Option, type S } from "effect-app"
|
|
2
|
+
import { Array, type Context, Effect, type Option, type S } from "effect-app"
|
|
3
3
|
import { type GetEffectContext, type RPCContextMap } from "effect-app/client"
|
|
4
4
|
import { type Tag } from "effect-app/Context"
|
|
5
5
|
import { typedValuesOf } from "effect-app/utils"
|
|
6
|
-
import {
|
|
7
|
-
import { type ContextTagWithDefault } from "../../layerUtils.js"
|
|
6
|
+
import { type ContextTagWithDefault, mergeOptionContexts } from "../../layerUtils.js"
|
|
8
7
|
import { sort } from "../tsort.js"
|
|
9
8
|
|
|
10
9
|
export type ContextWithLayer<
|
|
@@ -39,42 +38,7 @@ export namespace ContextWithLayer {
|
|
|
39
38
|
>
|
|
40
39
|
}
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
function*<T extends readonly { maker: any; handle: Effect<Context<any>> }[]>(makers: T) {
|
|
44
|
-
let context = Context.empty()
|
|
45
|
-
for (const mw of makers) {
|
|
46
|
-
yield* InfraLogger.logDebug("Building context for middleware", mw.maker.key ?? mw.maker)
|
|
47
|
-
const moreContext = yield* mw.handle.pipe(Effect.provide(context))
|
|
48
|
-
yield* InfraLogger.logDebug(
|
|
49
|
-
"Built context for middleware",
|
|
50
|
-
mw.maker.key ?? mw.maker,
|
|
51
|
-
(moreContext as any).toJSON().services
|
|
52
|
-
)
|
|
53
|
-
context = Context.merge(context, moreContext)
|
|
54
|
-
}
|
|
55
|
-
return context as Context.Context<Effect.Success<T[number]["handle"]>>
|
|
56
|
-
}
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
export const mergeOptionContexts = Effect.fnUntraced(
|
|
60
|
-
function*<T extends readonly { maker: any; handle: Effect<Option<Context<any>>> }[]>(makers: T) {
|
|
61
|
-
let context = Context.empty()
|
|
62
|
-
for (const mw of makers) {
|
|
63
|
-
yield* InfraLogger.logDebug("Building context for middleware", mw.maker.key ?? mw.maker)
|
|
64
|
-
const moreContext = yield* mw.handle.pipe(Effect.provide(context))
|
|
65
|
-
yield* InfraLogger.logDebug(
|
|
66
|
-
"Built context for middleware",
|
|
67
|
-
mw.maker.key ?? mw.maker,
|
|
68
|
-
Option.map(moreContext, (c) => (c as any).toJSON().services)
|
|
69
|
-
)
|
|
70
|
-
if (moreContext.value) {
|
|
71
|
-
context = Context.merge(context, moreContext.value)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return context
|
|
75
|
-
}
|
|
76
|
-
)
|
|
77
|
-
|
|
41
|
+
// Effect Rpc Middleware: no substitute atm. though maybe something could be achieved with Wrap, we just don't have type safety on the Request input etc.
|
|
78
42
|
export const implementMiddleware = <T extends Record<string, RPCContextMap.Any>>() =>
|
|
79
43
|
<
|
|
80
44
|
TI extends {
|
|
@@ -3,10 +3,20 @@ import { type Array, Effect } from "effect-app"
|
|
|
3
3
|
import { type HttpHeaders, type HttpRouter } from "effect-app/http"
|
|
4
4
|
import { type ContextTagWithDefault } from "../../layerUtils.js"
|
|
5
5
|
|
|
6
|
+
export interface GenericMiddlewareOptions<A, E> {
|
|
7
|
+
// Effect rpc middleware does not support changing payload or headers, but we do..
|
|
8
|
+
readonly next: Effect.Effect<A, E, HttpRouter.HttpRouter.Provided>
|
|
9
|
+
readonly payload: unknown
|
|
10
|
+
readonly headers: HttpHeaders.Headers
|
|
11
|
+
// readonly clientId: number
|
|
12
|
+
readonly rpc: { _tag: string } // Rpc.AnyWithProps
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
export type GenericMiddlewareMaker = <A, E>(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
16
|
+
options: GenericMiddlewareOptions<A, E>
|
|
17
|
+
) => Effect.Effect<A, E, HttpRouter.HttpRouter.Provided>
|
|
18
|
+
|
|
19
|
+
export const genericMiddleware = (i: GenericMiddlewareMaker) => i
|
|
10
20
|
|
|
11
21
|
export const genericMiddlewareMaker = <
|
|
12
22
|
T extends Array<
|
|
@@ -21,17 +31,14 @@ export const genericMiddlewareMaker = <
|
|
|
21
31
|
effect: Effect.gen(function*() {
|
|
22
32
|
const middlewaresInstances = yield* Effect.all(middlewares)
|
|
23
33
|
|
|
24
|
-
return <A, E
|
|
25
|
-
|
|
26
|
-
moduleName: string
|
|
34
|
+
return <A, E>(
|
|
35
|
+
options: GenericMiddlewareOptions<A, E>
|
|
27
36
|
) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
effect = middleware(effect, moduleName)
|
|
32
|
-
}
|
|
33
|
-
return effect(input, headers)
|
|
37
|
+
let next = options.next
|
|
38
|
+
for (const middleware of (middlewaresInstances as any[]).toReversed()) {
|
|
39
|
+
next = middleware({ ...options, next })
|
|
34
40
|
}
|
|
41
|
+
return next
|
|
35
42
|
}
|
|
36
43
|
})
|
|
37
44
|
} as any
|