Skip to content

Instantly share code, notes, and snippets.

@reggi
Last active February 10, 2021 06:59
Show Gist options
  • Select an option

  • Save reggi/099658fd33d5233afd22c4231480a8eb to your computer and use it in GitHub Desktop.

Select an option

Save reggi/099658fd33d5233afd22c4231480a8eb to your computer and use it in GitHub Desktop.
namespace Fox {
export namespace Suppress {
type ThenArg<T> = T extends Promise<infer U> ? U : T
type ReturnAdd<T extends Func, G> = ReturnType<T> extends Promise<infer U> ? Promise<ThenArg<ReturnType<T>> | G> : ReturnType<T> | G
export type Func = (...args: any[]) => any
export type Return<T extends Func> = ReturnAdd<T, undefined>
}
export namespace Use {
type ThenArg<T> = T extends Promise<infer U> ? U : T
type RelatedThen<A, B> = A extends Promise<infer U> ? Promise<B> : B
export type Func = (...args: any[]) => any
export type Return<A extends Func, B> = RelatedThen<ReturnType<A>, B>
export type CallbackValue<A extends Func> = ThenArg<ReturnType<A>>
}
export namespace Promisify {
type ThenArg<T> = T extends Promise<infer U> ? U : T
export type Return<A> = Promise<ThenArg<A>>
}
export namespace First {
export type Func = (...args: any[]) => any
export type Funcs = Func[]
export type Return<T extends Func[]> = ReturnType<T[number]>
export type Params<T extends Func[]> = Parameters<T[number]>
}
export namespace FirstAsync {
type ThenArg<T> = T extends Promise<infer U> ? U : T
export type Func = (...args: any[]) => any
export type Funcs = Func[]
export type Params<T extends Func[]> = Parameters<T[number]>
export type Return<T extends Func[]> = Promise<ThenArg<ReturnType<T[number]>>>
}
export namespace All {
export type Func = (...args: any[]) => any
export type Funcs = Func[]
type ReturnTuple<T extends readonly Func[]> = {
[K in keyof T]: T[K] extends Func ? ReturnType<T[K]> : never
}
export type Params<T extends Func[]> = Parameters<T[number]>
export type Return<T extends readonly Func[]> = ReturnTuple<T>
}
export namespace AllAsync {
type ThenArg<T> = T extends Promise<infer U> ? U : T
export type Func = (...args: any[]) => any
export type Funcs = Func[]
export type TupleRmPromise<T extends readonly Func[]> = {
[K in keyof T]: T[K] extends Func ? ThenArg<ReturnType<T[K]>> : never
}
export type Params<T extends Func[]> = Parameters<T[number]>
export type Return<T extends readonly Func[]> = Promise<TupleRmPromise<T>>
}
export namespace Lead {
export type Func = (args: any) => any
export type LeadParamSingle<T extends Func> = Parameters<T>['length'] extends 1 ? Parameters<T>[0] : never
export type Params<S extends string, F extends Func> = Record<S, LeadParamSingle<F>>
}
export namespace Deundefy {
export type Func = (...args: any[]) => any
export type Deundefy<T> = Exclude<T, undefined>
export type DeundefyPromise<T> = T extends Promise<infer U> ? Promise<Deundefy<U>> : Deundefy<T>
export type Return<T extends Func> = DeundefyPromise<ReturnType<T>>
}
export namespace Undefy {
export type Func = (...args: any[]) => any
export type UndefyPromise<T> = T extends Promise<infer U> ? Promise<undefined> : undefined
export type Return<T extends Func> = UndefyPromise<ReturnType<T>>
}
export namespace Flow {
export type Func = (...args: any[]) => any
export type Funcs = readonly Func[]
type FuncPass<A, B> = (x: A) => B
type Tail<T extends readonly FuncPass<any, any>[]> = FuncPass<T, void> extends ((h: any, ...r: infer R) => void) ? R : never;
export type Check<T extends readonly FuncPass<any, any>[]> = { [K in keyof T]:
K extends keyof Tail<T> ? (
[T[K], Tail<T>[K]] extends [FuncPass<infer A, infer R>, FuncPass<infer S, any>] ? (
[R] extends [S] ? T[K] : FuncPass<A, S>
) : never
) : T[K]
}
type FirstFnParam<Fns extends Funcs> = Parameters<Fns[0]>[0]
export type Params<T extends Funcs> = FirstFnParam<T>
type LengthOfTuple<T extends Funcs> = T extends { length: infer L } ? L : never;
type DropFirstInTuple<T extends Funcs> = ((...args: T) => any) extends (arg: any, ...rest: infer U) => any ? U : T;
type LastInTuple<T extends Funcs> = T[LengthOfTuple<DropFirstInTuple<T>>];
type LastFnReturns<Fns extends Funcs> = ReturnType<LastInTuple<Fns>>
export type Return<T extends Funcs> = LastFnReturns<T>
}
export namespace Journey {
// NOTE Journey's have one optional arg
export type Func = (a?: any) => any
export type Funcs = Func[]
}
export namespace JourneyKeyed {
// NOTE Journey's have one optional arg
export type Func = (a: any) => any
export type Funcs = { [k: string]: Func }
}
export namespace CliffFlat {
export type Func = (...args: any[]) => any
export type Funcs = { [k: string]: Func }
}
export namespace CliffKeyed {
export type Func = (...args: any[]) => any
export type Funcs = { [k: string]: Func }
}
export namespace Pick {
type ValuesToTuple<ObjT extends object, KeysT extends readonly (keyof ObjT)[]>
= { [index in keyof KeysT]: ObjT[Extract<KeysT[index], keyof ObjT>] }
export type Return<A extends object, B extends readonly (keyof A)[]> = ValuesToTuple<A, B>
}
export namespace Choosy {
export type Func = (...args: any[]) => any
export type Funcs = { [k: string]: Func }
export type ObjectReturn<T extends Funcs> = { [K in keyof T]: ReturnType<T[K]> }
export type Return<T extends Funcs, I> = Pick<ObjectReturn<T>, keyof T & keyof I>
export type Params<T extends Funcs> = { [K in keyof T]: Parameters<T[K]>[0] }
}
export namespace ChoosySpread {
export type Func = (...args: any[]) => any
export type Funcs = { [k: string]: Func }
export type ObjectReturn<T extends Funcs> = { [K in keyof T]: ReturnType<T[K]> }
export type Return<T extends Funcs, I> = Pick<ObjectReturn<T>, keyof T & keyof I>
export type Params<T extends Funcs> = { [K in keyof T]: Parameters<T[K]> }
// export type Params <T extends Funcs, G extends string> = G extends 'SINGLE' ? ParamsSingle<T> : ParamsSpread<T>
}
export namespace Parameterize {
export type Func = (...args: any[]) => any
export type Funcs = Func[]
type ZipTuple<T extends readonly any[], U extends readonly any[]> = {
[K in keyof T]: [T[K], K extends keyof U ? U[K] : never]
}
type KeyValTuplesToObject<K extends readonly PropertyKey[], V extends readonly any[]> =
ZipTuple<K, V>[number] extends infer Z ?
[Z] extends [[any, any]] ? { [P in Z[0]]: Extract<Z, [P, any]>[1] } : never : never
export type Param<A extends Func, B extends readonly any[]> = KeyValTuplesToObject<B, Parameters<A>>
}
// export namespace Profound {
// type FirstArg<T extends any> =
// T extends [infer R, ...any[]] ? R :
// T extends [] ? undefined :
// T;
// type ThenArg<T> = T extends Promise<infer U> ? U : T
// export type Func = (...args: any[]) => any
// export type Funcs = { [k: string]: Func }
// export type Callback<T extends Funcs> = { [K in keyof T]: FirstArg<ThenArg<ReturnType<T[K]>>> }
// type AllReturn<T extends Funcs> = { [K in keyof T]: FirstArg<ThenArg<ReturnType<T[K]>>> }
// type FirstParam<T extends Funcs> = Parameters<T[keyof T]>[0]
// export type Param<T extends Funcs> = AllReturn<T> | FirstParam<T>
// }
// export namespace ProfoundFix {
// type FirstArg<T extends any> =
// T extends [infer R, ...any[]] ? R :
// T extends [] ? undefined :
// T;
// type ThenArg<T> = T extends Promise<infer U> ? U : T
// export type Funcs = { [k: string]: any }
// export type CallbackArgs<T extends Funcs> = { [K in keyof T]: FirstArg<ThenArg<ReturnType<T[K]>>> }
// type UnionToIntersectionValues<U, K extends keyof U = keyof U> =
// (K extends unknown ? (k: U[K]) => void : never) extends ((k: infer I) => void) ? I : never
// export type Param<T extends Funcs> = UnionToIntersectionValues<{
// [K in keyof T]:
// K extends string ? { [KK in K]: ThenArg<ReturnType<T[K]>> } | Parameters<T[K]>[0] : never
// }>
// export type Func = (...args: any) => any
// // type CallbackReturn<T> = T extends Func ? ThenArg<ReturnType<T>> : never
// type IsPromise<T> = T extends Promise<infer U> ? true : null
// type FuncReturnsPromise<T extends Func> = IsPromise<ReturnType<T>>
// type ObjectHasPromise<T> = {
// [K in keyof T]: T[K] extends Func ? ReturnType<T[K]> extends Promise<infer U> ? true : null : null
// }[keyof T]
// type ObjectPromises<T> = {
// [K in keyof T]: T[K] extends Func ? ReturnType<T[K]> extends Promise<infer U> ? true : null : null
// }
// // type HasPromise<Fns, Cb extends Func> = ObjectHasPromise<Fns> | FuncReturnsPromise<Cb>
// type ObjectReturns<T> = {
// [K in keyof T]: T[K] extends Func ? ThenArg<ReturnType<T[K]>> : never
// }
// type ObjectHasPromises<Fns, Params> = Omit<ObjectPromises<Fns>, keyof Params>[keyof Omit<ObjectPromises<Fns>, keyof Params>]
// type HasPromiseDirect<Fns, R, Params> = ObjectHasPromises<Fns, Params> | IsPromise<R>
// export type HasPromise<Fns, R, Params> = HasPromiseDirect<Fns, R, Params> | IsPromise<R>
// // export type Return<Fns, Cb extends Func | undefined, Params> = HasPromise<Fns, Cb, Params> extends null ? ThenArg<ReturnType<Cb>> : Promise<ThenArg<ReturnType<Cb>>>
// // HasPromiseDirect<Fns, R> extends null ? ThenArg<R> : Promise<ThenArg<R>>
// // we set up return type based on if there's a promise in the funcs obj or the callback is a promise
// // we need two extensions to this :(
// // 1. if the key of the promise was passed into Params, it's not a promise anymore
// // 2. if the callback is not present, the return is set to the return value of all keys (promise depending)
// type SingleArgFn = (a: any) => any
// export type Return<FNS, CB, IN> =
// CB extends SingleArgFn ?
// HasPromise<FNS, ReturnType<CB>, IN> extends null ?
// ThenArg<ReturnType<CB>> :
// Promise<ThenArg<ReturnType<CB>>> :
// ObjectReturns<FNS>
// }
export namespace Profound {
export type Func = (...arg: any) => any
export type SingleArgFunc = (arg: any) => any
export type SingleArgFuncs = { [k: string]: SingleArgFunc }
export namespace Params {
type Func = (arg: any) => any
type Funcs = { [k: string]: Func }
type FirstArg<T extends any> =
T extends [infer R, ...any[]] ? R :
T extends [] ? undefined :
T;
type ThenArg<T> = T extends Promise<infer U> ? U : T
type UnionToIntersectionValues<U, K extends keyof U = keyof U> =
([K] extends [never]
? unknown
: K extends unknown
? (k: U[K]) => void
: never
) extends (k: infer I) => void ? I : never
export type FnsBoth<T extends Funcs> = UnionToIntersectionValues<{
[K in keyof T]: Parameters<T[K]>[0] extends undefined ?
{ [KK in K]?: ThenArg<ReturnType<T[K]>> } :
{ [KK in K]: ThenArg<ReturnType<T[K]>> } | FirstArg<Parameters<T[K]>>
}>
export type Keys<T> = { [K in keyof T]: T[K] }[keyof T]
export type ObjEmptyNever<T> = Keys<T> extends never ? never : T
export type ObjKeysWithoutNever<T> = { [K in keyof T]: T[K] extends never ? never : K }[keyof T]
export type ObjWithoutNever<T> = Pick<T, ObjKeysWithoutNever<T>>;
export type ObjClean<T> = ObjEmptyNever<ObjWithoutNever<T>>
export type FnsOverride<T> = { [K in keyof T]: T[K] extends Func ? ThenArg<ReturnType<T[K]>> : never }
export type FnArguments<T> = ObjClean<{ [K in keyof T]: T[K] extends (arg: any) => any ? Parameters<T[K]>[0] extends undefined ? never : Parameters<T[K]>[0] : never }>
export type Value<T extends Funcs> = FnArguments<T> extends never ? FnsBoth<T> | void : FnsBoth<T>
// export type Value<T> = FnsBoth<T>
}
export namespace Callback {
type ThenArg<T> = T extends Promise<infer U> ? U : T
export type ObjReturn<T> = { [K in keyof T]: T[K] extends SingleArgFunc ? ThenArg<ReturnType<T[K]>> : never }
}
export namespace Return {
export type IsPromise<T> = T extends Promise<infer I> ? T : never
export type FnIsPromise<T> = T extends (...args: any) => any ? IsPromise<ReturnType<T>> : never
export type FnIsPromiseFn<T> = T extends (...args: any) => any ? IsPromise<ReturnType<T>> extends never ? never : T : never
// gets object keys in object, never if no keys
export type Keys<T> = { [K in keyof T]: T[K] }[keyof T]
// get object if has keys, never if no keys
export type ObjEmptyNever<T> = Keys<T> extends never ? never : T
// gets object keys without value set to never
export type ObjKeysWithoutNever<T> = { [K in keyof T]: T[K] extends never ? never : K }[keyof T]
// gets object without never keys
export type ObjWithoutNever<T> = Pick<T, ObjKeysWithoutNever<T>>;
// gets functions that return promises in object never if empty, without never properties
export type ObjFnIsPromise<T> = ObjEmptyNever<ObjWithoutNever<{ [K in keyof T]: FnIsPromise<T[K]> }>>
// removes keys from B in A
export type ObjDiff<A, B> = Omit<A, keyof B>[keyof Omit<A, keyof B>]
// gets promises from A and removes them if theyr'e in C
export type ObjPromiseDiff<A, C> = ObjFnIsPromise<A> extends never ? never : ObjDiff<ObjFnIsPromise<A>, C>
// if B returns promise, or if there are promises in A and don't match keys in C
export type RetunsPromise<A, B, C> = FnIsPromiseFn<B> | ObjPromiseDiff<A, C>
// if promise, provides inner promise type
type ThenArg<T> = T extends Promise<infer U> ? U : T
// gets return type from function, withou inner promise if any
type ReturnValue<T extends SingleArgFunc> = T extends (args: any) => any ? ThenArg<ReturnType<T>> : never
// gets all return types in an object of functions
type ObjReturn<T extends SingleArgFuncs> = { [K in keyof T]: ReturnValue<T[K]> }
// checks A diffed with C for promsies, if so wraps all ObjectReturns in promise
type ReturnNoCallback<A extends SingleArgFuncs, B, C> = ObjPromiseDiff<A, C> extends never ? ObjReturn<A> : Promise<ObjReturn<A>>
// checks for promise, returns value of inner callback wrapped in promise
type ReturnYesCallback<A, B extends SingleArgFunc, C> = RetunsPromise<A, B, C> extends never ? ReturnValue<B> : Promise<ReturnValue<B>>
// checks if callback is available, before deligation of return
// export type Value<A, B, C> = unknown extends B ? ReturnNoCallback<A, B, C> : ReturnYesCallback<A, B, C>
// for some reason have to check if B actually returns something because `B` is known
export type Value<A extends SingleArgFuncs, B extends SingleArgFunc, C> = unknown extends ReturnValue<B> ? ReturnNoCallback<A, B, C> : ReturnYesCallback<A, B, C>
}
}
export namespace Tap {
export type Func = (...args: any[]) => any
export type RelatedThen<A, B> = A extends Promise<infer U> ? Promise<B> : B
export type Return<A extends Func, B> = RelatedThen<ReturnType<A>, B>
}
export namespace Acrew {
export type PlainObject = { [name: string]: any }
export type Func = (a: any) => any
export type Funcs = { [name: string]: Func }
type FirstArg<T extends any> =
T extends [infer R, ...any[]] ? R :
T extends [] ? undefined :
T;
type ThenArg<T> = T extends Promise<infer U> ? U : T
export type Callback<T extends Funcs> = { [K in keyof T]: FirstArg<ThenArg<ReturnType<T[K]>>> }
type AllReturn<T extends Funcs> = { [K in keyof T]: FirstArg<ThenArg<ReturnType<T[K]>>> }
type FirstParam<T extends Funcs> = Parameters<T[keyof T]>[0]
export type Param<T extends Funcs> = AllReturn<T> | FirstParam<T>
export type ObjectReturn<T extends Funcs> = { [K in keyof T]: ReturnType<T[K]> }
export type Return<A extends Funcs, G> = ObjectReturn<A> & G
}
// NOTE: ----- Cool But Depreacated types -----
// NOTE: This is cool returns { name: number, plan: [string, number]} based on how many args there are
// NOTE: But there's no way of knowing if an array is one arg arg or a flat one, so I can't use it YET!
// type LeadParam<T extends Func> = Parameters<T>['length'] extends 1 ? Parameters<T>[0] : Parameters<T>
// type KeysHasProps<T extends Funcs> = { [K in keyof T]: T[K] extends Func ? LeadParam<T[K]> extends [] ? never : K : never }[keyof T]
// type HasProps<T extends Funcs> = Pick<T, KeysHasProps<T>>
// NOTE: Checks to see if there is more than one input, if so it's a tuple.
// export type Params<T extends Funcs> = { [K in keyof HasProps<T>]: LeadParam<T[K]> }
// NOTE: This was also a cool idea, conditionaly setting return type based on input
// export type Params <T extends Funcs, G extends string> = G extends 'SINGLE' ? ParamsSingle<T> : ParamsSpread<T>
}
export class Fox {
// NOTE: Returns type `void`
static void = (() => { })()
// NOTE: Checkes obj is unresolved promise
static isPromise(obj: any) {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'
}
// NOTE: Supresses errors by catching them, and returning undefined instead
static suppress<T extends Fox.Suppress.Func>(fn: T) {
return (...input: Parameters<T>): Fox.Suppress.Return<T> => {
return Fox.use(fn, (err, value) => {
if (err) return undefined
return value
})(...input) as unknown as Fox.Suppress.Return<T>
}
}
// NOTE: Wraps any function provides callback value, returns promise or sync function with modified result
static use<A extends Fox.Use.Func, G>(fn: A, callback: (e: Error | null, a: Fox.Use.CallbackValue<A>) => G) {
return (...args: Parameters<A>): Fox.Use.Return<A, G> => {
try {
const v = fn(...args as any[])
if (Fox.isPromise(v)) {
return v.then((v: any) => callback(null, v)).catch((e: any) => callback(e, undefined as Fox.Use.CallbackValue<A>))
}
return callback(null, v) as Fox.Use.Return<A, G>
} catch (e) {
return callback(e, undefined as Fox.Use.CallbackValue<A>) as unknown as Fox.Use.Return<A, G>
}
}
}
// NOTE: Wraps any function asserts async updates return type
static promisify<A extends any[], P>(func: (...args: A) => P) {
return async (...args: A) => {
return func(...args) as unknown as Fox.Promisify.Return<P>
}
}
// NOTE: Runs fn[] returns first returned value where function did not throw, may or may not return promise
static first<A extends Fox.First.Funcs, B extends Fox.First.Return<A>>(...fns: A) {
return (...input: Fox.First.Params<A>): B => {
return fns.reduce((acq: (...args: any) => any, fn: (...args: any) => any) => {
return Fox.use(acq, (acqError, acqValue) => {
if (acqValue) return acqValue
return Fox.use(fn, (fnError, fnValue) => {
if (fnError) throw fnError
if (fnValue) return fnValue
})(...input as any[])
})
}, Fox.error(new Error('No Value')))()
}
}
// // NOTE Example
// static example = (() => {
// const alpha = (number: number) => number + 3
// const beta = (number: number) => number + 3
// const gamma = (number: number) => number + 3
// const a = Fox.first(alpha, beta, gamma)
// const b = Fox.firstAsync(alpha, beta, gamma)
// b(1).then(console.log)
// })()
// NOTE: Runs fn[] asserts that returned value must be promise
static firstAsync<A extends Fox.FirstAsync.Funcs>(...fns: A) {
return (...input: Fox.FirstAsync.Params<A>) => {
return Fox.promisify(Fox.first(...fns))(...input) as unknown as Fox.FirstAsync.Return<A>
}
}
// NOTE Runs fn[] provides input to all fn[] and runs them, may or may not return unresolved promise
static all<T extends Fox.All.Funcs>(...funcs: T) {
return (...input: Fox.All.Params<T>) => {
return funcs.map((fn) => {
return Fox.use(fn, (err, value) => {
if (err) throw err
return value
})(...input)
}) as Fox.All.Return<T>
}
}
// NOTE Runs fn[] same as all, resolves all promises
static allAsync<T extends Fox.AllAsync.Funcs>(...funcs: T) {
return (...input: Fox.AllAsync.Params<T>) => {
return Promise.all(Fox.all(...funcs)(...input)) as Fox.AllAsync.Return<T>
}
}
// NOTE lead('hello', (a) => b) => ({ hello: a}) => b
static lead<S extends string, F extends Fox.Lead.Func>(string: S, fn: F, e?: Error) {
return (input: Fox.Lead.Params<S, F>): ReturnType<F> => {
if (e && (input === undefined || !input.hasOwnProperty(string))) throw e || new Error(`No ${string} available`)
return fn(input[string])
}
}
// NOTE Example
// const meow = (name: string) => `${name} says meow`
// const meowLead = Fox.lead('name', meow, new Error('Missing name'))
// const v = meowLead({ name: 'Mittens' })
// console.log(v)
// NOTE lead('hello', (a, b) => c) => ({ hello: [a, b]}) => c
static leadArr<S extends string, F extends (...a: any[]) => any>(string: S, fn: F, e: Error) {
return (input: Record<S, Parameters<F>>): ReturnType<F> => {
if (e && (input === undefined || !input.hasOwnProperty(string))) throw e
return fn(...input[string])
}
}
// NOTE [() => undefined | user] => [() => user]
static deundefy<T extends Fox.Deundefy.Func>(func: T, err: Error) {
return (...args: Parameters<T>) => {
return Fox.use(func, (error, value) => {
if (error) throw error
if (value === undefined) throw err
return value
})(...args) as unknown as Fox.Deundefy.Return<T>
}
}
// NOTE [() => user] => [() => user | undefined]
static undefy<T extends Fox.Undefy.Func>(func: T) {
return (...args: Parameters<T>) => {
return Fox.use(func, (error, value) => {
if (error) return undefined
throw error
})(...args) as unknown as Fox.Undefy.Return<T>
}
}
// NOTE Returns a function that throws an error
static error(e: Error) {
return () => {
throw e
}
}
// NOTE Returns a function returns passed in value
static nest<T>(value: T): () => T {
return () => {
return value
}
}
// NOTE comperable to lodash.flow but with async and proper typing
static flow<T extends Fox.Flow.Funcs>(...funcs: Fox.Flow.Check<T>) {
return (input: Fox.Flow.Params<T>) => {
return funcs.reduce((acq: Fox.Flow.Func, fn: Fox.Flow.Func) => {
return Fox.use(acq, (acqError, acqValue) => {
if (acqError) throw acqError
return Fox.use(fn, (fnError, fnValue) => {
if (fnError) throw fnError
return fnValue
})(acqValue)
})
}, Fox.nest(input))(input) as Fox.Flow.Return<T>
}
}
// NOTE Reverses an array, pretty standard
static reverseArray(arr: any[]) {
var newArray: any[] = [];
for (var i = arr.length - 1; i >= 0; i--) {
newArray.push(arr[i]);
}
return newArray;
}
// NOTE Returns a tuple
static tuple<T extends readonly any[]>(...args: T) {
return args
}
// NOTE Reassigns arguments (FN, ({ a }) => Fox.tuple([]))
static reassignArgs = <A extends (...args: any[]) => any, BIn extends any[]>(a: A, b: (...a: BIn) => Parameters<A>) => {
return (...input: BIn): ReturnType<A> => {
return a(...b(...input))
}
}
// NOTE Recreation of array'd journey
static journey(funcs: Fox.Journey.Funcs) {
return (input: any = {}) => {
return funcs.reduce((acq: Fox.Journey.Func, fn: Fox.Journey.Func) => {
return Fox.use(acq, (acqError, acqValue) => {
if (acqError) throw acqError
return Fox.use(fn, (fnError, fnValue) => {
if (fnError) throw fnError
return { ...acqValue, ...fnValue }
})(acqValue)
})
}, Fox.nest(input))(input)
}
}
// NOTE Recreation of keye'd journey
static journeyKeyed(funcs: Fox.JourneyKeyed.Funcs) {
return (input: any = {}) => {
const keys = Object.keys(funcs)
return keys.reduce((acq: Fox.JourneyKeyed.Func, key: string) => {
return Fox.use(acq, (acqError, acqValue) => {
if (acqError) throw acqError
return Fox.use(funcs[key], (fnError, fnValue) => {
if (fnError) throw fnError
return { ...acqValue, [key]: fnValue }
})(acqValue)
})
}, Fox.nest(input))(input)
}
}
// NOTE standard pick function [({a, b, c}), 'a', 'c'] => {a, c}
static pick<T extends object, B extends (keyof T)[]>(o: T, ...keys: B) {
return keys.map(key => o[key]) as unknown as Fox.Pick.Return<T, B>
}
// NOTE Passes all args into all functions
static cliffFlat(funcs: Fox.CliffFlat.Funcs) {
return (...input: any) => {
const funcKeys = Object.keys(funcs)
return funcKeys.reduce((acq: Fox.CliffFlat.Func, key: string) => {
return Fox.use(acq, (acqError, acqValue) => {
if (acqError) throw acqError
return Fox.use(funcs[key], (fnError, fnValue) => {
if (fnError) throw fnError
return { ...acqValue, [key]: fnValue }
})(...input)
})
}, Fox.nest({}))()
}
}
// NOTE Passes key'd args into key'd functions
static cliffKeyed(funcs: Fox.CliffKeyed.Funcs) {
return (input: any) => {
const funcKeys = Object.keys(funcs)
return funcKeys.reduce((acq: Fox.CliffKeyed.Func, key: string) => {
return Fox.use(acq, (acqError, acqValue) => {
if (acqError) throw acqError
if (!input[key]) throw new Error(`Missing input key ${key}`)
return Fox.use(funcs[key], (fnError, fnValue) => {
if (fnError) throw fnError
return { ...acqValue, [key]: fnValue }
})(input[key])
})
}, Fox.nest({}))()
}
}
// NOTE Only runs functions where input keys match
static choosy<A extends Fox.Choosy.Funcs, B extends Partial<Fox.Choosy.Params<A>>>(funcs: A) {
return (input: B): Fox.Choosy.Return<A, B> => {
const inputKeys = Object.keys(input)
return inputKeys.reduce((acq: Fox.Choosy.Func, key: string) => {
return Fox.use(acq, (acqError, acqValue) => {
if (acqError) throw acqError
if (!input[key]) throw new Error(`Missing input key ${key}`)
return Fox.use(funcs[key], (fnError, fnValue) => {
if (fnError) throw fnError
return { ...acqValue, [key]: fnValue }
})(input[key])
})
}, Fox.nest({}))()
}
}
// static example = (() => {
// // NOTE Example
// Fox.choosy({
// })
// })()
// NOTE Only runs functions where input keys match
static choosySpread<A extends Fox.ChoosySpread.Funcs, B extends Partial<Fox.ChoosySpread.Params<A>>>(funcs: A) {
return (input: B): Fox.ChoosySpread.Return<A, B> => {
const inputKeys = Object.keys(input)
return inputKeys.reduce((acq: Fox.ChoosySpread.Func, key: string) => {
return Fox.use(acq, (acqError, acqValue) => {
if (acqError) throw acqError
if (!input[key] || !Array.isArray(input[key])) throw new Error(`Missing input key ${key}`)
return Fox.use(funcs[key], (fnError, fnValue) => {
if (fnError) throw fnError
return { ...acqValue, [key]: fnValue }
})(...input[key] as any[])
})
}, Fox.nest({}))()
}
}
static parameterize<A extends Fox.Parameterize.Func, B extends string[]>(fn: A, ...keys: B) {
return (input: Fox.Parameterize.Param<A, B>): ReturnType<A> => {
const pass = keys.map((value: string) => {
if (input[value]) return input[value]
throw new Error('Missing Parameter key ${key}')
})
return fn(...pass)
}
}
// ANCHOR New methods go here:
// NOTE "supress" version where if there's an error it doesn't throw, all callback items have possible undefined
// NOTE "keyed" a version where you can pass specific data to specific props sort of like cliffKeyed
// NOTE Takes func object of "needs", uses them for typing inputs to callback, calls needs if need
// NOTE Original implementation without a store
// static profound<A extends Fox.Profound.Funcs, B>(funcs: A, callback: (c: Fox.Profound.Callback<A>) => B) {
// return (input?: Fox.Profound.Param<A>) => {
// const funcsKeys = Object.keys(funcs)
// const valueFn = funcsKeys.reduce((acq: Fox.ChoosySpread.Func, key: string) => {
// return Fox.use(acq, (acqError, acqValue) => {
// if (acqError) throw acqError
// // NOTE: check if key already exists, we dont need to execute fun
// if (input && input[key as keyof Fox.Profound.Param<A>]) return acqValue
// return Fox.use(funcs[key], (fnError, fnValue) => {
// if (fnError) throw fnError
// return { ...acqValue, [key]: fnValue }
// })(input)
// })
// }, Fox.nest(input))
// return Fox.use(valueFn, (err, value) => {
// if (err) throw err
// return Fox.use(callback, (err, callbackValue) => {
// if (err) throw err
// return callbackValue
// })(value as Fox.Profound.Callback<A>)
// })()
// }
// }
// NOTE This is dumb and does not work...
// static profound<A extends Fox.Profound.Funcs, B>(funcs: A, callback: (c: Fox.Profound.Callback<A>) => B) {
// var store = {}
// return (input?: Fox.Profound.Param<A>) => {
// const funcsKeys = Object.keys(funcs)
// const valueFn = funcsKeys.reduce((acq: Fox.ChoosySpread.Func, key: string) => {
// return Fox.use(acq, (acqError, acqValue) => {
// if (acqError) throw acqError
// // NOTE: check if key already exists, we dont need to execute fun
// // console.log({ key, input })
// if (input && input[key as keyof Fox.Profound.Param<A>]) return acqValue
// return Fox.use(funcs[key], (fnError, fnValue) => {
// if (fnError) throw fnError
// return { ...acqValue, [key]: fnValue }
// })({ ...input })
// })
// }, Fox.nest({ ...input, ...store }))
// return Fox.use(valueFn, (err, value) => {
// if (err) throw err
// return Fox.use(callback, (err, callbackValue) => {
// if (err) throw err
// return callbackValue
// })(value as Fox.Profound.Callback<A>)
// })()
// }
// }
// static example = (() => {
// // NOTE Example
// const dog = ({ dogName }: { dogName: string }) => `doggy ${dogName} says woof`
// const cat = ({ catName }: { catName: string }) => `kitty ${catName} says meow`
// const example = Fox.profound({ dog, cat }, ({ dog, cat }) => dog + cat)
// const x = example({ dog: 'john', catName: 'spot' })
// })()
// static exampleClass = (() => {
// // NOTE Example Class
// class Example {
// alpha = (number: number) => number + 3
// alphaLead = Fox.lead('alpha', this.alpha)
// }
// const a = new Example()
// const b = a.alphaLead({ alpha: 1 })
// console.log(b)
// })()
static tap = class {
static spread<T extends Fox.Use.Func, G>(fn: T) {
return <D extends Parameters<T>>(...input: D) => {
return Fox.use(fn, () => {
return input
})(...input) as unknown as Fox.Tap.Return<T, D>
}
}
static single<T extends Fox.Use.Func, C, D extends C, G>(fn: (a: D) => G) {
return <D>(input?: D): Fox.Tap.RelatedThen<G, D> => {
return Fox.use(fn, () => {
return input
})(input as Parameters<T>[0]) as unknown as Fox.Tap.Return<T, D>
}
}
}
// static example = (async () => {
// // NOTE Example
// const runZ = Fox.tap.single(console.log)
// console.log(runZ('hello'))
// const runA = Fox.tap.spread((val, numb) => console.log(`----- runA ${numb}`))
// console.log(runA('hello', 3))
// const runB = Fox.tap.single(() => console.log('----- runB'))
// console.log(runB('meow')) // meow
// const runC = Fox.tap.spread(() => console.log('----- runC'))
// console.log(runC('meow')) // [meow]
// const runAsyncA = Fox.tap.single(async () => console.log('----- runAsyncA'))
// console.log(await runAsyncA())
// const runAsyncB = Fox.tap.single(async () => console.log('----- runAsyncB'))
// console.log(await runAsyncB('meow'))
// const runAsyncC = Fox.tap.spread(async () => console.log('----- runAsyncC'))
// console.log(await runAsyncC('meow'))
// })()
// static acrew<A extends Fox.Acrew.Funcs, B extends Fox.Acrew.Callback<A>, G>(funcs: A, callback: (a: B) => G) {
// return (input: Fox.Profound.Params<A>): Fox.Acrew.Return<A, G> => {
// const funcsKeys = Object.keys(funcs)
// const valueFn = funcsKeys.reduce((acq: Fox.ChoosySpread.Func, key: string) => {
// return Fox.use(acq, (acqError, acqValue) => {
// if (acqError) throw acqError
// // NOTE: check if key already exists, we dont need to execute fun
// if (input && input[key as keyof Fox.Profound.Param<A>]) return acqValue
// return Fox.use(funcs[key], (fnError, fnValue) => {
// if (fnError) throw fnError
// return { ...acqValue, [key]: fnValue }
// })(input)
// })
// }, Fox.nest(input))
// return Fox.use(valueFn, (err, value) => {
// if (err) throw err
// return Fox.use(callback, (err, callbackValue) => {
// if (err) throw err
// return { ...value as B, ...callbackValue as {} }
// })(value as B)
// })() as unknown as Fox.Acrew.Return<A, G>
// }
// }
// static acrewValue<A extends Fox.Acrew.Funcs, B extends Fox.Acrew.Callback<A>, G, F>(prein: F, funcs: A, callback: (a: B) => G) {
// return (input: Fox.Profound.Param<A> = {}): Fox.Acrew.Return<A, G> & F => {
// const funcsKeys = Object.keys(funcs)
// const valueFn = funcsKeys.reduce((acq: Fox.ChoosySpread.Func, key: string) => {
// return Fox.use(acq, (acqError, acqValue) => {
// if (acqError) throw acqError
// // NOTE: check if key already exists, we dont need to execute fun
// if (input && input[key as keyof Fox.Profound.Param<A>]) return acqValue
// return Fox.use(funcs[key], (fnError, fnValue) => {
// if (fnError) throw fnError
// return { ...acqValue, [key]: fnValue }
// })(input)
// })
// }, Fox.nest(input))
// return Fox.use(valueFn, (err, value) => {
// if (err) throw err
// return Fox.use(callback, (err, callbackValue) => {
// if (err) throw err
// return { ...value as B, ...callbackValue as {}, ...prein as {} }
// })(value as B)
// })() as unknown as Fox.Acrew.Return<A, G> & F
// }
// }
// NOTE If the value of function is false, throws error
static defalse<T extends Fox.Undefy.Func>(func: T, e: Error) {
return (...args: Parameters<T>) => {
return Fox.use(func, (error, value) => {
if (error) throw error
if (!value) throw e
return value
})(...args) as unknown as Fox.Undefy.Return<T>
}
}
// NOTE If the value of function is true, throws error
static detrue<T extends Fox.Undefy.Func>(func: T, e: Error) {
return (...args: Parameters<T>) => {
return Fox.use(func, (error, value) => {
if (error) throw error
if (value) throw e
return value
})(...args) as unknown as Fox.Undefy.Return<T>
}
}
static placeholder = <T>() => {
return <G extends string>(param: G) => {
return (a: Record<G, T>): T => {
throw new Error(`Need ${param}`)
}
}
}
static profoundRef = Symbol('Profound')
static isPlainObject(obj: any): obj is ({ [name: string]: any }) {
return obj && obj.constructor === Object || false;
}
static _pick(o: any, ...props: string[]) {
return Object.assign({}, ...props.map(prop => ({ [prop]: o[prop] })));
}
static profound<A extends Fox.Profound.SingleArgFuncs, G, B extends (a: Fox.Profound.Callback.ObjReturn<A>) => G, C extends Fox.Profound.Params.Value<A>>(funcs: A, callback?: B) {
function reduceObjectOfFuncs(funcs: any, input: any) {
if (!Fox.isPlainObject(input)) throw new Error('Needs to be plain object')
const keys = Object.keys(funcs)
return keys.reduce((acq: Fox.Profound.Func, key: string) => {
return Fox.use(acq, (acqError, acqValue) => {
if (acqError) throw acqError
if (acqValue && acqValue[key]) return acqValue
const isProfound = Boolean(funcs[key] &&
funcs[key].isProfound &&
funcs[key].isProfound === Fox.profoundRef &&
funcs[key].pass)
const fn = (isProfound) ? funcs[key].pass : funcs[key]
return Fox.use(fn, (fnError, fnValue) => {
if (fnError) throw fnError
const [value, data] = (isProfound) ? fnValue : [fnValue, {}]
return { ...acqValue, ...data, [key]: value }
})(acqValue)
})
}, Fox.nest(input))()
}
// NOTE returns (callbackValue)
// NOTE MARKER
function profoundInner(preInput: C): Fox.Profound.Return.Value<A, B, C> {
return Fox.use(reduceObjectOfFuncs, (err, input) => {
if (err) throw err
const keys = Object.keys(funcs)
if (!callback) return Fox._pick(input, ...keys)
return Fox.use(callback as Fox.Profound.Func, (err, callbackValue) => {
if (err) throw err
return callbackValue
})(input)
})(funcs, preInput)
}
// NOTE returns ([callbackValue, reduceObjectOfFuncsValues])
function pass(...preInput: any): any {
const prein = preInput[0] || {}
return Fox.use(reduceObjectOfFuncs, (err, input) => {
if (err) throw err
const keys = Object.keys(funcs)
if (!callback) return [Fox._pick(input, ...keys), input]
return Fox.use(callback as Fox.Profound.Func, (err, callbackValue) => {
if (err) throw err
return [callbackValue, input]
})(input)
})(funcs, prein)
}
profoundInner.isProfound = Fox.profoundRef
profoundInner.pass = pass
return profoundInner
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment