@effect-app/infra 2.16.8 → 2.17.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.
@@ -0,0 +1,731 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
+ /* eslint-disable @typescript-eslint/no-empty-object-type */
4
+ /* eslint-disable @typescript-eslint/no-explicit-any */
5
+ /*
6
+ TODO: Effect.retry(r2, optimisticConcurrencySchedule) / was for PATCH only
7
+ TODO: uninteruptible commands! was for All except GET.
8
+ */
9
+ import type * as HttpApp from "@effect/platform/HttpApp"
10
+ import { Rpc, RpcRouter } from "@effect/rpc"
11
+ import type { NonEmptyArray, NonEmptyReadonlyArray } from "effect-app"
12
+ import { Array, Cause, Chunk, Context, Effect, FiberRef, flow, Layer, Predicate, S, Schema, Stream } from "effect-app"
13
+ import type { GetEffectContext, RPCContextMap } from "effect-app/client/req"
14
+ import type { HttpServerError } from "effect-app/http"
15
+ import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect-app/http"
16
+ import { pretty, typedKeysOf, typedValuesOf } from "effect-app/utils"
17
+ import type { Contravariant } from "effect/Types"
18
+ import { logError, reportError } from "../errorReporter.js"
19
+ import { InfraLogger } from "../logger.js"
20
+ import type { Middleware } from "./routing/DynamicMiddleware.js"
21
+ import { makeRpc } from "./routing/DynamicMiddleware.js"
22
+
23
+ const logRequestError = logError("Request")
24
+ const reportRequestError = reportError("Request")
25
+
26
+ export type _R<T extends Effect<any, any, any>> = [T] extends [
27
+ Effect<any, any, infer R>
28
+ ] ? R
29
+ : never
30
+
31
+ export type _E<T extends Effect<any, any, any>> = [T] extends [
32
+ Effect<any, infer E, any>
33
+ ] ? E
34
+ : never
35
+
36
+ export type EffectDeps<A> = {
37
+ [K in keyof A as A[K] extends Effect<any, any, any> ? K : never]: A[K] extends Effect<any, any, any> ? A[K] : never
38
+ }
39
+ /**
40
+ * Plain jane JSON version
41
+ * @deprecated use HttpRpcRouterNoStream.toHttpApp once support options
42
+ */
43
+ export const toHttpApp = <R extends RpcRouter.RpcRouter<any, any>>(self: R, options?: {
44
+ readonly spanPrefix?: string
45
+ }): HttpApp.Default<
46
+ HttpServerError.RequestError,
47
+ RpcRouter.RpcRouter.Context<R>
48
+ > => {
49
+ const handler = RpcRouter.toHandler(self, options)
50
+ return Effect.withFiberRuntime((fiber) => {
51
+ const context = fiber.getFiberRef(FiberRef.currentContext)
52
+ const request = Context.unsafeGet(context, HttpServerRequest.HttpServerRequest)
53
+ return Effect.flatMap(
54
+ request.json,
55
+ (_) =>
56
+ handler(_).pipe(
57
+ Stream.provideContext(context),
58
+ Stream.runCollect,
59
+ Effect.map((_) => Chunk.toReadonlyArray(_)),
60
+ Effect.andThen((_) => {
61
+ let status = 200
62
+ for (const r of _.flat()) {
63
+ if (typeof r === "number") continue
64
+ const results = Array.isArray(r) ? r : [r]
65
+ if (results.some((_: S.ExitEncoded<any, any, any>) => _._tag === "Failure" && _.cause._tag === "Die")) {
66
+ status = 500
67
+ break
68
+ }
69
+ if (results.some((_: S.ExitEncoded<any, any, any>) => _._tag === "Failure" && _.cause._tag === "Fail")) {
70
+ status = 422 // 418
71
+ break
72
+ }
73
+ }
74
+ return HttpServerResponse.json(_, { status })
75
+ }),
76
+ Effect.orDie,
77
+ Effect.tapDefect(reportError("RPCHttpApp"))
78
+ )
79
+ )
80
+ })
81
+ }
82
+
83
+ export interface Hint<Err extends string> {
84
+ Err: Err
85
+ }
86
+
87
+ type HandleVoid<Expected, Actual, Result> = [Expected] extends [void]
88
+ ? [Actual] extends [void] ? Result : Hint<"You're returning non void for a void Response, please fix">
89
+ : Result
90
+
91
+ type AnyRequestModule = S.Schema.Any & { success?: S.Schema.Any; failure?: S.Schema.Any }
92
+
93
+ type GetSuccess<T> = T extends { success: S.Schema.Any } ? T["success"] : typeof S.Void
94
+
95
+ type GetSuccessShape<Action extends { success?: S.Schema.Any }, RT extends "d" | "raw"> = RT extends "raw"
96
+ ? S.Schema.Encoded<GetSuccess<Action>>
97
+ : S.Schema.Type<GetSuccess<Action>>
98
+ type GetFailure<T extends { failure?: S.Schema.Any }> = T["failure"] extends never ? typeof S.Never : T["failure"]
99
+
100
+ type HandlerFull<Action extends AnyRequestModule, RT extends "raw" | "d", A, E, R> = {
101
+ new(): {}
102
+ _tag: RT
103
+ stack: string
104
+ handler: (
105
+ req: S.Schema.Type<Action>
106
+ ) => Effect<
107
+ A,
108
+ E,
109
+ R
110
+ >
111
+ }
112
+
113
+ export interface Handler<Action extends AnyRequestModule, RT extends "raw" | "d", R> extends
114
+ HandlerFull<
115
+ Action,
116
+ RT,
117
+ GetSuccessShape<Action, RT>,
118
+ S.Schema.Type<GetFailure<Action>> | S.ParseResult.ParseError,
119
+ R
120
+ >
121
+ {
122
+ }
123
+
124
+ type AHandler<Action extends AnyRequestModule> = Handler<
125
+ Action,
126
+ any,
127
+ any
128
+ >
129
+
130
+ type Filter<T> = {
131
+ [K in keyof T as T[K] extends S.Schema.All & { success: S.Schema.Any; failure: S.Schema.Any } ? K : never]: T[K]
132
+ }
133
+
134
+ export const RouterSymbol = Symbol()
135
+ export interface RouterShape<Rsc> {
136
+ [RouterSymbol]: Rsc
137
+ }
138
+
139
+ type RPCRouteR<T extends Rpc.Rpc<any, any>> = [T] extends [
140
+ Rpc.Rpc<any, infer R>
141
+ ] ? R
142
+ : never
143
+
144
+ type RPCRouteReq<T extends Rpc.Rpc<any, any>> = [T] extends [
145
+ Rpc.Rpc<infer Req, any>
146
+ ] ? Req
147
+ : never
148
+
149
+ type Match<
150
+ Rsc extends Record<string, any>,
151
+ CTXMap extends Record<string, any>,
152
+ RT extends "raw" | "d",
153
+ Key extends keyof Rsc,
154
+ Context
155
+ > = {
156
+ // TODO: deal with HandleVoid and ability to extends from GetSuccessShape...
157
+ // aka we want to make sure that the return type is void if the success is void,
158
+ // and make sure A is the actual expected type
159
+
160
+ // note: the defaults of = never prevent the whole router to error
161
+ <A extends GetSuccessShape<Rsc[Key], RT>, R2 = never, E = never>(
162
+ f: Effect<A, E, R2>
163
+ ): HandleVoid<
164
+ GetSuccessShape<Rsc[Key], RT>,
165
+ A,
166
+ Handler<
167
+ Rsc[Key],
168
+ RT,
169
+ Exclude<
170
+ Context | Exclude<R2, GetEffectContext<CTXMap, Rsc[Key]["config"]>>,
171
+ HttpRouter.HttpRouter.Provided
172
+ >
173
+ >
174
+ >
175
+
176
+ <A extends GetSuccessShape<Rsc[Key], RT>, R2 = never, E = never>(
177
+ f: (req: S.Schema.Type<Rsc[Key]>) => Effect<A, E, R2>
178
+ ): HandleVoid<
179
+ GetSuccessShape<Rsc[Key], RT>,
180
+ A,
181
+ Handler<
182
+ Rsc[Key],
183
+ RT,
184
+ Exclude<
185
+ Context | Exclude<R2, GetEffectContext<CTXMap, Rsc[Key]["config"]>>,
186
+ HttpRouter.HttpRouter.Provided
187
+ >
188
+ >
189
+ >
190
+ }
191
+
192
+ export type RouteMatcher<
193
+ CTXMap extends Record<string, any>,
194
+ Rsc extends Record<string, any>,
195
+ Context
196
+ > = {
197
+ // use Rsc as Key over using Keys, so that the Go To on X.Action remain in tact in Controllers files
198
+ /**
199
+ * Requires the Type shape
200
+ */
201
+ [Key in keyof Filter<Rsc>]: Match<Rsc, CTXMap, "d", Key, Context> & {
202
+ success: Rsc[Key]["success"]
203
+ successRaw: S.SchemaClass<S.Schema.Encoded<Rsc[Key]["success"]>>
204
+ failure: Rsc[Key]["failure"]
205
+ /**
206
+ * Requires the Encoded shape (e.g directly undecoded from DB, so that we don't do multiple Decode/Encode)
207
+ */
208
+ raw: Match<Rsc, CTXMap, "raw", Key, Context>
209
+ }
210
+ }
211
+ // export interface RouteMatcher<
212
+ // Filtered extends Record<string, any>,
213
+ // CTXMap extends Record<string, any>,
214
+ // Rsc extends Filtered
215
+ // > extends RouteMatcherInt<Filtered, CTXMap, Rsc> {}
216
+
217
+ export const makeMiddleware = <
218
+ Context,
219
+ CTXMap extends Record<string, RPCContextMap.Any>,
220
+ RMW,
221
+ Layers extends NonEmptyReadonlyArray<Layer.Layer.Any> | never[]
222
+ >(content: Middleware<Context, CTXMap, RMW, Layers>): Middleware<Context, CTXMap, RMW, Layers> => content
223
+
224
+ export const makeRouter = <
225
+ Context,
226
+ CTXMap extends Record<string, RPCContextMap.Any>,
227
+ RMW,
228
+ Layers extends NonEmptyReadonlyArray<Layer.Layer.Any> | never[]
229
+ >(
230
+ middleware: Middleware<Context, CTXMap, RMW, Layers>,
231
+ devMode: boolean
232
+ ) => {
233
+ function matchFor<
234
+ const ModuleName extends string,
235
+ const Rsc extends Record<string, any>
236
+ >(
237
+ rsc: Rsc & { meta: { moduleName: ModuleName } }
238
+ ) {
239
+ const meta = rsc.meta
240
+ type Filtered = Filter<Rsc>
241
+ const filtered = typedKeysOf(rsc).reduce((acc, cur) => {
242
+ if (Predicate.isObject(rsc[cur]) && rsc[cur]["success"]) {
243
+ acc[cur as keyof Filtered] = rsc[cur]
244
+ }
245
+ return acc
246
+ }, {} as Filtered)
247
+
248
+ const items = typedKeysOf(filtered).reduce(
249
+ (prev, cur) => {
250
+ ;(prev as any)[cur] = Object.assign((fnOrEffect: any) => {
251
+ const stack = new Error().stack?.split("\n").slice(2).join("\n")
252
+ return Effect.isEffect(fnOrEffect)
253
+ ? class {
254
+ static stack = stack
255
+ static _tag = "d"
256
+ static handler = () => fnOrEffect
257
+ }
258
+ : class {
259
+ static stack = stack
260
+ static _tag = "d"
261
+ static handler = fnOrEffect
262
+ }
263
+ }, {
264
+ success: rsc[cur].success,
265
+ successRaw: S.encodedSchema(rsc[cur].success),
266
+ failure: rsc[cur].failure,
267
+ raw: // "Raw" variations are for when you don't want to decode just to encode it again on the response
268
+ // e.g for direct projection from DB
269
+ // but more importantly, to skip Effectful decoders, like to resolve relationships from the database or remote client.
270
+ (fnOrEffect: any) => {
271
+ const stack = new Error().stack?.split("\n").slice(2).join("\n")
272
+ return Effect.isEffect(fnOrEffect)
273
+ ? class {
274
+ static stack = stack
275
+ static _tag = "raw"
276
+ static handler = () => fnOrEffect
277
+ }
278
+ : class {
279
+ static stack = stack
280
+ static _tag = "raw"
281
+ static handler = (req: any, ctx: any) => fnOrEffect(req, { ...ctx, Response: rsc[cur].success })
282
+ }
283
+ }
284
+ })
285
+ return prev
286
+ },
287
+ {} as RouteMatcher<CTXMap, Rsc, Context>
288
+ )
289
+
290
+ type Keys = keyof Filtered
291
+
292
+ type GetSuccess<Layers extends ReadonlyArray<Layer.Layer.Any>> = Layers extends
293
+ NonEmptyReadonlyArray<Layer.Layer.Any> ? {
294
+ [k in keyof Layers]: Layer.Layer.Success<Layers[k]>
295
+ }[number]
296
+ : never
297
+
298
+ type GetContext<Layers extends ReadonlyArray<Layer.Layer.Any>> = Layers extends
299
+ NonEmptyReadonlyArray<Layer.Layer.Any> ? {
300
+ [k in keyof Layers]: Layer.Layer.Context<Layers[k]>
301
+ }[number]
302
+ : never
303
+
304
+ type GetError<Layers extends ReadonlyArray<Layer.Layer.Any>> = Layers extends NonEmptyReadonlyArray<Layer.Layer.Any>
305
+ ? { [k in keyof Layers]: Layer.Layer.Error<Layers[k]> }[number]
306
+ : never
307
+
308
+ const f = <
309
+ E,
310
+ R,
311
+ THandlers extends {
312
+ // import to keep them separate via | for type checking!!
313
+ [K in Keys]: AHandler<Rsc[K]>
314
+ },
315
+ TLayers extends NonEmptyReadonlyArray<Layer.Layer.Any> | never[]
316
+ >(
317
+ layers: TLayers,
318
+ make: Effect<THandlers, E, R>
319
+ ) => {
320
+ type ProvidedLayers =
321
+ | { [k in keyof Layers]: Layer.Layer.Success<Layers[k]> }[number]
322
+ | { [k in keyof TLayers]: Layer.Layer.Success<TLayers[k]> }[number]
323
+ type Router = RouterShape<Rsc>
324
+ const r: HttpRouter.HttpRouter.TagClass<
325
+ Router,
326
+ `${typeof meta.moduleName}Router`,
327
+ never,
328
+ Exclude<
329
+ | Context
330
+ | RPCRouteR<
331
+ { [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<THandlers[K]["handler"]>>> }[keyof Filter<Rsc>]
332
+ >,
333
+ HttpRouter.HttpRouter.Provided
334
+ >
335
+ > = (class Router extends HttpRouter.Tag(`${meta.moduleName}Router`)<Router>() {}) as any
336
+
337
+ const layer = r.use((router) =>
338
+ Effect.gen(function*() {
339
+ const controllers = yield* make
340
+ const rpc = yield* makeRpc(middleware)
341
+
342
+ // return make.pipe(Effect.map((c) => controllers(c, layers)))
343
+ const mapped = typedKeysOf(filtered).reduce((acc, cur) => {
344
+ const handler = controllers[cur as keyof typeof controllers]
345
+ const req = rsc[cur]
346
+
347
+ acc[cur] = rpc.effect(
348
+ handler._tag === "raw"
349
+ ? class extends (req as any) {
350
+ static success = S.encodedSchema(req.success)
351
+ get [Schema.symbolSerializable]() {
352
+ return this.constructor
353
+ }
354
+ get [Schema.symbolWithResult]() {
355
+ return {
356
+ failure: req.failure,
357
+ success: S.encodedSchema(req.success)
358
+ }
359
+ }
360
+ } as any
361
+ : req,
362
+ (req) =>
363
+ // TODO: render more data... similar to console?
364
+ Effect
365
+ .annotateCurrentSpan(
366
+ "requestInput",
367
+ Object.entries(req).reduce((prev, [key, value]: [string, unknown]) => {
368
+ prev[key] = key === "password"
369
+ ? "<redacted>"
370
+ : typeof value === "string" || typeof value === "number" || typeof value === "boolean"
371
+ ? typeof value === "string" && value.length > 256
372
+ ? (value.substring(0, 253) + "...")
373
+ : value
374
+ : Array.isArray(value)
375
+ ? `Array[${value.length}]`
376
+ : value === null || value === undefined
377
+ ? `${value}`
378
+ : typeof value === "object" && value
379
+ ? `Object[${Object.keys(value).length}]`
380
+ : typeof value
381
+ return prev
382
+ }, {} as Record<string, string | number | boolean>)
383
+ )
384
+ .pipe(
385
+ // can't use andThen due to some being a function and effect
386
+ Effect.zipRight(handler.handler(req as any) as any),
387
+ Effect.tapErrorCause((cause) => Cause.isFailure(cause) ? logRequestError(cause) : Effect.void),
388
+ Effect.tapDefect((cause) =>
389
+ Effect
390
+ .all([
391
+ reportRequestError(cause, {
392
+ action: `${meta.moduleName}.${req._tag}`
393
+ }),
394
+ Rpc.currentHeaders.pipe(Effect.andThen((headers) => {
395
+ return InfraLogger
396
+ .logError("Finished request", cause)
397
+ .pipe(Effect.annotateLogs({
398
+ action: `${meta.moduleName}.${req._tag}`,
399
+ req: pretty(req),
400
+ headers: pretty(headers)
401
+ // resHeaders: pretty(
402
+ // Object
403
+ // .entries(headers)
404
+ // .reduce((prev, [key, value]) => {
405
+ // prev[key] = value && typeof value === "string" ? snipString(value) : value
406
+ // return prev
407
+ // }, {} as Record<string, any>)
408
+ // )
409
+ }))
410
+ }))
411
+ ])
412
+ ),
413
+ devMode ? (_) => _ : Effect.catchAllDefect(() => Effect.die("Internal Server Error")),
414
+ Effect.withSpan("Request." + meta.moduleName + "." + req._tag, {
415
+ captureStackTrace: () => handler.stack
416
+ })
417
+ ),
418
+ meta.moduleName
419
+ ) // TODO
420
+ return acc
421
+ }, {} as any) as {
422
+ [K in Keys]: Rpc.Rpc<
423
+ Rsc[K],
424
+ Context | _R<ReturnType<THandlers[K]["handler"]>>
425
+ >
426
+ }
427
+
428
+ const rpcRouter = RpcRouter.make(...Object.values(mapped) as any) as RpcRouter.RpcRouter<
429
+ RPCRouteReq<typeof mapped[keyof typeof mapped]>,
430
+ RPCRouteR<typeof mapped[keyof typeof mapped]>
431
+ >
432
+ const httpApp = toHttpApp(rpcRouter, {
433
+ spanPrefix: rsc
434
+ .meta
435
+ .moduleName + "."
436
+ })
437
+ yield* router
438
+ .all(
439
+ "/",
440
+ httpApp as any,
441
+ // TODO: not queries.
442
+ { uninterruptible: true }
443
+ )
444
+ })
445
+ )
446
+
447
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
448
+ const routes = layer.pipe(
449
+ Layer.provideMerge(r.Live),
450
+ layers && Array.isNonEmptyReadonlyArray(layers) ? Layer.provide(layers as any) as any : (_) => _,
451
+ // TODO: only provide to the middleware?
452
+ middleware.dependencies ? Layer.provide(middleware.dependencies as any) : (_) => _
453
+ ) as Layer.Layer<
454
+ Router,
455
+ GetError<TLayers> | E,
456
+ | GetContext<TLayers>
457
+ | Exclude<
458
+ RMW | R,
459
+ ProvidedLayers
460
+ >
461
+ >
462
+
463
+ // Effect.Effect<HttpRouter.HttpRouter<unknown, HttpRouter.HttpRouter.DefaultServices>, never, UserRouter>
464
+
465
+ return {
466
+ moduleName: meta.moduleName,
467
+ Router: r,
468
+ routes
469
+ }
470
+ }
471
+
472
+ const effect: {
473
+ // Multiple times duplicated the "good" overload, so that errors will only mention the last overload when failing
474
+ // <
475
+ // E,
476
+ // R,
477
+ // Make extends (
478
+ // requests: RouteMatcher<CTXMap, Rsc, Context>
479
+ // ) => Effect<
480
+ // { [K in keyof Filter<Rsc>]: AHandler<Rsc[K]> },
481
+ // any,
482
+ // Strict extends true ? GetSuccess<MakeLayers["dependencies"]> : any
483
+ // >,
484
+ // THandlers extends { [K in keyof Filter<Rsc>]: AHandler<Rsc[K]> },
485
+ // MakeLayers extends { dependencies: NonEmptyReadonlyArray<Layer.Layer.Any> | never[] },
486
+ // Strict extends boolean = true
487
+ // >(
488
+ // layers: MakeLayers,
489
+ // make: Make & ((requests: RouteMatcher<CTXMap, Rsc, Context>) => Effect<THandlers, E, R>),
490
+ // strict?: Strict
491
+ // ): {
492
+ // moduleName: ModuleName
493
+ // Router: HttpRouter.HttpRouter.TagClass<
494
+ // RouterShape<Rsc>,
495
+ // `${ModuleName}Router`,
496
+ // never,
497
+ // | Exclude<Context, HttpRouter.HttpRouter.Provided>
498
+ // | Exclude<
499
+ // RPCRouteR<
500
+ // { [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<THandlers[K]["handler"]>>> }[keyof Filter<Rsc>]
501
+ // >,
502
+ // HttpRouter.HttpRouter.Provided
503
+ // >
504
+ // >
505
+ // routes: Layer.Layer<
506
+ // RouterShape<Rsc>,
507
+ // E | GetError<MakeLayers["dependencies"]>,
508
+ // | GetContext<MakeLayers["dependencies"]>
509
+ // // | GetContext<Layers> // elsewhere provided
510
+ // | Exclude<R | RMW, GetSuccess<MakeLayers["dependencies"]> | GetSuccess<Layers>>
511
+ // >
512
+ // }
513
+ <
514
+ const Make extends {
515
+ dependencies: Array<Layer.Layer.Any>
516
+ effect: Effect<
517
+ { [K in keyof Filter<Rsc>]: AHandler<Rsc[K]> },
518
+ any,
519
+ Make["strict"] extends false ? any : GetSuccess<Make["dependencies"]>
520
+ >
521
+ strict?: boolean
522
+ }
523
+ >(
524
+ make: Make
525
+ ): {
526
+ moduleName: ModuleName
527
+ Router: HttpRouter.HttpRouter.TagClass<
528
+ RouterShape<Rsc>,
529
+ `${ModuleName}Router`,
530
+ never,
531
+ | Exclude<Context, HttpRouter.HttpRouter.Provided>
532
+ | Exclude<
533
+ RPCRouteR<
534
+ {
535
+ [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<MakeHandlers<Make, Filter<Rsc>>[K]["handler"]>>>
536
+ }[keyof Filter<Rsc>]
537
+ >,
538
+ HttpRouter.HttpRouter.Provided
539
+ >
540
+ >
541
+ routes: Layer.Layer<
542
+ RouterShape<Rsc>,
543
+ MakeErrors<Make> | GetError<Make["dependencies"]>,
544
+ | GetContext<Make["dependencies"]>
545
+ // | GetContext<Layers> // elsewhere provided
546
+ | Exclude<MakeContext<Make> | RMW, GetSuccess<Make["dependencies"]> | GetSuccess<Layers>>
547
+ >
548
+ }
549
+ <
550
+ const Make extends {
551
+ dependencies: Array<Layer.Layer.Any>
552
+ effect: Effect<
553
+ { [K in keyof Filter<Rsc>]: AHandler<Rsc[K]> },
554
+ any,
555
+ GetSuccess<Make["dependencies"]>
556
+ >
557
+ strict?: boolean
558
+ }
559
+ >(
560
+ make: Make
561
+ ): {
562
+ moduleName: ModuleName
563
+ Router: HttpRouter.HttpRouter.TagClass<
564
+ RouterShape<Rsc>,
565
+ `${ModuleName}Router`,
566
+ never,
567
+ | Exclude<Context, HttpRouter.HttpRouter.Provided>
568
+ | Exclude<
569
+ RPCRouteR<
570
+ {
571
+ [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<MakeHandlers<Make, Filter<Rsc>>[K]["handler"]>>>
572
+ }[keyof Filter<Rsc>]
573
+ >,
574
+ HttpRouter.HttpRouter.Provided
575
+ >
576
+ >
577
+ routes: Layer.Layer<
578
+ RouterShape<Rsc>,
579
+ MakeErrors<Make> | GetError<Make["dependencies"]>,
580
+ | GetContext<Make["dependencies"]>
581
+ // | GetContext<Layers> // elsewhere provided
582
+ | Exclude<MakeContext<Make> | RMW, GetSuccess<Make["dependencies"]> | GetSuccess<Layers>>
583
+ >
584
+ }
585
+ <
586
+ const Make extends {
587
+ dependencies: Array<Layer.Layer.Any>
588
+ effect: Effect<
589
+ { [K in keyof Filter<Rsc>]: AHandler<Rsc[K]> },
590
+ any,
591
+ GetSuccess<Make["dependencies"]>
592
+ >
593
+ strict?: boolean
594
+ }
595
+ >(
596
+ make: Make
597
+ ): {
598
+ moduleName: ModuleName
599
+ Router: HttpRouter.HttpRouter.TagClass<
600
+ RouterShape<Rsc>,
601
+ `${ModuleName}Router`,
602
+ never,
603
+ | Exclude<Context, HttpRouter.HttpRouter.Provided>
604
+ | Exclude<
605
+ RPCRouteR<
606
+ {
607
+ [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<MakeHandlers<Make, Filter<Rsc>>[K]["handler"]>>>
608
+ }[keyof Filter<Rsc>]
609
+ >,
610
+ HttpRouter.HttpRouter.Provided
611
+ >
612
+ >
613
+ routes: Layer.Layer<
614
+ RouterShape<Rsc>,
615
+ MakeErrors<Make> | GetError<Make["dependencies"]>,
616
+ | GetContext<Make["dependencies"]>
617
+ // | GetContext<Layers> // elsewhere provided
618
+ | Exclude<MakeContext<Make> | RMW, GetSuccess<Make["dependencies"]> | GetSuccess<Layers>>
619
+ >
620
+ }
621
+ <
622
+ const Make extends {
623
+ dependencies: [
624
+ ...Make["dependencies"],
625
+ ...Exclude<Effect.Context<Make["effect"]>, MakeDepsOut<Make>> extends never ? []
626
+ : [Layer.Layer<Exclude<Effect.Context<Make["effect"]>, MakeDepsOut<Make>>, never, never>]
627
+ ]
628
+ effect: Effect<
629
+ { [K in keyof Filter<Rsc>]: AHandler<Rsc[K]> },
630
+ any,
631
+ any
632
+ >
633
+ strict?: boolean
634
+ }
635
+ >(
636
+ make: Make
637
+ ): {
638
+ moduleName: ModuleName
639
+ Router: HttpRouter.HttpRouter.TagClass<
640
+ RouterShape<Rsc>,
641
+ `${ModuleName}Router`,
642
+ never,
643
+ Exclude<Context, HttpRouter.HttpRouter.Provided>
644
+ > // | Exclude<
645
+ // RPCRouteR<
646
+ // { [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<THandlers[K]["handler"]>>> }[keyof Filter<Rsc>]
647
+ // >,
648
+ // HttpRouter.HttpRouter.Provided
649
+ // >
650
+ routes: any
651
+ }
652
+ } = ((m: { dependencies: any; effect: any; strict?: any }) => f(m.dependencies, m.effect)) as any
653
+
654
+ return Object.assign(effect, items)
655
+ }
656
+
657
+ type HR<T> = T extends HttpRouter.HttpRouter<any, infer R> ? R : never
658
+ type HE<T> = T extends HttpRouter.HttpRouter<infer E, any> ? E : never
659
+
660
+ type RequestHandlersTest = {
661
+ [key: string]: {
662
+ Router: { router: Effect<HttpRouter.HttpRouter<any, any>, any, any> }
663
+ routes: Layer.Layer<any, any, any>
664
+ moduleName: string
665
+ }
666
+ }
667
+ function matchAll<T extends RequestHandlersTest, A, E, R>(
668
+ handlers: T,
669
+ requestLayer: Layer.Layer<A, E, R>
670
+ ) {
671
+ const routers = typedValuesOf(handlers)
672
+
673
+ const rootRouter = class extends HttpRouter.Tag("RootRouter")<
674
+ "RootRouter",
675
+ HR<Effect.Success<typeof handlers[keyof typeof handlers]["Router"]["router"]>>,
676
+ HE<Effect.Success<typeof handlers[keyof typeof handlers]["Router"]["router"]>>
677
+ >() {}
678
+
679
+ const r = rootRouter
680
+ .use((router) =>
681
+ Effect.gen(function*() {
682
+ for (const route of routers) {
683
+ yield* router.mount(
684
+ ("/rpc/" + route.moduleName) as any,
685
+ yield* route
686
+ .Router
687
+ .router
688
+ .pipe(Effect.map(HttpRouter.use(flow(Effect.provide(requestLayer))))) as any
689
+ )
690
+ }
691
+ })
692
+ )
693
+ .pipe(Layer.provide(routers.map((r) => r.routes).flat() as unknown as NonEmptyArray<Layer.Layer.Any>))
694
+
695
+ return {
696
+ layer: r as Layer.Layer<
697
+ never,
698
+ Layer.Layer.Error<typeof handlers[keyof typeof handlers]["routes"]>,
699
+ Layer.Layer.Context<typeof handlers[keyof typeof handlers]["routes"]>
700
+ >,
701
+ Router: rootRouter as any as HttpRouter.HttpRouter.TagClass<
702
+ "RootRouter",
703
+ "RootRouter",
704
+ HE<Effect.Success<typeof handlers[keyof typeof handlers]["Router"]["router"]>>,
705
+ R | Exclude<HR<Effect.Success<typeof handlers[keyof typeof handlers]["Router"]["router"]>>, A>
706
+ >
707
+ }
708
+ }
709
+
710
+ return { matchAll, matchFor }
711
+ }
712
+
713
+ export type MakeDeps<Make> = Make extends { readonly dependencies: ReadonlyArray<Layer.Layer.Any> }
714
+ ? Make["dependencies"][number]
715
+ : never
716
+
717
+ export type MakeErrors<Make> = Make extends { readonly effect: Effect<any, infer E, any> } ? E
718
+ : never
719
+
720
+ export type MakeContext<Make> = Make extends { readonly effect: Effect<any, any, infer R> } ? R
721
+ : never
722
+
723
+ export type MakeHandlers<Make, Handlers extends Record<string, any>> = Make extends
724
+ { readonly effect: Effect<{ [K in keyof Handlers]: AHandler<Handlers[K]> }, any, any> }
725
+ ? Effect.Success<Make["effect"]>
726
+ : never
727
+
728
+ /**
729
+ * @since 3.9.0
730
+ */
731
+ export type MakeDepsOut<Make> = Contravariant.Type<MakeDeps<Make>[Layer.LayerTypeId]["_ROut"]>