@effect-app/infra 2.73.4 → 2.75.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/QueueMaker/errors.d.ts +1 -1
  3. package/dist/QueueMaker/errors.d.ts.map +1 -1
  4. package/dist/api/layerUtils.d.ts +22 -0
  5. package/dist/api/layerUtils.d.ts.map +1 -0
  6. package/dist/api/layerUtils.js +2 -0
  7. package/dist/api/routing/middleware/ContextProvider.d.ts +41 -0
  8. package/dist/api/routing/middleware/ContextProvider.d.ts.map +1 -0
  9. package/dist/api/routing/middleware/ContextProvider.js +27 -0
  10. package/dist/api/routing/middleware/DynamicMiddleware.d.ts +61 -0
  11. package/dist/api/routing/middleware/DynamicMiddleware.d.ts.map +1 -0
  12. package/dist/api/routing/middleware/DynamicMiddleware.js +45 -0
  13. package/dist/api/routing/middleware/dynamic-middleware.d.ts +26 -0
  14. package/dist/api/routing/middleware/dynamic-middleware.d.ts.map +1 -0
  15. package/dist/api/routing/middleware/dynamic-middleware.js +39 -0
  16. package/dist/api/routing/middleware/generic-middleware.d.ts +9 -0
  17. package/dist/api/routing/middleware/generic-middleware.d.ts.map +1 -0
  18. package/dist/api/routing/middleware/generic-middleware.js +20 -0
  19. package/dist/api/routing/middleware/middleware.d.ts +23 -0
  20. package/dist/api/routing/middleware/middleware.d.ts.map +1 -0
  21. package/dist/api/routing/middleware/middleware.js +91 -0
  22. package/dist/api/routing/middleware.d.ts +6 -0
  23. package/dist/api/routing/middleware.d.ts.map +1 -0
  24. package/dist/api/routing/middleware.js +8 -0
  25. package/dist/api/routing/tsort.d.ts +8 -0
  26. package/dist/api/routing/tsort.d.ts.map +1 -0
  27. package/dist/api/routing/tsort.js +51 -0
  28. package/dist/api/routing.d.ts +36 -46
  29. package/dist/api/routing.d.ts.map +1 -1
  30. package/dist/api/routing.js +7 -56
  31. package/package.json +33 -5
  32. package/src/api/layerUtils.ts +34 -0
  33. package/src/api/routing/middleware/ContextProvider.ts +148 -0
  34. package/src/api/routing/middleware/DynamicMiddleware.ts +317 -0
  35. package/src/api/routing/middleware/dynamic-middleware.ts +158 -0
  36. package/src/api/routing/middleware/generic-middleware.ts +38 -0
  37. package/src/api/routing/middleware/middleware.ts +120 -0
  38. package/src/api/routing/middleware.ts +7 -0
  39. package/src/api/routing/tsort.ts +56 -0
  40. package/src/api/routing.ts +47 -118
  41. package/test/controller.test.ts +212 -51
  42. package/test/dist/controller.legacy2.test.d.ts.map +1 -0
  43. package/test/dist/controller.legacy3.test.d.ts.map +1 -0
  44. package/test/dist/controller.test copy.d.ts +169 -0
  45. package/test/dist/controller.test copy.d.ts.map +1 -0
  46. package/test/dist/controller.test copy.js +152 -0
  47. package/test/dist/controller.test.d.ts.map +1 -1
  48. package/test/dist/controller6.test.d.ts.map +1 -0
  49. package/test/dist/controller7.test.d.ts.map +1 -0
  50. package/test/dist/filterApi.test.d.ts.map +1 -0
  51. package/dist/api/routing/DynamicMiddleware.d.ts +0 -91
  52. package/dist/api/routing/DynamicMiddleware.d.ts.map +0 -1
  53. package/dist/api/routing/DynamicMiddleware.js +0 -56
  54. package/src/api/routing/DynamicMiddleware.ts +0 -415
