@platformos/pos-cli
Version:
Manage your platformOS application
1,439 lines (1,423 loc) • 192 kB
JavaScript
var Sentry = (function (exports) {
/** Console logging verbosity for the SDK. */
var LogLevel;
(function (LogLevel) {
/** No logs will be generated. */
LogLevel[LogLevel["None"] = 0] = "None";
/** Only SDK internal errors will be logged. */
LogLevel[LogLevel["Error"] = 1] = "Error";
/** Information useful for debugging the SDK will be logged. */
LogLevel[LogLevel["Debug"] = 2] = "Debug";
/** All SDK actions will be logged. */
LogLevel[LogLevel["Verbose"] = 3] = "Verbose";
})(LogLevel || (LogLevel = {}));
/** JSDoc */
(function (Severity) {
/** JSDoc */
Severity["Fatal"] = "fatal";
/** JSDoc */
Severity["Error"] = "error";
/** JSDoc */
Severity["Warning"] = "warning";
/** JSDoc */
Severity["Log"] = "log";
/** JSDoc */
Severity["Info"] = "info";
/** JSDoc */
Severity["Debug"] = "debug";
/** JSDoc */
Severity["Critical"] = "critical";
})(exports.Severity || (exports.Severity = {}));
// tslint:disable:completed-docs
// tslint:disable:no-unnecessary-qualifier no-namespace
(function (Severity) {
/**
* Converts a string-based level into a {@link Severity}.
*
* @param level string representation of Severity
* @returns Severity
*/
function fromString(level) {
switch (level) {
case 'debug':
return Severity.Debug;
case 'info':
return Severity.Info;
case 'warn':
case 'warning':
return Severity.Warning;
case 'error':
return Severity.Error;
case 'fatal':
return Severity.Fatal;
case 'critical':
return Severity.Critical;
case 'log':
default:
return Severity.Log;
}
}
Severity.fromString = fromString;
})(exports.Severity || (exports.Severity = {}));
/** The status of an Span. */
var SpanStatus;
(function (SpanStatus) {
/** The operation completed successfully. */
SpanStatus["Ok"] = "ok";
/** Deadline expired before operation could complete. */
SpanStatus["DeadlineExceeded"] = "deadline_exceeded";
/** 401 Unauthorized (actually does mean unauthenticated according to RFC 7235) */
SpanStatus["Unauthenticated"] = "unauthenticated";
/** 403 Forbidden */
SpanStatus["PermissionDenied"] = "permission_denied";
/** 404 Not Found. Some requested entity (file or directory) was not found. */
SpanStatus["NotFound"] = "not_found";
/** 429 Too Many Requests */
SpanStatus["ResourceExhausted"] = "resource_exhausted";
/** Client specified an invalid argument. 4xx. */
SpanStatus["InvalidArgument"] = "invalid_argument";
/** 501 Not Implemented */
SpanStatus["Unimplemented"] = "unimplemented";
/** 503 Service Unavailable */
SpanStatus["Unavailable"] = "unavailable";
/** Other/generic 5xx. */
SpanStatus["InternalError"] = "internal_error";
/** Unknown. Any non-standard HTTP status code. */
SpanStatus["UnknownError"] = "unknown_error";
/** The operation was cancelled (typically by the user). */
SpanStatus["Cancelled"] = "cancelled";
/** Already exists (409) */
SpanStatus["AlreadyExists"] = "already_exists";
/** Operation was rejected because the system is not in a state required for the operation's */
SpanStatus["FailedPrecondition"] = "failed_precondition";
/** The operation was aborted, typically due to a concurrency issue. */
SpanStatus["Aborted"] = "aborted";
/** Operation was attempted past the valid range. */
SpanStatus["OutOfRange"] = "out_of_range";
/** Unrecoverable data loss or corruption */
SpanStatus["DataLoss"] = "data_loss";
})(SpanStatus || (SpanStatus = {}));
// tslint:disable:no-unnecessary-qualifier no-namespace
(function (SpanStatus) {
/**
* Converts a HTTP status code into a {@link SpanStatus}.
*
* @param httpStatus The HTTP response status code.
* @returns The span status or {@link SpanStatus.UnknownError}.
*/
// tslint:disable-next-line:completed-docs
function fromHttpCode(httpStatus) {
if (httpStatus < 400) {
return SpanStatus.Ok;
}
if (httpStatus >= 400 && httpStatus < 500) {
switch (httpStatus) {
case 401:
return SpanStatus.Unauthenticated;
case 403:
return SpanStatus.PermissionDenied;
case 404:
return SpanStatus.NotFound;
case 409:
return SpanStatus.AlreadyExists;
case 413:
return SpanStatus.FailedPrecondition;
case 429:
return SpanStatus.ResourceExhausted;
default:
return SpanStatus.InvalidArgument;
}
}
if (httpStatus >= 500 && httpStatus < 600) {
switch (httpStatus) {
case 501:
return SpanStatus.Unimplemented;
case 503:
return SpanStatus.Unavailable;
case 504:
return SpanStatus.DeadlineExceeded;
default:
return SpanStatus.InternalError;
}
}
return SpanStatus.UnknownError;
}
SpanStatus.fromHttpCode = fromHttpCode;
})(SpanStatus || (SpanStatus = {}));
/** The status of an event. */
(function (Status) {
/** The status could not be determined. */
Status["Unknown"] = "unknown";
/** The event was skipped due to configuration or callbacks. */
Status["Skipped"] = "skipped";
/** The event was sent to Sentry successfully. */
Status["Success"] = "success";
/** The client is currently rate limited and will try again later. */
Status["RateLimit"] = "rate_limit";
/** The event could not be processed. */
Status["Invalid"] = "invalid";
/** A server-side error ocurred during submission. */
Status["Failed"] = "failed";
})(exports.Status || (exports.Status = {}));
// tslint:disable:completed-docs
// tslint:disable:no-unnecessary-qualifier no-namespace
(function (Status) {
/**
* Converts a HTTP status code into a {@link Status}.
*
* @param code The HTTP response status code.
* @returns The send status or {@link Status.Unknown}.
*/
function fromHttpCode(code) {
if (code >= 200 && code < 300) {
return Status.Success;
}
if (code === 429) {
return Status.RateLimit;
}
if (code >= 400 && code < 500) {
return Status.Invalid;
}
if (code >= 500) {
return Status.Failed;
}
return Status.Unknown;
}
Status.fromHttpCode = fromHttpCode;
})(exports.Status || (exports.Status = {}));
/**
* Consumes the promise and logs the error when it rejects.
* @param promise A promise to forget.
*/
const setPrototypeOf = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array ? setProtoOf : mixinProperties); // tslint:disable-line:no-unbound-method
/**
* setPrototypeOf polyfill using __proto__
*/
function setProtoOf(obj, proto) {
// @ts-ignore
obj.__proto__ = proto;
return obj;
}
/**
* setPrototypeOf polyfill using mixin
*/
function mixinProperties(obj, proto) {
for (const prop in proto) {
if (!obj.hasOwnProperty(prop)) {
// @ts-ignore
obj[prop] = proto[prop];
}
}
return obj;
}
/** An error emitted by Sentry SDKs and related utilities. */
class SentryError extends Error {
constructor(message) {
super(message);
this.message = message;
// tslint:disable:no-unsafe-any
this.name = new.target.prototype.constructor.name;
setPrototypeOf(this, new.target.prototype);
}
}
/**
* Checks whether given value's type is one of a few Error or Error-like
* {@link isError}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isError(wat) {
switch (Object.prototype.toString.call(wat)) {
case '[object Error]':
return true;
case '[object Exception]':
return true;
case '[object DOMException]':
return true;
default:
return isInstanceOf(wat, Error);
}
}
/**
* Checks whether given value's type is ErrorEvent
* {@link isErrorEvent}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isErrorEvent(wat) {
return Object.prototype.toString.call(wat) === '[object ErrorEvent]';
}
/**
* Checks whether given value's type is DOMError
* {@link isDOMError}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isDOMError(wat) {
return Object.prototype.toString.call(wat) === '[object DOMError]';
}
/**
* Checks whether given value's type is DOMException
* {@link isDOMException}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isDOMException(wat) {
return Object.prototype.toString.call(wat) === '[object DOMException]';
}
/**
* Checks whether given value's type is a string
* {@link isString}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isString(wat) {
return Object.prototype.toString.call(wat) === '[object String]';
}
/**
* Checks whether given value's is a primitive (undefined, null, number, boolean, string)
* {@link isPrimitive}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isPrimitive(wat) {
return wat === null || (typeof wat !== 'object' && typeof wat !== 'function');
}
/**
* Checks whether given value's type is an object literal
* {@link isPlainObject}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isPlainObject(wat) {
return Object.prototype.toString.call(wat) === '[object Object]';
}
/**
* Checks whether given value's type is an Event instance
* {@link isEvent}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isEvent(wat) {
// tslint:disable-next-line:strict-type-predicates
return typeof Event !== 'undefined' && isInstanceOf(wat, Event);
}
/**
* Checks whether given value's type is an Element instance
* {@link isElement}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isElement(wat) {
// tslint:disable-next-line:strict-type-predicates
return typeof Element !== 'undefined' && isInstanceOf(wat, Element);
}
/**
* Checks whether given value's type is an regexp
* {@link isRegExp}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isRegExp(wat) {
return Object.prototype.toString.call(wat) === '[object RegExp]';
}
/**
* Checks whether given value has a then function.
* @param wat A value to be checked.
*/
function isThenable(wat) {
// tslint:disable:no-unsafe-any
return Boolean(wat && wat.then && typeof wat.then === 'function');
// tslint:enable:no-unsafe-any
}
/**
* Checks whether given value's type is a SyntheticEvent
* {@link isSyntheticEvent}.
*
* @param wat A value to be checked.
* @returns A boolean representing the result.
*/
function isSyntheticEvent(wat) {
// tslint:disable-next-line:no-unsafe-any
return isPlainObject(wat) && 'nativeEvent' in wat && 'preventDefault' in wat && 'stopPropagation' in wat;
}
/**
* Checks whether given value's type is an instance of provided constructor.
* {@link isInstanceOf}.
*
* @param wat A value to be checked.
* @param base A constructor to be used in a check.
* @returns A boolean representing the result.
*/
function isInstanceOf(wat, base) {
try {
// tslint:disable-next-line:no-unsafe-any
return wat instanceof base;
}
catch (_e) {
return false;
}
}
/**
* Truncates given string to the maximum characters count
*
* @param str An object that contains serializable values
* @param max Maximum number of characters in truncated string
* @returns string Encoded
*/
function truncate(str, max = 0) {
// tslint:disable-next-line:strict-type-predicates
if (typeof str !== 'string' || max === 0) {
return str;
}
return str.length <= max ? str : `${str.substr(0, max)}...`;
}
/**
* Join values in array
* @param input array of values to be joined together
* @param delimiter string to be placed in-between values
* @returns Joined values
*/
function safeJoin(input, delimiter) {
if (!Array.isArray(input)) {
return '';
}
const output = [];
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < input.length; i++) {
const value = input[i];
try {
output.push(String(value));
}
catch (e) {
output.push('[value cannot be serialized]');
}
}
return output.join(delimiter);
}
/**
* Checks if the value matches a regex or includes the string
* @param value The string value to be checked against
* @param pattern Either a regex or a string that must be contained in value
*/
function isMatchingPattern(value, pattern) {
if (isRegExp(pattern)) {
return pattern.test(value);
}
if (typeof pattern === 'string') {
return value.indexOf(pattern) !== -1;
}
return false;
}
/**
* Requires a module which is protected _against bundler minification.
*
* @param request The module path to resolve
*/
function dynamicRequire(mod, request) {
// tslint:disable-next-line: no-unsafe-any
return mod.require(request);
}
/**
* Checks whether we're in the Node.js or Browser environment
*
* @returns Answer to given question
*/
function isNodeEnv() {
// tslint:disable:strict-type-predicates
return Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]';
}
const fallbackGlobalObject = {};
/**
* Safely get global scope object
*
* @returns Global scope object
*/
function getGlobalObject() {
return (isNodeEnv()
? global
: typeof window !== 'undefined'
? window
: typeof self !== 'undefined'
? self
: fallbackGlobalObject);
}
/**
* UUID4 generator
*
* @returns string Generated UUID4.
*/
function uuid4() {
const global = getGlobalObject();
const crypto = global.crypto || global.msCrypto;
if (!(crypto === void 0) && crypto.getRandomValues) {
// Use window.crypto API if available
const arr = new Uint16Array(8);
crypto.getRandomValues(arr);
// set 4 in byte 7
// tslint:disable-next-line:no-bitwise
arr[3] = (arr[3] & 0xfff) | 0x4000;
// set 2 most significant bits of byte 9 to '10'
// tslint:disable-next-line:no-bitwise
arr[4] = (arr[4] & 0x3fff) | 0x8000;
const pad = (num) => {
let v = num.toString(16);
while (v.length < 4) {
v = `0${v}`;
}
return v;
};
return (pad(arr[0]) + pad(arr[1]) + pad(arr[2]) + pad(arr[3]) + pad(arr[4]) + pad(arr[5]) + pad(arr[6]) + pad(arr[7]));
}
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, c => {
// tslint:disable-next-line:no-bitwise
const r = (Math.random() * 16) | 0;
// tslint:disable-next-line:no-bitwise
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
/**
* Parses string form of URL into an object
* // borrowed from https://tools.ietf.org/html/rfc3986#appendix-B
* // intentionally using regex and not <a/> href parsing trick because React Native and other
* // environments where DOM might not be available
* @returns parsed URL object
*/
function parseUrl(url) {
if (!url) {
return {};
}
const match = url.match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/);
if (!match) {
return {};
}
// coerce to undefined values to empty string so we don't get 'undefined'
const query = match[6] || '';
const fragment = match[8] || '';
return {
host: match[4],
path: match[5],
protocol: match[2],
relative: match[5] + query + fragment,
};
}
/**
* Extracts either message or type+value from an event that can be used for user-facing logs
* @returns event's description
*/
function getEventDescription(event) {
if (event.message) {
return event.message;
}
if (event.exception && event.exception.values && event.exception.values[0]) {
const exception = event.exception.values[0];
if (exception.type && exception.value) {
return `${exception.type}: ${exception.value}`;
}
return exception.type || exception.value || event.event_id || '<unknown>';
}
return event.event_id || '<unknown>';
}
/** JSDoc */
function consoleSandbox(callback) {
const global = getGlobalObject();
const levels = ['debug', 'info', 'warn', 'error', 'log', 'assert'];
if (!('console' in global)) {
return callback();
}
const originalConsole = global.console;
const wrappedLevels = {};
// Restore all wrapped console methods
levels.forEach(level => {
if (level in global.console && originalConsole[level].__sentry_original__) {
wrappedLevels[level] = originalConsole[level];
originalConsole[level] = originalConsole[level].__sentry_original__;
}
});
// Perform callback manipulations
const result = callback();
// Revert restoration to wrapped state
Object.keys(wrappedLevels).forEach(level => {
originalConsole[level] = wrappedLevels[level];
});
return result;
}
/**
* Adds exception values, type and value to an synthetic Exception.
* @param event The event to modify.
* @param value Value of the exception.
* @param type Type of the exception.
* @hidden
*/
function addExceptionTypeValue(event, value, type) {
event.exception = event.exception || {};
event.exception.values = event.exception.values || [];
event.exception.values[0] = event.exception.values[0] || {};
event.exception.values[0].value = event.exception.values[0].value || value || '';
event.exception.values[0].type = event.exception.values[0].type || type || 'Error';
}
/**
* Adds exception mechanism to a given event.
* @param event The event to modify.
* @param mechanism Mechanism of the mechanism.
* @hidden
*/
function addExceptionMechanism(event, mechanism = {}) {
// TODO: Use real type with `keyof Mechanism` thingy and maybe make it better?
try {
// @ts-ignore
// tslint:disable:no-non-null-assertion
event.exception.values[0].mechanism = event.exception.values[0].mechanism || {};
Object.keys(mechanism).forEach(key => {
// @ts-ignore
event.exception.values[0].mechanism[key] = mechanism[key];
});
}
catch (_oO) {
// no-empty
}
}
/**
* A safe form of location.href
*/
function getLocationHref() {
try {
return document.location.href;
}
catch (oO) {
return '';
}
}
/**
* Given a child DOM element, returns a query-selector statement describing that
* and its ancestors
* e.g. [HTMLElement] => body > div > input#foo.btn[name=baz]
* @returns generated DOM path
*/
function htmlTreeAsString(elem) {
// try/catch both:
// - accessing event.target (see getsentry/raven-js#838, #768)
// - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly
// - can throw an exception in some circumstances.
try {
let currentElem = elem;
const MAX_TRAVERSE_HEIGHT = 5;
const MAX_OUTPUT_LEN = 80;
const out = [];
let height = 0;
let len = 0;
const separator = ' > ';
const sepLength = separator.length;
let nextStr;
while (currentElem && height++ < MAX_TRAVERSE_HEIGHT) {
nextStr = _htmlElementAsString(currentElem);
// bail out if
// - nextStr is the 'html' element
// - the length of the string that would be created exceeds MAX_OUTPUT_LEN
// (ignore this limit if we are on the first iteration)
if (nextStr === 'html' || (height > 1 && len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN)) {
break;
}
out.push(nextStr);
len += nextStr.length;
currentElem = currentElem.parentNode;
}
return out.reverse().join(separator);
}
catch (_oO) {
return '<unknown>';
}
}
/**
* Returns a simple, query-selector representation of a DOM element
* e.g. [HTMLElement] => input#foo.btn[name=baz]
* @returns generated DOM path
*/
function _htmlElementAsString(el) {
const elem = el;
const out = [];
let className;
let classes;
let key;
let attr;
let i;
if (!elem || !elem.tagName) {
return '';
}
out.push(elem.tagName.toLowerCase());
if (elem.id) {
out.push(`#${elem.id}`);
}
className = elem.className;
if (className && isString(className)) {
classes = className.split(/\s+/);
for (i = 0; i < classes.length; i++) {
out.push(`.${classes[i]}`);
}
}
const attrWhitelist = ['type', 'name', 'title', 'alt'];
for (i = 0; i < attrWhitelist.length; i++) {
key = attrWhitelist[i];
attr = elem.getAttribute(key);
if (attr) {
out.push(`[${key}="${attr}"]`);
}
}
return out.join('');
}
const INITIAL_TIME = Date.now();
let prevNow = 0;
const performanceFallback = {
now() {
let now = Date.now() - INITIAL_TIME;
if (now < prevNow) {
now = prevNow;
}
prevNow = now;
return now;
},
timeOrigin: INITIAL_TIME,
};
const crossPlatformPerformance = (() => {
if (isNodeEnv()) {
try {
const perfHooks = dynamicRequire(module, 'perf_hooks');
return perfHooks.performance;
}
catch (_) {
return performanceFallback;
}
}
if (getGlobalObject().performance) {
// Polyfill for performance.timeOrigin.
//
// While performance.timing.navigationStart is deprecated in favor of performance.timeOrigin, performance.timeOrigin
// is not as widely supported. Namely, performance.timeOrigin is undefined in Safari as of writing.
// tslint:disable-next-line:strict-type-predicates
if (performance.timeOrigin === undefined) {
// As of writing, performance.timing is not available in Web Workers in mainstream browsers, so it is not always a
// valid fallback. In the absence of a initial time provided by the browser, fallback to INITIAL_TIME.
// @ts-ignore
// tslint:disable-next-line:deprecation
performance.timeOrigin = (performance.timing && performance.timing.navigationStart) || INITIAL_TIME;
}
}
return getGlobalObject().performance || performanceFallback;
})();
/**
* Returns a timestamp in seconds with milliseconds precision since the UNIX epoch calculated with the monotonic clock.
*/
function timestampWithMs() {
return (crossPlatformPerformance.timeOrigin + crossPlatformPerformance.now()) / 1000;
}
const defaultRetryAfter = 60 * 1000; // 60 seconds
/**
* Extracts Retry-After value from the request header or returns default value
* @param now current unix timestamp
* @param header string representation of 'Retry-After' header
*/
function parseRetryAfterHeader(now, header) {
if (!header) {
return defaultRetryAfter;
}
const headerDelay = parseInt(`${header}`, 10);
if (!isNaN(headerDelay)) {
return headerDelay * 1000;
}
const headerDate = Date.parse(`${header}`);
if (!isNaN(headerDate)) {
return headerDate - now;
}
return defaultRetryAfter;
}
const defaultFunctionName = '<anonymous>';
/**
* Safely extract function name from itself
*/
function getFunctionName(fn) {
try {
if (!fn || typeof fn !== 'function') {
return defaultFunctionName;
}
return fn.name || defaultFunctionName;
}
catch (e) {
// Just accessing custom props in some Selenium environments
// can cause a "Permission denied" exception (see raven-js#495).
return defaultFunctionName;
}
}
// TODO: Implement different loggers for different environments
const global$1 = getGlobalObject();
/** Prefix for logging strings */
const PREFIX = 'Sentry Logger ';
/** JSDoc */
class Logger {
/** JSDoc */
constructor() {
this._enabled = false;
}
/** JSDoc */
disable() {
this._enabled = false;
}
/** JSDoc */
enable() {
this._enabled = true;
}
/** JSDoc */
log(...args) {
if (!this._enabled) {
return;
}
consoleSandbox(() => {
global$1.console.log(`${PREFIX}[Log]: ${args.join(' ')}`); // tslint:disable-line:no-console
});
}
/** JSDoc */
warn(...args) {
if (!this._enabled) {
return;
}
consoleSandbox(() => {
global$1.console.warn(`${PREFIX}[Warn]: ${args.join(' ')}`); // tslint:disable-line:no-console
});
}
/** JSDoc */
error(...args) {
if (!this._enabled) {
return;
}
consoleSandbox(() => {
global$1.console.error(`${PREFIX}[Error]: ${args.join(' ')}`); // tslint:disable-line:no-console
});
}
}
// Ensure we only have a single logger instance, even if multiple versions of @sentry/utils are being used
global$1.__SENTRY__ = global$1.__SENTRY__ || {};
const logger = global$1.__SENTRY__.logger || (global$1.__SENTRY__.logger = new Logger());
// tslint:disable:no-unsafe-any
/**
* Memo class used for decycle json objects. Uses WeakSet if available otherwise array.
*/
class Memo {
constructor() {
// tslint:disable-next-line
this._hasWeakSet = typeof WeakSet === 'function';
this._inner = this._hasWeakSet ? new WeakSet() : [];
}
/**
* Sets obj to remember.
* @param obj Object to remember
*/
memoize(obj) {
if (this._hasWeakSet) {
if (this._inner.has(obj)) {
return true;
}
this._inner.add(obj);
return false;
}
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < this._inner.length; i++) {
const value = this._inner[i];
if (value === obj) {
return true;
}
}
this._inner.push(obj);
return false;
}
/**
* Removes object from internal storage.
* @param obj Object to forget
*/
unmemoize(obj) {
if (this._hasWeakSet) {
this._inner.delete(obj);
}
else {
for (let i = 0; i < this._inner.length; i++) {
if (this._inner[i] === obj) {
this._inner.splice(i, 1);
break;
}
}
}
}
}
/**
* Wrap a given object method with a higher-order function
*
* @param source An object that contains a method to be wrapped.
* @param name A name of method to be wrapped.
* @param replacement A function that should be used to wrap a given method.
* @returns void
*/
function fill(source, name, replacement) {
if (!(name in source)) {
return;
}
const original = source[name];
const wrapped = replacement(original);
// Make sure it's a function first, as we need to attach an empty prototype for `defineProperties` to work
// otherwise it'll throw "TypeError: Object.defineProperties called on non-object"
// tslint:disable-next-line:strict-type-predicates
if (typeof wrapped === 'function') {
try {
wrapped.prototype = wrapped.prototype || {};
Object.defineProperties(wrapped, {
__sentry_original__: {
enumerable: false,
value: original,
},
});
}
catch (_Oo) {
// This can throw if multiple fill happens on a global object like XMLHttpRequest
// Fixes https://github.com/getsentry/sentry-javascript/issues/2043
}
}
source[name] = wrapped;
}
/**
* Encodes given object into url-friendly format
*
* @param object An object that contains serializable values
* @returns string Encoded
*/
function urlEncode(object) {
return Object.keys(object)
.map(
// tslint:disable-next-line:no-unsafe-any
key => `${encodeURIComponent(key)}=${encodeURIComponent(object[key])}`)
.join('&');
}
/**
* Transforms any object into an object literal with all it's attributes
* attached to it.
*
* @param value Initial source that we have to transform in order to be usable by the serializer
*/
function getWalkSource(value) {
if (isError(value)) {
const error = value;
const err = {
message: error.message,
name: error.name,
stack: error.stack,
};
for (const i in error) {
if (Object.prototype.hasOwnProperty.call(error, i)) {
err[i] = error[i];
}
}
return err;
}
if (isEvent(value)) {
const event = value;
const source = {};
source.type = event.type;
// Accessing event.target can throw (see getsentry/raven-js#838, #768)
try {
source.target = isElement(event.target)
? htmlTreeAsString(event.target)
: Object.prototype.toString.call(event.target);
}
catch (_oO) {
source.target = '<unknown>';
}
try {
source.currentTarget = isElement(event.currentTarget)
? htmlTreeAsString(event.currentTarget)
: Object.prototype.toString.call(event.currentTarget);
}
catch (_oO) {
source.currentTarget = '<unknown>';
}
// tslint:disable-next-line:strict-type-predicates
if (typeof CustomEvent !== 'undefined' && isInstanceOf(value, CustomEvent)) {
source.detail = event.detail;
}
for (const i in event) {
if (Object.prototype.hasOwnProperty.call(event, i)) {
source[i] = event;
}
}
return source;
}
return value;
}
/** Calculates bytes size of input string */
function utf8Length(value) {
// tslint:disable-next-line:no-bitwise
return ~-encodeURI(value).split(/%..|./).length;
}
/** Calculates bytes size of input object */
function jsonSize(value) {
return utf8Length(JSON.stringify(value));
}
/** JSDoc */
function normalizeToSize(object,
// Default Node.js REPL depth
depth = 3,
// 100kB, as 200kB is max payload size, so half sounds reasonable
maxSize = 100 * 1024) {
const serialized = normalize(object, depth);
if (jsonSize(serialized) > maxSize) {
return normalizeToSize(object, depth - 1, maxSize);
}
return serialized;
}
/** Transforms any input value into a string form, either primitive value or a type of the input */
function serializeValue(value) {
const type = Object.prototype.toString.call(value);
// Node.js REPL notation
if (typeof value === 'string') {
return value;
}
if (type === '[object Object]') {
return '[Object]';
}
if (type === '[object Array]') {
return '[Array]';
}
const normalized = normalizeValue(value);
return isPrimitive(normalized) ? normalized : type;
}
/**
* normalizeValue()
*
* Takes unserializable input and make it serializable friendly
*
* - translates undefined/NaN values to "[undefined]"/"[NaN]" respectively,
* - serializes Error objects
* - filter global objects
*/
// tslint:disable-next-line:cyclomatic-complexity
function normalizeValue(value, key) {
if (key === 'domain' && value && typeof value === 'object' && value._events) {
return '[Domain]';
}
if (key === 'domainEmitter') {
return '[DomainEmitter]';
}
if (typeof global !== 'undefined' && value === global) {
return '[Global]';
}
if (typeof window !== 'undefined' && value === window) {
return '[Window]';
}
if (typeof document !== 'undefined' && value === document) {
return '[Document]';
}
// React's SyntheticEvent thingy
if (isSyntheticEvent(value)) {
return '[SyntheticEvent]';
}
// tslint:disable-next-line:no-tautology-expression
if (typeof value === 'number' && value !== value) {
return '[NaN]';
}
if (value === void 0) {
return '[undefined]';
}
if (typeof value === 'function') {
return `[Function: ${getFunctionName(value)}]`;
}
return value;
}
/**
* Walks an object to perform a normalization on it
*
* @param key of object that's walked in current iteration
* @param value object to be walked
* @param depth Optional number indicating how deep should walking be performed
* @param memo Optional Memo class handling decycling
*/
function walk(key, value, depth = +Infinity, memo = new Memo()) {
// If we reach the maximum depth, serialize whatever has left
if (depth === 0) {
return serializeValue(value);
}
// If value implements `toJSON` method, call it and return early
// tslint:disable:no-unsafe-any
if (value !== null && value !== undefined && typeof value.toJSON === 'function') {
return value.toJSON();
}
// tslint:enable:no-unsafe-any
// If normalized value is a primitive, there are no branches left to walk, so we can just bail out, as theres no point in going down that branch any further
const normalized = normalizeValue(value, key);
if (isPrimitive(normalized)) {
return normalized;
}
// Create source that we will use for next itterations, either objectified error object (Error type with extracted keys:value pairs) or the input itself
const source = getWalkSource(value);
// Create an accumulator that will act as a parent for all future itterations of that branch
const acc = Array.isArray(value) ? [] : {};
// If we already walked that branch, bail out, as it's circular reference
if (memo.memoize(value)) {
return '[Circular ~]';
}
// Walk all keys of the source
for (const innerKey in source) {
// Avoid iterating over fields in the prototype if they've somehow been exposed to enumeration.
if (!Object.prototype.hasOwnProperty.call(source, innerKey)) {
continue;
}
// Recursively walk through all the child nodes
acc[innerKey] = walk(innerKey, source[innerKey], depth - 1, memo);
}
// Once walked through all the branches, remove the parent from memo storage
memo.unmemoize(value);
// Return accumulated values
return acc;
}
/**
* normalize()
*
* - Creates a copy to prevent original input mutation
* - Skip non-enumerablers
* - Calls `toJSON` if implemented
* - Removes circular references
* - Translates non-serializeable values (undefined/NaN/Functions) to serializable format
* - Translates known global objects/Classes to a string representations
* - Takes care of Error objects serialization
* - Optionally limit depth of final output
*/
function normalize(input, depth) {
try {
// tslint:disable-next-line:no-unsafe-any
return JSON.parse(JSON.stringify(input, (key, value) => walk(key, value, depth)));
}
catch (_oO) {
return '**non-serializable**';
}
}
/**
* Given any captured exception, extract its keys and create a sorted
* and truncated list that will be used inside the event message.
* eg. `Non-error exception captured with keys: foo, bar, baz`
*/
function extractExceptionKeysForMessage(exception, maxLength = 40) {
// tslint:disable:strict-type-predicates
const keys = Object.keys(getWalkSource(exception));
keys.sort();
if (!keys.length) {
return '[object has no keys]';
}
if (keys[0].length >= maxLength) {
return truncate(keys[0], maxLength);
}
for (let includedKeys = keys.length; includedKeys > 0; includedKeys--) {
const serialized = keys.slice(0, includedKeys).join(', ');
if (serialized.length > maxLength) {
continue;
}
if (includedKeys === keys.length) {
return serialized;
}
return truncate(serialized, maxLength);
}
return '';
}
// Slightly modified (no IE8 support, ES6) and transcribed to TypeScript
/** SyncPromise internal states */
var States;
(function (States) {
/** Pending */
States["PENDING"] = "PENDING";
/** Resolved / OK */
States["RESOLVED"] = "RESOLVED";
/** Rejected / Error */
States["REJECTED"] = "REJECTED";
})(States || (States = {}));
/**
* Thenable class that behaves like a Promise and follows it's interface
* but is not async internally
*/
class SyncPromise {
constructor(executor) {
this._state = States.PENDING;
this._handlers = [];
/** JSDoc */
this._resolve = (value) => {
this._setResult(States.RESOLVED, value);
};
/** JSDoc */
this._reject = (reason) => {
this._setResult(States.REJECTED, reason);
};
/** JSDoc */
this._setResult = (state, value) => {
if (this._state !== States.PENDING) {
return;
}
if (isThenable(value)) {
value.then(this._resolve, this._reject);
return;
}
this._state = state;
this._value = value;
this._executeHandlers();
};
// TODO: FIXME
/** JSDoc */
this._attachHandler = (handler) => {
this._handlers = this._handlers.concat(handler);
this._executeHandlers();
};
/** JSDoc */
this._executeHandlers = () => {
if (this._state === States.PENDING) {
return;
}
if (this._state === States.REJECTED) {
this._handlers.forEach(handler => {
if (handler.onrejected) {
handler.onrejected(this._value);
}
});
}
else {
this._handlers.forEach(handler => {
if (handler.onfulfilled) {
// tslint:disable-next-line:no-unsafe-any
handler.onfulfilled(this._value);
}
});
}
this._handlers = [];
};
try {
executor(this._resolve, this._reject);
}
catch (e) {
this._reject(e);
}
}
/** JSDoc */
toString() {
return '[object SyncPromise]';
}
/** JSDoc */
static resolve(value) {
return new SyncPromise(resolve => {
resolve(value);
});
}
/** JSDoc */
static reject(reason) {
return new SyncPromise((_, reject) => {
reject(reason);
});
}
/** JSDoc */
static all(collection) {
return new SyncPromise((resolve, reject) => {
if (!Array.isArray(collection)) {
reject(new TypeError(`Promise.all requires an array as input.`));
return;
}
if (collection.length === 0) {
resolve([]);
return;
}
let counter = collection.length;
const resolvedCollection = [];
collection.forEach((item, index) => {
SyncPromise.resolve(item)
.then(value => {
resolvedCollection[index] = value;
counter -= 1;
if (counter !== 0) {
return;
}
resolve(resolvedCollection);
})
.then(null, reject);
});
});
}
/** JSDoc */
then(onfulfilled, onrejected) {
return new SyncPromise((resolve, reject) => {
this._attachHandler({
onfulfilled: result => {
if (!onfulfilled) {
// TODO: ¯\_(ツ)_/¯
// TODO: FIXME
resolve(result);
return;
}
try {
resolve(onfulfilled(result));
return;
}
catch (e) {
reject(e);
return;
}
},
onrejected: reason => {
if (!onrejected) {
reject(reason);
return;
}
try {
resolve(onrejected(reason));
return;
}
catch (e) {
reject(e);
return;
}
},
});
});
}
/** JSDoc */
catch(onrejected) {
return this.then(val => val, onrejected);
}
/** JSDoc */
finally(onfinally) {
return new SyncPromise((resolve, reject) => {
let val;
let isRejected;
return this.then(value => {
isRejected = false;
val = value;
if (onfinally) {
onfinally();
}
}, reason => {
isRejected = true;
val = reason;
if (onfinally) {
onfinally();
}
}).then(() => {
if (isRejected) {
reject(val);
return;
}
// tslint:disable-next-line:no-unsafe-any
resolve(val);
});
});
}
}
/** A simple queue that holds promises. */
class PromiseBuffer {
constructor(_limit) {
this._limit = _limit;
/** Internal set of queued Promises */
this._buffer = [];
}
/**
* Says if the buffer is ready to take more requests
*/
isReady() {
return this._limit === undefined || this.length() < this._limit;
}
/**
* Add a promise to the queue.
*
* @param task Can be any PromiseLike<T>
* @returns The original promise.
*/
add(task) {
if (!this.isReady()) {
return SyncPromise.reject(new SentryError('Not adding Promise due to buffer limit reached.'));
}
if (this._buffer.indexOf(task) === -1) {
this._buffer.push(task);
}
task
.then(() => this.remove(task))
.then(null, () => this.remove(task).then(null, () => {
// We have to add this catch here otherwise we have an unhandledPromiseRejection
// because it's a new Promise chain.
}));
return task;
}
/**
* Remove a promise to the queue.
*
* @param task Can be any PromiseLike<T>
* @returns Removed promise.
*/
remove(task) {
const removedTask = this._buffer.splice(this._buffer.indexOf(task), 1)[0];
return removedTask;
}
/**
* This function returns the number of unresolved promises in the queue.
*/
length() {
return this._buffer.length;
}
/**
* This will drain the whole queue, returns true if queue is empty or drained.
* If timeout is provided and the queue takes longer to drain, the promise still resolves but with false.
*
* @param timeout Number in ms to wait until it resolves with false.
*/
drain(timeout) {
return new SyncPromise(resolve => {
const capturedSetTimeout = setTimeout(() => {
if (timeout && timeout > 0) {
resolve(false);
}
}, timeout);
SyncPromise.all(this._buffer)
.then(() => {
clearTimeout(capturedSetTimeout);
resolve(true);
})
.then(null, () => {
resolve(true);
});
});
}
}
/**
* Tells whether current environment supports Fetch API
* {@link supportsFetch}.
*
* @returns Answer to the given question.
*/
function supportsFetch() {
if (!('fetch' in getGlobalObject())) {
return false;
}
try {
// tslint:disable-next-line:no-unused-expression
new Headers();
// tslint:disable-next-line:no-unused-expression
new Request('');
// tslint:disable-next-line:no-unused-expression
new Response();
return true;
}
catch (e) {
return false;
}
}
/**
* isNativeFetch checks if the given function is a native implementation of fetch()
*/
functio