export type Omit = Pick> export type PromiseRes = { result: 'resolve', value: T } | { result: 'reject', value: Error } export type Recommendation = { dependentId: string dependentTitle: string dependentIcon: string, description: string version?: string } import { OperatorFunction } from 'rxjs' import { map } from 'rxjs/operators' export function trace (t: T): T { console.log(`TRACE`, t) return t } // curried description. This allows e.g somePromise.thentraceDesc('my result')) export function traceDesc (description: string): (t: T) => T { return t => { console.log(`TRACE`, description, t) return t } } // for use in observables. This allows e.g. someObservable.pipe(traceM('my result')) // the practical equivalent of `tap(t => console.log(t, description))` export function traceWheel (description?: string): OperatorFunction { return description ? map(traceDesc(description)) : map(trace) } export function traceThrowDesc (description: string, t: T | undefined): T { if (!t) throw new Error(description) return t } export function thenReturn (act1 : () => Promise, t: T): Promise { return act1().then(() => t) } export function modulateTime (ts: Date, count: number, unit: 'days' | 'hours' | 'minutes' | 'seconds' ) { const ms = inMs(count, unit) const toReturn = new Date(ts) toReturn.setMilliseconds( toReturn.getMilliseconds() + ms) return toReturn } export function inMs ( count: number, unit: 'days' | 'hours' | 'minutes' | 'seconds' ) { switch (unit){ case 'seconds' : return count * 1000 case 'minutes' : return inMs(count * 60, 'seconds') case 'hours' : return inMs(count * 60, 'minutes') case 'days' : return inMs(count * 24, 'hours') } } export async function tryAll ( promises: [Promise, Promise]): Promise<[PromiseRes, PromiseRes]> export async function tryAll ( promises: Promise[] ): Promise[]> { return Promise.all(promises.map( p => p .then (r => ({ result: 'resolve' as 'resolve', value: r })) .catch(e => ({ result: 'reject' as 'reject', value: e })), )) } // arr1 - arr2 export function diff (arr1: T[], arr2: T[]): T[] { return arr1.filter(x => !arr2.includes(x)) } // arr1 & arr2 export function both (arr1: T[], arr2: T[]): T[] { return arr1.filter(x => arr2.includes(x)) } export async function doForAtLeast (promises: Promise[], minTime: number): Promise { const returned = await Promise.all(promises.concat(pauseFor(minTime))) returned.pop() return returned } export function isEmptyObject (obj: object): boolean { if (!obj) return true return Object.keys(obj).length === 0 && obj.constructor === Object } export function pauseFor (ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)) } export type Valued = { [s: string]: T } export function toObject (t: T[], map: (t0: T) => string): Valued { return t.reduce( (acc, next) => { acc[map(next)] = next return acc }, { } as Valued) } export function toDedupObject (t: T[], t2: T[], map: (t0: T) => string): Valued { return toObject(t.concat(t2), map) } export function update (t: Valued, u: Valued): Valued { return { ...t, ...u} } export function fromObject (o : Valued): T[] { return Object.values(o) } export function deepCloneUnknown (value: T): T { if (typeof value !== 'object' || value === null) { return value } if (Array.isArray(value)) { return deepCloneArray(value) } return deepCloneObject(value) } export function deepCloneObject (source: T) { const result = { } Object.keys(source).forEach(key => { const value = source[key] result[key] = deepCloneUnknown(value) }, { }) return result as T } export function deepCloneArray (collection: any) { return collection.map(value => { return deepCloneUnknown(value) }) } export function partitionArray (ts: T[], condition: (t: T) => boolean): [T[], T[]] { const yes = [] as T[] const no = [] as T[] ts.forEach(t => { if (condition(t)) { yes.push(t) } else { no.push(t) } }) return [yes, no] } export const chill = () => { } export const chillAsync = async () => { } export function uniqueBy (ts: T[], uniqueBy: (t: T) => string, prioritize: (t1: T, t2: T) => T) { return Object.values(ts.reduce((acc, next) => { const previousValue = acc[uniqueBy(next)] if (previousValue) { acc[uniqueBy(next)] = prioritize(acc[uniqueBy(next)], previousValue) } else { acc[uniqueBy(next)] = previousValue } return acc }, { })) } export function capitalizeFirstLetter (string: string): string { return string.charAt(0).toUpperCase() + string.slice(1) } export const exists = (t: any) => { return t !== undefined } export type DeepPartial = { [k in keyof T]?: DeepPartial }