Last active
October 3, 2018 16:38
-
-
Save LexVocoder/782015fcf87decd9d35789e03c3928c9 to your computer and use it in GitHub Desktop.
DError - Observable-friendly errors with causes, inspired by VError ( DEPRECATED; see https://www.npmjs.com/package/yandere )
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Inspired by VError. | |
import { none, Option, some, option, None } from "ts-option"; | |
import * as extsprintf from 'extsprintf'; | |
import { defined, getType, toPrintable } from "./utilities"; | |
interface IError { | |
message: string; | |
name: string; | |
stack?: string; | |
} | |
//undefined, null, none, string, array, object, boolean | |
export function asIError(val: any): IError { | |
if (!defined(val)) { | |
return { | |
message: toPrintable(val), | |
name: getType(val), | |
stack: '', | |
} | |
} | |
if (defined(val.message) && defined(val.name) && defined(val.stack)) { | |
return val; | |
} | |
return { | |
message: defined(val.message) ? val.message : toPrintable(val), | |
name: defined(val.name) ? val.name : getType(val), | |
stack: defined(val.stack) ? val.stack : '', | |
} | |
} | |
// Returns the error names, messages, and stack traces for err and its causes. | |
// err's stack trace appears first. This is safe to call on anything | |
// matching the IError interface, including plain JS Error instances. | |
// | |
export function fullStack(err: IError): string { | |
var suffix = ''; | |
// `instanceof DError` was yielding false negatives | |
const cause: Option<IError> = (err && (<DError>err).cause) || none; | |
if (cause.isDefined) { | |
suffix = "\n" + | |
"/-----------\n" + // these are so hard to read otherwise | |
`| caused by: ` + fullStack(cause.get); | |
} | |
if (err.stack) { | |
return err.stack + suffix; | |
} else { | |
return `${err.name}: ${err.message}` + suffix; | |
} | |
} | |
// DError stands for "Deep Error". Unlike VError, this requires a cause. | |
// Why? Well, many things, particularly RxJS, have | |
// `any` types in their error handlers. VError, on the other hand, | |
// gets very confused when the cause is a string (or | |
// any other non-Error value). | |
// | |
// For documentation on formatting syntax, see `extsprintf`. | |
// | |
export class DError extends Error { | |
cause: Option<IError>; | |
constructor(cause: any, format: string, ...args: any[]) { | |
var myMessage = extsprintf.sprintf(format, ...args); | |
var fullMessage = myMessage; | |
const maybeCauseAsError: Option<IError> = isSomething(cause) ? some(asIError(cause)) : none; | |
if (maybeCauseAsError.isDefined) { | |
fullMessage += `: ${maybeCauseAsError.get.message}` | |
} | |
super(fullMessage); | |
this.cause = maybeCauseAsError; | |
this.name = 'DError'; | |
} | |
} | |
// true iff val is defined, non-null, and not `none` | |
function isSomething(val: any): boolean { | |
return defined(val) && val !== null && val !== none; | |
} | |
/** | |
function mapUnlessIsNothing<T>(val: any, fn: (any) => T): Option<T> { | |
if (!isSomething(val)) { | |
return none; | |
} | |
return option(fn(val)); | |
} | |
**/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment