posthog-js
Version:
Posthog-js allows you to automatically capture usage and send events to PostHog.
232 lines • 10.3 kB
JavaScript
;
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