All Downloads are FREE. Search and download functionalities are using the official Maven repository.

package.build.esm.object.js.map Maven / Gradle / Ivy

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy