import * as RA from "../Array.js" import * as Cause from "../Cause.js" import * as Chunk from "../Chunk.js" import type * as Context from "../Context.js" import * as Effect from "../Effect.js" import type * as Either from "../Either.js" import { dual, pipe } from "../Function.js" import type * as Request from "../Request.js" import type * as RequestResolver from "../RequestResolver.js" import type { NoInfer } from "../Types.js" import * as core from "./core.js" import { invokeWithInterrupt, zipWithOptions } from "./fiberRuntime.js" import { complete } from "./request.js" /** @internal */ export const make = ( runAll: (requests: Array>) => Effect.Effect ): RequestResolver.RequestResolver => new core.RequestResolverImpl((requests) => runAll(requests.map((_) => _.map((_) => _.request)))) /** @internal */ export const makeWithEntry = ( runAll: (requests: Array>>) => Effect.Effect ): RequestResolver.RequestResolver => new core.RequestResolverImpl((requests) => runAll(requests)) /** @internal */ export const makeBatched = , R>( run: (requests: RA.NonEmptyArray) => Effect.Effect ): RequestResolver.RequestResolver => new core.RequestResolverImpl( (requests) => { if (requests.length > 1) { return core.forEachSequentialDiscard(requests, (block) => { const filtered = block.filter((_) => !_.state.completed).map((_) => _.request) if (!RA.isNonEmptyArray(filtered)) { return core.void } return invokeWithInterrupt(run(filtered), block) }) } else if (requests.length === 1) { const filtered = requests[0].filter((_) => !_.state.completed).map((_) => _.request) if (!RA.isNonEmptyArray(filtered)) { return core.void } return run(filtered) } return core.void } ) /** @internal */ export const around = dual< ( before: Effect.Effect, after: (a: A2) => Effect.Effect ) => ( self: RequestResolver.RequestResolver ) => RequestResolver.RequestResolver, ( self: RequestResolver.RequestResolver, before: Effect.Effect, after: (a: A2) => Effect.Effect ) => RequestResolver.RequestResolver >(3, (self, before, after) => new core.RequestResolverImpl( (requests) => core.acquireUseRelease( before, () => self.runAll(requests), after ), Chunk.make("Around", self, before, after) )) /** @internal */ export const aroundRequests = dual< ( before: (requests: ReadonlyArray>) => Effect.Effect, after: (requests: ReadonlyArray>, _: A2) => Effect.Effect ) => ( self: RequestResolver.RequestResolver ) => RequestResolver.RequestResolver, ( self: RequestResolver.RequestResolver, before: (requests: ReadonlyArray>) => Effect.Effect, after: (requests: ReadonlyArray>, _: A2) => Effect.Effect ) => RequestResolver.RequestResolver >(3, (self, before, after) => new core.RequestResolverImpl( (requests) => { const flatRequests = requests.flatMap((chunk) => chunk.map((entry) => entry.request)) return core.acquireUseRelease( before(flatRequests), () => self.runAll(requests), (a2) => after(flatRequests, a2) ) }, Chunk.make("AroundRequests", self, before, after) )) /** @internal */ export const batchN = dual< (n: number) => ( self: RequestResolver.RequestResolver ) => RequestResolver.RequestResolver, ( self: RequestResolver.RequestResolver, n: number ) => RequestResolver.RequestResolver >(2, ( self: RequestResolver.RequestResolver, n: number ): RequestResolver.RequestResolver => new core.RequestResolverImpl( (requests) => { return n < 1 ? core.die(new Cause.IllegalArgumentException("RequestResolver.batchN: n must be at least 1")) : self.runAll( Array.from(Chunk.map( RA.reduce( requests, Chunk.empty>>(), (acc, chunk) => Chunk.appendAll(acc, Chunk.chunksOf(Chunk.unsafeFromArray(chunk), n)) ), (chunk) => Array.from(chunk) )) ) }, Chunk.make("BatchN", self, n) )) /** @internal */ export const mapInputContext = dual< ( f: (context: Context.Context) => Context.Context ) => >( self: RequestResolver.RequestResolver ) => RequestResolver.RequestResolver, , R0>( self: RequestResolver.RequestResolver, f: (context: Context.Context) => Context.Context ) => RequestResolver.RequestResolver >(2, , R0>( self: RequestResolver.RequestResolver, f: (context: Context.Context) => Context.Context ) => new core.RequestResolverImpl( (requests) => core.mapInputContext( self.runAll(requests), (context: Context.Context) => f(context) ), Chunk.make("MapInputContext", self, f) )) /** @internal */ export const eitherWith = dual< < A extends Request.Request, R2, B extends Request.Request, C extends Request.Request >( that: RequestResolver.RequestResolver, f: (_: Request.Entry) => Either.Either, Request.Entry> ) => ( self: RequestResolver.RequestResolver ) => RequestResolver.RequestResolver, < R, A extends Request.Request, R2, B extends Request.Request, C extends Request.Request >( self: RequestResolver.RequestResolver, that: RequestResolver.RequestResolver, f: (_: Request.Entry) => Either.Either, Request.Entry> ) => RequestResolver.RequestResolver >(3, < R, A extends Request.Request, R2, B extends Request.Request, C extends Request.Request >( self: RequestResolver.RequestResolver, that: RequestResolver.RequestResolver, f: (_: Request.Entry) => Either.Either, Request.Entry> ) => new core.RequestResolverImpl( (batch) => core.forEachSequential(batch, (requests) => { const [as, bs] = pipe( requests, RA.partitionMap(f) ) return zipWithOptions( self.runAll(Array.of(as)), that.runAll(Array.of(bs)), () => void 0, { concurrent: true } ) }), Chunk.make("EitherWith", self, that, f) )) /** @internal */ export const fromFunction = >( f: (request: A) => Request.Request.Success ): RequestResolver.RequestResolver => makeBatched((requests: RA.NonEmptyArray) => core.forEachSequentialDiscard( requests, (request) => complete(request, core.exitSucceed(f(request)) as any) ) ).identified("FromFunction", f) /** @internal */ export const fromFunctionBatched = >( f: (chunk: RA.NonEmptyArray) => Iterable> ): RequestResolver.RequestResolver => makeBatched((as: RA.NonEmptyArray) => Effect.forEach( f(as), (res, i) => complete(as[i], core.exitSucceed(res) as any), { discard: true } ) ).identified("FromFunctionBatched", f) /** @internal */ export const fromEffect = >( f: (a: A) => Effect.Effect, Request.Request.Error, R> ): RequestResolver.RequestResolver => makeBatched((requests: RA.NonEmptyArray) => Effect.forEach( requests, (a) => Effect.flatMap(Effect.exit(f(a)), (e) => complete(a, e as any)), { concurrency: "unbounded", discard: true } ) ).identified("FromEffect", f) /** @internal */ export const fromEffectTagged = < A extends Request.Request & { readonly _tag: string } >() => < Fns extends { readonly [Tag in A["_tag"]]: [Extract] extends [infer Req] ? Req extends Request.Request ? (requests: Array) => Effect.Effect, ReqE, any> : never : never } >( fns: Fns ): RequestResolver.RequestResolver< A, ReturnType extends Effect.Effect ? R : never > => makeBatched((requests: RA.NonEmptyArray) => { const grouped: Record> = {} const tags: Array = [] for (let i = 0, len = requests.length; i < len; i++) { if (tags.includes(requests[i]._tag)) { grouped[requests[i]._tag].push(requests[i]) } else { grouped[requests[i]._tag] = [requests[i]] tags.push(requests[i]._tag) } } return Effect.forEach( tags, (tag) => Effect.matchCauseEffect((fns[tag] as any)(grouped[tag]) as Effect.Effect, unknown, unknown>, { onFailure: (cause) => Effect.forEach(grouped[tag], (req) => complete(req, core.exitFail(cause) as any), { discard: true }), onSuccess: (res) => Effect.forEach(grouped[tag], (req, i) => complete(req, core.exitSucceed(res[i]) as any), { discard: true }) }), { concurrency: "unbounded", discard: true } ) }).identified("FromEffectTagged", fns) /** @internal */ export const never: RequestResolver.RequestResolver = make(() => Effect.never).identified("Never") /** @internal */ export const provideContext = dual< ( context: Context.Context ) => >( self: RequestResolver.RequestResolver ) => RequestResolver.RequestResolver, >( self: RequestResolver.RequestResolver, context: Context.Context ) => RequestResolver.RequestResolver >(2, (self, context) => mapInputContext( self, (_: Context.Context) => context ).identified("ProvideContext", self, context)) /** @internal */ export const race = dual< , R2>( that: RequestResolver.RequestResolver ) => , R>( self: RequestResolver.RequestResolver ) => RequestResolver.RequestResolver, , R, A2 extends Request.Request, R2>( self: RequestResolver.RequestResolver, that: RequestResolver.RequestResolver ) => RequestResolver.RequestResolver >(2, ( self: RequestResolver.RequestResolver, that: RequestResolver.RequestResolver ) => new core.RequestResolverImpl((requests) => Effect.race( self.runAll(requests as Array>>), that.runAll(requests as Array>>) ) ).identified("Race", self, that))