@effect-app/infra 2.73.3 → 2.74.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.
@@ -1,11 +1,16 @@
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 Array, Context, Effect, Layer, type NonEmptyArray, pipe, type Request, type S } from "effect-app"
4
+ import { Array, Cause, Context, Effect, Layer, type NonEmptyArray, ParseResult, pipe, type Request, type S, type Scope } from "effect-app"
5
5
  import type { GetEffectContext, RPCContextMap } from "effect-app/client/req"
6
- import { type HttpRouter } from "effect-app/http"
6
+ import { HttpHeaders, type HttpRouter, HttpServerRequest } from "effect-app/http"
7
+
8
+ import { pretty } from "effect-app/utils"
7
9
  import type * as EffectRequest from "effect/Request"
10
+ import { logError, reportError } from "../../errorReporter.js"
11
+ import { InfraLogger } from "../../logger.js"
8
12
  import { type LayersUtils } from "../routing.js"
13
+ import { type AnyContextWithLayer, implementMiddleware, mergeContexts } from "./dynamic-middleware.js"
9
14
 
10
15
  // utils:
11
16
  //
@@ -30,9 +35,10 @@ export type MakeRPCHandlerFactory<
30
35
  ) => Effect.Effect<
31
36
  EffectRequest.Request.Success<Req>,
32
37
  EffectRequest.Request.Error<Req>,
33
- HandlerR
38
+ // dynamic middlewares removes the dynamic context from HandlerR
39
+ Exclude<HandlerR, GetEffectContext<RequestContextMap, (T & S.Schema<Req, any, never>)["config"]>>
34
40
  >,
35
- moduleName?: string
41
+ moduleName: string
36
42
  ) => (
37
43
  req: Req,
38
44
  headers: any
@@ -64,7 +70,7 @@ export type RPCHandlerFactory<
64
70
  EffectRequest.Request.Error<Req>,
65
71
  HandlerR
66
72
  >,
67
- moduleName?: string
73
+ moduleName: string
68
74
  ) => (
69
75
  req: Req,
70
76
  headers: any
@@ -92,6 +98,14 @@ export interface ContextProviderId {
92
98
  _tag: "ContextProvider"
93
99
  }
94
100
 
101
+ type RequestContextMapProvider<RequestContextMap extends Record<string, RPCContextMap.Any>> = {
102
+ [K in keyof RequestContextMap]: AnyContextWithLayer<
103
+ { [K in keyof RequestContextMap]?: RequestContextMap[K]["contextActivation"] },
104
+ RequestContextMap[K]["service"],
105
+ S.Schema.Type<RequestContextMap[K]["error"]>
106
+ >
107
+ }
108
+
95
109
  export interface MiddlewareMake<
96
110
  RequestContextMap extends Record<string, RPCContextMap.Any>, // what services will the middleware provide dynamically to the handler, or raise errors.
97
111
  MakeMiddlewareE, // what the middleware construction can fail with
@@ -102,9 +116,11 @@ export interface MiddlewareMake<
102
116
  ContextProviderA, // what the context provider provides
103
117
  ContextProviderR extends HttpRouter.HttpRouter.Provided, // what the context provider requires
104
118
  MakeContextProviderE, // what the context provider construction can fail with
105
- MakeContextProviderR // what the context provider construction requires
119
+ MakeContextProviderR, // what the context provider construction requires
120
+ TI extends RequestContextMapProvider<RequestContextMap> // how to resolve the dynamic middleware
106
121
  > {
107
122
  dependencies?: MiddlewareDependencies
123
+ dynamicMiddlewares: TI
108
124
  contextProvider:
109
125
  & Context.Tag<
110
126
  ContextProviderId,
@@ -123,7 +139,7 @@ export interface MiddlewareMake<
123
139
  ) => Effect<
124
140
  MakeRPCHandlerFactory<RequestContextMap, ContextProviderA | HttpRouter.HttpRouter.Provided>,
125
141
  MakeMiddlewareE,
126
- MakeMiddlewareR // ...that's why MakeMiddlewareR is here
142
+ MakeMiddlewareR | Scope // ...that's why MakeMiddlewareR is here
127
143
  >
128
144
  }
129
145
 
@@ -161,17 +177,19 @@ export const mergeContextProviders = <
161
177
  Effect.Context<InstanceType<TDeps[number]>>
162
178
  >,
163
179
  LayersUtils.GetLayersError<{ [K in keyof TDeps]: TDeps[K]["Default"] }>,
164
- InstanceType<TDeps[number]>
180
+ LayersUtils.GetLayersSuccess<{ [K in keyof TDeps]: TDeps[K]["Default"] }>
165
181
  >
166
182
  } => ({
167
183
  dependencies: deps.map((_) => _.Default) as any,
168
184
  effect: Effect.gen(function*() {
169
- const services = yield* Effect.all(deps)
170
- // services are effects which return some Context.Context<...>
171
- // @effect-diagnostics effect/returnEffectInGen:off
172
- return Effect.all(services as any[]).pipe(
173
- Effect.map((_) => Context.mergeAll(..._ as any))
174
- )
185
+ const makers = yield* Effect.all(deps)
186
+ return Effect
187
+ .gen(function*() {
188
+ const services = (makers as any[]).map((handle, i) => ({ maker: deps[i], handle }))
189
+ // services are effects which return some Context.Context<...>
190
+ const context = yield* mergeContexts(services as any)
191
+ return context
192
+ })
175
193
  }) as any
176
194
  })
177
195
 
@@ -187,7 +205,11 @@ export const ContextProvider = <
187
205
  Dependencies extends NonEmptyArray<Layer.Layer.Any>