@@ -0,0 +1,34 @@
1
+ import { type Context, type Layer, type NonEmptyReadonlyArray } from "effect-app"
2
+
3
+ export namespace LayerUtils {
4
+ export type GetLayersSuccess<Layers extends ReadonlyArray<Layer.Layer.Any>> = Layers extends
5
+ NonEmptyReadonlyArray<Layer.Layer.Any> ? {
6
+ [k in keyof Layers]: Layer.Layer.Success<Layers[k]>
7
+ }[number]
8
+ : never
9
+
10
+ export type GetLayersContext<Layers extends ReadonlyArray<Layer.Layer.Any>> = Layers extends
11
+ NonEmptyReadonlyArray<Layer.Layer.Any> ? {
12
+ [k in keyof Layers]: Layer.Layer.Context<Layers[k]>
13
+ }[number]
14
+ : never
15
+
16
+ export type GetLayersError<Layers extends ReadonlyArray<Layer.Layer.Any>> = Layers extends
17
+ NonEmptyReadonlyArray<Layer.Layer.Any> ? {
18
+ [k in keyof Layers]: Layer.Layer.Error<Layers[k]>
19
+ }[number]
20
+ : never
21
+ }
22
+
23
+ export type ContextTagWithDefault<Id, A, LayerE, LayerR, Tag = unknown> =
24
+ & (Tag extends string ? Context.Tag<Id, { _tag: Tag } & A> : Context.Tag<Id, A>)
25
+ & {
26
+ Default: Layer.Layer<Id, LayerE, LayerR>
27
+ // new(a: A): Id
28
+ }
29
+
30
+ export namespace ContextTagWithDefault {
31
+ export type Base<A> = ContextTagWithDefault<any, any, A, any, any>
32
+ }
33
+
34
+ export type GetContext<T> = T extends Context.Context<infer Y> ? Y : never
@@ -0,0 +1,148 @@
1
+ import { type Array, Context, Effect, Layer, type NonEmptyArray, pipe, type Scope } from "effect-app"
2
+ import { type HttpRouter } from "effect-app/http"
3
+ import { type ContextTagWithDefault, type GetContext, type LayerUtils } from "../../layerUtils.js"
4
+ import { mergeContexts } from "./dynamic-middleware.js"
5
+
6
+ // the context provider provides additional stuff
7
+ export type ContextProviderShape<ContextProviderA, ContextProviderR extends HttpRouter.HttpRouter.Provided> = Effect<
8
+ Context.Context<ContextProviderA>,
9
+ never, // no errors are allowed
10
+ ContextProviderR
11
+ >
12
+
13
+ export interface ContextProviderId {
14
+ _tag: "ContextProvider"
15
+ }
16
+
17
+ // Note: the type here must be aligned with MergedContextProvider
18
+ export const mergeContextProviders = <
19
+ // TDeps is an array of services whit Default implementation
20
+ // each service is an effect which builds some context for each request
21
+ TDeps extends Array.NonEmptyReadonlyArray<
22
+ & (
23
+ // E = never => the context provided cannot trigger errors
24
+ // can't put HttpRouter.HttpRouter.Provided as R here because of variance
25
+ // (TDeps is an input type parameter so it's contravariant therefore Effect's R becomes contravariant too)
26
+ | Context.Tag<any, Effect<Context.Context<any>, never, any> & { _tag: any }>
27
+ | Context.Tag<any, Effect<Context.Context<any>, never, never> & { _tag: any }>
28
+ )
29
+ & {
30
+ new(...args: any[]): any
31
+ Default: Layer.Layer<Effect<Context.Context<any>> & { _tag: any }, any, any>
32
+ }
33
+ >
34
+ >(
35
+ ...deps: {
36
+ [K in keyof TDeps]: TDeps[K]["Service"] extends Effect<Context.Context<any>, never, HttpRouter.HttpRouter.Provided>
37
+ ? TDeps[K]
38
+ : `HttpRouter.HttpRouter.Provided are the only requirements ${TDeps[K]["Service"][
39
+ "_tag"
40
+ ]}'s returned effect can have`
41
+ }
42
+ ): {
43
+ dependencies: { [K in keyof TDeps]: TDeps[K]["Default"] }
44
+ effect: Effect.Effect<
45
+ Effect.Effect<
46
+ Context.Context<GetContext<Effect.Success<InstanceType<TDeps[number]>>>>,
47
+ never,
48
+ Effect.Context<InstanceType<TDeps[number]>>
49
+ >,
50
+ LayerUtils.GetLayersError<{ [K in keyof TDeps]: TDeps[K]["Default"] }>,
51
+ LayerUtils.GetLayersSuccess<{ [K in keyof TDeps]: TDeps[K]["Default"] }>
52
+ >
53
+ } => ({
54
+ dependencies: deps.map((_) => _.Default) as any,
55
+ effect: Effect.gen(function*() {
56
+ const makers = yield* Effect.all(deps)
57
+ return Effect
58
+ .gen(function*() {
59
+ const services = (makers as any[]).map((handle, i) => ({ maker: deps[i], handle }))
60
+ // services are effects which return some Context.Context<...>
61
+ const context = yield* mergeContexts(services as any)
62
+ return context
63
+ })
64
+ }) as any
65
+ })
66
+
67
+ export const ContextProvider = <
68
+ ContextProviderA,
69
+ MakeContextProviderE,
70
+ MakeContextProviderR,
71
+ ContextProviderR extends HttpRouter.HttpRouter.Provided,
72
+ Dependencies extends NonEmptyArray<Layer.Layer.Any>
73
+ >(
74
+ input: {
75
+ effect: Effect<
76
+ Effect<ContextProviderA, never, ContextProviderR>,
77
+ MakeContextProviderE,
78
+ MakeContextProviderR | Scope
79
+ >
80
+ dependencies?: Dependencies
81
+ }
82
+ ) => {
83
+ const ctx = Context.GenericTag<
84
+ ContextProviderId,
85
+ Effect<ContextProviderA, never, ContextProviderR>
86
+ >(
87
+ "ContextProvider"
88
+ )
89
+ const l = Layer.scoped(ctx, input.effect)
90
+ return Object.assign(ctx, {
91
+ Default: l.pipe(
92
+ input.dependencies ? Layer.provide(input.dependencies) as any : (_) => _
93
+ ) as Layer.Layer<
94
+ ContextProviderId,
95
+ | MakeContextProviderE
96
+ | LayerUtils.GetLayersError<Dependencies>,
97
+ | Exclude<MakeContextProviderR, LayerUtils.GetLayersSuccess<Dependencies>>
98
+ | LayerUtils.GetLayersContext<Dependencies>
99
+ >
100
+ })
101
+ }
102
+
103
+ // Note: the type here must be aligned with mergeContextProviders
104
+ export const MergedContextProvider = <
105
+ // TDeps is an array of services whit Default implementation
106
+ // each service is an effect which builds some context for each request
107
+ TDeps extends Array.NonEmptyReadonlyArray<
108
+ & (
109
+ // E = never => the context provided cannot trigger errors
110
+ // can't put HttpRouter.HttpRouter.Provided as R here because of variance
111
+ // (TDeps is an input type parameter so it's contravariant therefore Effect's R becomes contravariant too)
112
+ | Context.Tag<any, Effect<Context.Context<any>, never, any> & { _tag: any }>
113
+ | Context.Tag<any, Effect<Context.Context<any>, never, never> & { _tag: any }>
114
+ )
115
+ & {
116
+ new(...args: any[]): any
117
+ Default: Layer.Layer<Effect<Context.Context<any>> & { _tag: any }, any, any>
118
+ }
119
+ >
120
+ >(
121
+ ...deps: {
122
+ [K in keyof TDeps]: TDeps[K]["Service"] extends Effect<Context.Context<any>, never, HttpRouter.HttpRouter.Provided>
123
+ ? TDeps[K]
124
+ : `HttpRouter.HttpRouter.Provided are the only requirements ${TDeps[K]["Service"][
125
+ "_tag"
126
+ ]}'s returned effect can have`
127
+ }
128
+ ) =>
129
+ pipe(
130
+ deps as [Parameters<typeof mergeContextProviders>[0]],
131
+ (_) => mergeContextProviders(..._),
132
+ (_) => ContextProvider(_ as any)
133
+ ) as unknown as ContextTagWithDefault<
134
+ ContextProviderId,
135
+ Effect.Effect<
136
+ Context.Context<GetContext<Effect.Success<InstanceType<TDeps[number]>>>>,
137
+ never,
138
+ Effect.Context<InstanceType<TDeps[number]>>
139
+ >,
140
+ LayerUtils.GetLayersError<{ [K in keyof TDeps]: TDeps[K]["Default"] }>,
141
+ | Exclude<
142
+ InstanceType<TDeps[number]>,
143
+ LayerUtils.GetLayersSuccess<{ [K in keyof TDeps]: TDeps[K]["Default"] }>
144
+ >
145
+ | LayerUtils.GetLayersContext<{ [K in keyof TDeps]: TDeps[K]["Default"] }>
146
+ >
147
+
148
+ export const EmptyContextProvider = ContextProvider({ effect: Effect.succeed(Effect.succeed(Context.empty())) })
@@ -0,0 +1,317 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
3
+ /* eslint-disable @typescript-eslint/no-explicit-any */
4
+ import { Context, Effect, Layer, type NonEmptyArray, type Request, type S, type Scope } from "effect-app"
5
+ import type { GetEffectContext, RPCContextMap } from "effect-app/client/req"
6
+ import { type HttpRouter } from "effect-app/http"
7
+ import type * as EffectRequest from "effect/Request"
8
+ import { type ContextTagWithDefault, type LayerUtils } from "../../layerUtils.js"
9
+ import { type ContextProviderId, type ContextProviderShape } from "./ContextProvider.js"
10
+ import { type ContextWithLayer, implementMiddleware } from "./dynamic-middleware.js"
11
+ import { type GenericMiddlewareMaker, genericMiddlewareMaker } from "./generic-middleware.js"
12
+
13
+ // module:
14
+ //
15
+ export type MakeRPCHandlerFactory<
16
+ RequestContextMap extends Record<string, RPCContextMap.Any>,
17
+ MiddlewareR
18
+ > = <
19
+ T extends {
20
+ config?: Partial<Record<keyof RequestContextMap, any>>
21
+ },
22
+ Req extends S.TaggedRequest.All,
23
+ HandlerR
24
+ >(
25
+ schema: T & S.Schema<Req, any, never>,
26
+ handler: (
27
+ request: Req,
28
+ headers: any
29
+ ) => Effect.Effect<
30
+ EffectRequest.Request.Success<Req>,
31
+ EffectRequest.Request.Error<Req>,
32
+ // dynamic middlewares removes the dynamic context from HandlerR
33
+ Exclude<HandlerR, GetEffectContext<RequestContextMap, (T & S.Schema<Req, any, never>)["config"]>>
34
+ >,
35
+ moduleName: string
36
+ ) => (
37
+ req: Req,
38
+ headers: any
39
+ ) => Effect.Effect<
40
+ Request.Request.Success<Req>,
41
+ Request.Request.Error<Req> | RequestContextMapErrors<RequestContextMap>,
42
+ // the middleware will remove from HandlerR the dynamic context, but will also add some requirements
43
+ | MiddlewareR
44
+ // & S.Schema<Req, any, never> is useless here but useful when creating the middleware
45
+ | Exclude<HandlerR, GetEffectContext<RequestContextMap, (T & S.Schema<Req, any, never>)["config"]>>
46
+ >
47
+
48
+ export type RPCHandlerFactory<
49
+ RequestContextMap extends Record<string, RPCContextMap.Any>,
50
+ ContextProviderA
51
+ > = <
52
+ T extends {
53
+ config?: Partial<Record<keyof RequestContextMap, any>>
54
+ },
55
+ Req extends S.TaggedRequest.All,
56
+ HandlerR
57
+ >(
58
+ schema: T & S.Schema<Req, any, never>,
59
+ handler: (
60
+ request: Req,
61
+ headers: any
62
+ ) => Effect.Effect<
63
+ EffectRequest.Request.Success<Req>,
64
+ EffectRequest.Request.Error<Req>,
65
+ HandlerR
66
+ >,
67
+ moduleName: string
68
+ ) => (
69
+ req: Req,
70
+ headers: any
71
+ ) => Effect.Effect<
72
+ Request.Request.Success<Req>,
73
+ Request.Request.Error<Req> | RequestContextMapErrors<RequestContextMap>,
74
+ | HttpRouter.HttpRouter.Provided // because of the context provider and the middleware (Middleware)
75
+ | Exclude<
76
+ // the middleware will remove from HandlerR the dynamic context
77
+ // & S.Schema<Req, any, never> is useless here but useful when creating the middleware
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 handler
80
+ ContextProviderA
81
+ >
82
+ >
83
+
84
+ type RequestContextMapProvider<RequestContextMap extends Record<string, RPCContextMap.Any>> = {
85
+ [K in keyof RequestContextMap]: ContextWithLayer.Base<
86
+ { [K in keyof RequestContextMap]?: RequestContextMap[K]["contextActivation"] },
87
+ RequestContextMap[K]["service"],
88
+ S.Schema.Type<RequestContextMap[K]["error"]>
89
+ >
90
+ }
91
+
92
+ export interface MiddlewareMake<
93
+ RequestContextMap extends Record<string, RPCContextMap.Any>, // what services will the middleware provide dynamically to the handler, 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
+ DynamicMiddlewareProviders extends RequestContextMapProvider<RequestContextMap>, // how to resolve the dynamic middleware
101
+ GenericMiddlewareProviders extends Array<
102
+ ContextTagWithDefault.Base<GenericMiddlewareMaker>
103
+ >,
104
+ MakeMiddlewareE, // what the middleware construction can fail with
105
+ MakeMiddlewareR, // what the middleware requires to be constructed
106
+ MiddlewareDependencies extends NonEmptyArray<Layer.Layer.Any> // layers provided for the middleware to be constructed
107
+ > {
108
+ /* dynamic middlewares to be applied based on Request Configuration */
109
+ dynamicMiddlewares: DynamicMiddlewareProviders
110
+ /** generic middlewares are those which follow the (next) => (input, headers) => pattern */
111
+ genericMiddlewares: GenericMiddlewareProviders
112
+ /** static context providers */
113
+ contextProvider: ContextTagWithDefault<
114
+ ContextProviderId,
115
+ ContextProviderShape<ContextProviderA, ContextProviderR>,
116
+ MakeContextProviderE,
117
+ MakeContextProviderR
118
+ >
119
+
120
+ /* dependencies for the main middleware running just before the handler is called */
121
+ dependencies?: MiddlewareDependencies
122
+ // this actually builds "the middleware", i.e. returns the augmented handler factory when yielded...
123
+ execute?: (
124
+ maker: (
125
+ // MiddlewareR is set to ContextProviderA | HttpRouter.HttpRouter.Provided because that's what, at most
126
+ // a middleware can additionally require to get executed
127
+ cb: MakeRPCHandlerFactory<RequestContextMap, ContextProviderA | HttpRouter.HttpRouter.Provided>
128
+ ) => MakeRPCHandlerFactory<RequestContextMap, ContextProviderA | HttpRouter.HttpRouter.Provided>
129
+ ) => Effect<
130
+ MakeRPCHandlerFactory<RequestContextMap, ContextProviderA | HttpRouter.HttpRouter.Provided>,
131
+ MakeMiddlewareE,
132
+ MakeMiddlewareR | Scope // ...that's why MakeMiddlewareR is here
133
+ >
134
+ }
135
+
136
+ export interface MiddlewareMakerId {
137
+ _tag: "MiddlewareMaker"
138
+ }
139
+
140
+ export type Middleware<
141
+ RequestContextMap extends Record<string, RPCContextMap.Any>, // what services will the middlware provide dynamically to the handler, or raise errors.
142
+ MakeMiddlewareE, // what the middleware construction can fail with
143
+ MakeMiddlewareR, // what the middlware requires to be constructed
144
+ ContextProviderA // what the context provider provides
145
+ > = ContextTagWithDefault<
146
+ MiddlewareMakerId,
147
+ {
148
+ effect: RPCHandlerFactory<RequestContextMap, ContextProviderA>
149
+ },
150
+ MakeMiddlewareE,
151
+ MakeMiddlewareR,
152
+ "MiddlewareMaker"
153
+ >
154
+
155
+ export type RequestContextMapErrors<RequestContextMap extends Record<string, RPCContextMap.Any>> = S.Schema.Type<
156
+ RequestContextMap[keyof RequestContextMap]["error"]
157
+ >
158
+
159
+ // factory for middlewares
160
+ export const makeMiddleware =
161
+ // by setting RequestContextMap beforehand, execute contextual typing does not fuck up itself to anys
162
+ <
163
+ RequestContextMap extends Record<string, RPCContextMap.Any>
164
+ >() =>
165
+ <
166
+ //
167
+ // ContextProvider is a service that builds additional context for each request.
168
+ ContextProviderA, // what the context provider provides
169
+ ContextProviderR extends HttpRouter.HttpRouter.Provided, // what the context provider requires
170
+ MakeContextProviderE, // what the context provider construction can fail with
171
+ MakeContextProviderR, // what the context provider construction requires
172
+ RequestContextProviders extends RequestContextMapProvider<RequestContextMap>, // how to resolve the dynamic middleware
173
+ GenericMiddlewareProviders extends Array<
174
+ ContextTagWithDefault.Base<GenericMiddlewareMaker>
175
+ >,
176
+ MiddlewareDependencies extends NonEmptyArray<Layer.Layer.Any>, // layers provided for the middlware to be constructed
177
+ MakeMiddlewareE = never, // what the middleware construction can fail with
178
+ MakeMiddlewareR = never // what the middlware requires to be constructed
179
+ >(
180
+ make: MiddlewareMake<
181
+ RequestContextMap,
182
+ ContextProviderA,
183
+ ContextProviderR,
184
+ MakeContextProviderE,
185
+ MakeContextProviderR,
186
+ RequestContextProviders,
187
+ GenericMiddlewareProviders,
188
+ MakeMiddlewareE,
189
+ MakeMiddlewareR,
190
+ MiddlewareDependencies
191
+ >
192
+ ) => {
193
+ // type Id = MiddlewareMakerId &
194
+ const MiddlewareMaker = Context.GenericTag<
195
+ MiddlewareMakerId,
196
+ {
197
+ effect: RPCHandlerFactory<RequestContextMap, ContextProviderA>
198
+ _tag: "MiddlewareMaker"
199
+ }
200
+ >(
201
+ "MiddlewareMaker"
202
+ )
203
+
204
+ const dynamicMiddlewares = implementMiddleware<RequestContextMap>()(make.dynamicMiddlewares)
205
+ const middlewares = genericMiddlewareMaker(...make.genericMiddlewares)
206
+
207
+ const l = Layer.scoped(
208
+ MiddlewareMaker,
209
+ Effect
210
+ .all({
211
+ dynamicMiddlewares: dynamicMiddlewares.effect,
212
+ generic: middlewares.effect,
213
+ middleware: make.execute
214
+ ? make.execute((
215
+ cb: MakeRPCHandlerFactory<RequestContextMap, HttpRouter.HttpRouter.Provided | ContextProviderA>
216
+ ) => cb)
217
+ : Effect.succeed<
218
+ MakeRPCHandlerFactory<RequestContextMap, ContextProviderA | HttpRouter.HttpRouter.Provided>
219
+ >((_schema, handle) => (req, headers) => handle(req, headers)),
220
+ contextProvider: make.contextProvider // uses the middleware.contextProvider tag to get the context provider service
221
+ })
222
+ .pipe(
223
+ Effect.map(({ contextProvider, dynamicMiddlewares, generic, middleware }) => ({
224
+ _tag: "MiddlewareMaker" as const,
225
+ effect: makeRpcEffect<RequestContextMap, ContextProviderA>()(
226
+ (schema, handler, moduleName) => {
227
+ const h = middleware(schema, handler as any, moduleName)
228
+ return generic(
229
+ Effect.fnUntraced(function*(req, headers) {
230
+ yield* Effect.annotateCurrentSpan(
231
+ "request.name",
232
+ moduleName ? `${moduleName}.${req._tag}` : req._tag
233
+ )
234
+
235
+ // the contextProvider is an Effect that builds the context for the request
236
+ return yield* contextProvider.pipe(
237
+ Effect.flatMap((contextProviderContext) =>
238
+ // the dynamicMiddlewares is an Effect that builds the dynamiuc context for the request
239
+ dynamicMiddlewares(schema.config ?? {}, headers).pipe(
240
+ Effect.flatMap((dynamicContext) => h(req, headers).pipe(Effect.provide(dynamicContext))),
241
+ Effect.provide(contextProviderContext)
242
+ )
243
+ )
244
+ )
245
+ }) as any,
246
+ moduleName
247
+ )
248
+ }
249
+ )
250
+ }))
251
+ )
252
+ )
253
+
254
+ const middlewareLayer = l
255
+ .pipe(
256
+ Layer.provide(
257
+ Layer.mergeAll(
258
+ make.dependencies ? make.dependencies as any : Layer.empty,
259
+ ...(dynamicMiddlewares.dependencies as any),
260
+ make.contextProvider.Default,
261
+ ...middlewares.dependencies
262
+ )
263
+ )
264
+ ) as Layer.Layer<
265
+ MiddlewareMakerId,
266
+ | MakeMiddlewareE // what the middleware construction can fail with
267
+ | LayerUtils.GetLayersContext<typeof dynamicMiddlewares.dependencies>
268
+ | LayerUtils.GetLayersContext<typeof middlewares.dependencies> // what could go wrong when building the dynamic middleware provider
269
+ | Layer.Error<typeof make.contextProvider.Default>, // what could go wrong when building the context provider
270
+ | LayerUtils.GetLayersContext<MiddlewareDependencies> // what's needed to build layers
271
+ | LayerUtils.GetLayersContext<typeof middlewares.dependencies>
272
+ | LayerUtils.GetLayersContext<typeof dynamicMiddlewares.dependencies> // what's needed to build dynamic middleware layers
273
+ | Exclude<MakeMiddlewareR, LayerUtils.GetLayersSuccess<MiddlewareDependencies>> // what layers provides
274
+ | Layer.Context<typeof make.contextProvider.Default> // what's needed to build the contextProvider
275
+ >
276
+
277
+ return Object.assign(MiddlewareMaker, { Default: middlewareLayer })
278
+ }
279
+
280
+ // it just provides the right types without cluttering the implementation with them
281
+ function makeRpcEffect<
282
+ RequestContextMap extends Record<string, RPCContextMap.Any>,
283
+ ContextProviderA
284
+ >() {
285
+ return (
286
+ cb: <
287
+ T extends {
288
+ config?: Partial<Record<keyof RequestContextMap, any>>
289
+ },
290
+ Req extends S.TaggedRequest.All,
291
+ HandlerR
292
+ >(
293
+ schema: T & S.Schema<Req, any, never>,
294
+ handler: (
295
+ request: Req,
296
+ headers: any
297
+ ) => Effect.Effect<
298
+ EffectRequest.Request.Success<Req>,
299
+ EffectRequest.Request.Error<Req>,
300
+ HandlerR
301
+ >,
302
+ moduleName: string
303
+ ) => (
304
+ req: Req,
305
+ headers: any
306
+ ) => Effect.Effect<
307
+ Request.Request.Success<Req>,
308
+ Request.Request.Error<Req> | RequestContextMapErrors<RequestContextMap>,
309
+ | HttpRouter.HttpRouter.Provided // the context provider may require HttpRouter.Provided to run
310
+ | Exclude<
311
+ // it can also be removed from HandlerR
312
+ Exclude<HandlerR, GetEffectContext<RequestContextMap, (T & S.Schema<Req, any, never>)["config"]>>,
313
+ ContextProviderA
314
+ >
315
+ >
316
+ ) => cb
317
+ }
@@ -0,0 +1,158 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { Array, Context, Effect, Option, type S } from "effect-app"
3
+ import { type GetEffectContext, type RPCContextMap } from "effect-app/client"
4
+ import { type Tag } from "effect-app/Context"
5
+ import { typedValuesOf } from "effect-app/utils"
6
+ import { InfraLogger } from "../../../logger.js"
7
+ import { type ContextTagWithDefault } from "../../layerUtils.js"
8
+ import { sort } from "../tsort.js"
9
+
10
+ export type ContextWithLayer<
11
+ Config,
12
+ Id,
13
+ Service,
14
+ E,
15
+ R,
16
+ MakeE,
17
+ MakeR,
18
+ Tag extends string,
19
+ Args extends [config: Config, headers: Record<string, string>],
20
+ Dependencies extends any[]
21
+ > =
22
+ & ContextTagWithDefault<
23
+ Id,
24
+ { handle: (...args: Args) => Effect<Option<Context<Service>>, E, R>; _tag: Tag },
25
+ MakeE,
26
+ MakeR
27
+ >
28
+ & {
29
+ dependsOn?: Dependencies
30
+ }
31
+
32
+ export namespace ContextWithLayer {
33
+ export type Base<Config, Service, Error> =
34
+ | ContextWithLayer<
35
+ Config,
36
+ any,
37
+ Service,
38
+ Error,
39
+ any,
40
+ any,
41
+ any,
42
+ string,
43
+ any,
44
+ any
45
+ >
46
+ | ContextWithLayer<
47
+ Config,
48
+ any,
49
+ Service,
50
+ Error,
51
+ never,
52
+ any,
53
+ never,
54
+ any,
55
+ any,
56
+ any
57
+ >
58
+ | ContextWithLayer<
59
+ Config,
60
+ any,
61
+ Service,
62
+ Error,
63
+ any,
64
+ any,
65
+ never,
66
+ any,
67
+ any,
68
+ any
69
+ >
70
+ | ContextWithLayer<
71
+ Config,
72
+ any,
73
+ Service,
74
+ Error,
75
+ never,
76
+ any,
77
+ any,
78
+ any,
79
+ any,
80
+ any
81
+ >
82
+ }
83
+
84
+ export const mergeContexts = Effect.fnUntraced(
85
+ function*<T extends readonly { maker: any; handle: Effect<Context<any>> }[]>(makers: T) {
86
+ let context = Context.empty()
87
+ for (const mw of makers) {
88
+ yield* InfraLogger.logDebug("Building context for middleware", mw.maker.key ?? mw.maker)
89
+ const moreContext = yield* mw.handle.pipe(Effect.provide(context))
90
+ yield* InfraLogger.logDebug(
91
+ "Built context for middleware",
92
+ mw.maker.key ?? mw.maker,
93
+ (moreContext as any).toJSON().services
94
+ )
95
+ context = Context.merge(context, moreContext)
96
+ }
97
+ return context as Context.Context<Effect.Success<T[number]["handle"]>>
98
+ }
99
+ )
100
+
101
+ export const mergeOptionContexts = Effect.fnUntraced(
102
+ function*<T extends readonly { maker: any; handle: Effect<Option<Context<any>>> }[]>(makers: T) {
103
+ let context = Context.empty()
104
+ for (const mw of makers) {
105
+ yield* InfraLogger.logDebug("Building context for middleware", mw.maker.key ?? mw.maker)
106
+ const moreContext = yield* mw.handle.pipe(Effect.provide(context))
107
+ yield* InfraLogger.logDebug(
108
+ "Built context for middleware",
109
+ mw.maker.key ?? mw.maker,
110
+ Option.map(moreContext, (c) => (c as any).toJSON().services)
111
+ )
112
+ if (moreContext.value) {
113
+ context = Context.merge(context, moreContext.value)
114
+ }
115
+ }
116
+ return context
117
+ }
118
+ )
119
+
120
+ export const implementMiddleware = <T extends Record<string, RPCContextMap.Any>>() =>
121
+ <
122
+ TI extends {
123
+ [K in keyof T]: ContextWithLayer.Base<
124
+ { [K in keyof T]?: T[K]["contextActivation"] },
125
+ T[K]["service"],
126
+ S.Schema.Type<T[K]["error"]>
127
+ >
128
+ }
129
+ >(implementations: TI) => ({
130
+ dependencies: typedValuesOf(implementations).map((_) => _.Default) as {
131
+ [K in keyof TI]: TI[K]["Default"]
132
+ }[keyof TI][],
133
+ effect: Effect.gen(function*() {
134
+ const sorted = sort(typedValuesOf(implementations))
135
+
136
+ const makers = yield* Effect.all(sorted)
137
+ return Effect.fnUntraced(
138
+ function*(config: { [K in keyof T]?: T[K]["contextActivation"] }, headers: Record<string, string>) {
139
+ const ctx = yield* mergeOptionContexts(
140
+ Array.map(
141
+ makers,
142
+ (_, i) => ({ maker: sorted[i], handle: (_ as any).handle(config, headers) as any }) as any
143
+ )
144
+ )
145
+ return ctx as Context.Context<
146
+ GetEffectContext<T, typeof config>
147
+ >
148
+ }
149
+ ) as (
150
+ config: { [K in keyof T]?: T[K]["contextActivation"] },
151
+ headers: Record<string, string>
152
+ ) => Effect.Effect<
153
+ Context.Context<GetEffectContext<T, typeof config>>,
154
+ Effect.Error<ReturnType<Tag.Service<TI[keyof TI]>["handle"]>>,
155
+ Effect.Context<ReturnType<Tag.Service<TI[keyof TI]>["handle"]>>
156
+ >
157
+ })
158
+ })
@@ -0,0 +1,38 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { type Array, Effect } from "effect-app"
3
+ import { type HttpHeaders, type HttpRouter } from "effect-app/http"
4
+ import { type ContextTagWithDefault } from "../../layerUtils.js"
5
+
6
+ export type GenericMiddlewareMaker = <A, E>(
7
+ handle: (input: any, headers: HttpHeaders.Headers) => Effect.Effect<A, E, HttpRouter.HttpRouter.Provided>,
8
+ moduleName: string
9
+ ) => (input: any, headers: HttpHeaders.Headers) => Effect.Effect<A, E, HttpRouter.HttpRouter.Provided>
10
+
11
+ export const genericMiddlewareMaker = <
12
+ T extends Array<
13
+ ContextTagWithDefault.Base<GenericMiddlewareMaker>
14
+ >
15
+ >(...middlewares: T): {
16
+ dependencies: { [K in keyof T]: T[K]["Default"] }
17
+ effect: Effect.Effect<GenericMiddlewareMaker>
18
+ } => {
19
+ return {
20
+ dependencies: middlewares.map((_) => _.Default),
21
+ effect: Effect.gen(function*() {
22
+ const middlewaresInstances = yield* Effect.all(middlewares)
23
+
24
+ return <A, E, R>(
25
+ handle: (input: any, headers: HttpHeaders.Headers) => Effect.Effect<A, E, R>,
26
+ moduleName: string
27
+ ) => {
28
+ return (input: any, headers: HttpHeaders.Headers) => {
29
+ let effect = handle
30
+ for (const middleware of (middlewaresInstances as any[]).toReversed()) {
31
+ effect = middleware(effect, moduleName)
32
+ }
33
+ return effect(input, headers)
34
+ }
35
+ }
36
+ })
37
+ } as any
38
+ }