pax_global_header00006660000000000000000000000064127240124760014517gustar00rootroot0000000000000052 comment=6b998300e8cc2f732d80f99b7ecea37c97ab6ded currently-unhandled-0.4.1/000077500000000000000000000000001272401247600155105ustar00rootroot00000000000000currently-unhandled-0.4.1/.editorconfig000066400000000000000000000002761272401247600201720ustar00rootroot00000000000000root = true [*] indent_style = tab end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [{package.json,*.yml}] indent_style = space indent_size = 2 currently-unhandled-0.4.1/.gitattributes000066400000000000000000000000351272401247600204010ustar00rootroot00000000000000* text=auto *.js text eol=lf currently-unhandled-0.4.1/.gitignore000066400000000000000000000000731272401247600175000ustar00rootroot00000000000000node_modules .nyc_output coverage browser-bluebird-test.js currently-unhandled-0.4.1/.travis.yml000066400000000000000000000004171272401247600176230ustar00rootroot00000000000000language: node_js node_js: - '6' - '5' - '4' - '0.12' - '0.10' before_script: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" - sleep 3 # give xvfb some time to start after_script: - 'cat coverage/lcov.info | ./node_modules/.bin/coveralls' currently-unhandled-0.4.1/browser-test.js000066400000000000000000000014211272401247600205040ustar00rootroot00000000000000var assert = require('assert'); var delay = require('delay'); var fn = require('./'); it('works in a browser', function () { var currentlyUnhandled = fn(); var messages = function () { return currentlyUnhandled().map(function (event) { return event.reason.message; }); }; var p1; var p2; p1 = Promise.reject(new Error('foo')); return delay(300).then(function () { assert.deepEqual(messages(), ['foo']); p2 = Promise.reject(new Error('bar')); return delay(300); }).then(function () { assert.deepEqual(messages(), ['foo', 'bar']); p1.catch(function () {}); return delay(300); }).then(function () { assert.deepEqual(messages(), ['bar']); p2.catch(function () {}); return delay(300); }).then(function () { assert.deepEqual(messages(), []); }); }); currently-unhandled-0.4.1/browser.js000066400000000000000000000010611272401247600175270ustar00rootroot00000000000000'use strict'; var core = require('./core'); function unwrapEvent(event) { if (event && event.detail && event.detail.promise) { return event.detail; } return event; } module.exports = function (w) { w = w || window; var c = core(); w.addEventListener('unhandledrejection', function (event) { event = unwrapEvent(event); c.onUnhandledRejection(event.reason, event.promise); }); w.addEventListener('rejectionhandled', function (event) { event = unwrapEvent(event); c.onRejectionHandled(event.promise); }); return c.currentlyUnhandled; }; currently-unhandled-0.4.1/core.js000066400000000000000000000013531272401247600170000ustar00rootroot00000000000000'use strict'; var arrayFindIndex = require('array-find-index'); module.exports = function () { var unhandledRejections = []; function onUnhandledRejection(reason, promise) { unhandledRejections.push({reason: reason, promise: promise}); } function onRejectionHandled(promise) { var index = arrayFindIndex(unhandledRejections, function (x) { return x.promise === promise; }); unhandledRejections.splice(index, 1); } function currentlyUnhandled() { return unhandledRejections.map(function (entry) { return { reason: entry.reason, promise: entry.promise }; }); } return { onUnhandledRejection: onUnhandledRejection, onRejectionHandled: onRejectionHandled, currentlyUnhandled: currentlyUnhandled }; }; currently-unhandled-0.4.1/index.js000066400000000000000000000003721272401247600171570ustar00rootroot00000000000000'use strict'; var core = require('./core'); module.exports = function (p) { p = p || process; var c = core(); p.on('unhandledRejection', c.onUnhandledRejection); p.on('rejectionHandled', c.onRejectionHandled); return c.currentlyUnhandled; }; currently-unhandled-0.4.1/license000066400000000000000000000021401272401247600170520ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) James Talmage (github.com/jamestalmage) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. currently-unhandled-0.4.1/package.json000066400000000000000000000025301272401247600177760ustar00rootroot00000000000000{ "name": "currently-unhandled", "version": "0.4.1", "description": "Track the list of currently unhandled promise rejections.", "license": "MIT", "repository": "jamestalmage/currently-unhandled", "author": { "name": "James Talmage", "email": "james@talmage.io", "url": "github.com/jamestalmage" }, "engines": { "node": ">=0.10.0" }, "scripts": { "test": "xo && nyc ava" }, "files": [ "index.js", "core.js", "browser.js" ], "keywords": [ "unhandled", "unhandledRejection", "rejected", "promises" ], "dependencies": { "array-find-index": "^1.0.1" }, "devDependencies": { "ava": "^0.15.1", "bluebird": "^3.4.0", "browserify": "^13.0.1", "coveralls": "^2.11.9", "delay": "^1.3.1", "is-ci": "^1.0.8", "karma": "^0.13.22", "karma-browserify": "^5.0.5", "karma-chrome-launcher": "^1.0.1", "karma-firefox-launcher": "^1.0.0", "karma-mocha": "^1.0.1", "mocha": "^2.5.3", "nyc": "^6.4.0", "watchify": "^3.7.0", "xo": "^0.15.0" }, "browser": { "./index.js": "./browser.js" }, "ava": { "files": "test.js" }, "nyc": { "reporter": [ "lcov", "text" ] }, "xo": { "ignores": [ "browser-bluebird-test.js" ], "envs": [ "browser", "mocha", "node" ] } } currently-unhandled-0.4.1/readme.md000066400000000000000000000027351272401247600172760ustar00rootroot00000000000000# currently-unhandled [![Build Status](https://travis-ci.org/jamestalmage/currently-unhandled.svg?branch=master)](https://travis-ci.org/jamestalmage/currently-unhandled) [![Coverage Status](https://coveralls.io/repos/github/jamestalmage/currently-unhandled/badge.svg?branch=master)](https://coveralls.io/github/jamestalmage/currently-unhandled?branch=master) > Track the list of currently unhandled promise rejections. ## Install ``` $ npm install --save currently-unhandled ``` ## Usage ```js const currentlyUnhandled = require('currently-unhandled')(); // <- note the invocation var fooError = new Error('foo'); var p = Promise.reject(new Error('foo')); // on the next tick - unhandled rejected promise is added to the list: currentlyUnhandled(); //=> [{promise: p, reason: fooError}]' p.catch(() => {}); // on the next tick - handled promise is now removed from the list: currentlyUnhandled(); //=> []; ``` ## API ### currentlyUnhandled() Returns an array of objects with `promise` and `reason` properties representing the rejected promises that currently do not have a rejection handler. The list grows and shrinks as unhandledRejections are published, and later handled. ## Browser Support This module can be bundled with `browserify`. At time of writing, it will work with native Promises in the Chrome browser only. For best cross-browser support, use `bluebird` instead of native Promise support in browsers. ## License MIT © [James Talmage](http://github.com/jamestalmage) currently-unhandled-0.4.1/test.js000066400000000000000000000101601272401247600170230ustar00rootroot00000000000000import fs from 'fs'; import path from 'path'; import {EventEmitter} from 'events'; import test from 'ava'; import delay from 'delay'; import isCi from 'is-ci'; import {Server as KarmaServer} from 'karma'; import browser from './browser'; import node from './'; const implementations = {browser, node}; fs.writeFileSync( path.join(__dirname, 'browser-bluebird-test.js'), 'var Promise = require("bluebird");\n' + fs.readFileSync( path.join(__dirname, 'browser-test.js'), 'utf8' ) ); const mocks = { node() { const ee = new EventEmitter(); return { mock: { on(name, handler) { ee.on(name, handler); } }, unhandled({promise, reason}) { ee.emit('unhandledRejection', reason, promise); }, handled({promise}) { ee.emit('rejectionHandled', promise); } }; }, browser() { const ee = new EventEmitter(); return { mock: { addEventListener(name, handler) { ee.on(name, handler); } }, unhandled({promise, reason}) { ee.emit('unhandledrejection', {promise, reason}); }, handled({promise, reason}) { ee.emit('rejectionhandled', {promise, reason}); } }; } }; function event(reason) { return { promise: {}, reason: reason }; } function messagesHelper(currentlyUnhandled) { return () => currentlyUnhandled().map(obj => obj.reason.message); } function generateTests(type) { function setup() { const mock = mocks[type](); const currentlyUnhandled = implementations[type](mock.mock); const messages = messagesHelper(currentlyUnhandled); return {mock, currentlyUnhandled, messages}; } test(`${type}: adds promises as they are rejected`, t => { const {mock, currentlyUnhandled, messages} = setup(); const event1 = event(new Error('foo')); mock.unhandled(event1); let unhandled = currentlyUnhandled(); t.is(unhandled.length, 1); t.is(unhandled[0].promise, event1.promise); t.is(unhandled[0].reason.message, 'foo'); const event2 = event(new Error('bar')); mock.unhandled(event2); t.is(currentlyUnhandled().length, 2); t.deepEqual(messages(), ['foo', 'bar']); }); function removalMacro(t, index, expected) { const {mock, messages} = setup(); const events = [ event(new Error('foo')), event(new Error('bar')), event(new Error('baz')) ]; events.forEach(mock.unhandled); t.deepEqual(messages(), ['foo', 'bar', 'baz']); mock.handled(events[index]); t.deepEqual(messages(), expected); } test(`${type}: removes promises as they are handled (from beginning)`, removalMacro, 0, ['bar', 'baz']); test(`${type}: removes promises as they are handled (from middle)`, removalMacro, 1, ['foo', 'baz']); test(`${type}: removes promises as they are handled (from middle)`, removalMacro, 2, ['foo', 'bar']); } generateTests('browser'); generateTests('node'); test.serial('node: works as advertised', async t => { var currentlyUnhandled = node(); var messages = messagesHelper(currentlyUnhandled); var p1 = Promise.reject(new Error('foo')); await delay(10); t.deepEqual(messages(), ['foo']); var p2 = Promise.reject(new Error('bar')); await delay(10); t.deepEqual(messages(), ['foo', 'bar']); p1.catch(() => {}); await delay(10); t.deepEqual(messages(), ['bar']); p2.catch(() => {}); await delay(10); t.deepEqual(messages(), []); }); function browserMacro(t, file, browsers) { new KarmaServer({ frameworks: ['browserify', 'mocha'], files: [file], browsers, preprocessors: { [file]: ['browserify'] }, browserify: { debug: true }, singleRun: true, autoWatch: false }, exitCode => { if (exitCode) { t.fail(`karma exited with: ${exitCode}`); } t.end(); }).start(); } if (!isCi) { test.serial.cb('actual browser (native promise)', browserMacro, 'browser-test.js', ['Chrome']); // eslint-disable-line ava/test-ended } test.serial.cb('actual browser (bluebird)', browserMacro, 'browser-bluebird-test.js', isCi ? ['Firefox'] : ['Firefox', 'Chrome']); // eslint-disable-line ava/test-ended test.todo('add Firefox as tested browser when it supports the feature'); test.todo('add Safari as tested browser when it supports the feature'); test.todo('add IE as tested browser when it supports the feature');