UNPKG

posthog-js

Version:

Posthog-js allows you to automatically capture usage and send events to PostHog.

232 lines • 10.3 kB
"use strict"; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (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)); }; Object.defineProperty(exports, "__esModule", { value: true }); var globals_1 = require("@posthog/browser-common/utils/globals"); var globals_2 = require("../utils/globals"); var logger_1 = require("@posthog/browser-common/utils/logger"); var core_1 = require("@posthog/core"); var posthog_exceptions_1 = require("../posthog-exceptions"); var logger = (0, logger_1.createLogger)('[ExceptionAutocapture]'); var errorPropertiesBuilder = (0, posthog_exceptions_1.buildErrorPropertiesBuilder)(); // Exception autocapture must never throw into customer code. Do not log in the catch because // console.error may be instrumented and would re-enter exception autocapture. var safely = function (fn, fallback) { try { return fn(); } catch (_a) { return fallback; } }; var safelyBuildAndCapture = function (captureFn, buildProperties) { return safely(function () { return captureFn(buildProperties()); }, undefined); }; // Firefox throws on every read, write and call that touches an object from another compartment or // from a destroyed document, such as a handler left behind by a removed or cross-origin iframe. // The handler is probed inside the guard and called outside it, so an unreachable handler is // skipped while errors from a reachable one still reach the page. var isReachableFunction = function (handler) { return safely(function () { return (0, core_1.isFunction)(handler) && (0, core_1.isFunction)(handler.call); }, false); }; var markInstrumented = function (owner, key) { return safely(function () { owner[key].__POSTHOG_INSTRUMENTED__ = true; }, undefined); }; // The marker lives on the wrapper, so deleting it can throw on a frozen or unreachable handler. // It is guarded on its own so that failure cannot skip the restore, which is the functional half. var restore = function (owner, key, original) { safely(function () { var _a; (_a = owner[key]) === null || _a === void 0 ? true : delete _a.__POSTHOG_INSTRUMENTED__; }, undefined); safely(function () { owner[key] = original; }, undefined); }; // `window.onerror` exposes the location positionally, so preserve it when there is no Error object. var resolveOnErrorInput = function (_a) { var _b = __read(_a, 5), event = _b[0], source = _b[1], lineno = _b[2], colno = _b[3], error = _b[4]; if (error != null) { return error; } if ((0, core_1.isString)(event) && (0, core_1.isString)(source) && source.length > 0 && typeof ErrorEvent !== 'undefined') { try { return new ErrorEvent('error', { message: event, filename: source, lineno: lineno, colno: colno }); } catch (_c) { return event; } } return event; }; var wrapOnError = function (captureFn) { var win = globals_1.window; if (!win) { logger.info('window not available, cannot wrap onerror'); } var originalOnError = safely(function () { return win.onerror; }, undefined); win.onerror = function () { var _a; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } safelyBuildAndCapture(captureFn, function () { return errorPropertiesBuilder.buildFromUnknown(resolveOnErrorInput(args), { mechanism: { handled: false }, }); }); if (!isReachableFunction(originalOnError)) { return false; } return (_a = originalOnError.apply(void 0, __spreadArray([], __read(args), false))) !== null && _a !== void 0 ? _a : false; }; markInstrumented(win, 'onerror'); return function () { return restore(win, 'onerror', originalOnError); }; }; var wrapUnhandledRejection = function (captureFn, defaultReturnValue) { if (defaultReturnValue === void 0) { defaultReturnValue = false; } var win = globals_1.window; if (!win) { logger.info('window not available, cannot wrap onUnhandledRejection'); } var originalOnUnhandledRejection = safely(function () { return win.onunhandledrejection; }, undefined); win.onunhandledrejection = function (ev) { var _a; safelyBuildAndCapture(captureFn, function () { return errorPropertiesBuilder.buildFromUnknown(ev, { mechanism: { handled: false }, }); }); if (!isReachableFunction(originalOnUnhandledRejection)) { return defaultReturnValue; } return (_a = originalOnUnhandledRejection(ev)) !== null && _a !== void 0 ? _a : false; }; markInstrumented(win, 'onunhandledrejection'); return function () { return restore(win, 'onunhandledrejection', originalOnUnhandledRejection); }; }; var wrapConsoleError = function (captureFn) { var con = console; if (!con) { logger.info('console not available, cannot wrap console.error'); } var originalConsoleError = safely(function () { return con.error; }, undefined); con.error = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } // Check before selecting an Error argument, which loses the diagnostic prefix. // SDK loggers pass the prefix and diagnostic separately, including in older bundles. var isInternalDiagnostic = safely(function () { return args.length > 1 && (0, core_1.isString)(args[0]) && (/^\[PostHog\.js\](?: |$)/.test(args[0]) || args[0] === 'rrweb logger error:'); }, false); if (!isInternalDiagnostic) { safelyBuildAndCapture(captureFn, function () { var event; if (args.length == 1) { event = args[0]; } else { event = args.join(' '); } var error = args.find(function (arg) { return arg instanceof Error; }); return errorPropertiesBuilder.buildFromUnknown(error || event, { mechanism: { handled: true, type: 'onconsole' }, syntheticException: new Error('PostHog syntheticException'), skipFirstLines: 2, }); }); } if (isReachableFunction(originalConsoleError)) { originalConsoleError.apply(void 0, __spreadArray([], __read(args), false)); } }; markInstrumented(con, 'error'); return function () { return restore(con, 'error', originalConsoleError); }; }; var posthogErrorWrappingFunctions = { wrapOnError: wrapOnError, wrapUnhandledRejection: wrapUnhandledRejection, wrapConsoleError: wrapConsoleError, }; globals_2.assignableWindow.__PosthogExtensions__ = globals_2.assignableWindow.__PosthogExtensions__ || {}; globals_2.assignableWindow.__PosthogExtensions__.errorWrappingFunctions = posthogErrorWrappingFunctions; // we used to put these on window, and now we put them on __PosthogExtensions__ // but that means that old clients which lazily load this extension are looking in the wrong place // yuck, // so we also put them directly on the window // when 1.161.1 is the oldest version seen in production we can remove this globals_2.assignableWindow.posthogErrorWrappingFunctions = posthogErrorWrappingFunctions; var extendPostHogWithExceptionAutocapture = function (instance, response) { if (!(0, core_1.isFunction)(instance === null || instance === void 0 ? void 0 : instance.capture)) { return; } var autocaptureExceptions = response === null || response === void 0 ? void 0 : response.autocaptureExceptions; var errorsToIgnore = !(0, core_1.isNull)(autocaptureExceptions) && typeof autocaptureExceptions === 'object' && (0, core_1.isArray)(autocaptureExceptions.errors_to_ignore) ? autocaptureExceptions.errors_to_ignore.reduce(function (rules, rule) { try { rules.push(new RegExp(rule)); } catch (error) { logger.error('Ignoring invalid legacy exception exclusion rule', rule, error); } return rules; }, []) : []; var sendExceptionEvent = function (properties) { var _a; try { (_a = instance.capture) === null || _a === void 0 ? void 0 : _a.call(instance, '$exception', properties, { _noTruncate: true, _batchKey: 'exceptionEvent', _noHeatmaps: true, }); } catch (error) { logger.error('Failed to capture exception for a legacy client', error); } }; var captureException = function (properties) { if (errorsToIgnore.some(function (regex) { return properties.$exception_list.some(function (exception) { return regex.test(exception.value || ''); }); })) { return; } sendExceptionEvent(properties); }; wrapOnError(captureException); wrapUnhandledRejection(sendExceptionEvent, true); }; globals_2.assignableWindow.extendPostHogWithExceptionAutoCapture = extendPostHogWithExceptionAutocapture; globals_2.assignableWindow.extendPostHogWithExceptionAutocapture = extendPostHogWithExceptionAutocapture; exports.default = posthogErrorWrappingFunctions; //# sourceMappingURL=exception-autocapture.js.map