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