104 lines
2.4 KiB
TypeScript
104 lines
2.4 KiB
TypeScript
import toast from 'react-hot-toast'
|
|
|
|
type ExcludeErr<T> = Exclude<T, Error>
|
|
|
|
/**
|
|
* Similar to Error, but more lightweight, without the stack trace. It can also
|
|
* be used to represent a reason for not being able to provide an alternative,
|
|
* which isn't necessarily an error.
|
|
*/
|
|
export class Reason {
|
|
message: string
|
|
|
|
constructor(message: string) {
|
|
this.message = message
|
|
}
|
|
|
|
toError() {
|
|
return new Error(this.message)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is intentionally *not* exported due to misuse. We'd like to add a lint.
|
|
*/
|
|
function isErr<T>(value: ExcludeErr<T> | Error): value is Error {
|
|
return value instanceof Error
|
|
}
|
|
|
|
// Used to bubble errors up
|
|
export function err<T>(value: ExcludeErr<T> | Error): value is Error {
|
|
if (!isErr(value)) {
|
|
return false
|
|
}
|
|
|
|
// TODO: Remove this once we have a lint to prevent misuse of this function.
|
|
console.error(value)
|
|
|
|
return true
|
|
}
|
|
|
|
/** Takes array of maybe error and types narrows them into
|
|
* @returns [hasErr, arrayWithoutErr, arrayWithErr]
|
|
*/
|
|
export function cleanErrs<T>(
|
|
value: Array<ExcludeErr<T> | Error>
|
|
): [boolean, Array<ExcludeErr<T>>, Array<Error>] {
|
|
const argsWOutErr: Array<ExcludeErr<T>> = []
|
|
const argsWErr: Array<Error> = []
|
|
for (const v of value) {
|
|
if (isErr(v)) {
|
|
argsWErr.push(v)
|
|
} else {
|
|
argsWOutErr.push(v)
|
|
}
|
|
}
|
|
return [argsWOutErr.length !== value.length, argsWOutErr, argsWErr]
|
|
}
|
|
|
|
export function report(
|
|
message: string,
|
|
{ showToast }: { showToast: boolean } = { showToast: false }
|
|
) {
|
|
console.error(message)
|
|
if (showToast) {
|
|
toast.error(message, { id: 'error' })
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Report a promise rejection. The type of reason is `any` so that it matches
|
|
* Promise.prototype.catch.
|
|
*/
|
|
export function reportRejection(reason: any) {
|
|
report((reason ?? 'Unknown promise rejection').toString())
|
|
}
|
|
|
|
/**
|
|
* Report an error to the user. Trapping is the opposite of propagating an
|
|
* error. We should propagate errors in low-level functions and trap at the top
|
|
* level.
|
|
*/
|
|
export function trap<T>(
|
|
value: ExcludeErr<T> | Error,
|
|
opts?: {
|
|
altErr?: Error
|
|
suppress?: boolean
|
|
}
|
|
): value is Error {
|
|
if (!isErr(value)) {
|
|
return false
|
|
}
|
|
|
|
console.error(value)
|
|
opts?.suppress ||
|
|
toast.error((opts?.altErr ?? value ?? new Error('Unknown')).toString(), {
|
|
id: 'error',
|
|
})
|
|
return true
|
|
}
|
|
|
|
export function reject(errOrString: Error | string): Promise<never> {
|
|
return Promise.reject(errOrString)
|
|
}
|