@effect-app/infra 2.78.8 → 2.80.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 (47) 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 +1 -3
  6. package/dist/api/routing/middleware/ContextProvider.d.ts.map +1 -1
  7. package/dist/api/routing/middleware/ContextProvider.js +2 -1
  8. package/dist/api/routing/middleware/DynamicMiddleware.d.ts +44 -22
  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 +18 -11
  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 +9 -9
  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 +9 -7
  26. package/src/api/routing/middleware/DynamicMiddleware.ts +164 -80
  27. package/src/api/routing/middleware/generic-middleware.ts +57 -21
  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 +10 -10
  31. package/test/contextProvider.test.ts +152 -0
  32. package/test/controller.test.ts +27 -129
  33. package/test/dist/contextProvider.test.d.ts.map +1 -0
  34. package/test/dist/controller/test2.test.d.ts.map +1 -0
  35. package/test/dist/controller.legacy2.test.d.ts.map +1 -0
  36. package/test/dist/controller.legacy3.test.d.ts.map +1 -0
  37. package/test/dist/controller.test copy.js +129 -0
  38. package/test/dist/controller.test.d.ts.map +1 -1
  39. package/test/dist/controller5.test.d.ts.map +1 -0
  40. package/test/dist/controller6.test.d.ts.map +1 -0
  41. package/test/dist/controller7.test.d.ts.map +1 -0
  42. package/test/dist/dynamicContext.test.d.ts.map +1 -0
  43. package/test/dist/filterApi.test.d.ts.map +1 -0
  44. package/vitest.config.ts.timestamp-1711656440838-19c636fe320df.mjs +0 -0
  45. package/vitest.config.ts.timestamp-1711724061890-6ecedb0a07fdd.mjs +0 -0
  46. package/vitest.config.ts.timestamp-1711743489537-da8d9e5f66c9f.mjs +0 -0
  47. package/vitest.config.ts.timestamp-1711744615239-dcf257a844e01.mjs +37 -0
@@ -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 } 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
 
@@ -71,7 +73,7 @@ export type RPCHandlerFactory<
71
73
  ) => Effect.Effect<
72
74
  Request.Request.Success<Req>,
73
75
  Request.Request.Error<Req> | RequestContextMapErrors<RequestContextMap>,
74
- | HttpRouter.HttpRouter.Provided // because of the context provider and the middleware (Middleware)
76
+ | Scope.Scope // because of the context provider and the middleware (Middleware)
75
77
  | Exclude<
76
78
  // the middleware will remove from HandlerR the dynamic context
77
79
  // & S.Schema<Req, any, never> is useless here but useful when creating the middleware
@@ -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 | Scope.Scope 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
+ | Scope.Scope
118
+ >
119
+ ) => MakeRPCHandlerFactory<
120
+ RequestContextMap,
121
+ | GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
122
+ | Scope.Scope
123
+ >
129
124
  ) => Effect<
130
- MakeRPCHandlerFactory<RequestContextMap, ContextProviderA | HttpRouter.HttpRouter.Provided>,
125
+ MakeRPCHandlerFactory<
126
+ RequestContextMap,
127
+ | GenericMiddlewareMaker.Provided<GenericMiddlewareProviders[number]>
128
+ | Scope.Scope
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
+ Scope.Scope | 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
+ Scope.Scope | 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 })
@@ -313,7 +310,7 @@ function makeRpcEffect<
313
310
  ) => Effect.Effect<
314
311
  Request.Request.Success<Req>,
315
312
  Request.Request.Error<Req> | RequestContextMapErrors<RequestContextMap>,
316
- | HttpRouter.HttpRouter.Provided // the context provider may require HttpRouter.Provided to run
313
+ | Scope.Scope // the context provider may require Scope to run
317
314
  | Exclude<
318
315
  // it can also be removed from HandlerR
319
316
  Exclude<HandlerR, GetEffectContext<RequestContextMap, (T & S.Schema<Req, any, never>)["config"]>>,
@@ -322,3 +319,90 @@ function makeRpcEffect<
322
319
  >
323
320
  ) => cb
324
321
  }
322
+
323
+ // updated to support Scope.Scope
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, Scope.Scope>
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 | Scope.Scope>
339
+ }): Effect.Effect<SuccessValue, E, Scope.Scope>
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"
3
- import { type HttpHeaders, type HttpRouter } from "effect-app/http"
4
- import { type ContextTagWithDefault } from "../../layerUtils.js"
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, type Scope } from "effect-app"
5
+ import { type HttpHeaders } from "effect-app/http"
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, Scope.Scope>
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
@@ -3,46 +3,38 @@ import { Cause, Context, Effect, ParseResult } from "effect-app"
3
3
  import { pretty } from "effect-app/utils"
