A modern client-side JavaScript error handler
Download the CJS, ESM, UMD versions or install via NPM:
npm install @ryanmorr/blunder
Blunder offers the ability to normalize, enhance, capture, and report JavaScript errors in the browser:
import { monitor, subscribe, dispatch } from '@ryanmorr/blunder';
// Listen for global runtime errors
monitor();
// Subscribe to be notified when an exception is dispatched
subscribe((exception) => {
// Log the exception to the server
navigator.sendBeacon('/path/to/error/logger', JSON.stringify(exception));
});
// Manually dispatch an exception
dispatch('An error occurred!');
Exception
is a subclass of Error
to help normalize and enhance error instances:
import { Exception } from '@ryanmorr/blunder';
// Create an exception just like a regular error
const exception = new Exception('error message');
// Normalizes the standard properties
exception.name;
exception.message;
exception.stack;
Add custom metadata as an optional second argument to help identify the error:
const exception = new Exception('error message', {
timestamp: Date.now(),
url: document.location.href,
userAgent: navigator.userAgent
});
// The metadata is stored within the `data` property
exception.data.timestamp;
exception.data.url;
exception.data.userAgent;
Supports the cause
property to facilitate error chaining:
try {
connect();
} catch(error) {
throw new Exception('Connection failed', {cause: error});
}
Extend Exception
to create your own custom subclasses:
class APIException extends Exception {
constructor(code, options){
super('error code: ' + code, options);
this.code = code;
}
}
Includes a toJSON
method that returns a key/value object representing all the properties of the exception. It will convert some non-JSON friendly values to friendly ones (such as functions) and will detect and avoid circular references:
function trySomething() {
try {
throw new Exception('error message', {source: trySomething});
} catch(exception) {
const serialized = exception.toJSON();
serialized.data.source; //=> "[Function: trySomething]"
}
}
You can override the serializable
method in subclasses to customize how an instance is serialized:
class MyException extends Exception {
serializable() {
return `${this.name}: ${this.message}`;
}
}
const exception = new MyException('An error occurred');
JSON.stringify(exception); //=> "MyException: An error occurred"
Exception
also has a static from
method that can be used to convert a value into an Exception
instance:
// Supports error messages as strings
const exception = Exception.from('error message');
// Supports normal error instances (will copy standard properties)
const error = new Error();
const exception = Exception.from(error);
// Supports creating instances of subclasses
const exception = CustomException.from('error message');
exception instanceof CustomException; //=> true
Subscribe a callback function to be invoked with an Exception
instance when an error is dispatched, it returns an unsubscribe function:
import { subscribe } from '@ryanmorr/blunder';
const unsubscribe = subscribe((exception) => {
// An exception was dispatched
});
// Unsubscribe from future error notifications
unsubscribe();
Manually dispatch an Exception
to the error subscribers and return the Exception
instance:
import { dispatch } from '@ryanmorr/blunder';
// Supports strings
const exception = dispatch('error message');
// Supports normal error instances
dispatch(new Error());
// Supports custom metadata as an optional second argument
dispatch('error message', {foo: 1, bar: 2});
// Supports `cause` property
try {
trySomething();
} catch(error) {
dispatch('error message', {cause: error});
}
Enable global error monitoring by listening for the error
, unhandledrejection
, and rejectionhandled
events. You can customize which events to listen for by providing a configuration object. It returns a function to disable monitoring:
import { monitor } from '@ryanmorr/blunder';
// Listen for all error events by default
const stop = monitor();
// Disable error monitoring
stop();
// Optionally specify which events to listen for
monitor({
error: true,
unhandledrejection: true,
rejectionhandled: false
});
Internally wraps a function in a try/catch block for easy error handling. If an error is raised, it is automatically converted to an Exception
instance, dispatched to error subscribers, and then returned:
import { attempt } from '@ryanmorr/blunder';
const [result, exception] = attempt(() => {
throw new Error();
});
// If `exception` is defined, the attempt failed
if (exception) {
console.error(exception);
}
// If `result` is defined, the attempt was successful
if (result) {
console.log(result);
}
Supports custom metadata as an optional second argument that will be added to the Exception
instance if an error should arise:
const [result, exception] = attempt(callback, {
viewportWidth: window.innerWidth,
viewportHeight: window.innerHeight
});
Supports async functionality via promises by utilizing async/await syntax:
// Use promises directly
const [result, exception] = await attempt(fetch('/path/to/resource'));
// Use a function that returns a promise
const [result, exception] = await attempt(() => fetch('/path/to/resource'));
This project is dedicated to the public domain as described by the Unlicense.