@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.
Files changed (45) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/api/layerUtils.d.ts +2 -4
  3. package/dist/api/layerUtils.d.ts.map +1 -1
  4. package/dist/api/layerUtils.js +4 -5
  5. package/dist/api/routing/middleware/ContextProvider.d.ts +17 -12
  6. package/dist/api/routing/middleware/ContextProvider.d.ts.map +1 -1
  7. package/dist/api/routing/middleware/ContextProvider.js +15 -5
  8. package/dist/api/routing/middleware/DynamicMiddleware.d.ts +43 -21
  9. package/dist/api/routing/middleware/DynamicMiddleware.d.ts.map +1 -1
  10. package/dist/api/routing/middleware/DynamicMiddleware.js +60 -20
  11. package/dist/api/routing/middleware/generic-middleware.d.ts +17 -10
  12. package/dist/api/routing/middleware/generic-middleware.d.ts.map +1 -1
  13. package/dist/api/routing/middleware/generic-middleware.js +34 -8
  14. package/dist/api/routing/middleware/middleware.d.ts +16 -10
  15. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  16. package/dist/api/routing/middleware/middleware.js +41 -51
  17. package/dist/api/routing/middleware.d.ts +1 -0
  18. package/dist/api/routing/middleware.d.ts.map +1 -1
  19. package/dist/api/routing/middleware.js +2 -1
  20. package/dist/api/routing.d.ts +2 -2
  21. package/dist/api/routing.d.ts.map +1 -1
  22. package/dist/api/routing.js +3 -3
  23. package/package.json +1 -1
  24. package/src/api/layerUtils.ts +6 -9
  25. package/src/api/routing/middleware/ContextProvider.ts +84 -28
  26. package/src/api/routing/middleware/DynamicMiddleware.ts +162 -78
  27. package/src/api/routing/middleware/generic-middleware.ts +56 -20
  28. package/src/api/routing/middleware/middleware.ts +27 -35
  29. package/src/api/routing/middleware.ts +2 -0
  30. package/src/api/routing.ts +5 -5
  31. package/test/controller.test.ts +27 -47
  32. package/test/dist/controller/test2.test.d.ts.map +1 -0
  33. package/test/dist/controller.test copy.js +23 -46
  34. package/test/dist/controller.test.d.ts.map +1 -1
  35. package/test/dist/controller5.test.d.ts.map +1 -0
  36. package/test/dist/controller6.test.d.ts.map +1 -1
  37. package/test/dist/controller7.test.d.ts.map +1 -1
  38. package/test/dist/dynamicContext.test.d.ts.map +1 -0
  39. package/test/dynamicContext.test.ts +153 -0
  40. package/vitest.config.ts.timestamp-1711656440838-19c636fe320df.mjs +0 -0
  41. package/vitest.config.ts.timestamp-1711724061890-6ecedb0a07fdd.mjs +0 -0
  42. package/vitest.config.ts.timestamp-1711743489537-da8d9e5f66c9f.mjs +0 -0
  43. package/vitest.config.ts.timestamp-1711744615239-dcf257a844e01.mjs +37 -0
  44. package/test/dist/controller.test copy.d.ts +0 -169
  45. package/test/dist/controller.test copy.d.ts.map +0 -1