4
4
  import { logError, reportError } from "../../../errorReporter.js"
5
5
  import { InfraLogger } from "../../../logger.js"
6
- import { genericMiddleware, RequestCacheLayers } from "../../routing.js"
6
+ import { RequestCacheLayers, Tag } from "../../routing.js"
7
7
 
8
8
  const logRequestError = logError("Request")
9
9
  const reportRequestError = reportError("Request")
10
10
 
11
11
  export class DevMode extends Context.Reference<DevMode>()("DevMode", { defaultValue: () => false }) {}
12
- // Effect Rpc Middleware: Wrap
13
- export class RequestCacheMiddleware extends Effect.Service<RequestCacheMiddleware>()("RequestCacheMiddleware", {
14
- effect: Effect.gen(function*() {
15
- return genericMiddleware(Effect.fnUntraced(function*(options) {
16
- return yield* options.next.pipe(Effect.provide(RequestCacheLayers))
17
- }))
18
- })
19
- }) {}
20
12
 
21
- // Effect Rpc Middleware: Wrap
22
- export class ConfigureInterruptibility extends Effect.Service<ConfigureInterruptibility>()(
23
- "ConfigureInterruptibility",
24
- {
25
- effect: Effect.gen(function*() {
26
- return genericMiddleware(Effect.fnUntraced(function*(options) {
27
- return yield* options.next.pipe(
28
- // TODO: make this depend on query/command, and consider if middleware also should be affected. right now it's not.
29
- Effect.uninterruptible
30
- )
31
- }))
32
- })
33
- }
34
- ) {}
13
+ export class RequestCacheMiddleware extends Tag<RequestCacheMiddleware>()("RequestCacheMiddleware", { wrap: true })({
14
+ effect: Effect.succeed((options) => options.next.pipe(Effect.provide(RequestCacheLayers)))
15
+ }) {
16
+ }
35
17
 
