123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- Object.defineProperty(exports, '__esModule', { value: true });
- const browser = require('./browser.js');
- const debugBuild = require('./debug-build.js');
- const is = require('./is.js');
- const logger = require('./logger.js');
- const string = require('./string.js');
- /**
- * Replace a method in an object with a wrapped version of itself.
- *
- * @param source An object that contains a method to be wrapped.
- * @param name The name of the method to be wrapped.
- * @param replacementFactory A higher-order function that takes the original version of the given method and returns a
- * wrapped version. Note: The function returned by `replacementFactory` needs to be a non-arrow function, in order to
- * preserve the correct value of `this`, and the original method must be called using `origMethod.call(this, <other
- * args>)` or `origMethod.apply(this, [<other args>])` (rather than being called directly), again to preserve `this`.
- * @returns void
- */
- function fill(source, name, replacementFactory) {
- if (!(name in source)) {
- return;
- }
- const original = source[name] ;
- const wrapped = replacementFactory(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"
- if (typeof wrapped === 'function') {
- markFunctionWrapped(wrapped, original);
- }
- source[name] = wrapped;
- }
- /**
- * Defines a non-enumerable property on the given object.
- *
- * @param obj The object on which to set the property
- * @param name The name of the property to be set
- * @param value The value to which to set the property
- */
- function addNonEnumerableProperty(obj, name, value) {
- try {
- Object.defineProperty(obj, name, {
- // enumerable: false, // the default, so we can save on bundle size by not explicitly setting it
- value: value,
- writable: true,
- configurable: true,
- });
- } catch (o_O) {
- debugBuild.DEBUG_BUILD && logger.logger.log(`Failed to add non-enumerable property "${name}" to object`, obj);
- }
- }
- /**
- * Remembers the original function on the wrapped function and
- * patches up the prototype.
- *
- * @param wrapped the wrapper function
- * @param original the original function that gets wrapped
- */
- function markFunctionWrapped(wrapped, original) {
- try {
- const proto = original.prototype || {};
- wrapped.prototype = original.prototype = proto;
- addNonEnumerableProperty(wrapped, '__sentry_original__', original);
- } catch (o_O) {} // eslint-disable-line no-empty
- }
- /**
- * This extracts the original function if available. See
- * `markFunctionWrapped` for more information.
- *
- * @param func the function to unwrap
- * @returns the unwrapped version of the function if available.
- */
- function getOriginalFunction(func) {
- return func.__sentry_original__;
- }
- /**
- * 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(key => `${encodeURIComponent(key)}=${encodeURIComponent(object[key])}`)
- .join('&');
- }
- /**
- * Transforms any `Error` or `Event` into a plain object with all of their enumerable properties, and some of their
- * non-enumerable properties attached.
- *
- * @param value Initial source that we have to transform in order for it to be usable by the serializer
- * @returns An Event or Error turned into an object - or the value argurment itself, when value is neither an Event nor
- * an Error.
- */
- function convertToPlainObject(
- value,
- )
- {
- if (is.isError(value)) {
- return {
- message: value.message,
- name: value.name,
- stack: value.stack,
- ...getOwnProperties(value),
- };
- } else if (is.isEvent(value)) {
- const newObj
- = {
- type: value.type,
- target: serializeEventTarget(value.target),
- currentTarget: serializeEventTarget(value.currentTarget),
- ...getOwnProperties(value),
- };
- if (typeof CustomEvent !== 'undefined' && is.isInstanceOf(value, CustomEvent)) {
- newObj.detail = value.detail;
- }
- return newObj;
- } else {
- return value;
- }
- }
- /** Creates a string representation of the target of an `Event` object */
- function serializeEventTarget(target) {
- try {
- return is.isElement(target) ? browser.htmlTreeAsString(target) : Object.prototype.toString.call(target);
- } catch (_oO) {
- return '<unknown>';
- }
- }
- /** Filters out all but an object's own properties */
- function getOwnProperties(obj) {
- if (typeof obj === 'object' && obj !== null) {
- const extractedProps = {};
- for (const property in obj) {
- if (Object.prototype.hasOwnProperty.call(obj, property)) {
- extractedProps[property] = (obj )[property];
- }
- }
- return extractedProps;
- } else {
- return {};
- }
- }
- /**
- * 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) {
- const keys = Object.keys(convertToPlainObject(exception));
- keys.sort();
- if (!keys.length) {
- return '[object has no keys]';
- }
- if (keys[0].length >= maxLength) {
- return string.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 string.truncate(serialized, maxLength);
- }
- return '';
- }
- /**
- * Given any object, return a new object having removed all fields whose value was `undefined`.
- * Works recursively on objects and arrays.
- *
- * Attention: This function keeps circular references in the returned object.
- */
- function dropUndefinedKeys(inputValue) {
- // This map keeps track of what already visited nodes map to.
- // Our Set - based memoBuilder doesn't work here because we want to the output object to have the same circular
- // references as the input object.
- const memoizationMap = new Map();
- // This function just proxies `_dropUndefinedKeys` to keep the `memoBuilder` out of this function's API
- return _dropUndefinedKeys(inputValue, memoizationMap);
- }
- function _dropUndefinedKeys(inputValue, memoizationMap) {
- if (isPojo(inputValue)) {
- // If this node has already been visited due to a circular reference, return the object it was mapped to in the new object
- const memoVal = memoizationMap.get(inputValue);
- if (memoVal !== undefined) {
- return memoVal ;
- }
- const returnValue = {};
- // Store the mapping of this value in case we visit it again, in case of circular data
- memoizationMap.set(inputValue, returnValue);
- for (const key of Object.keys(inputValue)) {
- if (typeof inputValue[key] !== 'undefined') {
- returnValue[key] = _dropUndefinedKeys(inputValue[key], memoizationMap);
- }
- }
- return returnValue ;
- }
- if (Array.isArray(inputValue)) {
- // If this node has already been visited due to a circular reference, return the array it was mapped to in the new object
- const memoVal = memoizationMap.get(inputValue);
- if (memoVal !== undefined) {
- return memoVal ;
- }
- const returnValue = [];
- // Store the mapping of this value in case we visit it again, in case of circular data
- memoizationMap.set(inputValue, returnValue);
- inputValue.forEach((item) => {
- returnValue.push(_dropUndefinedKeys(item, memoizationMap));
- });
- return returnValue ;
- }
- return inputValue;
- }
- function isPojo(input) {
- if (!is.isPlainObject(input)) {
- return false;
- }
- try {
- const name = (Object.getPrototypeOf(input) ).constructor.name;
- return !name || name === 'Object';
- } catch (e) {
- return true;
- }
- }
- /**
- * Ensure that something is an object.
- *
- * Turns `undefined` and `null` into `String`s and all other primitives into instances of their respective wrapper
- * classes (String, Boolean, Number, etc.). Acts as the identity function on non-primitives.
- *
- * @param wat The subject of the objectification
- * @returns A version of `wat` which can safely be used with `Object` class methods
- */
- function objectify(wat) {
- let objectified;
- switch (true) {
- case wat === undefined || wat === null:
- objectified = new String(wat);
- break;
- // Though symbols and bigints do have wrapper classes (`Symbol` and `BigInt`, respectively), for whatever reason
- // those classes don't have constructors which can be used with the `new` keyword. We therefore need to cast each as
- // an object in order to wrap it.
- case typeof wat === 'symbol' || typeof wat === 'bigint':
- objectified = Object(wat);
- break;
- // this will catch the remaining primitives: `String`, `Number`, and `Boolean`
- case is.isPrimitive(wat):
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- objectified = new (wat ).constructor(wat);
- break;
- // by process of elimination, at this point we know that `wat` must already be an object
- default:
- objectified = wat;
- break;
- }
- return objectified;
- }
- exports.addNonEnumerableProperty = addNonEnumerableProperty;
- exports.convertToPlainObject = convertToPlainObject;
- exports.dropUndefinedKeys = dropUndefinedKeys;
- exports.extractExceptionKeysForMessage = extractExceptionKeysForMessage;
- exports.fill = fill;
- exports.getOriginalFunction = getOriginalFunction;
- exports.markFunctionWrapped = markFunctionWrapped;
- exports.objectify = objectify;
- exports.urlEncode = urlEncode;
- //# sourceMappingURL=object.js.map
|