rubico
Version:
[a]synchronous functional programming
1,894 lines (1,694 loc) • 90 kB
JavaScript
/**
* rubico v2.6.2
* https://github.com/a-synchronous/rubico
* (c) 2019-2024 Richard Tong
* rubico may be freely distributed under the MIT license.
*/
(function (root, rubico) {
for (const functionName in rubico) {
root[functionName] = rubico[functionName]
}
}(typeof globalThis == 'object' ? globalThis : this, (function () { 'use strict'
const isArray = Array.isArray
const isPromise = value => value != null && typeof value.then == 'function'
const areAnyValuesPromises = function (values) {
if (isArray(values)) {
const length = values.length
let index = -1
while (++index < length) {
const value = values[index]
if (isPromise(value)) {
return true
}
}
return false
}
for (const key in values) {
const value = values[key]
if (isPromise(value)) {
return true
}
}
return false
}
const promiseAll = Promise.all.bind(Promise)
const funcConcat = (
funcA, funcB,
) => function pipedFunction(...args) {
const intermediate = funcA(...args)
return isPromise(intermediate)
? intermediate.then(funcB)
: funcB(intermediate)
}
const funcApply = (func, args) => func(...args)
const __ = Symbol.for('placeholder')
// argument resolver for curry2
const curry2ResolveArg0 = (
baseFunc, arg1,
) => function arg0Resolver(arg0) {
return baseFunc(arg0, arg1)
}
// argument resolver for curry2
const curry2ResolveArg1 = (
baseFunc, arg0,
) => function arg1Resolver(arg1) {
return baseFunc(arg0, arg1)
}
const curry2 = function (baseFunc, arg0, arg1) {
return arg0 == __
? curry2ResolveArg0(baseFunc, arg1)
: curry2ResolveArg1(baseFunc, arg0)
}
const pipe = function (...args) {
const funcs = args.pop()
const pipeline = funcs.reduce(funcConcat)
if (args.length == 0) {
return pipeline
}
if (areAnyValuesPromises(args)) {
return promiseAll(args).then(curry2(funcApply, pipeline, __))
}
return pipeline(...args)
}
const compose = function (...args) {
const funcs = args.pop()
const composition = funcs.reduceRight(funcConcat)
if (args.length == 0) {
return composition
}
if (areAnyValuesPromises(args)) {
return promiseAll(args).then(curry2(funcApply, composition, __))
}
return composition(...args)
}
const always = value => function getter() { return value }
const thunkifyArgs = (func, args) => function thunk() {
return func(...args)
}
const thunkConditional = (
conditionalExpression, thunkOnTruthy, thunkOnFalsy,
) => conditionalExpression ? thunkOnTruthy() : thunkOnFalsy()
// argument resolver for curry3
const curry3ResolveArg0 = (
baseFunc, arg1, arg2,
) => function arg0Resolver(arg0) {
return baseFunc(arg0, arg1, arg2)
}
// argument resolver for curry3
const curry3ResolveArg1 = (
baseFunc, arg0, arg2,
) => function arg1Resolver(arg1) {
return baseFunc(arg0, arg1, arg2)
}
// argument resolver for curry3
const curry3ResolveArg2 = (
baseFunc, arg0, arg1,
) => function arg2Resolver(arg2) {
return baseFunc(arg0, arg1, arg2)
}
const curry3 = function (baseFunc, arg0, arg1, arg2) {
if (arg0 == __) {
return curry3ResolveArg0(baseFunc, arg1, arg2)
}
if (arg1 == __) {
return curry3ResolveArg1(baseFunc, arg0, arg2)
}
return curry3ResolveArg2(baseFunc, arg0, arg1)
}
// argument resolver for curryArgs2
const curryArgs2ResolveArgs0 = (
baseFunc, arg1, arg2,
) => function args0Resolver(...args) {
return baseFunc(args, arg1)
}
// argument resolver for curryArgs2
const curryArgs2ResolveArgs1 = (
baseFunc, arg0, arg2,
) => function arg1Resolver(...args) {
return baseFunc(arg0, args)
}
const curryArgs2 = function (baseFunc, arg0, arg1) {
if (arg0 == __) {
return curryArgs2ResolveArgs0(baseFunc, arg1)
}
return curryArgs2ResolveArgs1(baseFunc, arg0)
}
// argument resolver for curryArgs3
const curryArgs3ResolveArgs0 = (
baseFunc, arg1, arg2,
) => function args0Resolver(...args) {
return baseFunc(args, arg1, arg2)
}
// argument resolver for curryArgs3
const curryArgs3ResolveArgs1 = (
baseFunc, arg0, arg2,
) => function arg1Resolver(...args) {
return baseFunc(arg0, args, arg2)
}
// argument resolver for curryArgs3
const curryArgs3ResolveArgs2 = (
baseFunc, arg0, arg1,
) => function arg2Resolver(...args) {
return baseFunc(arg0, arg1, args)
}
const curryArgs3 = function (baseFunc, arg0, arg1, arg2) {
if (arg0 == __) {
return curryArgs3ResolveArgs0(baseFunc, arg1, arg2)
}
if (arg1 == __) {
return curryArgs3ResolveArgs1(baseFunc, arg0, arg2)
}
return curryArgs3ResolveArgs2(baseFunc, arg0, arg1)
}
// _tap(args Array, f function) -> Promise|any
const _tap = function (args, f) {
const result = args[0],
call = f(...args)
return isPromise(call) ? call.then(always(result)) : result
}
const tap = function (...args) {
const f = args.pop()
if (args.length == 0) {
return curryArgs2(_tap, __, f)
}
if (areAnyValuesPromises(args)) {
return promiseAll(args).then(curry2(_tap, __, f))
}
return _tap(args, f)
}
const _tapIf = function (predicate, f, args) {
const b = predicate(...args)
if (isPromise(b)) {
return b.then(curry3(
thunkConditional,
__,
thunkifyArgs(tap(f), args),
always(args[0]),
))
}
if (b) {
const execution = f(...args)
if (isPromise(execution)) {
return execution.then(always(args[0]))
}
}
return args[0]
}
tap.if = function (...args) {
if (args.length == 2) {
return curryArgs3(_tapIf, args[0], args[1], __)
}
const argsLength = args.length
const f = args[argsLength - 1]
const predicate = args[argsLength - 2]
const argValues = args.slice(0, -2)
if (areAnyValuesPromises(argValues)) {
return promiseAll(argValues).then(curry3(_tapIf, predicate, f, __))
}
return _tapIf(predicate, f, args)
}
const areAllValuesNonfunctions = function (values) {
if (isArray(values)) {
const length = values.length
let index = -1
while (++index < length) {
if (typeof values[index] == 'function') {
return false
}
}
return true
}
for (const key in values) {
if (typeof values[key] == 'function') {
return false
}
}
return true
}
const promiseObjectAllExecutor = object => function executor(resolve) {
const result = {}
let numPromises = 0
for (const key in object) {
const value = object[key]
if (isPromise(value)) {
numPromises += 1
value.then((key => function (res) {
result[key] = res
numPromises -= 1
if (numPromises == 0) {
resolve(result)
}
})(key))
} else {
result[key] = value
}
}
if (numPromises == 0) {
resolve(result)
}
}
const promiseObjectAll = object => new Promise(promiseObjectAllExecutor(object))
const functionArrayAll = function (funcs, args) {
const funcsLength = funcs.length,
result = Array(funcsLength)
let funcsIndex = -1, isAsync = false
while (++funcsIndex < funcsLength) {
const f = funcs[funcsIndex]
const resultItem = typeof f == 'function' ? f(...args) : f
if (isPromise(resultItem)) {
isAsync = true
}
result[funcsIndex] = resultItem
}
return isAsync ? promiseAll(result) : result
}
// argument resolver for curry4
const curry4ResolveArg0 = (
baseFunc, arg1, arg2, arg3,
) => function arg0Resolver(arg0) {
return baseFunc(arg0, arg1, arg2, arg3)
}
// argument resolver for curry4
const curry4ResolveArg1 = (
baseFunc, arg0, arg2, arg3,
) => function arg1Resolver(arg1) {
return baseFunc(arg0, arg1, arg2, arg3)
}
// argument resolver for curry4
const curry4ResolveArg2 = (
baseFunc, arg0, arg1, arg3,
) => function arg2Resolver(arg2) {
return baseFunc(arg0, arg1, arg2, arg3)
}
// argument resolver for curry4
const curry4ResolveArg3 = (
baseFunc, arg0, arg1, arg2,
) => function arg3Resolver(arg3) {
return baseFunc(arg0, arg1, arg2, arg3)
}
const curry4 = function (baseFunc, arg0, arg1, arg2, arg3) {
if (arg0 == __) {
return curry4ResolveArg0(baseFunc, arg1, arg2, arg3)
}
if (arg1 == __) {
return curry4ResolveArg1(baseFunc, arg0, arg2, arg3)
}
if (arg2 == __) {
return curry4ResolveArg2(baseFunc, arg0, arg1, arg3)
}
return curry4ResolveArg3(baseFunc, arg0, arg1, arg2)
}
const objectSet = function (object, property, value) {
object[property] = value
return object
}
const asyncFunctionArrayAllSeries = async function (funcs, args, result, funcsIndex) {
const funcsLength = funcs.length
while (++funcsIndex < funcsLength) {
const resultItem = funcs[funcsIndex](...args)
result[funcsIndex] = isPromise(resultItem) ? await resultItem : resultItem
}
return result
}
const functionArrayAllSeries = function (funcs, args) {
const funcsLength = funcs.length, result = []
let funcsIndex = -1
while (++funcsIndex < funcsLength) {
const resultItem = funcs[funcsIndex](...args)
if (isPromise(resultItem)) {
return resultItem.then(funcConcat(
curry3(objectSet, result, funcsIndex, __),
curry4(asyncFunctionArrayAllSeries, funcs, args, __, funcsIndex)))
}
result[funcsIndex] = resultItem
}
return result
}
const functionObjectAll = function (funcs, args) {
const result = {}, promises = []
for (const key in funcs) {
const f = funcs[key]
const resultItem = typeof f == 'function' ? f(...args) : f
if (isPromise(resultItem)) {
promises.push(resultItem.then(curry3(objectSet, result, key, __)))
} else {
result[key] = resultItem
}
}
return promises.length == 0 ? result : promiseAll(promises).then(always(result))
}
const _allValues = function (values) {
if (isArray(values)) {
return areAnyValuesPromises(values)
? promiseAll(values)
: values
}
return areAnyValuesPromises(values)
? promiseObjectAll(values)
: values
}
const all = function (...args) {
if (args.length == 1) {
const resolversOrValues = args[0]
if (isPromise(resolversOrValues)) {
return resolversOrValues.then(_allValues)
}
if (areAllValuesNonfunctions(resolversOrValues)) {
return _allValues(resolversOrValues)
}
return isArray(resolversOrValues)
? curryArgs2(functionArrayAll, resolversOrValues, __)
: curryArgs2(functionObjectAll, resolversOrValues, __)
}
const resolversOrValues = args[args.length - 1]
const argValues = args.slice(0, -1)
if (areAnyValuesPromises(argValues)) {
return isArray(resolversOrValues)
? promiseAll(argValues)
.then(curry2(functionArrayAll, resolversOrValues, __))
: promiseAll(argValues)
.then(curry2(functionObjectAll, resolversOrValues, __))
}
return isArray(resolversOrValues)
? functionArrayAll(resolversOrValues, argValues)
: functionObjectAll(resolversOrValues, argValues)
/*
////////////////////////////////////////////////////////////////
const funcs = args.pop()
if (args.length == 0) {
return isArray(funcs)
? curryArgs2(functionArrayAll, funcs, __)
: curryArgs2(functionObjectAll, funcs, __)
}
if (areAnyValuesPromises(args)) {
return isArray(funcs)
? promiseAll(args).then(curry2(functionArrayAll, funcs, __))
: promiseAll(args).then(curry2(functionObjectAll, funcs, __))
}
return isArray(funcs)
? functionArrayAll(funcs, args)
: functionObjectAll(funcs, args)
*/
}
all.series = function allSeries(...args) {
const funcs = args.pop()
if (args.length == 0) {
return curryArgs2(functionArrayAllSeries, funcs, __)
}
if (areAnyValuesPromises(args)) {
return promiseAll(args).then(curry2(functionArrayAllSeries, funcs, __))
}
return functionArrayAllSeries(funcs, args)
}
const objectAssign = Object.assign
// _assign(object Object, funcs Object<function>) -> Promise|Object
const _assign = function (object, funcs) {
const result = functionObjectAll(funcs, [object])
return isPromise(result)
? result.then(curry3(objectAssign, {}, object, __))
: ({ ...object, ...result })
}
const assign = function (arg0, arg1) {
if (arg1 == null) {
return curry2(_assign, __, arg0)
}
return isPromise(arg0)
? arg0.then(curry2(_assign, __, arg1))
: _assign(arg0, arg1)
}
const catcherApply = function (catcher, err, args) {
return catcher(err, ...args)
}
// _tryCatch(tryer function, catcher function, args Array) -> Promise
const _tryCatch = function (tryer, catcher, args) {
try {
const result = tryer(...args)
return isPromise(result)
? result.catch(curry3(catcherApply, catcher, __, args))
: result
} catch (error) {
return catcher(error, ...args)
}
}
const tryCatch = function (...args) {
if (args.length > 2) {
const catcher = args.pop(),
tryer = args.pop()
if (areAnyValuesPromises(args)) {
return promiseAll(args)
.then(curry3(_tryCatch, tryer, catcher, __))
}
return _tryCatch(tryer, catcher, args)
}
const tryer = args[0],
catcher = args[1]
return function tryCatcher(...args) {
return _tryCatch(tryer, catcher, args)
}
}
const thunkify3 = (func, arg0, arg1, arg2) => function thunk() {
return func(arg0, arg1, arg2)
}
const arrayConditional = function (array, args, funcsIndex) {
const lastIndex = array.length - 1
while ((funcsIndex += 2) < lastIndex) {
const predicate = array[funcsIndex],
resolverOrValue = array[funcsIndex + 1]
const predication = typeof predicate == 'function'
? predicate(...args)
: predicate
if (isPromise(predication)) {
return predication.then(curry3(
thunkConditional,
__,
typeof resolverOrValue == 'function'
? thunkifyArgs(resolverOrValue, args)
: always(resolverOrValue),
thunkify3(arrayConditional, array, args, funcsIndex),
))
}
if (predication) {
return typeof resolverOrValue == 'function'
? resolverOrValue(...args)
: resolverOrValue
}
}
// even number of array
if (funcsIndex == array.length) {
return undefined
}
const defaultResolverOrValue = array[lastIndex]
return typeof defaultResolverOrValue == 'function'
? defaultResolverOrValue(...args)
: defaultResolverOrValue
}
const thunkify2 = (func, arg0, arg1) => function thunk() {
return func(arg0, arg1)
}
const nonfunctionsConditional = function (array, index) {
const length = array.length,
lastIndex = length - 1
while ((index += 2) < lastIndex) {
const predication = array[index],
value = array[index + 1]
if (isPromise(predication)) {
return predication.then(curry3(
thunkConditional,
__,
always(value),
thunkify2(nonfunctionsConditional, array, index),
))
}
if (predication) {
return value
}
}
// even number of array values
if (index == length) {
return undefined
}
return array[index]
}
const switchCase = (...args) => {
const values = args.pop()
if (areAllValuesNonfunctions(values)) {
return nonfunctionsConditional(values, -2)
}
if (args.length == 0) {
return curryArgs3(arrayConditional, values, __, -2)
}
if (areAnyValuesPromises(args)) {
return promiseAll(args).then(curry3(arrayConditional, values, __, -2))
}
return arrayConditional(values, args, -2)
}
const symbolIterator = Symbol.iterator
const MappingIterator = (iterator, mapper) => ({
toString() {
return '[object MappingIterator]'
},
[symbolIterator]() {
return this
},
next() {
const iteration = iterator.next()
return iteration.done ? iteration
: { value: mapper(iteration.value), done: false }
},
})
const NextIteration = value => ({ value, done: false })
const symbolAsyncIterator = Symbol.asyncIterator
const MappingAsyncIterator = (asyncIterator, mapper) => ({
[symbolAsyncIterator]() {
return this
},
async next() {
const iteration = await asyncIterator.next()
if (iteration.done) {
return iteration
}
const mapped = mapper(iteration.value)
return isPromise(mapped)
? mapped.then(NextIteration)
: { value: mapped, done: false }
}
})
const isObject = value => {
if (value == null) {
return false
}
const typeofValue = typeof value
return (typeofValue == 'object') || (typeofValue == 'function')
}
const arrayMap = function (array, mapper) {
const arrayLength = array.length,
result = Array(arrayLength)
let index = -1,
isAsync = false
while (++index < arrayLength) {
const resultItem = mapper(array[index], index, array)
if (isPromise(resultItem)) {
isAsync = true
}
result[index] = resultItem
}
return isAsync ? promiseAll(result) : result
}
const callPropUnary = (value, property, arg0) => value[property](arg0)
const stringMap = function (string, mapper) {
const result = arrayMap(string, mapper)
return isPromise(result)
? result.then(curry3(callPropUnary, __, 'join', ''))
: result.join('')
}
const setMap = function (set, mapper) {
const result = new Set(),
promises = []
for (const item of set) {
const resultItem = mapper(item, item, set)
if (isPromise(resultItem)) {
promises.push(resultItem.then(curry3(callPropUnary, result, 'add', __)))
} else {
result.add(resultItem)
}
}
return promises.length == 0
? result
: promiseAll(promises).then(always(result))
}
const callPropBinary = (value, property, arg0, arg1) => value[property](arg0, arg1)
const mapMap = function (value, mapper) {
const result = new Map(),
promises = []
for (const [key, item] of value) {
const resultItem = mapper(item, key, value)
if (isPromise(resultItem)) {
promises.push(resultItem.then(
curry4(callPropBinary, result, 'set', key, __)))
} else {
result.set(key, resultItem)
}
}
return promises.length == 0
? result
: promiseAll(promises).then(always(result))
}
const objectMap = function (object, mapper) {
const result = {}
let isAsync = false
for (const key in object) {
const resultItem = mapper(object[key], key, object)
if (isPromise(resultItem)) {
isAsync = true
}
result[key] = resultItem
}
return isAsync ? promiseObjectAll(result) : result
}
const arrayMapSeriesAsync = async function (
array, mapper, result, index,
) {
const arrayLength = array.length
while (++index < arrayLength) {
const resultItem = mapper(array[index], index)
result[index] = isPromise(resultItem) ? await resultItem : resultItem
}
return result
}
const arrayMapSeries = function (array, mapper) {
const arrayLength = array.length,
result = Array(arrayLength)
let index = -1
while (++index < arrayLength) {
const resultItem = mapper(array[index], index)
if (isPromise(resultItem)) {
return resultItem.then(funcConcat(
curry3(objectSet, result, index, __),
curry4(arrayMapSeriesAsync, array, mapper, __, index)))
}
result[index] = resultItem
}
return result
}
const stringMapSeries = function (string, mapper) {
const result = arrayMapSeries(string, mapper)
return isPromise(result)
? result.then(curry3(callPropUnary, __, 'join', ''))
: result.join('')
}
const thunkify4 = (func, arg0, arg1, arg2, arg3) => function thunk() {
return func(arg0, arg1, arg2, arg3)
}
// _objectMapSeriesAsync(
// object Object,
// f function,
// result Object,
// doneKeys Object
// ) -> Promise<object>
const _objectMapSeriesAsync = async function (object, f, result, doneKeys) {
for (const key in object) {
if (key in doneKeys) {
continue
}
let resultItem = f(object[key])
if (isPromise(resultItem)) {
resultItem = await resultItem
}
result[key] = resultItem
}
return result
}
const objectMapSeries = function (object, f) {
const result = {}
const doneKeys = {}
for (const key in object) {
doneKeys[key] = true
const resultItem = f(object[key], key, object)
if (isPromise(resultItem)) {
return resultItem.then(funcConcat(
curry3(objectSet, result, key, __),
thunkify4(_objectMapSeriesAsync, object, f, result, doneKeys),
))
}
result[key] = resultItem
}
return result
}
const setAdd = function (set, value) {
set.add(value)
return set
}
// _setMapSeriesAsync(
// iterator Iterator,
// f function,
// result Set,
// ) -> Promise<Set>
const _setMapSeriesAsync = async function (iterator, f, result) {
let iteration = iterator.next()
while (!iteration.done) {
let resultItem = f(iteration.value)
if (isPromise(resultItem)) {
resultItem = await resultItem
}
result.add(resultItem)
iteration = iterator.next()
}
return result
}
const setMapSeries = function (set, f) {
const result = new Set()
const iterator = set[symbolIterator]()
let iteration = iterator.next()
while (!iteration.done) {
const resultItem = f(iteration.value)
if (isPromise(resultItem)) {
return resultItem.then(funcConcat(
curry2(setAdd, result, __),
thunkify3(_setMapSeriesAsync, iterator, f, result),
))
}
result.add(resultItem)
iteration = iterator.next()
}
return result
}
const mapSet = function setting(source, key, value) {
return source.set(key, value)
}
// _mapMapSeriesAsync(
// iterator Iterator,
// f function,
// result Map,
// ) -> Promise<Map>
const _mapMapSeriesAsync = async function (iterator, f, result) {
let iteration = iterator.next()
while (!iteration.done) {
let resultItem = f(iteration.value[1])
if (isPromise(resultItem)) {
resultItem = await resultItem
}
result.set(iteration.value[0], resultItem)
iteration = iterator.next()
}
return result
}
const mapMapSeries = function (map, f) {
const result = new Map()
const iterator = map[symbolIterator]()
let iteration = iterator.next()
while (!iteration.done) {
const key = iteration.value[0]
const resultItem = f(iteration.value[1])
if (isPromise(resultItem)) {
return resultItem.then(funcConcat(
curry3(mapSet, result, key, __),
thunkify3(_mapMapSeriesAsync, iterator, f, result),
))
}
result.set(key, resultItem)
iteration = iterator.next()
}
return result
}
const tapSync = func => function tapping(...args) {
func(...args)
return args[0]
}
const promiseRace = Promise.race.bind(Promise)
const arrayMapPoolAsync = async function (
array, f, concurrencyLimit, result, index, promises,
) {
const arrayLength = array.length
while (++index < arrayLength) {
if (promises.size >= concurrencyLimit) {
await promiseRace(promises)
}
const resultItem = f(array[index])
if (isPromise(resultItem)) {
const selfDeletingPromise = resultItem.then(
tapSync(() => promises.delete(selfDeletingPromise)))
promises.add(selfDeletingPromise)
result[index] = selfDeletingPromise
} else {
result[index] = resultItem
}
}
return promiseAll(result)
}
const arrayMapPool = function (array, concurrency, f) {
const arrayLength = array.length,
result = Array(arrayLength)
let index = -1
while (++index < arrayLength) {
const resultItem = f(array[index])
if (isPromise(resultItem)) {
const promises = new Set(),
selfDeletingPromise = resultItem.then(
tapSync(() => promises.delete(selfDeletingPromise)))
promises.add(selfDeletingPromise)
result[index] = selfDeletingPromise
return arrayMapPoolAsync(
array, f, concurrency, result, index, promises)
}
result[index] = resultItem
}
return result
}
const stringMapPool = function (s, concurrency, f) {
const result = arrayMapPool(s, concurrency, f)
return isPromise(result)
? result.then(curry3(callPropUnary, __, 'join', ''))
: result.join('')
}
const _setMapPoolAsync = async function (
s, iterator, concurrency, f, result, promises,
) {
let iteration = iterator.next()
while (!iteration.done) {
if (promises.size >= concurrency) {
await promiseRace(promises)
}
const resultItem = f(iteration.value, iteration.value, s)
if (isPromise(resultItem)) {
const selfDeletingPromise = resultItem.then(resolvedValue => {
promises.delete(selfDeletingPromise)
result.add(resolvedValue)
})
promises.add(selfDeletingPromise)
} else {
result.add(resultItem)
}
iteration = iterator.next()
}
if (promises.size > 0) {
await promiseAll(promises)
}
return result
}
const setMapPool = function (s, concurrency, f) {
const result = new Set()
const iterator = s[symbolIterator]()
let iteration = iterator.next()
while (!iteration.done) {
const resultItem = f(iteration.value, iteration.value, s)
if (isPromise(resultItem)) {
const promises = new Set()
const selfDeletingPromise = resultItem.then(resolvedValue => {
promises.delete(selfDeletingPromise)
result.add(resolvedValue)
})
promises.add(selfDeletingPromise)
return _setMapPoolAsync(s, iterator, concurrency, f, result, promises)
}
result.add(resultItem)
iteration = iterator.next()
}
return result
}
const _mapMapPoolAsync = async function (
m, iterator, concurrency, f, result, promises,
) {
let iteration = iterator.next()
while (!iteration.done) {
if (promises.size >= concurrency) {
await promiseRace(promises)
}
const key = iteration.value[0]
const resultItem = f(iteration.value[1], key, m)
if (isPromise(resultItem)) {
result.set(key, resultItem)
const selfDeletingPromise = resultItem.then(resolvedValue => {
promises.delete(selfDeletingPromise)
result.set(key, resolvedValue)
})
promises.add(selfDeletingPromise)
} else {
result.set(key, resultItem)
}
iteration = iterator.next()
}
if (promises.size > 0) {
await promiseAll(promises)
}
return result
}
const mapMapPool = function (m, concurrency, f) {
const result = new Map()
const iterator = m[symbolIterator]()
let iteration = iterator.next()
while (!iteration.done) {
const key = iteration.value[0]
const resultItem = f(iteration.value[1], key, m)
if (isPromise(resultItem)) {
const promises = new Set()
result.set(key, resultItem)
const selfDeletingPromise = resultItem.then(resolvedValue => {
promises.delete(selfDeletingPromise)
result.set(key, resolvedValue)
})
promises.add(selfDeletingPromise)
return _mapMapPoolAsync(m, iterator, concurrency, f, result, promises)
}
result.set(key, resultItem)
iteration = iterator.next()
}
return result
}
const _objectMapPoolAsync = async function (
o, concurrency, f, result, doneKeys, promises,
) {
for (const key in o) {
if (key in doneKeys) {
continue
}
if (promises.size >= concurrency) {
await promiseRace(promises)
}
const resultItem = f(o[key], key, o)
if (isPromise(resultItem)) {
result[key] = resultItem
const selfDeletingPromise = resultItem.then(resolvedValue => {
promises.delete(selfDeletingPromise)
result[key] = resolvedValue
})
promises.add(selfDeletingPromise)
} else {
result[key] = resultItem
}
}
if (promises.size > 0) {
await promiseAll(promises)
}
return result
}
const objectMapPool = function (o, concurrency, f) {
const result = {}
const doneKeys = {}
for (const key in o) {
doneKeys[key] = true
const resultItem = f(o[key], key, o)
if (isPromise(resultItem)) {
const promises = new Set()
result[key] = resultItem
const selfDeletingPromise = resultItem.then(resolvedValue => {
promises.delete(selfDeletingPromise)
result[key] = resolvedValue
})
promises.add(selfDeletingPromise)
return _objectMapPoolAsync(o, concurrency, f, result, doneKeys, promises)
}
result[key] = resultItem
}
return result
}
const _curryArity = (arity, func, args) => function curried(...curriedArgs) {
const argsLength = args.length,
curriedArgsLength = curriedArgs.length,
nextArgs = []
let argsIndex = -1,
curriedArgsIndex = -1,
numCurriedPlaceholders = 0
while (++argsIndex < argsLength) {
const arg = args[argsIndex]
if (arg == __ && (curriedArgsIndex += 1) < curriedArgsLength) {
const curriedArg = curriedArgs[curriedArgsIndex]
if (curriedArg == __) {
numCurriedPlaceholders += 1
}
nextArgs.push(curriedArg)
} else {
nextArgs.push(arg)
}
if (nextArgs.length == arity) {
return numCurriedPlaceholders == 0
? func(...nextArgs)
: curryArity(arity, func, nextArgs)
}
}
while (++curriedArgsIndex < curriedArgsLength) {
const curriedArg = curriedArgs[curriedArgsIndex]
if (curriedArg == __) {
numCurriedPlaceholders += 1
}
nextArgs.push(curriedArg)
if (nextArgs.length == arity) {
return numCurriedPlaceholders == 0
? func(...nextArgs)
: curryArity(arity, func, nextArgs)
}
}
return curryArity(arity, func, nextArgs)
}
const curryArity = function (arity, func, args) {
const argsLength = args.length
if (argsLength < arity) {
return _curryArity(arity, func, args)
}
let argsIndex = -1
while (++argsIndex < argsLength) {
const arg = args[argsIndex]
if (arg == __) {
return _curryArity(arity, func, args)
}
}
return func(...args)
}
const spread2 = func => function spreading2([arg0, arg1]) {
return func(arg0, arg1)
}
const objectMapEntries = function (object, mapper) {
const result = {},
promises = []
for (const key in object) {
const value = object[key],
mapping = mapper([key, value])
if (isPromise(mapping)) {
promises.push(mapping.then(
spread2(curryArity(3, objectSet, [result]))))
} else {
result[mapping[0]] = mapping[1]
}
}
return promises.length == 0
? result
: promiseAll(promises).then(always(result))
}
// (mapper function, result Map, promises Array<Promise>) => (key any, value any) => ()
const mapMapEntriesForEachCallback = (
mapper, result, promises,
) => function callback(value, key) {
const mapping = mapper([key, value])
if (isPromise(mapping)) {
promises.push(mapping.then(spread2(curryArity(3, mapSet, [result]))))
} else {
result.set(mapping[0], mapping[1])
}
}
const mapMapEntries = function (source, mapper) {
const result = new Map(),
promises = []
source.forEach(mapMapEntriesForEachCallback(mapper, result, promises))
return promises.length == 0
? result
: promiseAll(promises).then(always(result))
}
const _map = function (value, mapper) {
if (isArray(value)) {
return arrayMap(value, mapper)
}
if (value == null) {
return value
}
if (typeof value.then == 'function') {
return value.then(mapper)
}
if (typeof value.map == 'function') {
return value.map(mapper)
}
if (typeof value == 'string' || value.constructor == String) {
return stringMap(value, mapper)
}
if (value.constructor == Set) {
return setMap(value, mapper)
}
if (value.constructor == Map) {
return mapMap(value, mapper)
}
if (typeof value[symbolIterator] == 'function') {
return MappingIterator(value[symbolIterator](), mapper)
}
if (typeof value[symbolAsyncIterator] == 'function') {
return MappingAsyncIterator(value[symbolAsyncIterator](), mapper)
}
if (value.constructor == Object) {
return objectMap(value, mapper)
}
return mapper(value)
}
const map = function (arg0, arg1) {
if (arg1 == null) {
return curry2(_map, __, arg0)
}
return isPromise(arg0)
? arg0.then(curry2(_map, __, arg1))
: _map(arg0, arg1)
}
// _mapEntries(value Object|Map, mapper function) -> Object|Map
const _mapEntries = (value, mapper) => {
if (value == null) {
throw new TypeError('value is not an Object or Map')
}
if (value.constructor == Object) {
return objectMapEntries(value, mapper)
}
if (value.constructor == Map) {
return mapMapEntries(value, mapper)
}
throw new TypeError('value is not an Object or Map')
}
map.entries = function mapEntries(arg0, arg1) {
if (arg1 == null) {
return curry2(_mapEntries, __, arg0)
}
return isPromise(arg0)
? arg0.then(curry2(_mapEntries, __, arg1))
: _mapEntries(arg0, arg1)
}
const _mapSeries = function (collection, f) {
if (isArray(collection)) {
return arrayMapSeries(collection, f)
}
if (collection == null) {
throw new TypeError(`invalid collection ${collection}`)
}
if (typeof collection == 'string' || collection.constructor == String) {
return stringMapSeries(collection, f)
}
if (collection.constructor == Set) {
return setMapSeries(collection, f)
}
if (collection.constructor == Map) {
return mapMapSeries(collection, f)
}
if (collection.constructor == Object) {
return objectMapSeries(collection, f)
}
throw new TypeError(`invalid collection ${collection}`)
}
map.series = function mapSeries(arg0, arg1) {
if (arg1 == null) {
return curry2(_mapSeries, __, arg0)
}
return isPromise(arg0)
? arg0.then(curry2(_mapSeries, __, arg1))
: _mapSeries(arg0, arg1)
}
const _mapPool = function (collection, concurrency, f) {
if (isArray(collection)) {
return arrayMapPool(collection, concurrency, f)
}
if (collection == null) {
throw new TypeError(`invalid collection ${collection}`)
}
if (typeof collection == 'string' || collection.constructor == String) {
return stringMapPool(collection, concurrency, f)
}
if (collection.constructor == Set) {
return setMapPool(collection, concurrency, f)
}
if (collection.constructor == Map) {
return mapMapPool(collection, concurrency, f)
}
if (collection.constructor == Object) {
return objectMapPool(collection, concurrency, f)
}
throw new TypeError(`invalid collection ${collection}`)
}
map.pool = function mapPool(arg0, arg1, arg2) {
if (arg2 == null) {
return curry3(_mapPool, __, arg0, arg1)
}
return isPromise(arg0)
? arg0.then(curry3(_mapPool, __, arg1, arg2))
: _mapPool(arg0, arg1, arg2)
}
const FilteringIterator = (iterator, predicate) => ({
[symbolIterator]() {
return this
},
next() {
let iteration = iterator.next()
while (!iteration.done) {
const { value } = iteration
if (predicate(value)) {
return { value, done: false }
}
iteration = iterator.next()
}
return iteration
},
})
const FilteringAsyncIterator = (asyncIterator, predicate) => ({
isAsyncIteratorDone: false,
[symbolAsyncIterator]() {
return this
},
async next() {
while (!this.isAsyncIteratorDone) {
const { value, done } = await asyncIterator.next()
if (done) {
this.isAsyncIteratorDone = true
} else {
const predication = predicate(value)
if (isPromise(predication) ? await predication : predication) {
return { value, done: false }
}
}
}
return { value: undefined, done: true }
},
})
const arrayExtendMap = function (
array, values, valuesMapper, valuesIndex,
) {
const valuesLength = values.length
let arrayIndex = array.length - 1
while (++valuesIndex < valuesLength) {
array[++arrayIndex] = valuesMapper(values[valuesIndex], valuesIndex, array)
}
return array
}
const arrayFilterByConditions = function (
array, result, index, conditions,
) {
const arrayLength = array.length
let conditionsIndex = -1
while (++index < arrayLength) {
if (conditions[++conditionsIndex]) {
result.push(array[index])
}
}
return result
}
const arrayFilter = function (array, predicate) {
const arrayLength = array.length,
result = []
let index = -1,
resultIndex = -1
while (++index < arrayLength) {
const item = array[index],
shouldIncludeItem = predicate(item, index, array)
if (isPromise(shouldIncludeItem)) {
return promiseAll(
arrayExtendMap([shouldIncludeItem], array, predicate, index)
).then(curry4(arrayFilterByConditions, array, result, index - 1, __))
}
if (shouldIncludeItem) {
result[++resultIndex] = item
}
}
return result
}
const stringFilter = function (string, predicate) {
const filteredCharactersArray = arrayFilter(string, predicate)
return isPromise(filteredCharactersArray)
? filteredCharactersArray.then(curry3(callPropUnary, __, 'join', ''))
: filteredCharactersArray.join('')
}
const thunkify1 = (func, arg0) => function thunk() {
return func(arg0)
}
const noop = function () {}
const setFilter = function (value, predicate) {
const result = new Set(),
resultAdd = result.add.bind(result),
promises = []
for (const item of value) {
const predication = predicate(item, item, value)
if (isPromise(predication)) {
promises.push(predication.then(curry3(
thunkConditional, __, thunkify1(resultAdd, item), noop)))
} else if (predication) {
result.add(item)
}
}
return promises.length == 0
? result
: promiseAll(promises).then(always(result))
}
const mapFilter = function (map, predicate) {
const result = new Map(),
promises = []
for (const [key, item] of map) {
const predication = predicate(item, key, map)
if (isPromise(predication)) {
promises.push(predication.then(curry3(thunkConditional,
__,
thunkify4(callPropBinary, result, 'set', key, item),
noop)))
} else if (predication) {
result.set(key, item)
}
}
return promises.length == 0 ? result
: promiseAll(promises).then(always(result))
}
const objectSetIf = function (
object, key, value, condition,
) {
if (condition) {
object[key] = value
}
}
const objectFilter = function (object, predicate) {
const result = {},
promises = []
for (const key in object) {
const item = object[key],
shouldIncludeItem = predicate(item, key, object)
if (isPromise(shouldIncludeItem)) {
promises.push(shouldIncludeItem.then(
curry4(objectSetIf, result, key, object[key], __)))
} else if (shouldIncludeItem) {
result[key] = item
}
}
return promises.length == 0
? result
: promiseAll(promises).then(always(result))
}
const _filter = function (value, predicate) {
if (isArray(value)) {
return arrayFilter(value, predicate)
}
if (value == null) {
return value
}
if (typeof value == 'string' || value.constructor == String) {
return stringFilter(value, predicate)
}
if (value.constructor == Set) {
return setFilter(value, predicate)
}
if (value.constructor == Map) {
return mapFilter(value, predicate)
}
if (typeof value.filter == 'function') {
return value.filter(predicate)
}
if (typeof value[symbolIterator] == 'function') {
return FilteringIterator(value[symbolIterator](), predicate)
}
if (typeof value[symbolAsyncIterator] == 'function') {
return FilteringAsyncIterator(value[symbolAsyncIterator](), predicate)
}
if (value.constructor == Object) {
return objectFilter(value, predicate)
}
return value
}
const filter = function (arg0, arg1) {
if (typeof arg0 == 'function') {
return curry2(_filter, __, arg0)
}
return _filter(arg0, arg1)
}
const objectValues = Object.values
const objectProto = Object.prototype
const nativeObjectToString = objectProto.toString
const objectToString = value => nativeObjectToString.call(value)
const generatorFunctionTag = '[object GeneratorFunction]'
const isGeneratorFunction = value => objectToString(value) == generatorFunctionTag
const asyncGeneratorFunctionTag = '[object AsyncGeneratorFunction]'
const isAsyncGeneratorFunction = value => objectToString(value) == asyncGeneratorFunctionTag
const iteratorReduceAsync = async function (
iterator, reducer, result,
) {
let iteration = iterator.next()
if (iteration.done) {
return result
}
while (!iteration.done) {
result = reducer(result, iteration.value)
if (isPromise(result)) {
result = await result
}
iteration = iterator.next()
}
return result
}
const iteratorReduce = function (iterator, reducer, result) {
let iteration = iterator.next()
if (iteration.done) {
return result
}
if (result === undefined) {
result = iteration.value
iteration = iterator.next()
}
while (!iteration.done) {
result = reducer(result, iteration.value)
if (isPromise(result)) {
return result.then(curry3(iteratorReduceAsync, iterator, reducer, __))
}
iteration = iterator.next()
}
return result
}
const asyncIteratorReduce = async function (asyncIterator, reducer, result) {
let iteration = await asyncIterator.next()
if (iteration.done) {
return result
}
if (result === undefined) {
result = iteration.value
iteration = await asyncIterator.next()
}
while (!iteration.done) {
result = await reducer(result, iteration.value)
iteration = await asyncIterator.next()
}
return result
}
const arrayReduceAsync = async function (
array, reducer, result, index,
) {
const length = array.length
while (++index < length) {
result = reducer(result, array[index], index, array)
if (isPromise(result)) {
result = await result
}
}
return result
}
const arrayReduce = function (array, reducer, result) {
const arrayLength = array.length
let index = -1
if (result === undefined) {
result = array[++index]
}
while (++index < arrayLength) {
result = reducer(result, array[index], index, array)
if (isPromise(result)) {
return result.then(curry4(arrayReduceAsync, array, reducer, __, index))
}
}
return result
}
// argument resolver for curry5
const curry5ResolveArg0 = (
baseFunc, arg1, arg2, arg3, arg4,
) => function arg0Resolver(arg0) {
return baseFunc(arg0, arg1, arg2, arg3, arg4)
}
// argument resolver for curry5
const curry5ResolveArg1 = (
baseFunc, arg0, arg2, arg3, arg4,
) => function arg1Resolver(arg1) {
return baseFunc(arg0, arg1, arg2, arg3, arg4)
}
// argument resolver for curry5
const curry5ResolveArg2 = (
baseFunc, arg0, arg1, arg3, arg4,
) => function arg2Resolver(arg2) {
return baseFunc(arg0, arg1, arg2, arg3, arg4)
}
// argument resolver for curry5
const curry5ResolveArg3 = (
baseFunc, arg0, arg1, arg2, arg4,
) => function arg3Resolver(arg3) {
return baseFunc(arg0, arg1, arg2, arg3, arg4)
}
// argument resolver for curry5
const curry5ResolveArg4 = (
baseFunc, arg0, arg1, arg2, arg3,
) => function arg3Resolver(arg4) {
return baseFunc(arg0, arg1, arg2, arg3, arg4)
}
const curry5 = function (baseFunc, arg0, arg1, arg2, arg3, arg4) {
if (arg0 == __) {
return curry5ResolveArg0(baseFunc, arg1, arg2, arg3, arg4)
}
if (arg1 == __) {
return curry5ResolveArg1(baseFunc, arg0, arg2, arg3, arg4)
}
if (arg2 == __) {
return curry5ResolveArg2(baseFunc, arg0, arg1, arg3, arg4)
}
if (arg3 == __) {
return curry5ResolveArg3(baseFunc, arg0, arg1, arg2, arg4)
}
return curry5ResolveArg4(baseFunc, arg0, arg1, arg2, arg3)
}
const objectKeys = Object.keys
const objectReduceAsync = async function (object, reducer, result, keys, index) {
const keysLength = keys.length
while (++index < keysLength) {
const key = keys[index]
result = reducer(result, object[key], key, object)
if (isPromise(result)) {
result = await result
}
}
return result
}
const objectReduce = function (object, reducer, result) {
const keys = objectKeys(object),
keysLength = keys.length
let index = -1
if (result === undefined) {
result = object[keys[++index]]
}
while (++index < keysLength) {
const key = keys[index]
result = reducer(result, object[key], key, object)
if (isPromise(result)) {
return result.then(curry5(objectReduceAsync, object, reducer, __, keys, index))
}
}
return result
}
const mapReduceAsync = async function (
map, reducer, result, mapEntriesIter,
) {
for (const [key, value] of mapEntriesIter) {
result = reducer(result, value, key, map)
if (isPromise(result)) {
result = await result
}
}
return result
}
const mapReduce = function (map, reducer, result) {
const mapEntriesIter = map.entries()
if (result === undefined) {
const firstIteration = mapEntriesIter.next()
if (firstIteration.done) {
return result
}
result = firstIteration.value[1]
}
for (const [key, value] of mapEntriesIter) {
result = reducer(result, value, key, map)
if (isPromise(result)) {
return result.then(curry4(
mapReduceAsync, map, reducer, __, mapEntriesIter))
}
}
return result
}
const reducerConcat = (
reducerA, reducerB,
) => function pipedReducer(result, item) {
const intermediate = reducerA(result, item)
return isPromise(intermediate)
? intermediate.then(curry2(reducerB, __, item))
: reducerB(intermediate, item)
}
const genericReduce = function (collection, reducer, result) {
if (isArray(collection)) {
return arrayReduce(collection, reducer, result)
}
if (collection == null) {
return result === undefined
? curry2(reducer, collection, __)
: reducer(result, collection)
}
if (collection.constructor == Map) {
return mapReduce(collection, reducer, result)
}
if (typeof collection[symbolIterator] == 'function') {
return iteratorReduce(
collection[symbolIterator](), reducer, result)
}
if (typeof collection[symbolAsyncIterator] == 'function') {
return asyncIteratorReduce(
collection[symbolAsyncIterator](), reducer, result)
}
if (typeof collection.reduce == 'function') {
return collection.reduce(reducer, result)
}
if (typeof collection.chain == 'function') {
return collection.chain(curry2(reducer, result, __))
}
if (typeof collection.flatMap == 'function') {
return collection.flatMap(curry2(reducer, result, __))
}
if (collection.constructor == Object) {
return objectReduce(collection, reducer, result)
}
return result === undefined
? curry2(reducer, collection, __)
: reducer(result, collection)
}
// _reduce(collection any, reducer function, initialValue function|any) -> Promise
const _reduce = function (collection, reducer, initialValue) {
if (typeof initialValue == 'function') {
const actualInitialValue = initialValue(collection)
return isPromise(actualInitialValue)
? actualInitialValue.then(curry3(genericReduce, collection, reducer, __))
: genericReduce(collection, reducer, actualInitialValue)
}
return isPromise(initialValue)
? initialValue.then(curry3(genericReduce, collection, reducer, __))
: genericReduce(collection, reducer, initialValue)
}
const reduce = function (...args) {
if (typeof args[0] == 'function') {
return curry3(_reduce, __, args[0], args[1])
}
if (isPromise(args[0])) {
return args[0].then(curry3(_reduce, __, args[1], args[2]))
}
return _reduce(args[0], args[1], args[2])
}
const isBinary = ArrayBuffer.isView
const add = (a, b) => a + b
const _arrayExtend = function (array, values) {
const arrayLength = array.length,
valuesLength = values.length
let valuesIndex = -1
while (++valuesIndex < valuesLength) {
array[arrayLength + valuesIndex] = values[valuesIndex]
}
return array
}
const arrayExtend = function (array, values) {
if (isArray(values) || isBinary(values)) {
return _arrayExtend(array, values)
}
array.push(values)
return array
}
const globalThisHasBuffer = typeof Buffer == 'function'
const bufferAlloc = globalThisHasBuffer ? Buffer.alloc : noop
const _binaryExtend = function (typedArray, array) {
const offset = typedArray.length
const result = globalThisHasBuffer && typedArray.constructor == Buffer
? bufferAlloc(offset + array.length)
: new typedArray.constructor(offset + array.length)
result.set(typedArray)
result.set(array, offset)
return result
}
const binaryExtend = function (typedArray, array) {
if (isArray(array) || isBinary(array)) {
return _binaryExtend(typedArray, array)
}
return _binaryExtend(typedArray, [array])
}
const isNodeReadStream = value => value != null && typeof value.pipe == 'function'
const __streamWrite = stream => function appender(
chunk, encoding, callback,
) {
stream.write(chunk, encoding, callback)
return stream
}
const _streamExtendExecutor = (
resultStream, stream,
) => function executor(resolve, reject) {
stream.on('data', __streamWrite(resultStream))
stream.on('end', thunkify1(resolve, resultStream))
stream.on('error', reject)
}
const _streamExtend = (
resultStream, stream,
) => new Promise(_streamExtendExecutor(resultStream, stream))
const streamExtend = function (stream, values) {
if (isNodeReadStream(values)) {
return _streamExtend(stream, values)
}
stream.write(values)
return stream
}
const setExtend = function (set, values) {
if (values != null && values.constructor == Set) {
for (const value of values) {
set.add(value)
}
return set
}
return set.add(values)
}
const callConcat = function (object, values) {
return object.concat(values)
}
const identityTransform = function (collection, transducer, accum) {
const nil = genericReduce(collection, transducer(noop), null)
return isPromise(nil) ? nil.then(always(accum)) : accum
}
const genericTransform = function (collection, transducer, accum) {
if (isArray(accum)) {
return genericReduce(collection, transducer(arrayExtend), accum)
}
if (isBinary(accum)) {
const intermediateArray = genericReduce(collection, transducer(arrayExtend), [])
return isPromise(intermediateArray)
? intermediateArray.then(curry2(binaryExtend, accum, __))
: binaryExtend(accum, intermediateArray)
}
if (accum == null) {
return identityTransform(collection, transducer, accum)
}
const constructor