36
- // Effect Rpc Middleware: Wrap
37
- export class MiddlewareLogger extends Effect.Service<MiddlewareLogger>()("MiddlewareLogger", {
38
- effect: Effect.gen(function*() {
39
- return genericMiddleware(Effect.fnUntraced(function*({ headers, next, payload, rpc }) {
40
- const devMode = yield* DevMode
18
+ export class ConfigureInterruptibility
19
+ extends Tag<ConfigureInterruptibility>()("ConfigureInterruptibility", { wrap: true })({
20
+ effect: Effect.succeed((options) =>
21
+ options.next.pipe(
22
+ // TODO: make this depend on query/command, and consider if middleware also should be affected. right now it's not.
23
+ Effect.uninterruptible
24
+ )
25
+ )
26
+ })
27
+ {
28
+ }
41
29
 
42
- return yield* Effect
43
- .annotateCurrentSpan(
44
- "requestInput",
45
- typeof payload === "object" && payload !== null
30
+ export class MiddlewareLogger extends Tag<MiddlewareLogger>()("MiddlewareLogger", { wrap: true })({
31
+ effect: Effect.gen(function*() {
32
+ const devMode = yield* DevMode
33
+ return ({ headers, next, payload, rpc }) =>
34
+ Effect
35
+ .annotateCurrentSpan({
36
+ "request.name": rpc._tag,
37
+ "requestInput": typeof payload === "object" && payload !== null
46
38
  ? Object.entries(payload).reduce((prev, [key, value]: [string, unknown]) => {
47
39
  prev[key] = key === "password"
48
40
  ? "<redacted>"
@@ -60,7 +52,7 @@ export class MiddlewareLogger extends Effect.Service<MiddlewareLogger>()("Middle
60
52
  return prev
61
53
  }, {} as Record<string, string | number | boolean>)
62
54
  : payload
63
- )
55
+ })
64
56
  .pipe(
65
57
  // can't use andThen due to some being a function and effect
66
58
  Effect.zipRight(next),
@@ -92,9 +84,9 @@ export class MiddlewareLogger extends Effect.Service<MiddlewareLogger>()("Middle
92
84
  ),
93
85
  devMode ? (_) => _ : Effect.catchAllDefect(() => Effect.die("Internal Server Error"))
94
86
  )
95
- }))
96
87
  })
97
- }) {}
88
+ }) {
89
+ }
98
90
 
99
91
  export const DefaultGenericMiddlewares = [
100
92
  RequestCacheMiddleware,
@@ -5,3 +5,5 @@ export * from "./middleware/DynamicMiddleware.js"
5
5
  export * from "./middleware/generic-middleware.js"
6
6
  export * from "./middleware/middleware.js"
7
7
  // codegen:end
8
+
9
+ export * as Middleware from "./middleware.js"
@@ -4,14 +4,14 @@
4
4
  /* eslint-disable @typescript-eslint/no-explicit-any */
5
5
  import { determineMethod, isCommand } from "@effect-app/infra/api/routing/utils"
6
6
  import { Rpc, RpcGroup, RpcServer } from "@effect/rpc"
7
- import { type Array, Duration, Effect, Layer, type NonEmptyReadonlyArray, Predicate, Request, S, Schedule, Schema } from "effect-app"
7
+ import { type Array, Duration, Effect, Layer, type NonEmptyReadonlyArray, Predicate, Request, S, Schedule, Schema, type Scope } from "effect-app"
8
8
  import type { GetEffectContext, GetEffectError, RPCContextMap } from "effect-app/client/req"
9
9
  import { type HttpHeaders, HttpRouter } from "effect-app/http"
10
10
  import { typedKeysOf, typedValuesOf } from "effect-app/utils"
11
11
  import type { Contravariant } from "effect/Types"
12
12
  import { type YieldWrap } from "effect/Utils"
13
13
  import { type LayerUtils } from "./layerUtils.js"
14
- import { DevMode, type Middleware } from "./routing/middleware.js"
14
+ import { DevMode, type RouterMiddleware } from "./routing/middleware.js"
15
15
 
16
16
  export * from "./routing/middleware.js"
17
17
 
@@ -123,7 +123,7 @@ type Match<
123
123
  RT,
124
124
  Exclude<
125
125
  Exclude<R2, GetEffectContext<RequestContextMap, Resource[Key]["config"]>>,
126
- HttpRouter.HttpRouter.Provided
126
+ Scope.Scope
127
127
  >
128
128
  >
129
129
 
@@ -134,7 +134,7 @@ type Match<
134
134
  RT,
135
135
  Exclude<
136
136
  Exclude<R2, GetEffectContext<RequestContextMap, Resource[Key]["config"]>>,
137
- HttpRouter.HttpRouter.Provided
137
+ Scope.Scope
138
138
  >
139
139
  >
140
140
  }
@@ -168,7 +168,7 @@ export const makeRouter = <
168
168
  MakeMiddlewareR,
169
169
  ContextProviderA
170
170
  >(
171
- middleware: Middleware<
171
+ middleware: RouterMiddleware<
172
172
  RequestContextMap,
173
173
  MakeMiddlewareE,
174
174
  MakeMiddlewareR,
@@ -340,7 +340,7 @@ export const makeRouter = <
340
340
  | GetEffectContext<RequestContextMap, Resource[K]["config"]>
341
341
  | ContextProviderA
342
342
  >,
343
- HttpRouter.HttpRouter.Provided
343
+ Scope.Scope
344
344
  >
345
345
  >
346
346
  } = (impl: Record<keyof RequestModules, any>) =>
@@ -405,8 +405,7 @@ export const makeRouter = <
405
405
  handle(req, headers).pipe(
406
406
  Effect.withSpan("Request." + meta.moduleName + "." + resource._tag, {
407
407
  captureStackTrace: () => handler.stack
408
- }),
409
- Effect.provideService(DevMode, devMode)
408
+ })
410
409
  ),
411
410
  meta.moduleName
412
411
  ),
@@ -469,7 +468,8 @@ export const makeRouter = <
469
468
  Layer.provide([
470
469
  ...dependencies ?? [],
471
470
  middleware.Default
472
- ] as any) as any
471
+ ] as any) as any,
472
+ Layer.provide(Layer.succeed(DevMode, devMode))
473
473
  )
474
474
  ) as (Layer.Layer<
475
475
  Router,
@@ -751,7 +751,7 @@ export const makeRouter = <
751
751
  // RPCRouteR<
752
752
  // { [K in keyof Filter<Resource>]: Rpc.Rpc<Resource[K], Effect.Context<ReturnType<THandlers[K]["handler"]>>> }[keyof Filter<Resource>]
753
753
  // >,
754
- // HttpRouter.HttpRouter.Provided
754
+ // Scope.Scope
755
755
  // >
756
756
  routes: any
757
757