import { flatten, identity, last, range } from 'remeda' import { Mapper } from './Mapper' import { Modifier, ModifierP, ModifierV, ModifierVP } from './Modifier' import { NonEmptyArray } from './NonEmptyArray' import { ResultsAccumulator } from './ResultsAccumulator' import { AlwaysTrueTypeGuard } from './typescript' export async function mapAsync(values: In[], mapper: (value: In, ...args: Args) => Promise, ...args: Args) { return Promise.all(values.map(value => mapper(value, ...args))) } export async function flatMapAsync(values: In[], mapper: (value: In, ...args: Args) => Promise, ...args: Args) { const results = await mapAsync(values, mapper, ...args) return flatten(results) } export async function mapIndexAsync(values: In[], mapper: (value: In, index: number, ...args: Args) => Promise, ...args: Args) { return Promise.all(values.map((value, index) => mapper(value, index, ...args))) } export async function repeatAsync(count: number, mapper: (index: number, ...args: Args) => Promise, ...args: Args) { return mapAsync(range(0, count), mapper, ...args) } export async function parMap(mapper: (value: In, ...args: Args) => Promise, values: In[], ...args: Args) { return parallel(values.map(value => mapper(value, ...args))) } export async function parallelMap(values: In[], mapper: (value: In, ...args: Args) => Promise, ...args: Args) { return parallel(values.map(value => mapper(value, ...args))) } export async function parallelMapGet(getter: (...args: Args) => Promise, mapper: (value: In, ...args: Args) => Promise, ...args: Args) { const values = await getter(...args) return parallel(values.map(value => mapper(value, ...args))) } export async function parallelFlatMap(values: In[], mapper: (value: In, ...args: Args) => Promise, ...args: Args) { const resultsArray = await Promise.all(values.map(value => mapper(value, ...args))) return flatten(resultsArray) } export async function allFlat(values: Promise[]) { return flatten(await Promise.all(values)) } export async function parallelMapIndex(values: In[], mapper: (value: In, index: number, ...args: Args) => Promise, ...args: Args) { return parallel(values.map((value, index) => mapper(value, index, ...args))) } export async function parallelMapEvery(values: In[], mapper: (value: In, ...args: Args) => Promise, ...args: Args) { const results = await parallelMap(values, mapper, ...args) return results.every(identity) } export async function sequentialMap(values: In[], mapper: (value: In, ...args: Args) => Promise, ...args: Args) { const results: Out[] = [] for (const value of values) { results.push(await mapper(value, ...args)) } return results } export const mapMaybeIntoAccumulator = (mapper: Mapper) => (values: A[]) => { return values.reduce>(function (accumulator, value) { const result = mapper(value) if (result) { accumulator.successes.push(result) } else { accumulator.failures.push(value) } return accumulator }, { successes: [], failures: [] }) } export async function sequentialMapAsyncGen(values: AsyncGenerator, mapper: (value: In, ...args: Args) => Promise, ...args: Args) { const results: Out[] = [] for await (const value of values) { results.push(await mapper(value, ...args)) } return results } export async function parallelMapAsyncGen(values: AsyncGenerator, mapper: (value: In, ...args: Args) => Promise, ...args: Args) { const promises: Promise[] = [] for await (const value of values) { promises.push(mapper(value, ...args)) } return parallel(promises) } export const sequentialReduce = (modifiers: Modifier[]) => (value: Val) => { return modifiers.reduce((value, modifier) => modifier(value), value) } export const chain = sequentialReduce /** * Map multiple modifiers over a single value * V = Variadic */ export const sequentialReduceV = (modifiers: ModifierV[], ...args: Args) => (value: Val) => { return modifiers.reduce(($value, modifier) => { return modifier($value, ...args) }, value) } export const sequentialReducePushV = (modifiers: ModifierV[], ...args: Args) => (value: Val) => { const initial: NonEmptyArray = [value] return modifiers.reduce>((values: NonEmptyArray, modifier): NonEmptyArray => { return [...values, modifier(last(values), ...args)] }, initial) } export const sequentialReduceP = (modifiers: ModifierP[]) => async (value: Val) => { return modifiers.reduce>(async ($value, modifier) => modifier(await $value), Promise.resolve(value)) } export const pipeP = sequentialReduceP export const sequentialReduceVP = (modifiers: ModifierVP[], ...args: Args) => async (value: Val) => { return modifiers.reduce>(async ($value, modifier) => { return modifier(await $value, ...args) }, Promise.resolve(value)) } export const parSeqMapVP = (isDepthFirst: boolean) => (modifiers: ModifierVP[], ...args: Args) => async (values: Val[]) => { if (isDepthFirst) { return sequentialMap(values, value => { return sequentialReduceVP(modifiers, ...args)(value) }) } else { return sequentialMap(modifiers, modifier => { return parallelMap(values, modifier, ...args) }) } } export function parallel(promises: [Promise, Promise, Promise, Promise, Promise

, Promise, Promise, Promise, Promise, Promise, Promise, Promise]): Promise<[A, M, K, N, P, D, G, C, O, L, Q, R]>; export function parallel(promises: [Promise, Promise, Promise, Promise, Promise

, Promise, Promise, Promise, Promise, Promise, Promise]): Promise<[A, M, K, N, P, D, G, C, O, L, Q]>; export function parallel(promises: [Promise, Promise, Promise, Promise, Promise

, Promise, Promise, Promise, Promise, Promise]): Promise<[A, M, K, N, P, D, G, C, O, L]>; export function parallel(promises: [Promise, Promise, Promise, Promise, Promise

, Promise, Promise, Promise, Promise]): Promise<[A, M, K, N, P, D, G, C, O]>; export function parallel(promises: [Promise, Promise, Promise, Promise, Promise

, Promise, Promise, Promise]): Promise<[A, M, K, N, P, D, G, C]>; export function parallel(promises: [Promise, Promise, Promise, Promise, Promise

, Promise, Promise]): Promise<[A, M, K, N, P, D, G]>; export function parallel(promises: [Promise, Promise, Promise, Promise, Promise

, Promise]): Promise<[A, M, K, N, P, D]>; export function parallel(promises: [Promise, Promise, Promise, Promise, Promise

]): Promise<[A, M, K, N, P]>; export function parallel(promises: [Promise, Promise, Promise, Promise]): Promise<[A, M, K, N]>; export function parallel(promises: [Promise, Promise, Promise]): Promise<[A, M, K]>; export function parallel(promises: [Promise, Promise]): Promise<[A, M]>; export function parallel(promises: Promise[]): Promise; export function parallel(promises: Promise[]): Promise { return Promise.allSettled(promises).then(rethrowAny) } export async function together(mappers: Array<(...args: Args) => Promise>, ...args: Args) { return parallel(mappers.map(mapper => mapper(...args))) } export const rethrowErrors = (isError: (e: unknown) => e is Err) => (results: PromiseSettledResult[]) => { const reasons = [] const values = [] for (const result of results) { if (result.status === 'fulfilled') { values.push(result.value) } else { if (isError(result.reason)) { reasons.push(result.reason) } else { throw result.reason } } } if (reasons.length) { throw flatten(reasons) } else { return values } } export const rethrowAny = rethrowErrors(AlwaysTrueTypeGuard) /** * Await promise resolution in the background without blocking */ export const toBackground = (promise: Promise): void => {} export const trap = (promise: Promise) => promise.catch((reason) => { console.error(reason) process.exit(1) })