188
206
  >(
189
207
  input: {
190
- effect: Effect<Effect<ContextProviderA, never, ContextProviderR>, MakeContextProviderE, MakeContextProviderR>
208
+ effect: Effect<
209
+ Effect<ContextProviderA, never, ContextProviderR>,
210
+ MakeContextProviderE,
211
+ MakeContextProviderR | Scope
212
+ >
191
213
  dependencies?: Dependencies
192
214
  }
193
215
  ) => {
@@ -197,7 +219,7 @@ export const ContextProvider = <
197
219
  >(
198
220
  "ContextProvider"
199
221
  )
200
- const l = Layer.effect(ctx, input.effect)
222
+ const l = Layer.scoped(ctx, input.effect)
201
223
  return Object.assign(ctx, {
202
224
  Default: l.pipe(
203
225
  input.dependencies ? Layer.provide(input.dependencies) as any : (_) => _
@@ -238,8 +260,8 @@ export const MergedContextProvider = <
238
260
  }
239
261
  ) =>
240
262
  pipe(
241
- deps as Parameters<typeof mergeContextProviders>[0],
242
- mergeContextProviders,
263
+ deps as [Parameters<typeof mergeContextProviders>[0]],
264
+ (_) => mergeContextProviders(..._),
243
265
  (_) => ContextProvider(_ as any)
244
266
  ) as unknown as
245
267
  & Context.Tag<
@@ -288,6 +310,76 @@ export type RequestContextMapErrors<RequestContextMap extends Record<string, RPC
288
310
  RequestContextMap[keyof RequestContextMap]["error"]
289
311
  >
290
312
 
313
+ const logRequestError = logError("Request")
314
+ const reportRequestError = reportError("Request")
315
+
316
+ export class DevMode extends Context.Reference<DevMode>()("DevMode", { defaultValue: () => false }) {}
317
+
318
+ // TODO: pull out to a generic middleware system..
319
+ export const requestMiddleware = <A, E, R>(
320
+ handle: (input: any, headers: HttpHeaders.Headers) => Effect.Effect<A, E, R>,
321
+ moduleName: string
322
+ ) =>
323
+ Effect.fnUntraced(function*(input: any, rpcHeaders: HttpHeaders.Headers) {
324
+ const devMode = yield* DevMode
325
+ // merge in the request headers
326
+ // we should consider if we should merge them into rpc headers on the Protocol layer instead.
327
+ const httpReq = yield* HttpServerRequest.HttpServerRequest
328
+ const headers = HttpHeaders.merge(httpReq.headers, rpcHeaders)
329
+
330
+ return yield* Effect
331
+ .annotateCurrentSpan(
332
+ "requestInput",
333
+ Object.entries(input).reduce((prev, [key, value]: [string, unknown]) => {
334
+ prev[key] = key === "password"
335
+ ? "<redacted>"
336
+ : typeof value === "string" || typeof value === "number" || typeof value === "boolean"
337
+ ? typeof value === "string" && value.length > 256
338
+ ? (value.substring(0, 253) + "...")
339
+ : value
340
+ : Array.isArray(value)
341
+ ? `Array[${value.length}]`
342
+ : value === null || value === undefined
343
+ ? `${value}`
344
+ : typeof value === "object" && value
345
+ ? `Object[${Object.keys(value).length}]`
346
+ : typeof value
347
+ return prev
348
+ }, {} as Record<string, string | number | boolean>)
349
+ )
350
+ .pipe(
351
+ // can't use andThen due to some being a function and effect
352
+ Effect.zipRight(handle(input, headers)),
353
+ // TODO: support ParseResult if the error channel of the request allows it.. but who would want that?
354
+ Effect.catchAll((_) => ParseResult.isParseError(_) ? Effect.die(_) : Effect.fail(_)),
355
+ Effect.tapErrorCause((cause) => Cause.isFailure(cause) ? logRequestError(cause) : Effect.void),
356
+ Effect.tapDefect((cause) =>
357
+ Effect
358
+ .all([
359
+ reportRequestError(cause, {
360
+ action: `${moduleName}.${input._tag}`
361
+ }),
362
+ InfraLogger
363
+ .logError("Finished request", cause)
364
+ .pipe(Effect.annotateLogs({
365
+ action: `${moduleName}.${input._tag}`,
366
+ req: pretty(input),
367
+ headers: pretty(headers)
368
+ // resHeaders: pretty(
369
+ // Object
370
+ // .entries(headers)
371
+ // .reduce((prev, [key, value]) => {
372
+ // prev[key] = value && typeof value === "string" ? snipString(value) : value
373
+ // return prev
374
+ // }, {} as Record<string, any>)
375
+ // )
376
+ }))
377
+ ])
378
+ ),
379
+ devMode ? (_) => _ : Effect.catchAllDefect(() => Effect.die("Internal Server Error"))
380
+ )
381
+ })
382
+
291
383
  // factory for middlewares
292
384
  export const makeMiddleware =
293
385
  // by setting RequestContextMap beforehand, execute contextual typing does not fuck up itself to anys
@@ -303,7 +395,8 @@ export const makeMiddleware =
303
395
  ContextProviderA, // what the context provider provides
304
396
  ContextProviderR extends HttpRouter.HttpRouter.Provided, // what the context provider requires
305
397
  MakeContextProviderE, // what the context provider construction can fail with
306
- MakeContextProviderR // what the context provider construction requires
398
+ MakeContextProviderR, // what the context provider construction requires
399
+ TI extends RequestContextMapProvider<RequestContextMap> // how to resolve the dynamic middleware
307
400
  >(
308
401
  make: MiddlewareMake<
309
402
  RequestContextMap,
@@ -313,7 +406,8 @@ export const makeMiddleware =
313
406
  ContextProviderA,
314
407
  ContextProviderR,
315
408
  MakeContextProviderE,
316
- MakeContextProviderR
409
+ MakeContextProviderR,
410
+ TI
317
411
  >
318
412
  ) => {
319
413
  // type Id = MiddlewareMakerId &
@@ -327,33 +421,44 @@ export const makeMiddleware =
327
421
  "MiddlewareMaker"
328
422
  )
329
423
 
330
- const l = Layer.effect(
424
+ const dynamicMiddlewares = implementMiddleware<RequestContextMap>()(make.dynamicMiddlewares)
425
+
426
+ const l = Layer.scoped(
331
427
  MiddlewareMaker,
332
428
  Effect
333
429
  .all({
430
+ dynamicMiddlewares: dynamicMiddlewares.effect,
334
431
  middleware: make.execute((
335
432
  cb: MakeRPCHandlerFactory<RequestContextMap, HttpRouter.HttpRouter.Provided | ContextProviderA>
336
433
  ) => cb),
337
434
  contextProvider: make.contextProvider // uses the middleware.contextProvider tag to get the context provider service
338
435
  })
339
436
  .pipe(
340
- Effect.map(({ contextProvider, middleware }) => ({
437
+ Effect.map(({ contextProvider, dynamicMiddlewares, middleware }) => ({
341
438
  _tag: "MiddlewareMaker" as const,
342
439
  effect: makeRpcEffect<RequestContextMap, ContextProviderA>()(
343
440
  (schema, handler, moduleName) => {
344
- const h = middleware(schema, handler, moduleName)
345
- return (req, headers) =>
346
- // the contextProvider is an Effect that builds the context for the request
347
- contextProvider.pipe(
348
- Effect.flatMap((ctx) =>
349
- h(req, headers)
350
- .pipe(
351
- Effect.provide(ctx),
352
- // TODO: make this depend on query/command, and consider if middleware also should be affected or not.
353
- Effect.uninterruptible
441
+ const h = middleware(schema, handler as any, moduleName)
442
+ return requestMiddleware(
443
+ Effect.fnUntraced(function*(req, headers) {
444
+ yield* Effect.annotateCurrentSpan(
445
+ "request.name",
446
+ moduleName ? `${moduleName}.${req._tag}` : req._tag
447
+ )
448
+
449
+ // the contextProvider is an Effect that builds the context for the request
450
+ return yield* contextProvider.pipe(
451
+ Effect.flatMap((contextProviderContext) =>
452
+ // the dynamicMiddlewares is an Effect that builds the dynamiuc context for the request
453
+ dynamicMiddlewares(schema.config ?? {}, headers).pipe(
454
+ Effect.flatMap((dynamicContext) => h(req, headers).pipe(Effect.provide(dynamicContext))),
455
+ Effect.provide(contextProviderContext)
354
456
  )
457
+ )
355
458
  )
356
- )
459
+ }) as any,
460
+ moduleName
461
+ )
357
462
  }
358
463
  )
359
464
  }))
@@ -361,13 +466,20 @@ export const makeMiddleware =
361
466
  )
362
467
  const middlewareLayer = l
363
468
  .pipe(
364
- make.dependencies ? Layer.provide(make.dependencies) as any : (_) => _,
365
- Layer.provide(make.contextProvider.Default)
469
+ Layer.provide(
470
+ Layer.mergeAll(
471
+ make.dependencies ? make.dependencies as any : Layer.empty,
472
+ ...(dynamicMiddlewares.dependencies as any),
473
+ make.contextProvider.Default
474
+ )
475
+ )
366
476
  ) as Layer.Layer<
367
477
  MiddlewareMakerId,
368
478
  | MakeMiddlewareE // what the middleware construction can fail with
479
+ | LayersUtils.GetLayersContext<typeof dynamicMiddlewares.dependencies> // what could go wrong when building the dynamic middleware provider
369
480
  | Layer.Error<typeof make.contextProvider.Default>, // what could go wrong when building the context provider
370
481
  | LayersUtils.GetLayersContext<MiddlewareDependencies> // what's needed to build layers
482
+ | LayersUtils.GetLayersContext<typeof dynamicMiddlewares.dependencies> // what's needed to build dynamic middleware layers
371
483
  | Exclude<MakeMiddlewareR, LayersUtils.GetLayersSuccess<MiddlewareDependencies>> // what layers provides
372
484
  | Layer.Context<typeof make.contextProvider.Default> // what's needed to build the contextProvider
373
485
  >
@@ -397,7 +509,7 @@ function makeRpcEffect<
397
509
  EffectRequest.Request.Error<Req>,
398
510
  HandlerR
399
511
  >,
400
- moduleName?: string
512
+ moduleName: string
401
513
  ) => (
402
514
  req: Req,
403
515
  headers: any
@@ -0,0 +1,154 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { Array, Context, Effect, type Layer, Option, type S } from "effect-app"
3
+ import { type GetEffectContext, type RPCContextMap } from "effect-app/client"
4
+ import { type Tag } from "effect-app/Context"
5
+ import { typedValuesOf } from "effect-app/utils"
6
+ import { InfraLogger } from "../../logger.js"
7
+ import { sort } from "./tsort.js"
8
+
9
+ export type ContextWithLayer<
10
+ Config,
11
+ Id,
12
+ Service,
13
+ E,
14
+ R,
15
+ MakeE,
16
+ MakeR,
17
+ Tag extends string,
18
+ Args extends [config: Config, headers: Record<string, string>],
19
+ Dependencies extends any[]
20
+ > =
21
+ & Context.Tag<
22
+ Id,
23
+ { handle: (...args: Args) => Effect<Option<Context<Service>>, E, R>; _tag: Tag }
24
+ >
25
+ & {
26
+ Default: Layer.Layer<Id, MakeE, MakeR>
27
+ dependsOn?: Dependencies
28
+ }
29
+
30
+ export type AnyContextWithLayer<Config, Service, Error> =
31
+ | ContextWithLayer<
32
+ Config,
33
+ any,
34
+ Service,
35
+ Error,
36
+ any,
37
+ any,
38
+ any,
39
+ string,
40
+ any,
41
+ any
42
+ >
43
+ | ContextWithLayer<
44
+ Config,
45
+ any,
46
+ Service,
47
+ Error,
48
+ never,
49
+ any,
50
+ never,
51
+ any,
52
+ any,
53
+ any
54
+ >
55
+ | ContextWithLayer<
56
+ Config,
57
+ any,
58
+ Service,
59
+ Error,
60
+ any,
61
+ any,
62
+ never,
63
+ any,
64
+ any,
65
+ any
66
+ >
67
+ | ContextWithLayer<
68
+ Config,
69
+ any,
70
+ Service,
71
+ Error,
72
+ never,
73
+ any,
74
+ any,
75
+ any,
76
+ any,
77
+ any
78
+ >
79
+
80
+ export const mergeContexts = Effect.fnUntraced(
81
+ function*<T extends readonly { maker: any; handle: Effect<Context<any>> }[]>(makers: T) {
82
+ let context = Context.empty()
83
+ for (const mw of makers) {
84
+ yield* InfraLogger.logDebug("Building context for middleware", mw.maker.key ?? mw.maker)
85
+ const moreContext = yield* mw.handle.pipe(Effect.provide(context))
86
+ yield* InfraLogger.logDebug(
87
+ "Built context for middleware",
88
+ mw.maker.key ?? mw.maker,
89
+ (moreContext as any).toJSON().services
90
+ )
91
+ context = Context.merge(context, moreContext)
92
+ }
93
+ return context as Context.Context<Effect.Success<T[number]["handle"]>>
94
+ }
95
+ )
96
+
97
+ export const mergeOptionContexts = Effect.fnUntraced(
98
+ function*<T extends readonly { maker: any; handle: Effect<Option<Context<any>>> }[]>(makers: T) {
99
+ let context = Context.empty()
100
+ for (const mw of makers) {
101
+ yield* InfraLogger.logDebug("Building context for middleware", mw.maker.key ?? mw.maker)
102
+ const moreContext = yield* mw.handle.pipe(Effect.provide(context))
103
+ yield* InfraLogger.logDebug(
104
+ "Built context for middleware",
105
+ mw.maker.key ?? mw.maker,
106
+ Option.map(moreContext, (c) => (c as any).toJSON().services)
107
+ )
108
+ if (moreContext.value) {
109
+ context = Context.merge(context, moreContext.value)
110
+ }
111
+ }
112
+ return context
113
+ }
114
+ )
115
+
116
+ export const implementMiddleware = <T extends Record<string, RPCContextMap.Any>>() =>
117
+ <
118
+ TI extends {
119
+ [K in keyof T]: AnyContextWithLayer<
120
+ { [K in keyof T]?: T[K]["contextActivation"] },
121
+ T[K]["service"],
122
+ S.Schema.Type<T[K]["error"]>
123
+ >
124
+ }
125
+ >(implementations: TI) => ({
126
+ dependencies: typedValuesOf(implementations).map((_) => _.Default) as {
127
+ [K in keyof TI]: TI[K]["Default"]
128
+ }[keyof TI][],
129
+ effect: Effect.gen(function*() {
130
+ const sorted = sort(typedValuesOf(implementations))
131
+
132
+ const makers = yield* Effect.all(sorted)
133
+ return Effect.fnUntraced(
134
+ function*(config: { [K in keyof T]?: T[K]["contextActivation"] }, headers: Record<string, string>) {
135
+ const ctx = yield* mergeOptionContexts(
136
+ Array.map(
137
+ makers,
138
+ (_, i) => ({ maker: sorted[i], handle: (_ as any).handle(config, headers) as any }) as any
139
+ )
140
+ )
141
+ return ctx as Context.Context<
142
+ GetEffectContext<T, typeof config>
143
+ >
144
+ }
145
+ ) as (
146
+ config: { [K in keyof T]?: T[K]["contextActivation"] },
147
+ headers: Record<string, string>
148
+ ) => Effect.Effect<
149
+ Context.Context<GetEffectContext<T, typeof config>>,
150
+ Effect.Error<ReturnType<Tag.Service<TI[keyof TI]>["handle"]>>,
151
+ Effect.Context<ReturnType<Tag.Service<TI[keyof TI]>["handle"]>>
152
+ >
153
+ })
154
+ })
@@ -0,0 +1,56 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+ export function tsort(edges) {
4
+ const nodes = new Map(), sorted = [], visited = new Map()
5
+
6
+ const Node = function(id) {
7
+ this.id = id
8
+ this.afters = []
9
+ }
10
+
11
+ edges.forEach((v) => {
12
+ const from = v[0], to = v[1]
13
+ if (!nodes.get(from)) nodes.set(from, new Node(from))
14
+ if (!nodes.get(to)) nodes.set(to, new Node(to))
15
+ nodes.get(from).afters.push(to)
16
+ })
17
+ ;[...nodes.keys()].forEach(function visit(idstr, ancestors) {
18
+ const node = nodes.get(idstr), id = node.id
19
+
20
+ if (visited.get(idstr)) return
21
+ if (!Array.isArray(ancestors)) ancestors = []
22
+
23
+ ancestors.push(id)
24
+ visited.set(idstr, true)
25
+ node.afters.forEach(function(afterID) {
26
+ if (ancestors.indexOf(afterID) >= 0) {
27
+ throw new Error("closed chain : " + afterID + " is in " + id)
28
+ }
29
+ visit(
30
+ afterID,
31
+ ancestors.map(function(v) {
32
+ return v
33
+ })
34
+ )
35
+ })
36
+ sorted.unshift(id)
37
+ })
38
+
39
+ return sorted
40
+ }
41
+
42
+ export const createEdges = <T extends { dependsOn?: any[] }>(dep: readonly T[]) => {
43
+ const result = []
44
+ dep.forEach((key) => {
45
+ key.dependsOn?.forEach((n) => {
46
+ result.push([n, key])
47
+ })
48
+ })
49
+ return result
50
+ }
51
+
52
+ export const sort = <T>(dep: readonly (T & { dependsOn?: any[] })[]): readonly T[] => {
53
+ const edges = createEdges(dep)
54
+ const result = tsort(edges)
55
+ return result.concat(dep.filter((v) => !result.includes(v)))
56
+ }
@@ -3,16 +3,14 @@
3
3
  /* eslint-disable @typescript-eslint/no-empty-object-type */
4
4
  /* eslint-disable @typescript-eslint/no-explicit-any */
5
5
  import { determineMethod, isCommand } from "@effect-app/infra/api/routing/utils"
6
- import { logError, reportError } from "@effect-app/infra/errorReporter"
7
- import { InfraLogger } from "@effect-app/infra/logger"
8
6
  import { Rpc, RpcGroup, RpcServer } from "@effect/rpc"
9
- import { Array, Cause, Duration, Effect, Layer, type NonEmptyArray, type NonEmptyReadonlyArray, ParseResult, Predicate, Request, S, Schedule, Schema } from "effect-app"
7
+ import { type Array, Duration, Effect, Layer, type NonEmptyArray, type NonEmptyReadonlyArray, Predicate, Request, S, Schedule, Schema } from "effect-app"
10
8
  import type { GetEffectContext, GetEffectError, RPCContextMap } from "effect-app/client/req"
11
9
  import { type HttpHeaders, HttpRouter } from "effect-app/http"
12
- import { pretty, typedKeysOf, typedValuesOf } from "effect-app/utils"
10
+ import { typedKeysOf, typedValuesOf } from "effect-app/utils"
13
11
  import type { Contravariant } from "effect/Types"
14
12
  import { type YieldWrap } from "effect/Utils"
15
- import { type Middleware } from "./routing/DynamicMiddleware.js"
13
+ import { DevMode, type Middleware } from "./routing/DynamicMiddleware.js"
16
14
 
17
15
  export * from "./routing/DynamicMiddleware.js"
18
16
 
@@ -36,9 +34,6 @@ export namespace LayersUtils {
36
34
  : never
37
35
  }
38
36
 
39
- const logRequestError = logError("Request")
40
- const reportRequestError = reportError("Request")
41
-
42
37
  // retry just once on optimistic concurrency exceptions
43
38
  const optimisticConcurrencySchedule = Schedule.once.pipe(
44
39
  Schedule.intersect(Schedule.recurWhile<any>((a) => a?._tag === "OptimisticConcurrencyException"))
@@ -423,64 +418,17 @@ export const makeRouter = <
423
418
  }
424
419
  } as any
425
420
  : resource,
426
- rpc.effect(resource, (input: any, headers: HttpHeaders.Headers) =>
427
- // TODO: render more data... similar to console?
428
- Effect
429
- .annotateCurrentSpan(
430
- "requestInput",
431
- Object.entries(input).reduce((prev, [key, value]: [string, unknown]) => {
432
- prev[key] = key === "password"
433
- ? "<redacted>"
434
- : typeof value === "string" || typeof value === "number" || typeof value === "boolean"
435
- ? typeof value === "string" && value.length > 256
436
- ? (value.substring(0, 253) + "...")
437
- : value
438
- : Array.isArray(value)
439
- ? `Array[${value.length}]`
440
- : value === null || value === undefined
441
- ? `${value}`
442
- : typeof value === "object" && value
443
- ? `Object[${Object.keys(value).length}]`
444
- : typeof value
445
- return prev
446
- }, {} as Record<string, string | number | boolean>)
447
- )
448
- .pipe(
449
- // can't use andThen due to some being a function and effect
450
- Effect.zipRight(handle(input, headers)),
451
- // TODO: support ParseResult if the error channel of the request allows it.. but who would want that?
452
- Effect.catchAll((_) => ParseResult.isParseError(_) ? Effect.die(_) : Effect.fail(_)),
453
- Effect.tapErrorCause((cause) => Cause.isFailure(cause) ? logRequestError(cause) : Effect.void),
454
- Effect.tapDefect((cause) =>
455
- Effect
456
- .all([
457
- reportRequestError(cause, {
458
- action: `${meta.moduleName}.${resource._tag}`
459
- }),
460
- InfraLogger
461
- .logError("Finished request", cause)
462
- .pipe(Effect.annotateLogs({
463
- action: `${meta.moduleName}.${resource._tag}`,
464
- req: pretty(resource),
465
- headers: pretty(headers)
466
- // resHeaders: pretty(
467
- // Object
468
- // .entries(headers)
469
- // .reduce((prev, [key, value]) => {
470
- // prev[key] = value && typeof value === "string" ? snipString(value) : value
471
- // return prev
472
- // }, {} as Record<string, any>)
473
- // )
474
- }))
475
- ])
476
- ),
477
- // NOTE: this does not catch errors from the middlewares..
478
- // we should re-evalute this in any case..
479
- devMode ? (_) => _ : Effect.catchAllDefect(() => Effect.die("Internal Server Error")),
421
+ rpc.effect(
422
+ resource,
423
+ (req, headers) =>
424
+ handle(req, headers).pipe(
480
425
  Effect.withSpan("Request." + meta.moduleName + "." + resource._tag, {
481
426
  captureStackTrace: () => handler.stack
482
- })
483
- ), meta.moduleName),
427
+ }),
428
+ Effect.provideService(DevMode, devMode)
429
+ ),
430
+ meta.moduleName
431
+ ),
484
432
  meta.moduleName
485
433
  ] as const
486
434
  return acc