UNPKG

caught-core

Version:
568 lines (549 loc) 20.7 kB
var typeFlagToIndexMap = {}; function createFlag(type, flag, extra) { type = String(type); flag = String(flag); extra = extra ? String(extra) : ''; var rawUniFlag = "".concat(type, "-").concat(flag); var index = typeFlagToIndexMap[rawUniFlag]; index = index ? index++ : 0; typeFlagToIndexMap[flag] = index; return "".concat(rawUniFlag, "-").concat(index).concat(extra && '-' + extra); } function toRawType(val) { return Object.prototype.toString.call(val).slice(8, -1); } var hasProto = '__proto__' in {}; var isBoolean = function (val) { return typeof val === 'boolean'; }; var isObject = function (val) { return val !== null && typeof val === 'object'; }; var isPlainObject = function (val) { return toRawType(val) === 'object'; }; var isFunction = function (val) { return typeof val === 'function'; }; var isArray = function (val) { return Array.isArray(val); }; var isNative = function (Ctor) { return typeof Ctor === 'function' && /native code/.test(Ctor.toString()); }; var getObjectValues = function (obj) { var val = []; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { val.push(obj[key]); } } return val; }; function setInternalPropWritable(target, writable) { Object.defineProperties(target, { length: { writable: writable }, name: { writable: writable } }); } function forwardAdditionalPropetry(proxyFunc, original) { function forwardProp(source) { source.forEach(function (prop) { proxyFunc[prop] = original[prop]; }); } setInternalPropWritable(proxyFunc, true); forwardProp(Object.getOwnPropertyNames(original)); setInternalPropWritable(proxyFunc, false); try { if (isNative(Symbol)) { forwardProp(Object.getOwnPropertySymbols(original)); } } catch (error) { } } function coverProto(proxyFunc, original) { proxyFunc.prototype = original.prototype; if (hasProto) { proxyFunc.__proto__ = original.__proto__; } else { var proto_1 = Object.getPrototypeOf(original); Object.getOwnPropertyNames(proto_1).forEach(function (key) { Object.defineProperty(proxyFunc, key, { get: function () { return proto_1[key]; } }); }); } } function proxyCreater(caught) { var proxyId = 0; var scheduler = caught.scheduler; function proxyCaught(rawFunc, config) { if (!isFunction(rawFunc)) { if (process.env.NODE_ENV !== 'production') { console.error('The first parameter of createCaught must be the function type'); } return rawFunc; } var meta; var isObjectConfig = isPlainObject(config); if (isObjectConfig) { var others = config.others; if (others) { var _meta = (meta || (meta = {})); _meta.proxyCaught = { others: others }; } } var cProxyId = proxyId++; var type = 'proxyError'; var flag = isObjectConfig ? config.flag : config; var customInsertInfo = scheduler.createCustomInsert(type, flag, cProxyId); var proxyFunc = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } try { return rawFunc.apply(this, args); } catch (error) { if (isPlainObject(config) && config.withArgs) { var _meta = (meta || (meta = {})); (_meta.proxyCaught || (_meta.proxyCaught = {})).args = args; } var errorInfo = { error: error, meta: meta }; customInsertInfo(errorInfo); throw error; } }; coverProto(proxyFunc, rawFunc); forwardAdditionalPropetry(proxyFunc, rawFunc); return proxyFunc; } return proxyCaught; } function initProxy(caught) { caught.proxyCaught = proxyCreater(caught); } /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } function callHook(caught, hookName, args, handler) { var _a; var i = 0; var hooks; try { hooks = caught.hooksMap[hookName]; for (var i_1 = 0; i_1 < hooks.length; i_1++) { var cb = hooks[i_1]; if (isFunction(cb)) { var r = cb.apply(null, args); isFunction(handler) && handler(r); } } } catch (error) { if (process.env.NODE_ENV !== 'production') { console.error("Some errors occurred while executing ".concat((_a = hooks === null || hooks === void 0 ? void 0 : hooks.pluginNameList) === null || _a === void 0 ? void 0 : _a[i], " hooks")); } } } function updateMeta(originalInfo, meta) { var originalMeta = originalInfo.meta; if (isObject(meta)) { originalInfo.meta = originalMeta ? __assign(__assign({}, originalMeta), meta) : meta; } } var pending = true; var cbs = []; var fireCbs = function () { try { pending = true; cbs.forEach(function (cb) { return isFunction(cb) && cb(); }); } catch (error) { if (process.env.NODE_ENV !== 'production') { console.error('Error executing callback list'); } } finally { cbs.length = 0; } }; function fireLoop(callback, noWait) { cbs.push(callback); if (noWait) return fireCbs(); if (pending) { pending = false; if (isNative(Promise)) { Promise.resolve().then(fireCbs); } else { setTimeout(fireCbs); } } } var InfoList = (function (_super) { __extends(InfoList, _super); function InfoList() { return _super !== null && _super.apply(this, arguments) || this; } return InfoList; }(Array)); var HookNames; (function (HookNames) { HookNames["ADDHOOK"] = "addInfo"; HookNames["SUCCESSHOOK"] = "success"; HookNames["FAILHOOK"] = "fail"; HookNames["SCHEDULABLEHOOK"] = "schedulable"; HookNames["JSERRORHOOK"] = "jsError"; HookNames["STATICERRORHOOK"] = "staticError"; HookNames["PROMISERECTIONHOOK"] = "promiseRejection"; })(HookNames || (HookNames = {})); var HookArray = (function (_super) { __extends(HookArray, _super); function HookArray() { return _super !== null && _super.apply(this, arguments) || this; } return HookArray; }(Array)); var Scheduler = (function () { function Scheduler(caught) { this.caught = caught; this.queueIndex = -1; this.cachedFailMap = Object.create(null); this.failCount = 0; this.tickStart = false; this.stop = false; } Scheduler.prototype.pendingInsertInfo = function (info, isRetry) { var _this_1 = this; if (this.stop) return; callHook(this.caught, HookNames.ADDHOOK, [info], function (meta) { updateMeta(info, meta); }); var pendigQueue = this.createPendingQueue(); if (!isRetry) { var sameIndex = this.getSameIndex(pendigQueue, info); if (sameIndex > -1) { var preInfo = pendigQueue[sameIndex]; this.combineInfo(preInfo, info); } else { info.count = 1; pendigQueue.push(info); } } if (!this.tickStart) { this.tickStart = true; fireLoop(function () { try { _this_1.tickStart = false; _this_1.queueCache = null; if (pendigQueue.length) { var notify = _this_1.createNotify(pendigQueue, _this_1.queueIndex); var errorHandler = _this_1.caught.config.errorHandler; errorHandler(pendigQueue, notify); } } catch (error) { if (process.env.NODE_ENV !== 'production') { console.error('Some errors occurred during error reporting'); } } }, this.caught.config.sync); } }; Scheduler.prototype.createCustomInsert = function (type, flag, extra) { flag = createFlag(type, flag, extra); var _this = this; return function customInsertInfo(customInfo) { var info = __assign(__assign({}, customInfo), { type: type, flag: flag, time: +new Date }); _this.pendingInsertInfo(info); }; }; Scheduler.prototype.retryHandler = function () { var hasCachedFailInfos = Object.keys(this.cachedFailMap).length; hasCachedFailInfos && this.pendingInsertInfo({ flag: '', type: '', error: [], meta: [] }, true); }; Scheduler.prototype.getSameIndex = function (queue, info) { return queue.findIndex(function (_a) { var flag = _a.flag; return flag === info.flag; }); }; Scheduler.prototype.combineInfo = function (preInfo, info) { var count = preInfo.count; if (count >= this.caught.config.sameLimit) return; preInfo.count = ++count; preInfo.error = combineArr(preInfo.error, info.error); preInfo.meta = combineArr(preInfo.meta, info.meta); }; Scheduler.prototype.createInitQueue = function () { var uniqueInfos = []; for (var index in this.cachedFailMap) { var cq = this.cachedFailMap[index]; for (var i = 0; i < cq.length; i++) { var info = cq[i]; var sameIndex = this.getSameIndex(uniqueInfos, info); if (sameIndex > -1) { var preInfo = uniqueInfos[sameIndex]; this.combineInfo(preInfo, info); } else { uniqueInfos.push(info); } } delete this.cachedFailMap[index]; } return uniqueInfos; }; Scheduler.prototype.createPendingQueue = function () { if (this.tickStart) { return this.queueCache || []; } else { var pendigQueue = __spreadArray([], this.createInitQueue(), true); return (++this.queueIndex, this.queueCache = pendigQueue); } }; Scheduler.prototype.createNotify = function (currentPendigQueue, currentIndex) { function notify(result) { result = !!result; if (result) { callHook(this.caught, HookNames.SUCCESSHOOK, [currentPendigQueue, currentIndex]); } else { this.cachedFailMap[currentIndex] = currentPendigQueue; this.failCount++; if (this.failCount >= this.caught.config.failMaxNum) { this.stop = true; this.cachedFailMap = Object.create(null); } callHook(this.caught, HookNames.FAILHOOK, [currentPendigQueue, currentIndex]); } } return notify.bind(this); }; return Scheduler; }()); function initScheduler(caught) { var scheduler = caught.scheduler = new Scheduler(caught); var createCustomInsert = caught.createCustomInsert = scheduler.createCustomInsert.bind(scheduler); callHook(caught, HookNames.SCHEDULABLEHOOK, [createCustomInsert]); } function combineArr(father, child) { if (!father) return child; if (!child) return father; var isArrC = isArray(child); return isArray(father) ? isArrC ? __spreadArray(__spreadArray([], father, true), child, true) : father.push(child) : isArrC ? child.unshift(father) : [father, child]; } function setJsErrorEvent(caught) { var scheduler = caught.scheduler; var customInsertInfo = scheduler.createCustomInsert('jsError', 'emergency'); window.addEventListener('error', function (e) { var message = e.message, filename = e.filename, lineno = e.lineno, colno = e.colno, error = e.error; var jsErrorInfo = { error: error, message: message, filename: filename, lineno: lineno, colno: colno }; callHook(caught, HookNames.JSERRORHOOK, [jsErrorInfo, e], function (meta) { updateMeta(jsErrorInfo, meta); }); customInsertInfo(jsErrorInfo); }); } function setStaticErrorEvent(caught) { var scheduler = caught.scheduler; var customInsertInfo = scheduler.createCustomInsert('staticError', 'staticResource'); window.addEventListener('error', function (e) { var target = e.target; if (target !== window) { var resourceUrl = target.href || target.src; var staticErrorInfo_1 = { error: "url: ".concat(resourceUrl, " is fail to load"), meta: { targetName: target.localName, resourceUrl: resourceUrl }, }; callHook(caught, HookNames.STATICERRORHOOK, [staticErrorInfo_1, e], function (meta) { updateMeta(staticErrorInfo_1, meta); }); customInsertInfo(staticErrorInfo_1); } }, true); } function setPromiseRectionEvent(caught) { var scheduler = caught.scheduler; var customInsertInfo = scheduler.createCustomInsert('promiseRection', 'uncaughtPromise'); window.addEventListener('unhandledrejection', function (e) { var promiseRectionInfo = { error: (e.reason && e.reason.msg) || e.reason || '' }; callHook(caught, HookNames.PROMISERECTIONHOOK, [promiseRectionInfo, e], function (meta) { updateMeta(promiseRectionInfo, meta); }); customInsertInfo(promiseRectionInfo); }); } function initListener(caught) { var _a = getListerSwitch(caught.config.listeners), jsError = _a.jsError, staticError = _a.staticError, promiseRection = _a.promiseRection; jsError && setJsErrorEvent(caught); staticError && setStaticErrorEvent(caught); promiseRection && setPromiseRectionEvent(caught); } function getListerSwitch(listeners) { return isBoolean(listeners) ? { jsError: listeners, staticError: listeners, promiseRection: listeners } : listeners; } var defaultRetryTime = 5000; var defaultFailMaxNum = 5; var defaultSameLimit = 10; function createConfig(config) { return __assign({}, config); } function verifyConfig(rawConfig) { var errorHandler = rawConfig.errorHandler; if (!isFunction(errorHandler)) return false; return true; } function normalizeConfig(rawConfig) { var sameLimit = rawConfig.sameLimit, retryTime = rawConfig.retryTime, failMaxNum = rawConfig.failMaxNum, listeners = rawConfig.listeners, errorHandler = rawConfig.errorHandler, _a = rawConfig.plugins, plugins = _a === void 0 ? [] : _a; return { sameLimit: Number(sameLimit) || defaultSameLimit, retry: setBooleanValue(rawConfig, 'retry', true), retryTime: Number(retryTime) || defaultRetryTime, failMaxNum: Number(failMaxNum) || defaultFailMaxNum, listeners: normalizeListenerConfig(listeners, rawConfig), errorHandler: errorHandler, plugins: isArray(plugins) ? plugins : [], sync: setBooleanValue(rawConfig, 'retry', false) }; } function normalizeListenerConfig(listeners, rawConfig) { if (!('listeners' in rawConfig)) return true; if (!isPlainObject(listeners)) return isBoolean(listeners) ? listeners : !!listeners; return { jsError: setBooleanValue(listeners, 'jsError', true), staticError: setBooleanValue(listeners, 'staticError', true), promiseRection: setBooleanValue(listeners, 'promiseRection', true), }; } function setBooleanValue(nest, key, defaultVal) { var val = nest[key]; return key in nest ? isBoolean(val) ? val : !!val : defaultVal; } function createInnerConfig(caught, rawConfig) { if (verifyConfig(rawConfig)) { var normalized = normalizeConfig(rawConfig); return caught.config = createConfig(normalized); } } function createHooksMap() { var hooksMap = Object.create(null); getObjectValues(HookNames).forEach(function (key) { hooksMap[key] = []; }); return hooksMap; } function createHookRegister(hm) { var register = Object.create(null); getObjectValues(HookNames).forEach(function (key) { register[key] = function (pluginName, hook) { var hookArray = hm[key]; var pluginNameList = hookArray.pluginNameList || (hookArray.pluginNameList = []); if (pluginNameList.indexOf(pluginName) === -1) { hookArray.push(hook); pluginNameList.push(pluginName); } }; }); return register; } function initHooks(caught) { var hooksMap = caught.hooksMap = createHooksMap(); var plugins = caught.config.plugins; if (!isArray(plugins)) return; var register = createHookRegister(hooksMap); plugins.forEach(function (pluginInstance) { pluginInstance.apply(register); }); } function createCaught(rawConfig) { var caught = Object.create(null); createInnerConfig(caught, rawConfig); if (!caught.config) { if (process.env.NODE_ENV !== 'production') { console.error('Please check the configuration for createCaught'); } return; } initHooks(caught); initScheduler(caught); initListener(caught); initProxy(caught); return { createCustomInsert: caught.createCustomInsert, proxyCaught: caught.proxyCaught }; } export { HookArray, HookNames, InfoList, createCaught as default };