@effect-app/infra 2.87.2 → 2.89.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 (50) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/dist/api/{routing/middleware/ContextProvider.d.ts → ContextProvider.d.ts} +1 -1
  3. package/dist/api/ContextProvider.d.ts.map +1 -0
  4. package/dist/api/ContextProvider.js +38 -0
  5. package/dist/api/routing/middleware/RouterMiddleware.d.ts +15 -28
  6. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
  7. package/dist/api/routing/middleware/RouterMiddleware.js +2 -5
  8. package/dist/api/routing/middleware/RpcMiddleware.d.ts +19 -23
  9. package/dist/api/routing/middleware/RpcMiddleware.d.ts.map +1 -1
  10. package/dist/api/routing/middleware/RpcMiddleware.js +1 -1
  11. package/dist/api/routing/middleware/dynamic-middleware.d.ts +1 -9
  12. package/dist/api/routing/middleware/dynamic-middleware.d.ts.map +1 -1
  13. package/dist/api/routing/middleware/generic-middleware.d.ts +24 -22
  14. package/dist/api/routing/middleware/generic-middleware.d.ts.map +1 -1
  15. package/dist/api/routing/middleware/generic-middleware.js +24 -8
  16. package/dist/api/routing/middleware/middleware-api.d.ts +68 -36
  17. package/dist/api/routing/middleware/middleware-api.d.ts.map +1 -1
  18. package/dist/api/routing/middleware/middleware-api.js +45 -45
  19. package/dist/api/routing/middleware/middleware.d.ts +7 -7
  20. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  21. package/dist/api/routing/middleware/middleware.js +5 -5
  22. package/dist/api/routing/middleware.d.ts +0 -1
  23. package/dist/api/routing/middleware.d.ts.map +1 -1
  24. package/dist/api/routing/middleware.js +1 -2
  25. package/dist/api/routing.d.ts +1 -1
  26. package/dist/api/routing.d.ts.map +1 -1
  27. package/dist/api/routing.js +12 -10
  28. package/package.json +6 -6
  29. package/src/api/{routing/middleware/ContextProvider.ts → ContextProvider.ts} +1 -1
  30. package/src/api/routing/middleware/RouterMiddleware.ts +22 -134
  31. package/src/api/routing/middleware/RpcMiddleware.ts +28 -23
  32. package/src/api/routing/middleware/dynamic-middleware.ts +0 -47
  33. package/src/api/routing/middleware/generic-middleware.ts +84 -35
  34. package/src/api/routing/middleware/middleware-api.ts +190 -159
  35. package/src/api/routing/middleware/middleware.ts +5 -5
  36. package/src/api/routing/middleware.ts +0 -1
  37. package/src/api/routing.ts +22 -20
  38. package/test/contextProvider.test.ts +28 -25
  39. package/test/controller.test.ts +44 -9
  40. package/test/dist/contextProvider.test.d.ts.map +1 -1
  41. package/test/dist/controller.test.d.ts.map +1 -1
  42. package/test/dist/fixtures.d.ts +39 -45
  43. package/test/dist/fixtures.d.ts.map +1 -1
  44. package/test/dist/fixtures.js +27 -14
  45. package/test/dist/requires.test.d.ts.map +1 -1
  46. package/test/fixtures.ts +29 -18
  47. package/test/layerUtils.test.ts +1 -2
  48. package/test/requires.test.ts +146 -103
  49. package/dist/api/routing/middleware/ContextProvider.d.ts.map +0 -1
  50. package/dist/api/routing/middleware/ContextProvider.js +0 -38
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-app/infra",
3
- "version": "2.87.2",
3
+ "version": "2.89.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.52.0"
16
+ "effect-app": "2.53.0"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@azure/cosmos": "^4.5.0",
@@ -278,6 +278,10 @@
278
278
  "types": "./dist/adapters/redis-client.d.ts",
279
279
  "default": "./dist/adapters/redis-client.js"
280
280
  },
281
+ "./api/ContextProvider": {
282
+ "types": "./dist/api/ContextProvider.d.ts",
283
+ "default": "./dist/api/ContextProvider.js"
284
+ },
281
285
  "./api/codec": {
282
286
  "types": "./dist/api/codec.d.ts",
283
287
  "default": "./dist/api/codec.js"
@@ -302,10 +306,6 @@
302
306
  "types": "./dist/api/routing/middleware.d.ts",
303
307
  "default": "./dist/api/routing/middleware.js"
304
308
  },
