@effect-app/infra 2.78.7 → 2.79.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 +12 -0
- package/dist/api/layerUtils.d.ts +2 -4
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +4 -5
- package/dist/api/routing/middleware/ContextProvider.d.ts +17 -12
- package/dist/api/routing/middleware/ContextProvider.d.ts.map +1 -1
- package/dist/api/routing/middleware/ContextProvider.js +15 -5
- package/dist/api/routing/middleware/DynamicMiddleware.d.ts +43 -21
- package/dist/api/routing/middleware/DynamicMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/DynamicMiddleware.js +60 -20
- package/dist/api/routing/middleware/generic-middleware.d.ts +17 -10
- package/dist/api/routing/middleware/generic-middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/generic-middleware.js +34 -8
- package/dist/api/routing/middleware/middleware.d.ts +16 -10
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +41 -51
- package/dist/api/routing/middleware.d.ts +1 -0
- package/dist/api/routing/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware.js +2 -1
- package/dist/api/routing.d.ts +2 -2
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +3 -3
- package/package.json +1 -1
- package/src/api/layerUtils.ts +6 -9
- package/src/api/routing/middleware/ContextProvider.ts +84 -28
- package/src/api/routing/middleware/DynamicMiddleware.ts +162 -78
- package/src/api/routing/middleware/generic-middleware.ts +56 -20
- package/src/api/routing/middleware/middleware.ts +27 -35
- package/src/api/routing/middleware.ts +2 -0
- package/src/api/routing.ts +5 -5
- package/test/controller.test.ts +27 -47
- package/test/dist/controller/test2.test.d.ts.map +1 -0
- package/test/dist/controller.test copy.js +23 -46
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/controller5.test.d.ts.map +1 -0
- package/test/dist/controller6.test.d.ts.map +1 -1
- package/test/dist/controller7.test.d.ts.map +1 -1
- package/test/dist/dynamicContext.test.d.ts.map +1 -0
- package/test/dynamicContext.test.ts +153 -0
- package/vitest.config.ts.timestamp-1711656440838-19c636fe320df.mjs +0 -0
- package/vitest.config.ts.timestamp-1711724061890-6ecedb0a07fdd.mjs +0 -0
- package/vitest.config.ts.timestamp-1711743489537-da8d9e5f66c9f.mjs +0 -0
- package/vitest.config.ts.timestamp-1711744615239-dcf257a844e01.mjs +37 -0
- package/test/dist/controller.test copy.d.ts +0 -169
- package/test/dist/controller.test copy.d.ts.map +0 -1
|
@@ -1,8 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { Context, Effect, Layer, type NonEmptyArray, pipe, type Scope } from "effect-app"
|
|
3
|
+
|
|
2
4
|
import { type HttpRouter } from "effect-app/http"
|
|
3
5
|
import { type Tag } from "effect/Context"
|
|
6
|
+
import { type YieldWrap } from "effect/Utils"
|
|
4
7
|
import { type ContextTagWithDefault, type GetContext, type LayerUtils, mergeContexts } from "../../layerUtils.js"
|
|
5
8
|
|
|
9
|
+
export namespace EffectGenUtils {
|
|
10
|
+
export type Success<EG> = EG extends Effect<infer A, infer _E, infer _R> ? A
|
|
11
|
+
// there could be a case where the generator function does not yield anything, so we need to handle that
|
|
12
|
+
: EG extends (..._: infer _3) => Generator<never, infer A, infer _2> ? A
|
|
13
|
+
: EG extends (..._: infer _3) => Generator<YieldWrap<Effect<infer _, infer _E, infer _R>>, infer A, infer _2> ? A
|
|
14
|
+
: never
|
|
15
|
+
|
|
16
|
+
export type Error<EG> = EG extends Effect<infer _A, infer E, infer _R> ? E
|
|
17
|
+
// there could be a case where the generator function does not yield anything, so we need to handle that
|
|
18
|
+
: EG extends (..._: infer _3) => Generator<never, infer _A, infer _2> ? never
|
|
19
|
+
: EG extends (..._: infer _3) => Generator<YieldWrap<Effect<infer _, infer E, infer _R>>, infer _A, infer _2> ? E
|
|
20
|
+
: never
|
|
21
|
+
|
|
22
|
+
export type Context<EG> = EG extends Effect<infer _A, infer _E, infer R> ? R
|
|
23
|
+
// there could be a case where the generator function does not yield anything, so we need to handle that
|
|
24
|
+
: EG extends (..._: infer _3) => Generator<never, infer _A, infer _2> ? never
|
|
25
|
+
: EG extends (..._: infer _3) => Generator<YieldWrap<Effect<infer _, infer _E, infer R>>, infer _A, infer _2> ? R
|
|
26
|
+
: never
|
|
27
|
+
}
|
|
28
|
+
|
|
6
29
|
// the context provider provides additional stuff
|
|
7
30
|
export type ContextProviderShape<ContextProviderA, ContextProviderR extends HttpRouter.HttpRouter.Provided> = Effect<
|
|
8
31
|
Context.Context<ContextProviderA>,
|
|
@@ -14,51 +37,75 @@ export interface ContextProviderId {
|
|
|
14
37
|
_tag: "ContextProvider"
|
|
15
38
|
}
|
|
16
39
|
|
|
40
|
+
// ContextTagWithDefault.Base<Effect<Context.Context<infer _1>, never, infer _R> & { _tag: infer _2 }>
|
|
41
|
+
|
|
17
42
|
/**
|
|
18
43
|
* TDeps is an array of services with Default implementation
|
|
19
44
|
* each service is an effect which builds some context for each request
|
|
20
45
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
46
|
+
type TDepsArr<TDeps extends ReadonlyArray<any>> = {
|
|
47
|
+
// the following freaking shit helps me with nested variance issues: it wasn't sufficient to use never/any/unknown for
|
|
48
|
+
// the various type parameters, not anymore because of () => Generator<YieldWrap<Effect craziness
|
|
49
|
+
// existential types may help, and all the following usages of infer _ have that meaning: I do not care which is the
|
|
50
|
+
// actual type in that position, I just wanna set the overall structure
|
|
51
|
+
[K in keyof TDeps]: TDeps[K] extends //
|
|
23
52
|
// E = never => the context provided cannot trigger errors
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
53
|
+
// _R extends HttpRouter.HttpRouter.Provided => the context provided can only have what HttpRouter.Provided provides as requirements
|
|
54
|
+
(
|
|
55
|
+
ContextTagWithDefault.Base<Effect<Context.Context<infer _1>, never, infer _R> & { _tag: infer _2 }>
|
|
56
|
+
) ? [_R] extends [HttpRouter.HttpRouter.Provided] ? TDeps[K]
|
|
57
|
+
: `HttpRouter.HttpRouter.Provided is the only requirement ${TDeps[K]["Service"]["_tag"]}'s returned effect can have`
|
|
58
|
+
: TDeps[K] extends (
|
|
59
|
+
ContextTagWithDefault.Base<
|
|
60
|
+
& (() => Generator<
|
|
61
|
+
infer _YW,
|
|
62
|
+
infer _1,
|
|
63
|
+
infer _2
|
|
64
|
+
>)
|
|
65
|
+
& { _tag: infer _3 }
|
|
66
|
+
>
|
|
67
|
+
) // [_YW] extends [never] if no yield* is used and just some context is returned
|
|
68
|
+
? [_YW] extends [never] ? TDeps[K]
|
|
69
|
+
: [_YW] extends [YieldWrap<Effect<infer _2, never, infer _R>>]
|
|
70
|
+
? [_R] extends [HttpRouter.HttpRouter.Provided] ? TDeps[K]
|
|
71
|
+
: `HttpRouter.HttpRouter.Provided is the only requirement ${TDeps[K]["Service"][
|
|
72
|
+
"_tag"
|
|
73
|
+
]}'s returned effect can have`
|
|
74
|
+
: "WTF are you yielding man?"
|
|
75
|
+
: `You cannot throw errors from providers`
|
|
36
76
|
}
|
|
37
77
|
|
|
38
78
|
// Note: the type here must be aligned with MergedContextProvider
|
|
39
79
|
export const mergeContextProviders = <
|
|
40
|
-
TDeps extends
|
|
80
|
+
TDeps extends ReadonlyArray<any>
|
|
41
81
|
>(
|
|
42
|
-
|
|
82
|
+
// long life to reverse mapped types
|
|
83
|
+
...deps: TDepsArr<TDeps>
|
|
43
84
|
): {
|
|
44
85
|
dependencies: { [K in keyof TDeps]: TDeps[K]["Default"] }
|
|
45
86
|
effect: Effect.Effect<
|
|
46
87
|
Effect.Effect<
|
|
47
88
|
// we need to merge all contexts into one
|
|
48
|
-
Context.Context<GetContext<
|
|
89
|
+
Context.Context<GetContext<EffectGenUtils.Success<Tag.Service<TDeps[number]>>>>,
|
|
49
90
|
never,
|
|
50
|
-
|
|
91
|
+
EffectGenUtils.Context<Tag.Service<TDeps[number]>>
|
|
51
92
|
>,
|
|
52
93
|
LayerUtils.GetLayersError<{ [K in keyof TDeps]: TDeps[K]["Default"] }>,
|
|
53
94
|
LayerUtils.GetLayersSuccess<{ [K in keyof TDeps]: TDeps[K]["Default"] }>
|
|
54
95
|
>
|
|
55
96
|
} => ({
|
|
56
|
-
dependencies: deps.map((_) => _.Default) as any,
|
|
97
|
+
dependencies: deps.map((_) => (_ as any).Default) as any,
|
|
57
98
|
effect: Effect.gen(function*() {
|
|
58
|
-
|
|
99
|
+
// uses the tags to request the context providers
|
|
100
|
+
const makers = yield* Effect.all(deps as any[])
|
|
59
101
|
return Effect
|
|
60
102
|
.gen(function*() {
|
|
61
|
-
const services = (makers as any[]).map((handle, i) => (
|
|
103
|
+
const services = (makers as any[]).map((handle, i) => (
|
|
104
|
+
{
|
|
105
|
+
maker: deps[i],
|
|
106
|
+
handle: handle[Symbol.toStringTag] === "GeneratorFunction" ? Effect.fnUntraced(handle)() : handle
|
|
107
|
+
}
|
|
108
|
+
))
|
|
62
109
|
// services are effects which return some Context.Context<...>
|
|
63
110
|
const context = yield* mergeContexts(services as any)
|
|
64
111
|
return context
|
|
@@ -76,7 +123,12 @@ export const ContextProvider = <
|
|
|
76
123
|
>(
|
|
77
124
|
input: {
|
|
78
125
|
effect: Effect<
|
|
79
|
-
Effect<ContextProviderA, never, ContextProviderR
|
|
126
|
+
| Effect<ContextProviderA, never, ContextProviderR>
|
|
127
|
+
| (() => Generator<
|
|
128
|
+
YieldWrap<Effect<any, never, ContextProviderR>>,
|
|
129
|
+
ContextProviderA,
|
|
130
|
+
any
|
|
131
|
+
>),
|
|
80
132
|
MakeContextProviderE,
|
|
81
133
|
MakeContextProviderR | Scope
|
|
82
134
|
>
|
|
@@ -89,7 +141,10 @@ export const ContextProvider = <
|
|
|
89
141
|
>(
|
|
90
142
|
"ContextProvider"
|
|
91
143
|
)
|
|
92
|
-
const
|
|
144
|
+
const e = input.effect.pipe(
|
|
145
|
+
Effect.map((eg) => (eg as any)[Symbol.toStringTag] === "GeneratorFunction" ? Effect.fnUntraced(eg as any)() : eg)
|
|
146
|
+
)
|
|
147
|
+
const l = Layer.scoped(ctx, e as any)
|
|
93
148
|
return Object.assign(ctx, {
|
|
94
149
|
Default: l.pipe(
|
|
95
150
|
input.dependencies ? Layer.provide(input.dependencies) as any : (_) => _
|
|
@@ -105,9 +160,10 @@ export const ContextProvider = <
|
|
|
105
160
|
|
|
106
161
|
// Note: the type here must be aligned with mergeContextProviders
|
|
107
162
|
export const MergedContextProvider = <
|
|
108
|
-
TDeps extends
|
|
163
|
+
TDeps extends ReadonlyArray<any>
|
|
109
164
|
>(
|
|
110
|
-
|
|
165
|
+
// long life to reverse mapped types
|
|
166
|
+
...deps: TDepsArr<TDeps>
|
|
111
167
|
) =>
|
|
112
168
|
pipe(
|
|
113
169
|
deps as [Parameters<typeof mergeContextProviders>[0]],
|
|
@@ -117,9 +173,9 @@ export const MergedContextProvider = <
|
|
|
117
173
|
ContextProviderId,
|
|
118
174
|
Effect.Effect<
|
|
119
175
|
// we need to merge all contexts into one
|
|
120
|
-
Context.Context<GetContext<
|
|
176
|
+
Context.Context<GetContext<EffectGenUtils.Success<Tag.Service<TDeps[number]>>>>,
|
|
121
177
|
never,
|
|
122
|
-
|
|
178
|
+
EffectGenUtils.Context<Tag.Service<TDeps[number]>>
|
|
123
179
|
>,
|
|
124
180
|
LayerUtils.GetLayersError<{ [K in keyof TDeps]: TDeps[K]["Default"] }>,
|
|
125
181
|
| Exclude<
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
|
-
import {
|
|
4
|
+
import { Rpc, RpcMiddleware } from "@effect/rpc"
|
|
5
|
+
import { type SuccessValue, type TagClass } from "@effect/rpc/RpcMiddleware"
|
|
6
|
+
import { Console, Context, Effect, Layer, type NonEmptyReadonlyArray, type Request, type S, type Schema, type Scope, Unify } from "effect-app"
|
|
5
7
|
import type { GetEffectContext, RPCContextMap } from "effect-app/client/req"
|
|
6
|
-
import { type HttpRouter } from "effect-app/http"
|
|
8
|
+
import { type HttpHeaders, type HttpRouter } from "effect-app/http"
|
|
9
|
+
import { type TagUnify, type TagUnifyIgnore } from "effect/Context"
|
|
7
10
|
import type * as EffectRequest from "effect/Request"
|
|
8
11
|
import { type ContextTagWithDefault, type LayerUtils } from "../../layerUtils.js"
|
|
9
|
-
import { type ContextProviderId, type ContextProviderShape } from "./ContextProvider.js"
|
|
10
12
|
import { type ContextWithLayer, implementMiddleware } from "./dynamic-middleware.js"
|
|
11
13
|
import { type GenericMiddlewareMaker, genericMiddlewareMaker } from "./generic-middleware.js"
|
|
12
14
|
|
|
@@ -91,16 +93,8 @@ type RequestContextMapProvider<RequestContextMap extends Record<string, RPCConte
|
|
|
91
93
|
|
|
92
94
|
export interface MiddlewareMake<
|
|
93
95
|
RequestContextMap extends Record<string, RPCContextMap.Any>, // what services will the middleware provide dynamically to the next, or raise errors.
|
|
94
|
-
//
|
|
95
|
-
// ContextProvider is a service that builds additional context for each request.
|
|
96
|
-
ContextProviderA, // what the context provider provides
|
|
97
|
-
ContextProviderR extends HttpRouter.HttpRouter.Provided, // what the context provider requires
|
|
98
|
-
MakeContextProviderE, // what the context provider construction can fail with
|
|
99
|
-
MakeContextProviderR, // what the context provider construction requires
|
|
100
96
|
DynamicMiddlewareProviders extends RequestContextMapProvider<RequestContextMap>, // how to resolve the dynamic middleware
|
|
101
|
-
GenericMiddlewareProviders extends
|
|
102
|
-
ContextTagWithDefault.Base<GenericMiddlewareMaker>
|
|
103
|
-
>,
|
|
97
|
+
GenericMiddlewareProviders extends NonEmptyReadonlyArray<GenericMiddlewareMaker>,
|
|
104
98
|
MakeMiddlewareE, // what the middleware construction can fail with
|
|
105
99
|
MakeMiddlewareR, // what the middleware requires to be constructed
|
|
106
100
|
MiddlewareDependencies extends NonEmptyReadonlyArray<Layer.Layer.Any> // layers provided for the middleware to be constructed
|
|
@@ -109,25 +103,30 @@ export interface MiddlewareMake<
|
|
|
109
103
|
dynamicMiddlewares: DynamicMiddlewareProviders
|
|
110
104
|
/** generic middlewares are those which follow the (next) => (input, headers) => pattern */
|
|
111
105
|
genericMiddlewares: GenericMiddlewareProviders
|
|
112
|
-
/** static context providers */
|
|
113
|
-
contextProvider?: ContextTagWithDefault<
|
|
114
|
-
ContextProviderId,
|
|
115
|
-
ContextProviderShape<ContextProviderA, ContextProviderR>,
|
|
116
|
-
MakeContextProviderE,
|
|
117
|
-
MakeContextProviderR
|
|
118
|
-
>
|
|
119
106
|
|
|
120
107
|
/* dependencies for the main middleware running just before the next is called */
|
|
121
108
|
dependencies?: MiddlewareDependencies
|
|
122
109
|
// this actually builds "the middleware", i.e. returns the augmented next factory when yielded...
|
|
123
110
|
execute?: (
|
|
124
111
|
maker: (
|
|
125
|
-
// MiddlewareR is set to
|
|
112
|
+
// MiddlewareR is set to GenericMiddlewareProviders | HttpRouter.HttpRouter.Provided because that's what, at most
|
|
126
113
|
// a middleware can additionally require to get executed
|
|
127
|
-
cb: MakeRPCHandlerFactory<
|
|
128
|
-
|
|
114
|
+
cb: MakeRPCHandlerFactory<
|
|
115
|
+
RequestContextMap,
|
|
116
|
+
| GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
117
|
+
| HttpRouter.HttpRouter.Provided
|
|
118
|
+
>
|
|
119
|
+
) => MakeRPCHandlerFactory<
|
|
120
|
+
RequestContextMap,
|
|
121
|
+
| GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
122
|
+
| HttpRouter.HttpRouter.Provided
|
|
123
|
+
>
|
|
129
124
|
) => Effect<
|
|
130
|
-
MakeRPCHandlerFactory<
|
|
125
|
+
MakeRPCHandlerFactory<
|
|
126
|
+
RequestContextMap,
|
|
127
|
+
| GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
128
|
+
| HttpRouter.HttpRouter.Provided
|
|
129
|
+
>,
|
|
131
130
|
MakeMiddlewareE,
|
|
132
131
|
MakeMiddlewareR | Scope // ...that's why MakeMiddlewareR is here
|
|
133
132
|
>
|
|
@@ -137,7 +136,7 @@ export interface MiddlewareMakerId {
|
|
|
137
136
|
_tag: "MiddlewareMaker"
|
|
138
137
|
}
|
|
139
138
|
|
|
140
|
-
export type
|
|
139
|
+
export type RouterMiddleware<
|
|
141
140
|
RequestContextMap extends Record<string, RPCContextMap.Any>, // what services will the middlware provide dynamically to the next, or raise errors.
|
|
142
141
|
MakeMiddlewareE, // what the middleware construction can fail with
|
|
143
142
|
MakeMiddlewareR, // what the middlware requires to be constructed
|
|
@@ -145,11 +144,11 @@ export type Middleware<
|
|
|
145
144
|
> = ContextTagWithDefault<
|
|
146
145
|
MiddlewareMakerId,
|
|
147
146
|
{
|
|
147
|
+
_tag: "MiddlewareMaker"
|
|
148
148
|
effect: RPCHandlerFactory<RequestContextMap, ContextProviderA>
|
|
149
149
|
},
|
|
150
150
|
MakeMiddlewareE,
|
|
151
|
-
MakeMiddlewareR
|
|
152
|
-
"MiddlewareMaker"
|
|
151
|
+
MakeMiddlewareR
|
|
153
152
|
>
|
|
154
153
|
|
|
155
154
|
export type RequestContextMapErrors<RequestContextMap extends Record<string, RPCContextMap.Any>> = S.Schema.Type<
|
|
@@ -164,25 +163,13 @@ export const makeMiddleware =
|
|
|
164
163
|
>() =>
|
|
165
164
|
<
|
|
166
165
|
RequestContextProviders extends RequestContextMapProvider<RequestContextMap>, // how to resolve the dynamic middleware
|
|
167
|
-
GenericMiddlewareProviders extends
|
|
168
|
-
ContextTagWithDefault.Base<GenericMiddlewareMaker>
|
|
169
|
-
>,
|
|
166
|
+
GenericMiddlewareProviders extends NonEmptyReadonlyArray<GenericMiddlewareMaker>,
|
|
170
167
|
MiddlewareDependencies extends NonEmptyReadonlyArray<Layer.Layer.Any>, // layers provided for the middlware to be constructed
|
|
171
|
-
//
|
|
172
|
-
// ContextProvider is a service that builds additional context for each request.
|
|
173
|
-
ContextProviderA = never, // what the context provider provides
|
|
174
|
-
ContextProviderR extends HttpRouter.HttpRouter.Provided = never, // what the context provider requires
|
|
175
|
-
MakeContextProviderE = never, // what the context provider construction can fail with
|
|
176
|
-
MakeContextProviderR = never, // what the context provider construction requires
|
|
177
168
|
MakeMiddlewareE = never, // what the middleware construction can fail with
|
|
178
169
|
MakeMiddlewareR = never // what the middlware requires to be constructed
|
|
179
170
|
>(
|
|
180
171
|
make: MiddlewareMake<
|
|
181
172
|
RequestContextMap,
|
|
182
|
-
ContextProviderA,
|
|
183
|
-
ContextProviderR,
|
|
184
|
-
MakeContextProviderE,
|
|
185
|
-
MakeContextProviderR,
|
|
186
173
|
RequestContextProviders,
|
|
187
174
|
GenericMiddlewareProviders,
|
|
188
175
|
MakeMiddlewareE,
|
|
@@ -193,7 +180,10 @@ export const makeMiddleware =
|
|
|
193
180
|
const MiddlewareMaker = Context.GenericTag<
|
|
194
181
|
MiddlewareMakerId,
|
|
195
182
|
{
|
|
196
|
-
effect: RPCHandlerFactory<
|
|
183
|
+
effect: RPCHandlerFactory<
|
|
184
|
+
RequestContextMap,
|
|
185
|
+
GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
186
|
+
>
|
|
197
187
|
_tag: "MiddlewareMaker"
|
|
198
188
|
}
|
|
199
189
|
>(
|
|
@@ -211,58 +201,67 @@ export const makeMiddleware =
|
|
|
211
201
|
generic: middlewares.effect,
|
|
212
202
|
middleware: make.execute
|
|
213
203
|
? make.execute((
|
|
214
|
-
cb: MakeRPCHandlerFactory<
|
|
204
|
+
cb: MakeRPCHandlerFactory<
|
|
205
|
+
RequestContextMap,
|
|
206
|
+
HttpRouter.HttpRouter.Provided | GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
207
|
+
>
|
|
215
208
|
) => cb)
|
|
216
209
|
: Effect.succeed<
|
|
217
|
-
MakeRPCHandlerFactory<
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
210
|
+
MakeRPCHandlerFactory<
|
|
211
|
+
RequestContextMap,
|
|
212
|
+
HttpRouter.HttpRouter.Provided | GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
213
|
+
>
|
|
214
|
+
>((_schema, next) => (payload, headers) => next(payload, headers))
|
|
222
215
|
})
|
|
223
216
|
.pipe(
|
|
224
|
-
Effect.map(({
|
|
217
|
+
Effect.map(({ dynamicMiddlewares, generic, middleware }) => ({
|
|
225
218
|
_tag: "MiddlewareMaker" as const,
|
|
226
|
-
effect: makeRpcEffect<
|
|
219
|
+
effect: makeRpcEffect<
|
|
220
|
+
RequestContextMap,
|
|
221
|
+
GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
|
|
222
|
+
>()(
|
|
227
223
|
(schema, next, moduleName) => {
|
|
228
224
|
const h = middleware(schema, next as any, moduleName)
|
|
229
225
|
return (payload, headers) =>
|
|
230
|
-
Effect
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
226
|
+
Effect
|
|
227
|
+
.gen(function*() {
|
|
228
|
+
const gen = generic({
|
|
229
|
+
payload,
|
|
230
|
+
headers,
|
|
231
|
+
clientId: 0, // TODO: get the clientId from the request context
|
|
232
|
+
rpc: {
|
|
233
|
+
...Rpc.fromTaggedRequest(schema as any),
|
|
234
|
+
// middlewares ? // todo: get from actual middleware flow?
|
|
235
|
+
annotations: Context.empty(), // TODO //Annotations(schema as any),
|
|
236
|
+
// successSchema: schema.success ?? Schema.Void,
|
|
237
|
+
// errorSchema: schema.failure ?? Schema.Never,
|
|
238
|
+
payloadSchema: schema,
|
|
239
|
+
_tag: `${moduleName}.${payload._tag}`,
|
|
240
|
+
key: `${moduleName}.${payload._tag}` /* ? */
|
|
241
|
+
// clientId: 0 as number /* ? */
|
|
242
|
+
}, // todo: make moduleName part of the tag on S.Req creation.
|
|
243
|
+
next:
|
|
244
|
+
// the contextProvider is an Effect that builds the context for the request
|
|
245
|
+
// the dynamicMiddlewares is an Effect that builds the dynamiuc context for the request
|
|
246
|
+
dynamicMiddlewares(schema.config ?? {}, headers).pipe(
|
|
247
|
+
Effect.flatMap((dynamicContext) => h(payload, headers).pipe(Effect.provide(dynamicContext)))
|
|
248
|
+
) as any
|
|
249
|
+
})
|
|
250
|
+
console.log({ gen })
|
|
240
251
|
|
|
241
|
-
|
|
242
|
-
return yield* contextProvider.pipe(
|
|
243
|
-
Effect.flatMap((contextProviderContext) =>
|
|
244
|
-
// the dynamicMiddlewares is an Effect that builds the dynamiuc context for the request
|
|
245
|
-
dynamicMiddlewares(schema.config ?? {}, headers).pipe(
|
|
246
|
-
Effect.flatMap((dynamicContext) =>
|
|
247
|
-
h(payload, headers).pipe(Effect.provide(dynamicContext))
|
|
248
|
-
),
|
|
249
|
-
Effect.provide(Option.getOrElse(contextProviderContext, () => Context.empty()))
|
|
250
|
-
)
|
|
251
|
-
)
|
|
252
|
-
)
|
|
253
|
-
}) as any
|
|
252
|
+
return yield* gen
|
|
254
253
|
})
|
|
255
|
-
|
|
254
|
+
.pipe(Effect.onExit(Console.log)) as any // why?
|
|
256
255
|
}
|
|
257
256
|
)
|
|
258
|
-
}))
|
|
257
|
+
})),
|
|
258
|
+
Effect.onExit(Console.log)
|
|
259
259
|
)
|
|
260
260
|
)
|
|
261
261
|
|
|
262
262
|
const dependencies = [
|
|
263
263
|
...(make.dependencies ? make.dependencies : []),
|
|
264
264
|
...(dynamicMiddlewares.dependencies as any),
|
|
265
|
-
...(make.contextProvider?.Default ? [make.contextProvider.Default] : []),
|
|
266
265
|
...middlewares.dependencies
|
|
267
266
|
]
|
|
268
267
|
const middlewareLayer = l
|
|
@@ -271,14 +270,12 @@ export const makeMiddleware =
|
|
|
271
270
|
) as Layer.Layer<
|
|
272
271
|
MiddlewareMakerId,
|
|
273
272
|
| MakeMiddlewareE // what the middleware construction can fail with
|
|
274
|
-
| LayerUtils.
|
|
275
|
-
| LayerUtils.
|
|
276
|
-
| MakeContextProviderE, // what could go wrong when building the context provider
|
|
273
|
+
| LayerUtils.GetLayersError<typeof dynamicMiddlewares.dependencies>
|
|
274
|
+
| LayerUtils.GetLayersError<typeof middlewares.dependencies>, // what could go wrong when building the dynamic middleware provider
|
|
277
275
|
| LayerUtils.GetLayersContext<MiddlewareDependencies> // what's needed to build layers
|
|
278
276
|
| LayerUtils.GetLayersContext<typeof middlewares.dependencies>
|
|
279
277
|
| LayerUtils.GetLayersContext<typeof dynamicMiddlewares.dependencies> // what's needed to build dynamic middleware layers
|
|
280
278
|
| Exclude<MakeMiddlewareR, LayerUtils.GetLayersSuccess<MiddlewareDependencies>> // what layers provides
|
|
281
|
-
| MakeContextProviderR // what's needed to build the contextProvider
|
|
282
279
|
>
|
|
283
280
|
|
|
284
281
|
return Object.assign(MiddlewareMaker, { Default: middlewareLayer })
|
|
@@ -322,3 +319,90 @@ function makeRpcEffect<
|
|
|
322
319
|
>
|
|
323
320
|
) => cb
|
|
324
321
|
}
|
|
322
|
+
|
|
323
|
+
// updated to support HttpRouter.HttpRouter.Provided
|
|
324
|
+
export interface RpcMiddleware<Provides, E> {
|
|
325
|
+
(options: {
|
|
326
|
+
readonly clientId: number
|
|
327
|
+
readonly rpc: Rpc.AnyWithProps
|
|
328
|
+
readonly payload: unknown
|
|
329
|
+
readonly headers: HttpHeaders.Headers
|
|
330
|
+
}): Effect.Effect<Provides, E, HttpRouter.HttpRouter.Provided>
|
|
331
|
+
}
|
|
332
|
+
export interface RpcMiddlewareWrap<Provides, E> {
|
|
333
|
+
(options: {
|
|
334
|
+
readonly clientId: number
|
|
335
|
+
readonly rpc: Rpc.AnyWithProps
|
|
336
|
+
readonly payload: unknown
|
|
337
|
+
readonly headers: HttpHeaders.Headers
|
|
338
|
+
readonly next: Effect.Effect<SuccessValue, E, Provides | HttpRouter.HttpRouter.Provided>
|
|
339
|
+
}): Effect.Effect<SuccessValue, E, HttpRouter.HttpRouter.Provided>
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
type RpcOptionsOriginal = {
|
|
343
|
+
readonly wrap?: boolean
|
|
344
|
+
readonly optional?: boolean
|
|
345
|
+
readonly failure?: Schema.Schema.All
|
|
346
|
+
readonly provides?: Context.Tag<any, any>
|
|
347
|
+
readonly requiredForClient?: boolean
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export const Tag = <Self>() =>
|
|
351
|
+
<
|
|
352
|
+
const Name extends string,
|
|
353
|
+
const Options extends RpcOptionsOriginal
|
|
354
|
+
>(
|
|
355
|
+
id: Name,
|
|
356
|
+
options?: Options | undefined
|
|
357
|
+
) =>
|
|
358
|
+
<E, R, L extends NonEmptyReadonlyArray<Layer.Layer.Any>>(opts: {
|
|
359
|
+
effect: Effect.Effect<
|
|
360
|
+
TagClass.Wrap<Options> extends true ? RpcMiddlewareWrap<
|
|
361
|
+
TagClass.Provides<Options>,
|
|
362
|
+
TagClass.Failure<Options>
|
|
363
|
+
>
|
|
364
|
+
: RpcMiddleware<
|
|
365
|
+
TagClass.Service<Options>,
|
|
366
|
+
TagClass.FailureService<Options>
|
|
367
|
+
>,
|
|
368
|
+
E,
|
|
369
|
+
R
|
|
370
|
+
>
|
|
371
|
+
dependencies?: L
|
|
372
|
+
}): RpcMiddleware.TagClass<Self, Name, Options> & {
|
|
373
|
+
Default: Layer.Layer<Self, E | LayerUtils.GetLayersError<L>, Exclude<R, LayerUtils.GetLayersSuccess<L>>>
|
|
374
|
+
} =>
|
|
375
|
+
class extends RpcMiddleware.Tag<Self>()(id, options) {
|
|
376
|
+
static readonly Default = Layer.scoped(this, opts.effect as any).pipe(
|
|
377
|
+
Layer.provide([Layer.empty, ...opts.dependencies ?? []])
|
|
378
|
+
)
|
|
379
|
+
static override [Unify.typeSymbol]?: unknown
|
|
380
|
+
static override [Unify.unifySymbol]?: TagUnify<typeof this>
|
|
381
|
+
static override [Unify.ignoreSymbol]?: TagUnifyIgnore
|
|
382
|
+
} as any
|
|
383
|
+
|
|
384
|
+
// export const Tag = <Self>() =>
|
|
385
|
+
// <
|
|
386
|
+
// const Name extends string,
|
|
387
|
+
// const Options extends Omit<RpcOptionsOriginal, "wrap">
|
|
388
|
+
// >(
|
|
389
|
+
// id: Name,
|
|
390
|
+
// options?: Options | undefined
|
|
391
|
+
// ) =>
|
|
392
|
+
// OurTag<Self>()(id, { ...options, wrap: true } as Options & { wrap: true }) as <
|
|
393
|
+
// E,
|
|
394
|
+
// R,
|
|
395
|
+
// L extends NonEmptyReadonlyArray<Layer.Layer.Any>
|
|
396
|
+
// >(opts: {
|
|
397
|
+
// effect: Effect.Effect<
|
|
398
|
+
// RpcMiddlewareWrap<
|
|
399
|
+
// RpcMiddleware.TagClass.Provides<Options>,
|
|
400
|
+
// RpcMiddleware.TagClass.Failure<Options>
|
|
401
|
+
// >,
|
|
402
|
+
// E,
|
|
403
|
+
// R
|
|
404
|
+
// >
|
|
405
|
+
// dependencies?: L
|
|
406
|
+
// }) => RpcMiddleware.TagClass<Self, Name, Options> & {
|
|
407
|
+
// Default: Layer.Layer<Self, E | LayerUtils.GetLayersError<L>, Exclude<R, LayerUtils.GetLayersSuccess<L>>>
|
|
408
|
+
// }
|
|
@@ -1,44 +1,80 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { type
|
|
2
|
+
import { type Rpc, type RpcMiddleware } from "@effect/rpc"
|
|
3
|
+
import { type SuccessValue, type TagClassAny } from "@effect/rpc/RpcMiddleware"
|
|
4
|
+
import { type Array, Context, Effect, type Layer } from "effect-app"
|
|
3
5
|
import { type HttpHeaders, type HttpRouter } from "effect-app/http"
|
|
4
|
-
import {
|
|
6
|
+
import { InfraLogger } from "../../../logger.js"
|
|
5
7
|
|
|
6
|
-
export interface GenericMiddlewareOptions<
|
|
8
|
+
export interface GenericMiddlewareOptions<E> {
|
|
7
9
|
// Effect rpc middleware does not support changing payload or headers, but we do..
|
|
8
|
-
readonly next: Effect.Effect<
|
|
10
|
+
readonly next: Effect.Effect<SuccessValue, E, HttpRouter.HttpRouter.Provided>
|
|
9
11
|
readonly payload: unknown
|
|
10
12
|
readonly headers: HttpHeaders.Headers
|
|
11
|
-
|
|
12
|
-
readonly rpc:
|
|
13
|
+
readonly clientId: number
|
|
14
|
+
readonly rpc: Rpc.AnyWithProps
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
export type GenericMiddlewareMaker =
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
export type GenericMiddlewareMaker = TagClassAny & { Default: Layer.Layer.Any } // todo; and Layer..
|
|
18
|
+
|
|
19
|
+
export namespace GenericMiddlewareMaker {
|
|
20
|
+
export type Provided<T> = T extends TagClassAny
|
|
21
|
+
? T extends { provides: Context.Tag<any, any> } ? Context.Tag.Identifier<T["provides"]> : never
|
|
22
|
+
: never
|
|
23
|
+
}
|
|
18
24
|
|
|
19
25
|
export const genericMiddleware = (i: GenericMiddlewareMaker) => i
|
|
20
26
|
|
|
21
27
|
export const genericMiddlewareMaker = <
|
|
22
|
-
T extends Array<
|
|
23
|
-
ContextTagWithDefault.Base<GenericMiddlewareMaker>
|
|
24
|
-
>
|
|
28
|
+
T extends Array<GenericMiddlewareMaker>
|
|
25
29
|
>(...middlewares: T): {
|
|
26
30
|
dependencies: { [K in keyof T]: T[K]["Default"] }
|
|
27
|
-
effect: Effect.Effect<
|
|
31
|
+
effect: Effect.Effect<RpcMiddleware.RpcMiddlewareWrap<any, any>>
|
|
28
32
|
} => {
|
|
33
|
+
// we want to run them in reverse order
|
|
34
|
+
middlewares = middlewares.toReversed() as any
|
|
29
35
|
return {
|
|
30
36
|
dependencies: middlewares.map((_) => _.Default),
|
|
31
37
|
effect: Effect.gen(function*() {
|
|
32
|
-
const
|
|
38
|
+
const context = yield* Effect.context()
|
|
39
|
+
// const middlewares: readonly (RpcMiddlewareWrap<any, any> | RpcMiddleware.RpcMiddleware<any, any>)[] =
|
|
40
|
+
// (yield* Effect.all(
|
|
41
|
+
// middlewares
|
|
42
|
+
// )) as any
|
|
33
43
|
|
|
34
|
-
return <
|
|
35
|
-
options: GenericMiddlewareOptions<
|
|
44
|
+
return <E>(
|
|
45
|
+
options: GenericMiddlewareOptions<E>
|
|
36
46
|
) => {
|
|
37
|
-
let
|
|
38
|
-
|
|
39
|
-
|
|
47
|
+
let handler = options.next
|
|
48
|
+
// copied from RpcMiddleare
|
|
49
|
+
for (const tag of middlewares) {
|
|
50
|
+
if (tag.wrap) {
|
|
51
|
+
const middleware = Context.unsafeGet(context, tag)
|
|
52
|
+
handler = InfraLogger.logDebug("Applying middleware " + tag.key).pipe(
|
|
53
|
+
Effect.zipRight(middleware({ ...options, next: handler as any }))
|
|
54
|
+
) as any
|
|
55
|
+
} else if (tag.optional) {
|
|
56
|
+
const middleware = Context.unsafeGet(context, tag) as RpcMiddleware.RpcMiddleware<any, any>
|
|
57
|
+
const previous = handler
|
|
58
|
+
handler = InfraLogger.logDebug("Applying middleware " + tag.key).pipe(
|
|
59
|
+
Effect.zipRight(Effect.matchEffect(middleware(options), {
|
|
60
|
+
onFailure: () => previous,
|
|
61
|
+
onSuccess: tag.provides !== undefined
|
|
62
|
+
? (value) => Effect.provideService(previous, tag.provides as any, value)
|
|
63
|
+
: (_) => previous
|
|
64
|
+
}))
|
|
65
|
+
)
|
|
66
|
+
} else {
|
|
67
|
+
const middleware = Context.unsafeGet(context, tag) as RpcMiddleware.RpcMiddleware<any, any>
|
|
68
|
+
handler = InfraLogger.logDebug("Applying middleware " + tag.key).pipe(
|
|
69
|
+
Effect.zipRight(
|
|
70
|
+
tag.provides !== undefined
|
|
71
|
+
? Effect.provideServiceEffect(handler, tag.provides as any, middleware(options))
|
|
72
|
+
: Effect.zipRight(middleware(options), handler)
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
}
|
|
40
76
|
}
|
|
41
|
-
return
|
|
77
|
+
return handler
|
|
42
78
|
}
|
|
43
79
|
})
|
|
44
80
|
} as any
|