@effect-app/infra 2.20.1 → 2.21.1

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,919 @@
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
+ export type AnyRequestModule = S.Schema.Any & {
92
+ _tag: string
93
+ config?: any
94
+ success?: S.Schema.Any
95
+ failure?: S.Schema.Any
96
+ }
97
+ export interface AddAction<Actions extends AnyRequestModule, Accum extends Record<string, any> = {}> {
98
+ accum: Accum
99
+ add<A extends Handler<Actions, any, any>>(
100
+ a: A
101
+ ): Exclude<Actions, A extends Handler<infer M, any, any> ? M : never> extends never ?
102
+ & Accum
103
+ & { [K in A extends Handler<infer M, any, any> ? M extends AnyRequestModule ? M["_tag"] : never : never]: A }
104
+ :
105
+ & AddAction<
106
+ Exclude<Actions, A extends Handler<infer M, any, any> ? M : never>,
107
+ & Accum
108
+ & { [K in A extends Handler<infer M, any, any> ? M extends AnyRequestModule ? M["_tag"] : never : never]: A }
109
+ >
110
+ & Accum
111
+ & { [K in A extends Handler<infer M, any, any> ? M extends AnyRequestModule ? M["_tag"] : never : never]: A }
112
+ }
113
+
114
+ export interface Method<
115
+ Rsc extends Record<string, AnyRequestModule>,
116
+ K extends keyof Rsc,
117
+ RT extends "d" | "raw",
118
+ CTXMap extends Record<string, any>,
119
+ Context,
120
+ Accum
121
+ > {
122
+ // TODO: deal with HandleVoid and ability to extends from GetSuccessShape...
123
+ // aka we want to make sure that the return type is void if the success is void,
124
+ // and make sure A is the actual expected type
125
+
126
+ // note: the defaults of = never prevent the whole router to error
127
+ <A extends GetSuccessShape<Rsc[K], RT>, R2 = never, E = never>(
128
+ f: Effect<A, E, R2>
129
+ ): keyof { [k in keyof Rsc as k extends K | keyof Accum ? never : k]: Rsc[k] } extends never ?
130
+ & {
131
+ [k in K]: HandleVoid<
132
+ GetSuccessShape<Rsc[K], RT>,
133
+ A,
134
+ Handler<
135
+ Rsc[K],
136
+ RT,
137
+ Exclude<
138
+ Context | Exclude<R2, GetEffectContext<CTXMap, Rsc[K]["config"]>>,
139
+ HttpRouter.HttpRouter.Provided
140
+ >
141
+ >
142
+ >
143
+ }
144
+ & Accum
145
+ : RRouter<
146
+ { [k in keyof Rsc as k extends K | keyof Accum ? never : k]: Rsc[k] },
147
+ CTXMap,
148
+ Context,
149
+ {
150
+ [k in K]: HandleVoid<
151
+ GetSuccessShape<Rsc[K], RT>,
152
+ A,
153
+ Handler<
154
+ Rsc[K],
155
+ RT,
156
+ Exclude<
157
+ Context | Exclude<R2, GetEffectContext<CTXMap, Rsc[K]["config"]>>,
158
+ HttpRouter.HttpRouter.Provided
159
+ >
160
+ >
161
+ >
162
+ } & Accum
163
+ >
164
+
165
+ <A extends GetSuccessShape<Rsc[K], RT>, R2 = never, E = never>(
166
+ f: (req: S.Schema.Type<Rsc[K]>) => Effect<A, E, R2>
167
+ ): keyof { [k in keyof Rsc as k extends K | keyof Accum ? never : k]: Rsc[k] } extends never ?
168
+ & {
169
+ [k in K]: HandleVoid<
170
+ GetSuccessShape<Rsc[K], RT>,
171
+ A,
172
+ Handler<
173
+ Rsc[K],
174
+ RT,
175
+ Exclude<
176
+ Context | Exclude<R2, GetEffectContext<CTXMap, Rsc[K]["config"]>>,
177
+ HttpRouter.HttpRouter.Provided
178
+ >
179
+ >
180
+ >
181
+ }
182
+ & Accum
183
+ : RRouter<
184
+ { [k in keyof Rsc as k extends K | keyof Accum ? never : k]: Rsc[k] },
185
+ CTXMap,
186
+ Context,
187
+ {
188
+ [k in K]: HandleVoid<
189
+ GetSuccessShape<Rsc[K], RT>,
190
+ A,
191
+ Handler<
192
+ Rsc[K],
193
+ RT,
194
+ Exclude<
195
+ Context | Exclude<R2, GetEffectContext<CTXMap, Rsc[K]["config"]>>,
196
+ HttpRouter.HttpRouter.Provided
197
+ >
198
+ >
199
+ >
200
+ } & Accum
201
+ >
202
+ }
203
+
204
+ type RRouter<Rsc extends Record<string, AnyRequestModule>, CTXMap extends Record<string, any>, Context, Accum = {}> = {
205
+ [K in keyof Rsc]:
206
+ & (Method<Rsc, K, "d", CTXMap, Context, Accum>)
207
+ & {
208
+ raw: Method<Rsc, K, "raw", CTXMap, Context, Accum>
209
+ }
210
+ }
211
+
212
+ type GetSuccess<T> = T extends { success: S.Schema.Any } ? T["success"] : typeof S.Void
213
+
214
+ type GetSuccessShape<Action extends { success?: S.Schema.Any }, RT extends "d" | "raw"> = RT extends "raw"
215
+ ? S.Schema.Encoded<GetSuccess<Action>>
216
+ : S.Schema.Type<GetSuccess<Action>>
217
+ type GetFailure<T extends { failure?: S.Schema.Any }> = T["failure"] extends never ? typeof S.Never : T["failure"]
218
+
219
+ type HandlerFull<Action extends AnyRequestModule, RT extends "raw" | "d", A, E, R> = {
220
+ new(): {}
221
+ _tag: RT
222
+ stack: string
223
+ handler: (
224
+ req: S.Schema.Type<Action>
225
+ ) => Effect<
226
+ A,
227
+ E,
228
+ R
229
+ >
230
+ }
231
+
232
+ export interface Handler<Action extends AnyRequestModule, RT extends "raw" | "d", R> extends
233
+ HandlerFull<
234
+ Action,
235
+ RT,
236
+ GetSuccessShape<Action, RT>,
237
+ S.Schema.Type<GetFailure<Action>> | S.ParseResult.ParseError,
238
+ R
239
+ >
240
+ {
241
+ }
242
+
243
+ type AHandler<Action extends AnyRequestModule> = Handler<
244
+ Action,
245
+ any,
246
+ any
247
+ >
248
+
249
+ type Filter<T> = {
250
+ [K in keyof T as T[K] extends AnyRequestModule ? K : never]: T[K]
251
+ }
252
+
253
+ export const RouterSymbol = Symbol()
254
+ export interface RouterShape<Rsc> {
255
+ [RouterSymbol]: Rsc
256
+ }
257
+
258
+ type RPCRouteR<T extends Rpc.Rpc<any, any>> = [T] extends [
259
+ Rpc.Rpc<any, infer R>
260
+ ] ? R
261
+ : never
262
+
263
+ type RPCRouteReq<T extends Rpc.Rpc<any, any>> = [T] extends [
264
+ Rpc.Rpc<infer Req, any>
265
+ ] ? Req
266
+ : never
267
+
268
+ type Match<
269
+ Rsc extends Record<string, any>,
270
+ CTXMap extends Record<string, any>,
271
+ RT extends "raw" | "d",
272
+ Key extends keyof Rsc,
273
+ Context
274
+ > = {
275
+ // TODO: deal with HandleVoid and ability to extends from GetSuccessShape...
276
+ // aka we want to make sure that the return type is void if the success is void,
277
+ // and make sure A is the actual expected type
278
+
279
+ // note: the defaults of = never prevent the whole router to error
280
+ <A extends GetSuccessShape<Rsc[Key], RT>, R2 = never, E = never>(
281
+ f: Effect<A, E, R2>
282
+ ): HandleVoid<
283
+ GetSuccessShape<Rsc[Key], RT>,
284
+ A,
285
+ Handler<
286
+ Rsc[Key],
287
+ RT,
288
+ Exclude<
289
+ Context | Exclude<R2, GetEffectContext<CTXMap, Rsc[Key]["config"]>>,
290
+ HttpRouter.HttpRouter.Provided
291
+ >
292
+ >
293
+ >
294
+
295
+ <A extends GetSuccessShape<Rsc[Key], RT>, R2 = never, E = never>(
296
+ f: (req: S.Schema.Type<Rsc[Key]>) => Effect<A, E, R2>
297
+ ): HandleVoid<
298
+ GetSuccessShape<Rsc[Key], RT>,
299
+ A,
300
+ Handler<
301
+ Rsc[Key],
302
+ RT,
303
+ Exclude<
304
+ Context | Exclude<R2, GetEffectContext<CTXMap, Rsc[Key]["config"]>>,
305
+ HttpRouter.HttpRouter.Provided
306
+ >
307
+ >
308
+ >
309
+ }
310
+
311
+ export type RouteMatcher<
312
+ CTXMap extends Record<string, any>,
313
+ Rsc extends Record<string, any>,
314
+ Context
315
+ > = {
316
+ // use Rsc as Key over using Keys, so that the Go To on X.Action remain in tact in Controllers files
317
+ /**
318
+ * Requires the Type shape
319
+ */
320
+ [Key in keyof Filter<Rsc>]: Match<Rsc, CTXMap, "d", Key, Context> & {
321
+ success: Rsc[Key]["success"]
322
+ successRaw: S.SchemaClass<S.Schema.Encoded<Rsc[Key]["success"]>>
323
+ failure: Rsc[Key]["failure"]
324
+ /**
325
+ * Requires the Encoded shape (e.g directly undecoded from DB, so that we don't do multiple Decode/Encode)
326
+ */
327
+ raw: Match<Rsc, CTXMap, "raw", Key, Context>
328
+ }
329
+ }
330
+ // export interface RouteMatcher<
331
+ // Filtered extends Record<string, any>,
332
+ // CTXMap extends Record<string, any>,
333
+ // Rsc extends Filtered
334
+ // > extends RouteMatcherInt<Filtered, CTXMap, Rsc> {}
335
+
336
+ export const makeMiddleware = <
337
+ Context,
338
+ CTXMap extends Record<string, RPCContextMap.Any>,
339
+ RMW,
340
+ Layers extends NonEmptyReadonlyArray<Layer.Layer.Any> | never[]
341
+ >(content: Middleware<Context, CTXMap, RMW, Layers>): Middleware<Context, CTXMap, RMW, Layers> => content
342
+
343
+ export const makeRouter = <
344
+ Context,
345
+ CTXMap extends Record<string, RPCContextMap.Any>,
346
+ RMW,
347
+ Layers extends NonEmptyReadonlyArray<Layer.Layer.Any> | never[]
348
+ >(
349
+ middleware: Middleware<Context, CTXMap, RMW, Layers>,
350
+ devMode: boolean
351
+ ) => {
352
+ function matchFor<
353
+ const ModuleName extends string,
354
+ const Rsc extends Record<string, any>
355
+ >(
356
+ rsc: Rsc & { meta: { moduleName: ModuleName } }
357
+ ) {
358
+ const meta = rsc.meta
359
+ type Filtered = Filter<Rsc>
360
+ const filtered = typedKeysOf(rsc).reduce((acc, cur) => {
361
+ if (Predicate.isObject(rsc[cur]) && rsc[cur]["success"]) {
362
+ acc[cur as keyof Filtered] = rsc[cur]
363
+ }
364
+ return acc
365
+ }, {} as Filtered)
366
+
367
+ const items = typedKeysOf(filtered).reduce(
368
+ (prev, cur) => {
369
+ ;(prev as any)[cur] = Object.assign((fnOrEffect: any) => {
370
+ const stack = new Error().stack?.split("\n").slice(2).join("\n")
371
+ return Effect.isEffect(fnOrEffect)
372
+ ? class {
373
+ static request = rsc[cur]
374
+ static stack = stack
375
+ static _tag = "d"
376
+ static handler = () => fnOrEffect
377
+ }
378
+ : class {
379
+ static request = rsc[cur]
380
+ static stack = stack
381
+ static _tag = "d"
382
+ static handler = fnOrEffect
383
+ }
384
+ }, {
385
+ success: rsc[cur].success,
386
+ successRaw: S.encodedSchema(rsc[cur].success),
387
+ failure: rsc[cur].failure,
388
+ raw: // "Raw" variations are for when you don't want to decode just to encode it again on the response
389
+ // e.g for direct projection from DB
390
+ // but more importantly, to skip Effectful decoders, like to resolve relationships from the database or remote client.
391
+ (fnOrEffect: any) => {
392
+ const stack = new Error().stack?.split("\n").slice(2).join("\n")
393
+ return Effect.isEffect(fnOrEffect)
394
+ ? class {
395
+ static request = rsc[cur]
396
+ static stack = stack
397
+ static _tag = "raw"
398
+ static handler = () => fnOrEffect
399
+ }
400
+ : class {
401
+ static request = rsc[cur]
402
+ static stack = stack
403
+ static _tag = "raw"
404
+ static handler = (req: any, ctx: any) => fnOrEffect(req, { ...ctx, Response: rsc[cur].success })
405
+ }
406
+ }
407
+ })
408
+ return prev
409
+ },
410
+ {} as RouteMatcher<CTXMap, Rsc, Context>
411
+ )
412
+
413
+ type Keys = keyof Filtered
414
+
415
+ type GetSuccess<Layers extends ReadonlyArray<Layer.Layer.Any>> = Layers extends
416
+ NonEmptyReadonlyArray<Layer.Layer.Any> ? {
417
+ [k in keyof Layers]: Layer.Layer.Success<Layers[k]>
418
+ }[number]
419
+ : never
420
+
421
+ type GetContext<Layers extends ReadonlyArray<Layer.Layer.Any>> = Layers extends
422
+ NonEmptyReadonlyArray<Layer.Layer.Any> ? {
423
+ [k in keyof Layers]: Layer.Layer.Context<Layers[k]>
424
+ }[number]
425
+ : never
426
+
427
+ type GetError<Layers extends ReadonlyArray<Layer.Layer.Any>> = Layers extends NonEmptyReadonlyArray<Layer.Layer.Any>
428
+ ? { [k in keyof Layers]: Layer.Layer.Error<Layers[k]> }[number]
429
+ : never
430
+
431
+ const f = <
432
+ E,
433
+ R,
434
+ THandlers extends {
435
+ // import to keep them separate via | for type checking!!
436
+ [K in Keys]: AHandler<Rsc[K]>
437
+ },
438
+ TLayers extends NonEmptyReadonlyArray<Layer.Layer.Any> | never[]
439
+ >(
440
+ layers: TLayers,
441
+ make: Effect<THandlers, E, R>
442
+ ) => {
443
+ type ProvidedLayers =
444
+ | { [k in keyof Layers]: Layer.Layer.Success<Layers[k]> }[number]
445
+ | { [k in keyof TLayers]: Layer.Layer.Success<TLayers[k]> }[number]
446
+ type Router = RouterShape<Rsc>
447
+ const r: HttpRouter.HttpRouter.TagClass<
448
+ Router,
449
+ `${typeof meta.moduleName}Router`,
450
+ never,
451
+ Exclude<
452
+ | Context
453
+ | RPCRouteR<
454
+ { [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<THandlers[K]["handler"]>>> }[keyof Filter<Rsc>]
455
+ >,
456
+ HttpRouter.HttpRouter.Provided
457
+ >
458
+ > = (class Router extends HttpRouter.Tag(`${meta.moduleName}Router`)<Router>() {}) as any
459
+
460
+ const layer = r.use((router) =>
461
+ Effect.gen(function*() {
462
+ const controllers = yield* make
463
+ const rpc = yield* makeRpc(middleware)
464
+
465
+ // return make.pipe(Effect.map((c) => controllers(c, layers)))
466
+ const mapped = typedKeysOf(filtered).reduce((acc, cur) => {
467
+ const handler = controllers[cur as keyof typeof controllers]
468
+ const req = rsc[cur]
469
+
470
+ acc[cur] = rpc.effect(
471
+ handler._tag === "raw"
472
+ ? class extends (req as any) {
473
+ static success = S.encodedSchema(req.success)
474
+ get [Schema.symbolSerializable]() {
475
+ return this.constructor
476
+ }
477
+ get [Schema.symbolWithResult]() {
478
+ return {
479
+ failure: req.failure,
480
+ success: S.encodedSchema(req.success)
481
+ }
482
+ }
483
+ } as any
484
+ : req,
485
+ (req) =>
486
+ // TODO: render more data... similar to console?
487
+ Effect
488
+ .annotateCurrentSpan(
489
+ "requestInput",
490
+ Object.entries(req).reduce((prev, [key, value]: [string, unknown]) => {
491
+ prev[key] = key === "password"
492
+ ? "<redacted>"
493
+ : typeof value === "string" || typeof value === "number" || typeof value === "boolean"
494
+ ? typeof value === "string" && value.length > 256
495
+ ? (value.substring(0, 253) + "...")
496
+ : value
497
+ : Array.isArray(value)
498
+ ? `Array[${value.length}]`
499
+ : value === null || value === undefined
500
+ ? `${value}`
501
+ : typeof value === "object" && value
502
+ ? `Object[${Object.keys(value).length}]`
503
+ : typeof value
504
+ return prev
505
+ }, {} as Record<string, string | number | boolean>)
506
+ )
507
+ .pipe(
508
+ // can't use andThen due to some being a function and effect
509
+ Effect.zipRight(handler.handler(req as any) as any),
510
+ Effect.tapErrorCause((cause) => Cause.isFailure(cause) ? logRequestError(cause) : Effect.void),
511
+ Effect.tapDefect((cause) =>
512
+ Effect
513
+ .all([
514
+ reportRequestError(cause, {
515
+ action: `${meta.moduleName}.${req._tag}`
516
+ }),
517
+ Rpc.currentHeaders.pipe(Effect.andThen((headers) => {
518
+ return InfraLogger
519
+ .logError("Finished request", cause)
520
+ .pipe(Effect.annotateLogs({
521
+ action: `${meta.moduleName}.${req._tag}`,
522
+ req: pretty(req),
523
+ headers: pretty(headers)
524
+ // resHeaders: pretty(
525
+ // Object
526
+ // .entries(headers)
527
+ // .reduce((prev, [key, value]) => {
528
+ // prev[key] = value && typeof value === "string" ? snipString(value) : value
529
+ // return prev
530
+ // }, {} as Record<string, any>)
531
+ // )
532
+ }))
533
+ }))
534
+ ])
535
+ ),
536
+ devMode ? (_) => _ : Effect.catchAllDefect(() => Effect.die("Internal Server Error")),
537
+ Effect.withSpan("Request." + meta.moduleName + "." + req._tag, {
538
+ captureStackTrace: () => handler.stack
539
+ })
540
+ ),
541
+ meta.moduleName
542
+ ) // TODO
543
+ return acc
544
+ }, {} as any) as {
545
+ [K in Keys]: Rpc.Rpc<
546
+ Rsc[K],
547
+ Context | _R<ReturnType<THandlers[K]["handler"]>>
548
+ >
549
+ }
550
+
551
+ const rpcRouter = RpcRouter.make(...Object.values(mapped) as any) as RpcRouter.RpcRouter<
552
+ RPCRouteReq<typeof mapped[keyof typeof mapped]>,
553
+ RPCRouteR<typeof mapped[keyof typeof mapped]>
554
+ >
555
+ const httpApp = toHttpApp(rpcRouter, {
556
+ spanPrefix: rsc
557
+ .meta
558
+ .moduleName + "."
559
+ })
560
+ yield* router
561
+ .all(
562
+ "/",
563
+ httpApp as any,
564
+ // TODO: not queries.
565
+ { uninterruptible: true }
566
+ )
567
+ })
568
+ )
569
+
570
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
571
+ const routes = layer.pipe(
572
+ Layer.provideMerge(r.Live),
573
+ layers && Array.isNonEmptyReadonlyArray(layers) ? Layer.provide(layers as any) as any : (_) => _,
574
+ // TODO: only provide to the middleware?
575
+ middleware.dependencies ? Layer.provide(middleware.dependencies as any) : (_) => _
576
+ ) as Layer.Layer<
577
+ Router,
578
+ GetError<TLayers> | E,
579
+ | GetContext<TLayers>
580
+ | Exclude<
581
+ RMW | R,
582
+ ProvidedLayers
583
+ >
584
+ >
585
+
586
+ // Effect.Effect<HttpRouter.HttpRouter<unknown, HttpRouter.HttpRouter.DefaultServices>, never, UserRouter>
587
+
588
+ return {
589
+ moduleName: meta.moduleName,
590
+ Router: r,
591
+ routes
592
+ }
593
+ }
594
+
595
+ const effect: {
596
+ // Multiple times duplicated the "good" overload, so that errors will only mention the last overload when failing
597
+ <
598
+ const Make extends {
599
+ dependencies: Array<Layer.Layer.Any>
600
+ effect: Effect<
601
+ { [K in keyof Filter<Rsc>]: AHandler<Rsc[K]> },
602
+ any,
603
+ Make["strict"] extends false ? any : GetSuccess<Make["dependencies"]>
604
+ >
605
+ strict?: boolean
606
+ /** @deprecated */
607
+ readonly ಠ_ಠ: never
608
+ }
609
+ >(
610
+ make: Make
611
+ ): {
612
+ moduleName: ModuleName
613
+ Router: HttpRouter.HttpRouter.TagClass<
614
+ RouterShape<Rsc>,
615
+ `${ModuleName}Router`,
616
+ never,
617
+ | Exclude<Context, HttpRouter.HttpRouter.Provided>
618
+ | Exclude<
619
+ RPCRouteR<
620
+ {
621
+ [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<MakeHandlers<Make, Filter<Rsc>>[K]["handler"]>>>
622
+ }[keyof Filter<Rsc>]
623
+ >,
624
+ HttpRouter.HttpRouter.Provided
625
+ >
626
+ >
627
+ routes: Layer.Layer<
628
+ RouterShape<Rsc>,
629
+ MakeErrors<Make> | GetError<Make["dependencies"]>,
630
+ | GetContext<Make["dependencies"]>
631
+ // | GetContext<Layers> // elsewhere provided
632
+ | Exclude<MakeContext<Make> | RMW, GetSuccess<Make["dependencies"]> | GetSuccess<Layers>>
633
+ >
634
+ }
635
+ <
636
+ const Make extends {
637
+ dependencies: Array<Layer.Layer.Any>
638
+ effect: Effect<
639
+ { [K in keyof Filter<Rsc>]: AHandler<Rsc[K]> },
640
+ any,
641
+ Make["strict"] extends false ? any : GetSuccess<Make["dependencies"]>
642
+ >
643
+ strict?: boolean
644
+ /** @deprecated */
645
+ readonly ಠ_ಠ: never
646
+ }
647
+ >(
648
+ make: Make
649
+ ): {
650
+ moduleName: ModuleName
651
+ Router: HttpRouter.HttpRouter.TagClass<
652
+ RouterShape<Rsc>,
653
+ `${ModuleName}Router`,
654
+ never,
655
+ | Exclude<Context, HttpRouter.HttpRouter.Provided>
656
+ | Exclude<
657
+ RPCRouteR<
658
+ {
659
+ [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<MakeHandlers<Make, Filter<Rsc>>[K]["handler"]>>>
660
+ }[keyof Filter<Rsc>]
661
+ >,
662
+ HttpRouter.HttpRouter.Provided
663
+ >
664
+ >
665
+ routes: Layer.Layer<
666
+ RouterShape<Rsc>,
667
+ MakeErrors<Make> | GetError<Make["dependencies"]>,
668
+ | GetContext<Make["dependencies"]>
669
+ // | GetContext<Layers> // elsewhere provided
670
+ | Exclude<MakeContext<Make> | RMW, GetSuccess<Make["dependencies"]> | GetSuccess<Layers>>
671
+ >
672
+ }
673
+ <
674
+ const Make extends {
675
+ dependencies: Array<Layer.Layer.Any>
676
+ effect: Effect<
677
+ { [K in keyof Filter<Rsc>]: AHandler<Rsc[K]> },
678
+ any,
679
+ Make["strict"] extends false ? any : GetSuccess<Make["dependencies"]>
680
+ >
681
+ strict?: boolean
682
+ /** @deprecated */
683
+ readonly ಠ_ಠ: never
684
+ }
685
+ >(
686
+ make: Make
687
+ ): {
688
+ moduleName: ModuleName
689
+ Router: HttpRouter.HttpRouter.TagClass<
690
+ RouterShape<Rsc>,
691
+ `${ModuleName}Router`,
692
+ never,
693
+ | Exclude<Context, HttpRouter.HttpRouter.Provided>
694
+ | Exclude<
695
+ RPCRouteR<
696
+ {
697
+ [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<MakeHandlers<Make, Filter<Rsc>>[K]["handler"]>>>
698
+ }[keyof Filter<Rsc>]
699
+ >,
700
+ HttpRouter.HttpRouter.Provided
701
+ >
702
+ >
703
+ routes: Layer.Layer<
704
+ RouterShape<Rsc>,
705
+ MakeErrors<Make> | GetError<Make["dependencies"]>,
706
+ | GetContext<Make["dependencies"]>
707
+ // | GetContext<Layers> // elsewhere provided
708
+ | Exclude<MakeContext<Make> | RMW, GetSuccess<Make["dependencies"]> | GetSuccess<Layers>>
709
+ >
710
+ }
711
+ <
712
+ const Make extends {
713
+ dependencies: Array<Layer.Layer.Any>
714
+ effect: Effect<
715
+ { [K in keyof Filter<Rsc>]: AHandler<Rsc[K]> },
716
+ any,
717
+ GetSuccess<Make["dependencies"]>
718
+ >
719
+ strict?: boolean
720
+ /** @deprecated */
721
+ readonly ಠ_ಠ: never
722
+ }
723
+ >(
724
+ make: Make
725
+ ): {
726
+ moduleName: ModuleName
727
+ Router: HttpRouter.HttpRouter.TagClass<
728
+ RouterShape<Rsc>,
729
+ `${ModuleName}Router`,
730
+ never,
731
+ | Exclude<Context, HttpRouter.HttpRouter.Provided>
732
+ | Exclude<
733
+ RPCRouteR<
734
+ {
735
+ [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<MakeHandlers<Make, Filter<Rsc>>[K]["handler"]>>>
736
+ }[keyof Filter<Rsc>]
737
+ >,
738
+ HttpRouter.HttpRouter.Provided
739
+ >
740
+ >
741
+ routes: Layer.Layer<
742
+ RouterShape<Rsc>,
743
+ MakeErrors<Make> | GetError<Make["dependencies"]>,
744
+ | GetContext<Make["dependencies"]>
745
+ // | GetContext<Layers> // elsewhere provided
746
+ | Exclude<MakeContext<Make> | RMW, GetSuccess<Make["dependencies"]> | GetSuccess<Layers>>
747
+ >
748
+ }
749
+ <
750
+ const Make extends {
751
+ dependencies: Array<Layer.Layer.Any>
752
+ effect: Effect<
753
+ { [K in keyof Filter<Rsc>]: AHandler<Rsc[K]> },
754
+ any,
755
+ GetSuccess<Make["dependencies"]>
756
+ >
757
+ strict?: boolean
758
+ }
759
+ >(
760
+ make: Make
761
+ ): {
762
+ moduleName: ModuleName
763
+ Router: HttpRouter.HttpRouter.TagClass<
764
+ RouterShape<Rsc>,
765
+ `${ModuleName}Router`,
766
+ never,
767
+ | Exclude<Context, HttpRouter.HttpRouter.Provided>
768
+ | Exclude<
769
+ RPCRouteR<
770
+ {
771
+ [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<MakeHandlers<Make, Filter<Rsc>>[K]["handler"]>>>
772
+ }[keyof Filter<Rsc>]
773
+ >,
774
+ HttpRouter.HttpRouter.Provided
775
+ >
776
+ >
777
+ routes: Layer.Layer<
778
+ RouterShape<Rsc>,
779
+ MakeErrors<Make> | GetError<Make["dependencies"]>,
780
+ | GetContext<Make["dependencies"]>
781
+ // | GetContext<Layers> // elsewhere provided
782
+ | Exclude<MakeContext<Make> | RMW, GetSuccess<Make["dependencies"]> | GetSuccess<Layers>>
783
+ >
784
+ }
785
+ <
786
+ const Make extends {
787
+ dependencies: [
788
+ ...Make["dependencies"],
789
+ ...Exclude<Effect.Context<Make["effect"]>, MakeDepsOut<Make>> extends never ? []
790
+ : [Layer.Layer<Exclude<Effect.Context<Make["effect"]>, MakeDepsOut<Make>>, never, never>]
791
+ ]
792
+ effect: Effect<
793
+ { [K in keyof Filter<Rsc>]: AHandler<Rsc[K]> },
794
+ any,
795
+ any
796
+ >
797
+ strict?: boolean
798
+ }
799
+ >(
800
+ make: Make
801
+ ): {
802
+ moduleName: ModuleName
803
+ Router: HttpRouter.HttpRouter.TagClass<
804
+ RouterShape<Rsc>,
805
+ `${ModuleName}Router`,
806
+ never,
807
+ Exclude<Context, HttpRouter.HttpRouter.Provided>
808
+ > // | Exclude<
809
+ // RPCRouteR<
810
+ // { [K in keyof Filter<Rsc>]: Rpc.Rpc<Rsc[K], _R<ReturnType<THandlers[K]["handler"]>>> }[keyof Filter<Rsc>]
811
+ // >,
812
+ // HttpRouter.HttpRouter.Provided
813
+ // >
814
+ routes: any
815
+ }
816
+ } = ((m: { dependencies: any; effect: any; strict?: any }) => f(m.dependencies, m.effect)) as any
817
+
818
+ const total = Object.keys(filtered).length
819
+
820
+ // TODO
821
+ const accum: Record<string, any> = {}
822
+
823
+ const router2: RRouter<
824
+ Filtered,
825
+ CTXMap,
826
+ Context
827
+ > = typedKeysOf(filtered).reduce((prev, cur) => {
828
+ prev[cur] = Object.assign((it: any) => {
829
+ accum[cur] = items[cur](it)
830
+ if (Object.keys(accum).length === total) return accum
831
+ return router2
832
+ }, {
833
+ raw: (it: any) => {
834
+ accum[cur] = items[cur](it)
835
+ if (Object.keys(accum).length === total) return accum
836
+ return router2
837
+ }
838
+ })
839
+ return prev
840
+ }, {} as any)
841
+
842
+ return Object.assign(effect, router2)
843
+ }
844
+
845
+ type HR<T> = T extends HttpRouter.HttpRouter<any, infer R> ? R : never
846
+ type HE<T> = T extends HttpRouter.HttpRouter<infer E, any> ? E : never
847
+
848
+ type RequestHandlersTest = {
849
+ [key: string]: {
850
+ Router: { router: Effect<HttpRouter.HttpRouter<any, any>, any, any> }
851
+ routes: Layer.Layer<any, any, any>
852
+ moduleName: string
853
+ }
854
+ }
855
+ function matchAll<T extends RequestHandlersTest, A, E, R>(
856
+ handlers: T,
857
+ requestLayer: Layer.Layer<A, E, R>
858
+ ) {
859
+ const routers = typedValuesOf(handlers)
860
+
861
+ const rootRouter = class extends HttpRouter.Tag("RootRouter")<
862
+ "RootRouter",
863
+ HR<Effect.Success<typeof handlers[keyof typeof handlers]["Router"]["router"]>>,
864
+ HE<Effect.Success<typeof handlers[keyof typeof handlers]["Router"]["router"]>>
865
+ >() {}
866
+
867
+ const r = rootRouter
868
+ .use((router) =>
869
+ Effect.gen(function*() {
870
+ for (const route of routers) {
871
+ yield* router.mount(
872
+ ("/rpc/" + route.moduleName) as any,
873
+ yield* route
874
+ .Router
875
+ .router
876
+ .pipe(Effect.map(HttpRouter.use(flow(Effect.provide(requestLayer))))) as any
877
+ )
878
+ }
879
+ })
880
+ )
881
+ .pipe(Layer.provide(routers.map((r) => r.routes).flat() as unknown as NonEmptyArray<Layer.Layer.Any>))
882
+
883
+ return {
884
+ layer: r as Layer.Layer<
885
+ never,
886
+ Layer.Layer.Error<typeof handlers[keyof typeof handlers]["routes"]>,
887
+ Layer.Layer.Context<typeof handlers[keyof typeof handlers]["routes"]>
888
+ >,
889
+ Router: rootRouter as any as HttpRouter.HttpRouter.TagClass<
890
+ "RootRouter",
891
+ "RootRouter",
892
+ HE<Effect.Success<typeof handlers[keyof typeof handlers]["Router"]["router"]>>,
893
+ R | Exclude<HR<Effect.Success<typeof handlers[keyof typeof handlers]["Router"]["router"]>>, A>
894
+ >
895
+ }
896
+ }
897
+
898
+ return { matchAll, matchFor }
899
+ }
900
+
901
+ export type MakeDeps<Make> = Make extends { readonly dependencies: ReadonlyArray<Layer.Layer.Any> }
902
+ ? Make["dependencies"][number]
903
+ : never
904
+
905
+ export type MakeErrors<Make> = Make extends { readonly effect: Effect<any, infer E, any> } ? E
906
+ : never
907
+
908
+ export type MakeContext<Make> = Make extends { readonly effect: Effect<any, any, infer R> } ? R
909
+ : never
910
+
911
+ export type MakeHandlers<Make, Handlers extends Record<string, any>> = Make extends
912
+ { readonly effect: Effect<{ [K in keyof Handlers]: AHandler<Handlers[K]> }, any, any> }
913
+ ? Effect.Success<Make["effect"]>
914
+ : never
915
+
916
+ /**
917
+ * @since 3.9.0
918
+ */
919
+ export type MakeDepsOut<Make> = Contravariant.Type<MakeDeps<Make>[Layer.LayerTypeId]["_ROut"]>