305
- "./api/routing/middleware/ContextProvider": {
306
- "types": "./dist/api/routing/middleware/ContextProvider.d.ts",
307
- "default": "./dist/api/routing/middleware/ContextProvider.js"
308
- },
309
309
  "./api/routing/middleware/RouterMiddleware": {
310
310
  "types": "./dist/api/routing/middleware/RouterMiddleware.d.ts",
311
311
  "default": "./dist/api/routing/middleware/RouterMiddleware.js"
@@ -4,7 +4,7 @@ import { Context, Effect, Layer, type NonEmptyReadonlyArray, pipe, type Scope }
4
4
  import { type HttpRouter } from "effect-app/http"
5
5
  import { type Tag } from "effect/Context"
6
6
  import { type YieldWrap } from "effect/Utils"
7
- import { type ContextTagWithDefault, type GetContext, type LayerUtils, mergeContexts } from "../../layerUtils.js"
7
+ import { type ContextTagWithDefault, type GetContext, type LayerUtils, mergeContexts } from "./layerUtils.js"
8
8
 
9
9
  export namespace EffectGenUtils {
10
10
  export type Success<EG> = EG extends Effect<infer A, infer _E, infer _R> ? A
@@ -1,149 +1,37 @@
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 { type Effect, type Request, type S, type Scope } from "effect-app"
5
- import type { GetEffectContext, RPCContextMap } from "effect-app/client/req"
6
- import type * as EffectRequest from "effect/Request"
7
- import { type ContextTagWithDefault } from "../../layerUtils.js"
8
- import { type ContextWithLayer } from "./dynamic-middleware.js"
4
+ import { type Context, type Layer, type S } from "effect-app"
5
+ import type { GetContextConfig, RPCContextMap } from "effect-app/client/req"
6
+ import { type MiddlewareMakerId } from "./middleware-api.js"
7
+ import { type TagClass } from "./RpcMiddleware.js"
9
8
 
10
9
  // module:
11
10
  //
12
- export type MakeRPCHandlerFactory<
13
- RequestContextMap extends Record<string, RPCContextMap.Any>,
14
- MiddlewareR
15
- > = <
16
- T extends {
17
- config?: Partial<Record<keyof RequestContextMap, any>>
18
- },
19
- Req extends S.TaggedRequest.All,
20
- HandlerR
21
- >(
22
- schema: T & S.Schema<Req, any, never>,
23
- next: (
24
- request: Req,
25
- headers: any
26
- ) => Effect.Effect<
27
- EffectRequest.Request.Success<Req>,
28
- EffectRequest.Request.Error<Req>,
29
- // dynamic middlewares removes the dynamic context from HandlerR
30
- Exclude<HandlerR, GetEffectContext<RequestContextMap, (T & S.Schema<Req, any, never>)["config"]>>
31
- >,
32
- moduleName: string
33
- ) => (
34
- req: Req,
35
- headers: any
36
- ) => Effect.Effect<
37
- Request.Request.Success<Req>,
38
- Request.Request.Error<Req> | RequestContextMapErrors<RequestContextMap>,
39
- // the middleware will remove from HandlerR the dynamic context, but will also add some requirements
40
- | MiddlewareR
41
- // & S.Schema<Req, any, never> is useless here but useful when creating the middleware
42
- | Exclude<HandlerR, GetEffectContext<RequestContextMap, (T & S.Schema<Req, any, never>)["config"]>>
43
- >
44
-
45
- export type RPCHandlerFactory<
46
- RequestContextMap extends Record<string, RPCContextMap.Any>,
47
- ContextProviderA
48
- > = <
49
- T extends {
50
- config?: Partial<Record<keyof RequestContextMap, any>>
51
- },
52
- Req extends S.TaggedRequest.All,
53
- HandlerR
54
- >(
55
- schema: T & S.Schema<Req, any, never>,
56
- next: (
57
- request: Req,
58
- headers: any
59
- ) => Effect.Effect<
60
- EffectRequest.Request.Success<Req>,
61
- EffectRequest.Request.Error<Req>,
62
- HandlerR
63
- >,
64
- moduleName: string
65
- ) => (
66
- req: Req,
67
- headers: any
68
- ) => Effect.Effect<
69
- Request.Request.Success<Req>,
70
- Request.Request.Error<Req> | RequestContextMapErrors<RequestContextMap>,
71
- | Scope.Scope // because of the context provider and the middleware (Middleware)
72
- | Exclude<
73
- // the middleware will remove from HandlerR the dynamic context
74
- // & S.Schema<Req, any, never> is useless here but useful when creating the middleware
75
- Exclude<HandlerR, GetEffectContext<RequestContextMap, (T & S.Schema<Req, any, never>)["config"]>>,
76
- // the context provider provides additional stuff both to the middleware and the next
77
- ContextProviderA
78
- >
79
- >
80
-
81
- export type RequestContextMapProvider<RequestContextMap extends Record<string, RPCContextMap.Any>> = {
82
- [K in keyof RequestContextMap]: ContextWithLayer.Base<
83
- { [K in keyof RequestContextMap]?: RequestContextMap[K]["contextActivation"] },
84
- RequestContextMap[K]["service"],
85
- S.Schema.Type<RequestContextMap[K]["error"]>
86
- >
87
- }
88
-
89
- export interface MiddlewareMakerId {
90
- _tag: "MiddlewareMaker"
91
- }
92
11
 
93
12
  export type RouterMiddleware<
94
13
  RequestContextMap extends Record<string, RPCContextMap.Any>, // what services will the middlware provide dynamically to the next, or raise errors.
95
14
  MakeMiddlewareE, // what the middleware construction can fail with
96
15
  MakeMiddlewareR, // what the middlware requires to be constructed
97
- ContextProviderA // what the context provider provides
98
- > = ContextTagWithDefault<
99
- MiddlewareMakerId,
100
- {
101
- _tag: "MiddlewareMaker"
102
- effect: RPCHandlerFactory<RequestContextMap, ContextProviderA>
103
- },
104
- MakeMiddlewareE,
105
- MakeMiddlewareR
106
- >
16
+ ContextProviderA, // what the context provider provides
17
+ ContextProviderE, // what the context provider may fail with
18
+ ContextProviderR // what the context provider may error with
19
+ > =
20
+ & TagClass<
21
+ MiddlewareMakerId,
22
+ "MiddlewareMaker",
23
+ {
24
+ wrap: true
25
+ provides: [Context.Tag<ContextProviderA, ContextProviderA>] // ContextProviderA extends never ? never : [Context.Tag<ContextProviderA, ContextProviderA>] // TODO: Tag<A>, Tag<B>
26
+ requires: [Context.Tag<ContextProviderR, ContextProviderR>] // ContextProviderE extends never ? never : [Context.Tag<ContextProviderR, ContextProviderR>] // TODO: Tag<A>, Tag<B>
27
+ failure: ContextProviderE
28
+ }
29
+ >
30
+ & {
31
+ Default: Layer.Layer<MiddlewareMakerId, MakeMiddlewareE, MakeMiddlewareR>
32
+ requestContext: Context.Tag<"RequestContextConfig", GetContextConfig<RequestContextMap>>
33
+ }
107
34
 
108
35
  export type RequestContextMapErrors<RequestContextMap extends Record<string, RPCContextMap.Any>> = S.Schema.Type<
109
36
  RequestContextMap[keyof RequestContextMap]["error"]
110
37
  >
111
-
112
- // it just provides the right types without cluttering the implementation with them
113
- export function makeRpcEffect<
114
- RequestContextMap extends Record<string, RPCContextMap.Any>,
115
- ContextProviderA
116
- >() {
117
- return (
118
- cb: <
119
- T extends {
120
- config?: Partial<Record<keyof RequestContextMap, any>>
121
- },
122
- Req extends S.TaggedRequest.All,
123
- HandlerR
124
- >(
125
- schema: T & S.Schema<Req, any, never>,
126
- next: (
127
- request: Req,
128
- headers: any
129
- ) => Effect.Effect<
130
- EffectRequest.Request.Success<Req>,
131
- EffectRequest.Request.Error<Req>,
132
- HandlerR
133
- >,
134
- moduleName: string
135
- ) => (
136
- req: Req,
137
- headers: any
138
- ) => Effect.Effect<
139
- Request.Request.Success<Req>,
140
- Request.Request.Error<Req> | RequestContextMapErrors<RequestContextMap>,
141
- | Scope.Scope // the context provider may require Scope to run
142
- | Exclude<
143
- // it can also be removed from HandlerR
144
- Exclude<HandlerR, GetEffectContext<RequestContextMap, (T & S.Schema<Req, any, never>)["config"]>>,
145
- ContextProviderA
146
- >
147
- >
148
- ) => cb
149
- }
@@ -4,12 +4,12 @@
4
4
  import { type Rpc, RpcMiddleware } from "@effect/rpc"
5
5
  import { type SuccessValue, type TypeId } from "@effect/rpc/RpcMiddleware"
6
6
  import { type Context, type Effect, Layer, type NonEmptyReadonlyArray, type Option, type S, type Schema, type Scope, Unify } from "effect-app"
7
- import type { AnyService, ContextRepr, RPCContextMap } from "effect-app/client/req"
7
+ import type { AnyService, ContextTagArray, RPCContextMap } from "effect-app/client/req"
8
8
  import { type HttpHeaders } from "effect-app/http"
9
9
  import { type TagUnify, type TagUnifyIgnore } from "effect/Context"
10
10
  import { type LayerUtils } from "../../layerUtils.js"
11
11
 
12
- // updated to support Scope.Scope
12
+ // updated to support Scope.Scope and Requires
13
13
  export interface RpcMiddleware<Provides, E, Requires> {
14
14
  (options: {
15
15
  readonly clientId: number
@@ -47,19 +47,18 @@ export type DependsOn = {
47
47
  readonly dependsOn: NonEmptyReadonlyArray<AnyDynamic> | undefined
48
48
  }
49
49
 
50
- type RpcOptionsDynamic<Key extends string, A extends RPCContextMap.Any> = RpcOptionsOriginal & {
50
+ interface RpcOptionsDynamic<Key extends string, A extends RPCContextMap.Any> extends RpcOptionsOriginal {
51
51
  readonly dynamic: RpcDynamic<Key, A>
52
52
  readonly dependsOn?: NonEmptyReadonlyArray<AnyDynamic> | undefined
53
53
  }
54
54
 
55
55
  export type Dynamic<Options> = Options extends RpcOptionsDynamic<any, any> ? true : false
56
56
 
57
- export interface RpcMiddlewareDynamicWrap<E, R, Config> {
57
+ export interface RpcMiddlewareDynamicWrap<E, R, _Config> {
58
58
  (options: {
59
59
  readonly next: Effect.Effect<SuccessValue, E, Scope.Scope | R>
60
- readonly config: Config // todo
61
60
  readonly clientId: number
62
- readonly rpc: Rpc.AnyWithProps
61
+ readonly rpc: Rpc.AnyWithProps // TODO & { annotations: Context.Context<RequestContextMap<Config>> }
63
62
  readonly payload: unknown
64
63
  readonly headers: HttpHeaders.Headers
65
64
  }): Effect.Effect<
@@ -69,11 +68,10 @@ export interface RpcMiddlewareDynamicWrap<E, R, Config> {
69
68
  >
70
69
  }
71
70
 
72
- export interface RpcMiddlewareDynamicNormal<A, E, R, Config> {
71
+ export interface RpcMiddlewareDynamicNormal<A, E, R, _Config> {
73
72
  (options: {
74
- readonly config: Config // todo
75
73
  readonly clientId: number
76
- readonly rpc: Rpc.AnyWithProps
74
+ readonly rpc: Rpc.AnyWithProps // TODO & { annotations: Context.Context<RequestContextMap<Config>> }
77
75
  readonly payload: unknown
78
76
  readonly headers: HttpHeaders.Headers
79
77
  }): Effect.Effect<
@@ -86,8 +84,8 @@ export interface RpcMiddlewareDynamicNormal<A, E, R, Config> {
86
84
  export interface TagClassAny extends Context.Tag<any, any> {
87
85
  readonly [TypeId]: TypeId
88
86
  readonly optional: boolean
89
- readonly provides?: Context.Tag<any, any> | ContextRepr | undefined
90
- readonly requires?: Context.Tag<any, any> | ContextRepr | undefined
87
+ readonly provides?: Context.Tag<any, any> | ContextTagArray | undefined
88
+ readonly requires?: Context.Tag<any, any> | ContextTagArray | undefined
91
89
  readonly failure: Schema.Schema.All
92
90
  readonly requiredForClient: boolean
93
91
  readonly wrap: boolean
@@ -105,9 +103,9 @@ export declare namespace TagClass {
105
103
  readonly optional?: false
106
104
  } ? Context.Tag.Identifier<Options["provides"]>
107
105
  : Options extends {
108
- readonly provides: ContextRepr
106
+ readonly provides: ContextTagArray
109
107
  readonly optional?: false
110
- } ? ContextRepr.Identifier<Options["provides"]>
108
+ } ? ContextTagArray.Identifier<Options["provides"]>
111
109
  : never
112
110
 
113
111
  /**
@@ -118,8 +116,8 @@ export declare namespace TagClass {
118
116
  readonly requires: Context.Tag<any, any>
119
117
  } ? Context.Tag.Identifier<Options["requires"]>
120
118
  : Options extends {
121
- readonly requires: ContextRepr
122
- } ? ContextRepr.Identifier<Options["requires"]>
119
+ readonly requires: ContextTagArray
120
+ } ? ContextTagArray.Identifier<Options["requires"]>
123
121
  : never
124
122
 
125
123
  /**
@@ -130,7 +128,8 @@ export declare namespace TagClass {
130
128
  ? Context.Tag.Service<Options["provides"]>
131
129
  : Options extends { readonly dynamic: RpcDynamic<any, infer A> }
132
130
  ? Options extends { wrap: true } ? void : AnyService.Bla<A["service"]>
133
- : Options extends { readonly provides: ContextRepr } ? Context.Context<ContextRepr.Identifier<Options["provides"]>>
131
+ : Options extends { readonly provides: ContextTagArray }
132
+ ? Context.Context<ContextTagArray.Identifier<Options["provides"]>>
134
133
  : void
135
134
 
136
135
  /**
@@ -139,7 +138,8 @@ export declare namespace TagClass {
139
138
  */
140
139
  export type FailureSchema<Options> = Options extends
141
140
  { readonly failure: Schema.Schema.All; readonly optional?: false } ? Options["failure"]
142
- : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? A["error"]
141
+ // actually not, the Failure depends on Dynamic Middleware Configuration!
142
+ // : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? A["error"]
143
143
  : typeof Schema.Never
144
144
 
145
145
  /**
@@ -148,6 +148,7 @@ export declare namespace TagClass {
148
148
  */
149
149
  export type Failure<Options> = Options extends
150
150
  { readonly failure: Schema.Schema<infer _A, infer _I, infer _R>; readonly optional?: false } ? _A
151
+ // actually not, the Failure depends on Dynamic Middleware Configuration!
151
152
  : Options extends { readonly dynamic: RpcDynamic<any, infer A> } ? S.Schema.Type<A["error"]>
152
153
  : never
153
154
 
@@ -191,10 +192,10 @@ export declare namespace TagClass {
191
192
  readonly optional: Optional<Options>
192
193
  readonly failure: FailureSchema<Options>
193
194
  readonly provides: Options extends { readonly provides: Context.Tag<any, any> } ? Options["provides"]
194
- : Options extends { readonly provides: ContextRepr } ? Options["provides"]
195
+ : Options extends { readonly provides: ContextTagArray } ? Options["provides"]
195
196
  : undefined
196
197
  readonly requires: Options extends { readonly requires: Context.Tag<any, any> } ? Options["requires"]
197
- : Options extends { readonly requires: ContextRepr } ? Options["requires"]
198
+ : Options extends { readonly requires: ContextTagArray } ? Options["requires"]
198
199
  : undefined
199
200
  readonly dynamic: Options extends RpcOptionsDynamic<any, any> ? Options["dynamic"]
200
201
  : undefined
@@ -226,8 +227,8 @@ export interface TagClass<
226
227
  >
227
228
  : TagClass.Wrap<Options> extends true ? RpcMiddlewareWrap<
228
229
  TagClass.Provides<Options>,
229
- TagClass.Requires<Options>,
230
- TagClass.Failure<Options>
230
+ TagClass.Failure<Options>,
231
+ TagClass.Requires<Options>
231
232
  >
232
233
  : RpcMiddleware<
233
234
  TagClass.Service<Options>,
@@ -245,7 +246,7 @@ export const Tag = <Self>() =>
245
246
  id: Name,
246
247
  options?: Options | undefined
247
248
  ) =>
248
- <E, R, L extends NonEmptyReadonlyArray<Layer.Layer.Any>>(opts: {
249
+ <E, R, L extends ReadonlyArray<Layer.Layer.Any>>(opts: {
249
250
  effect: Effect.Effect<
250
251
  Options extends RpcOptionsDynamic<any, any> ? TagClass.Wrap<Options> extends true ? RpcMiddlewareDynamicWrap<
251
252
  TagClass.FailureService<Options>,
@@ -273,7 +274,11 @@ export const Tag = <Self>() =>
273
274
  >
274
275
  dependencies?: L
275
276
  }): TagClass<Self, Name, Options> & {
276
- Default: Layer.Layer<Self, E | LayerUtils.GetLayersError<L>, Exclude<R, LayerUtils.GetLayersSuccess<L>>>
277
+ Default: Layer.Layer<
278
+ Self,
279
+ E | LayerUtils.GetLayersError<L>,
280
+ Exclude<R, LayerUtils.GetLayersSuccess<L>> | LayerUtils.GetLayersContext<L>
281
+ >
277
282
  } =>
278
283
  class extends RpcMiddleware.Tag<Self>()(id, options as any) {
279
284
  static readonly dynamic = options && "dynamic" in options ? options.dynamic : undefined
@@ -1,47 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { type ContextTagWithDefault } from "../../layerUtils.js"
3
- import { type RpcMiddlewareDynamicNormal, type RpcMiddlewareDynamicWrap } from "./RpcMiddleware.js"
4
-
5
- export type RpcMiddlewareDynamic<A, E, R, Config> = [A] extends [void] ? RpcMiddlewareDynamicWrap<E, R, Config>
6
- : RpcMiddlewareDynamicNormal<A, E, R, Config>
7
-
8
- export type ContextWithLayer<
9
- Config,
10
- Service,
11
- Error,
12
- Dependencies,
13
- Id,
14
- LayerE,
15
- LayerR
16
- > =
17
- & (
18
- | ContextTagWithDefault<
19
- Id,
20
- // todo
21
- RpcMiddlewareDynamic<Service, Error, any, Config>,
22
- LayerE,
23
- LayerR
24
- >
25
- | ContextTagWithDefault<
26
- Id,
27
- // todo
28
- RpcMiddlewareDynamic<Service, Error, never, Config>,
29
- LayerE,
30
- LayerR
31
- >
32
- )
33
- & {
34
- dependsOn?: Dependencies
35
- }
36
-
37
- export namespace ContextWithLayer {
38
- export type Base<Config, Service, Error> = ContextWithLayer<
39
- Config,
40
- Service,
41
- Error,
42
- any,
43
- any,
44
- any,
45
- any
46
- >
47
- }
@@ -1,67 +1,105 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { type Rpc, type RpcMiddleware } from "@effect/rpc"
3
- import { type SuccessValue } from "@effect/rpc/RpcMiddleware"
4
- import { type Array, Context, Effect, type Layer, type NonEmptyReadonlyArray, Option, type Scope } from "effect-app"
5
- import { type ContextRepr } from "effect-app/client"
6
- import { type HttpHeaders } from "effect-app/http"
2
+ import { type RpcMiddleware } from "@effect/rpc"
3
+ import { Context, Effect, type Layer, type NonEmptyReadonlyArray, Option, type S, type Scope } from "effect-app"
4
+ import { type ContextTagArray } from "effect-app/client"
7
5
  import { InfraLogger } from "../../../logger.js"
8
- import { type TagClassAny } from "./RpcMiddleware.js"
9
-
10
- export interface GenericMiddlewareOptions<E> {
11
- // Effect rpc middleware does not support changing payload or headers, but we do..
12
- readonly next: Effect.Effect<SuccessValue, E, Scope.Scope>
13
- readonly payload: unknown
14
- readonly headers: HttpHeaders.Headers
15
- readonly clientId: number
16
- readonly rpc: Rpc.AnyWithProps
17
- }
6
+ import { type RpcMiddlewareWrap, type TagClassAny } from "./RpcMiddleware.js"
7
+
8
+ // Effect rpc middleware does not support changing payload or headers, but we do..
18
9
 
19
- export type GenericMiddlewareMaker = TagClassAny & { Default: Layer.Layer.Any } // todo; and Layer..
10
+ export type MiddlewareMaker = TagClassAny & { Default: Layer.Layer.Any } // todo; and Layer..
20
11
 
21
- export namespace GenericMiddlewareMaker {
12
+ export namespace MiddlewareMaker {
22
13
  export type ApplyServices<A extends TagClassAny, R> = Exclude<R, Provided<A>> | Required<A>
14
+
23
15
  export type ApplyManyServices<A extends NonEmptyReadonlyArray<TagClassAny>, R> =
24
16
  | Exclude<R, { [K in keyof A]: Provided<A[K]> }[number]>
25
17
  | { [K in keyof A]: Required<A[K]> }[number]
18
+
19
+ export type ManyProvided<A extends ReadonlyArray<TagClassAny>> = A extends NonEmptyReadonlyArray<TagClassAny>
20
+ ? { [K in keyof A]: Provided<A[K]> }[number]
21
+ : Provided<A[number]>
22
+ export type ManyRequired<A extends ReadonlyArray<TagClassAny>> = A extends NonEmptyReadonlyArray<TagClassAny>
23
+ ? { [K in keyof A]: Required<A[K]> }[number]
24
+ : Required<A[number]>
25
+ export type ManyErrors<A extends ReadonlyArray<TagClassAny>> = A extends NonEmptyReadonlyArray<TagClassAny>
26
+ ? { [K in keyof A]: Errors<A[K]> }[number]
27
+ : Errors<A[number]>
28
+
26
29
  export type Provided<T> = T extends TagClassAny
27
30
  ? T extends { provides: Context.Tag<any, any> } ? Context.Tag.Identifier<T["provides"]>
28
- : T extends { provides: ContextRepr } ? ContextRepr.Identifier<T["provides"]>
31
+ : T extends { provides: ContextTagArray } ? ContextTagArray.Identifier<T["provides"]>
32
+ : never
33
+ : never
34
+
35
+ export type Errors<T> = T extends TagClassAny ? T extends { failure: S.Schema.Any } ? S.Schema.Type<T["failure"]>
29
36
  : never
30
37
  : never
31
38
 
32
39
  export type Required<T> = T extends TagClassAny
33
40
  ? T extends { requires: Context.Tag<any, any> } ? Context.Tag.Identifier<T["requires"]>
34
- : T extends { requires: ContextRepr } ? ContextRepr.Identifier<T["requires"]>
41
+ : T extends { requires: ContextTagArray } ? ContextTagArray.Identifier<T["requires"]>
35
42
  : never
36
43
  : never
37
44
  }
38
45
 
39
- export const genericMiddlewareMaker = <
40
- T extends Array<GenericMiddlewareMaker>
41
- >(...middlewares: T): {
42
- dependencies: { [K in keyof T]: T[K]["Default"] }
43
- effect: Effect.Effect<RpcMiddleware.RpcMiddlewareWrap<any, any>>
46
+ export const middlewareMaker = <
47
+ MiddlewareProviders extends ReadonlyArray<MiddlewareMaker>
48
+ >(middlewares: MiddlewareProviders): {
49
+ dependencies: { [K in keyof MiddlewareProviders]: MiddlewareProviders[K]["Default"] }
50
+ effect: Effect.Effect<
51
+ RpcMiddlewareWrap<
52
+ MiddlewareMaker.ManyProvided<MiddlewareProviders>,
53
+ MiddlewareMaker.ManyErrors<MiddlewareProviders>,
54
+ Exclude<
55
+ MiddlewareMaker.ManyRequired<MiddlewareProviders>,
56
+ MiddlewareMaker.ManyProvided<MiddlewareProviders>
57
+ > extends never ? never
58
+ : Exclude<MiddlewareMaker.ManyRequired<MiddlewareProviders>, MiddlewareMaker.ManyProvided<MiddlewareProviders>>
59
+ >
60
+ >
44
61
  } => {
45
- // we want to run them in reverse order
62
+ // we want to run them in reverse order because latter middlewares will provide context to former ones
46
63
  middlewares = middlewares.toReversed() as any
64
+
47
65
  return {
48
66
  dependencies: middlewares.map((_) => _.Default),
49
67
  effect: Effect.gen(function*() {
50
68
  const context = yield* Effect.context()
51
- return <E>(
52
- options: GenericMiddlewareOptions<E>
69
+
70
+ // returns a Effect/RpcMiddlewareWrap with Scope in requirements
71
+ return (
72
+ options: Parameters<
73
+ RpcMiddlewareWrap<
74
+ MiddlewareMaker.ManyProvided<MiddlewareProviders>,
75
+ never,
76
+ Scope.Scope
77
+ >
78
+ >[0]
53
79
  ) => {
80
+ // we start with the actual handler
54
81
  let handler = options.next
55
- // copied from RpcMiddleare
82
+
83
+ // inspired from Effect/RpcMiddleware
56
84
  for (const tag of middlewares) {
57
85
  if (tag.wrap) {
86
+ // use the tag to get the middleware from context
58
87
  const middleware = Context.unsafeGet(context, tag)
88
+
89
+ // wrap the current handler, allowing the middleware to run before and after it
59
90
  handler = InfraLogger.logDebug("Applying middleware wrap " + tag.key).pipe(
60
91
  Effect.zipRight(middleware({ ...options, next: handler }))
61
92
  ) as any
62
93
  } else if (tag.optional) {
94
+ // use the tag to get the middleware from context
95
+ // if the middleware fails to run, we will ignore the error
63
96
  const middleware = Context.unsafeGet(context, tag) as RpcMiddleware.RpcMiddleware<any, any>
97
+
64
98
  const previous = handler
99
+
100
+ // set the previous handler to run after the middleware
101
+ // if the middleware is not present, we just return the previous handler
102
+ // otherwise the middleware will provide some context to be provided to the previous handler
65
103
  handler = InfraLogger.logDebug("Applying middleware optional " + tag.key).pipe(
66
104
  Effect.zipRight(Effect.matchEffect(middleware(options), {
67
105
  onFailure: () => previous,
@@ -74,24 +112,35 @@ export const genericMiddlewareMaker = <
74
112
  }))
75
113
  )
76
114
  } else if (tag.dynamic) {
115
+ // use the tag to get the middleware from context
77
116
  const middleware = Context.unsafeGet(context, tag) as RpcMiddleware.RpcMiddleware<any, any>
117
+
78
118
  const previous = handler
119
+
120
+ // set the previous handler to run after the middleware
121
+ // we do expect the middleware to be present, but the context might not be available
122
+ // if it is, we provide it to the previous handler
79
123
  handler = InfraLogger.logDebug("Applying middleware dynamic " + tag.key, tag.dynamic).pipe(
80
124
  Effect.zipRight(
81
125
  middleware(options).pipe(
82
- Effect.flatMap((value) =>
83
- Option.isSome(value)
84
- ? Context.isContext(value.value)
85
- ? Effect.provide(previous, value.value)
86
- : Effect.provideService(previous, tag.dynamic!.settings.service!, /* TODO */ value.value)
126
+ Effect.flatMap((o) =>
127
+ Option.isSome(o)
128
+ ? Context.isContext(o.value)
129
+ ? Effect.provide(previous, o.value)
130
+ : Effect.provideService(previous, tag.dynamic!.settings.service!, /* TODO */ o.value)
87
131
  : previous
88
132
  )
89
133
  )
90
134
  )
91
- )
135
+ ) as any
92
136
  } else {
137
+ // use the tag to get the middleware from context
93
138
  const middleware = Context.unsafeGet(context, tag) as RpcMiddleware.RpcMiddleware<any, any>
139
+
94
140
  const previous = handler
141
+
142
+ // set the previous handler to run after the middleware
143
+ // we do expect both the middleware and the context to be present
95
144
  handler = InfraLogger.logDebug("Applying middleware " + tag.key).pipe(
96
145
  Effect.zipRight(
97
146
  tag.provides !== undefined
@@ -104,7 +153,7 @@ export const genericMiddlewareMaker = <
104
153
  )
105
154
  : Effect.zipRight(middleware(options), previous)
106
155
  )
107
- )
156
+ ) as any
108
157
  }
109
158
  }
110
159
  return handler