@@ -1,8 +1,31 @@
1
- import { type Array, Context, Effect, Layer, type NonEmptyArray, pipe, type Scope } from "effect-app"
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
- type TDepsArr = Array.NonEmptyReadonlyArray<
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
- // 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
- | ContextTagWithDefault.Base<Effect<Context.Context<any>, never, any> & { _tag: any }>
27
- | ContextTagWithDefault.Base<Effect<Context.Context<any>, never, never> & { _tag: any }>
28
- >
29
-
30
- type ConstrainDeps<TDeps extends TDepsArr> = {
31
- [K in keyof TDeps]: TDeps[K]["Service"] extends Effect<Context.Context<any>, never, HttpRouter.HttpRouter.Provided>
32
- ? TDeps[K]
33
- : `HttpRouter.HttpRouter.Provided are the only requirements ${TDeps[K]["Service"][
34
- "_tag"
35
- ]}'s returned effect can have`
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 TDepsArr
80
+ TDeps extends ReadonlyArray<any>
41
81
  >(
42
- ...deps: ConstrainDeps<TDeps>
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<Effect.Success<Tag.Service<TDeps[number]>>>>,
89
+ Context.Context<GetContext<EffectGenUtils.Success<Tag.Service<TDeps[number]>>>>,
49
90
  never,
50
- Effect.Context<Tag.Service<TDeps[number]>>
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
- const makers = yield* Effect.all(deps)
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) => ({ maker: deps[i], handle }))
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 l = Layer.scoped(ctx, input.effect)
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 TDepsArr
163
+ TDeps extends ReadonlyArray<any>
109
164
  >(
110
- ...deps: ConstrainDeps<TDeps>
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<Effect.Success<Tag.Service<TDeps[number]>>>>,
176
+ Context.Context<GetContext<EffectGenUtils.Success<Tag.Service<TDeps[number]>>>>,
121
177
  never,
122
- Effect.Context<Tag.Service<TDeps[number]>>
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 { Context, Effect, Layer, type NonEmptyReadonlyArray, Option, type Request, type S, type Scope } from "effect-app"
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 ReadonlyArray<
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 ContextProviderA | HttpRouter.HttpRouter.Provided because that's what, at most
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<RequestContextMap, ContextProviderA | HttpRouter.HttpRouter.Provided>
128
- ) => MakeRPCHandlerFactory<RequestContextMap, ContextProviderA | HttpRouter.HttpRouter.Provided>
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<RequestContextMap, ContextProviderA | HttpRouter.HttpRouter.Provided>,
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 Middleware<
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 ReadonlyArray<
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<RequestContextMap, ContextProviderA>
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<RequestContextMap, HttpRouter.HttpRouter.Provided | ContextProviderA>
204
+ cb: MakeRPCHandlerFactory<
205
+ RequestContextMap,
206
+ HttpRouter.HttpRouter.Provided | GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
207
+ >
215
208
  ) => cb)
216
209
  : Effect.succeed<
217
- MakeRPCHandlerFactory<RequestContextMap, ContextProviderA | HttpRouter.HttpRouter.Provided>
218
- >((_schema, next) => (payload, headers) => next(payload, headers)),
219
- contextProvider: make.contextProvider
220
- ? make.contextProvider.pipe(Effect.map(Effect.map(Option.some)))
221
- : Effect.succeed(Effect.succeed(Option.none())) // uses the middleware.contextProvider tag to get the context provider service
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(({ contextProvider, dynamicMiddlewares, generic, middleware }) => ({
217
+ Effect.map(({ dynamicMiddlewares, generic, middleware }) => ({
225
218
  _tag: "MiddlewareMaker" as const,
226
- effect: makeRpcEffect<RequestContextMap, ContextProviderA>()(
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.gen(function*() {
231
- return yield* generic({
232
- payload,
233
- headers,
234
- rpc: { _tag: `${moduleName}.${payload._tag}` }, // todo: make moduleName part of the tag on S.Req creation.
235
- next: Effect.gen(function*() {
236
- yield* Effect.annotateCurrentSpan(
237
- "request.name",
238
- moduleName ? `${moduleName}.${payload._tag}` : payload._tag
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
- // the contextProvider is an Effect that builds the context for the request
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
- }) as any // why?
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.GetLayersContext<typeof dynamicMiddlewares.dependencies>
275
- | LayerUtils.GetLayersContext<typeof middlewares.dependencies> // what could go wrong when building the dynamic middleware provider
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 Array, Effect } from "effect-app"
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 { type ContextTagWithDefault } from "../../layerUtils.js"
6
+ import { InfraLogger } from "../../../logger.js"
5
7
 
6
- export interface GenericMiddlewareOptions<A, E> {
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<A, E, HttpRouter.HttpRouter.Provided>
10
+ readonly next: Effect.Effect<SuccessValue, E, HttpRouter.HttpRouter.Provided>
9
11
  readonly payload: unknown
10
12
  readonly headers: HttpHeaders.Headers
11
- // readonly clientId: number
12
- readonly rpc: { _tag: string } // Rpc.AnyWithProps
13
+ readonly clientId: number
14
+ readonly rpc: Rpc.AnyWithProps
13
15
  }
14
16
 
15
- export type GenericMiddlewareMaker = <A, E>(
16
- options: GenericMiddlewareOptions<A, E>
17
- ) => Effect.Effect<A, E, HttpRouter.HttpRouter.Provided>
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<GenericMiddlewareMaker>
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 middlewaresInstances = yield* Effect.all(middlewares)
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 <A, E>(
35
- options: GenericMiddlewareOptions<A, E>
44
+ return <E>(
45
+ options: GenericMiddlewareOptions<E>
36
46
  ) => {
37
- let next = options.next
38
- for (const middleware of (middlewaresInstances as any[]).toReversed()) {
39
- next = middleware({ ...options, next })
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 next
77
+ return handler
42
78
  }
43
79
  })
44
80
  } as any