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