chore: split app to commons and web (squash commit)

This commit is contained in:
Andrew Bastin
2022-12-02 02:57:46 -05:00
parent fb827e3586
commit 3d004f2322
535 changed files with 1487 additions and 501 deletions

View File

@@ -0,0 +1,44 @@
import { clone } from "lodash-es"
/**
* Sorts the array based on the sort func.
* NOTE: Creates a new array, if you don't need ref
* to original array, use `arrayUnsafeSort` for better perf
* @param sortFunc Sort function to sort against
*/
export const arraySort =
<T>(sortFunc: (a: T, b: T) => number) =>
(arr: T[]) => {
const newArr = clone(arr)
newArr.sort(sortFunc)
return newArr
}
/**
* Sorts an array based on the sort func.
* Unsafe because this sort mutates the passed array
* and returns it. So use it if you do not want the
* original array for better performance
* @param sortFunc sort function to sort against (same as Array.sort)
*/
export const arrayUnsafeSort =
<T>(sortFunc: (a: T, b: T) => number) =>
(arr: T[]) => {
arr.sort(sortFunc)
return arr
}
/**
* Equivalent to `Array.prototype.flatMap`
* @param mapFunc The map function
* @returns
*/
export const arrayFlatMap =
<T, U>(mapFunc: (value: T, index: number, arr: T[]) => U[]) =>
(arr: T[]) =>
arr.flatMap(mapFunc)
export const stringArrayJoin = (separator: string) => (arr: string[]) =>
arr.join(separator)

View File

@@ -0,0 +1,22 @@
/**
* Logs the current value and returns the same value
* @param x The value to log
* @returns The parameter `x` passed to this
*/
export const trace = <T>(x: T) => {
console.log(x)
return x
}
/**
* Logs the annotated current value and returns the same value
* @param name The name of the log
* @curried_param `x` The value to log
* @returns The parameter `x` passed to this
*/
export const namedTrace =
(name: string) =>
<T>(x: T) => {
console.log(`${name}:`, x)
return x
}

View File

@@ -0,0 +1,3 @@
export const throwError = (message: string): never => {
throw new Error(message)
}

View File

@@ -0,0 +1,19 @@
import * as TO from "fp-ts/TaskOption"
export const readFileAsText = (file: File) =>
TO.tryCatch(
() =>
new Promise<string>((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => {
resolve(reader.result as string)
}
reader.onerror = () => {
reject(new Error("File err"))
}
reader.readAsText(file)
})
)

View File

@@ -0,0 +1,12 @@
type FormDataEntry = {
key: string
value: string | Blob
}
export const toFormData = (values: FormDataEntry[]) => {
const formData = new FormData()
values.forEach(({ key, value }) => formData.append(key, value))
return formData
}

View File

@@ -0,0 +1,17 @@
import * as O from "fp-ts/Option"
import { flow } from "fp-ts/function"
/**
* Checks and Parses JSON string
* @param str Raw JSON data to be parsed
* @returns Option type with some(JSON data) or none
*/
export const safeParseJSON = (str: string): O.Option<object> =>
O.tryCatch(() => JSON.parse(str))
/**
* Checks if given string is a JSON string
* @param str Raw string to be checked
* @returns If string is a JSON string
*/
export const isJSON = flow(safeParseJSON, O.isSome)

View File

@@ -0,0 +1,65 @@
import { pipe } from "fp-ts/function"
import { isEqual, cloneDeep } from "lodash-es"
import { JSPrimitive, TypeFromPrimitive } from "./primtive"
export const objRemoveKey =
<T, K extends keyof T>(key: K) =>
(obj: T): Omit<T, K> =>
pipe(cloneDeep(obj), (e) => {
delete e[key]
return e
})
export const objFieldMatches =
<T, K extends keyof T, V extends T[K]>(
fieldName: K,
matches: ReadonlyArray<V>
) =>
// eslint-disable-next-line no-unused-vars
(obj: T): obj is T & { [_ in K]: V } =>
matches.findIndex((x) => isEqual(obj[fieldName], x)) !== -1
export const objHasProperty =
<O extends object, K extends string, P extends JSPrimitive | undefined>(
prop: K,
type?: P
) =>
// eslint-disable-next-line
(obj: O): obj is O & { [_ in K]: TypeFromPrimitive<P> } =>
// eslint-disable-next-line
prop in obj && (type === undefined || typeof (obj as any)[prop] === type)
type TypeFromPrimitiveArray<P extends JSPrimitive | undefined> =
P extends "undefined"
? undefined
: P extends "object"
? object[] | null
: P extends "boolean"
? boolean[]
: P extends "number"
? number[]
: P extends "bigint"
? bigint[]
: P extends "string"
? string[]
: P extends "symbol"
? symbol[]
: P extends "function"
? Function[] // eslint-disable-line @typescript-eslint/ban-types
: unknown[]
// The ban-types silence is because in this case,
// we can't get the Function type info to make a better guess
export const objHasArrayProperty =
<O extends object, K extends string, P extends JSPrimitive>(
prop: K,
type: P
) =>
// eslint-disable-next-line
(obj: O): obj is O & { [_ in K]: TypeFromPrimitiveArray<P> } =>
prop in obj &&
Array.isArray((obj as any)[prop]) &&
(obj as any)[prop].every(
(val: unknown) => typeof val === type // eslint-disable-line
)

View File

@@ -0,0 +1,19 @@
import * as O from "fp-ts/Option"
import * as A from "fp-ts/Array"
import { pipe } from "fp-ts/function"
/**
* Tries to match one of the given predicates.
* If a predicate is matched, the associated value is returned in a Some.
* Else if none of the predicates is matched, None is returned.
* @param choice An array of tuples having a predicate function and the selected value
* @returns A function which takes the input and returns an Option
*/
export const optionChoose =
<T, V>(choice: Array<[(x: T) => boolean, V]>) =>
(input: T): O.Option<V> =>
pipe(
choice,
A.findFirst(([pred]) => pred(input)),
O.map(([, value]) => value)
)

View File

@@ -0,0 +1,37 @@
export type JSPrimitive =
| "undefined"
| "object"
| "boolean"
| "number"
| "bigint"
| "string"
| "symbol"
| "function"
export type TypeFromPrimitive<P extends JSPrimitive | undefined> =
P extends "undefined"
? undefined
: P extends "object"
? object | null // typeof null === "object"
: P extends "boolean"
? boolean
: P extends "number"
? number
: P extends "bigint"
? bigint
: P extends "string"
? string
: P extends "symbol"
? symbol
: P extends "function"
? Function // eslint-disable-line @typescript-eslint/ban-types
: unknown
// The ban-types silence is because in this case,
// we can't get the Function type info to make a better guess
export const isOfType =
<T extends JSPrimitive>(type: T) =>
(value: unknown): value is T =>
// eslint-disable-next-line valid-typeof
typeof value === type

View File

@@ -0,0 +1,44 @@
/**
* Converts an array of key-value tuples (for e.g ["key", "value"]), into a record.
* (for eg. output -> { "key": "value" })
* NOTE: This function will discard duplicate key occurances and only keep the last occurance. If you do not want that behaviour,
* use `tupleWithSamesKeysToRecord`.
* @param tuples Array of tuples ([key, value])
* @returns A record with value corresponding to the last occurance of that key
*/
export const tupleToRecord = <
KeyType extends string | number | symbol,
ValueType
>(
tuples: [KeyType, ValueType][]
): Record<KeyType, ValueType> =>
tuples.length > 0
? (Object.assign as any)(...tuples.map(([key, val]) => ({ [key]: val }))) // This is technically valid, but we have no way of telling TypeScript it is valid. Hence the assertion
: {}
/**
* Converts an array of key-value tuples (for e.g ["key", "value"]), into a record.
* (for eg. output -> { "key": ["value"] })
* NOTE: If you do not want the array as values (because of duplicate keys) and want to instead get the last occurance, use `tupleToRecord`
* @param tuples Array of tuples ([key, value])
* @returns A Record with values being arrays corresponding to each key occurance
*/
export const tupleWithSameKeysToRecord = <
KeyType extends string | number | symbol,
ValueType
>(
tuples: [KeyType, ValueType][]
): Record<KeyType, ValueType[]> => {
// By the end of the function we do ensure this typing, this can't be infered now though, hence the assertion
const out = {} as Record<KeyType, ValueType[]>
for (const [key, value] of tuples) {
if (!out[key]) {
out[key] = [value]
} else {
out[key].push(value)
}
}
return out
}

View File

@@ -0,0 +1,13 @@
import * as TE from "fp-ts/TaskEither"
/**
* A utility type which gives you the type of the left value of a TaskEither
*/
export type TELeftType<T extends TE.TaskEither<any, any>> =
T extends TE.TaskEither<
infer U,
// eslint-disable-next-line
infer _
>
? U
: never