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

package.build.cjs.normalize.js.map Maven / Gradle / Ivy

There is a newer version: 8.38.0
Show newest version
{"version":3,"file":"normalize.js","sources":["../../src/normalize.ts"],"sourcesContent":["import type { Primitive } from '@sentry/types';\n\nimport { isSyntheticEvent, isVueViewModel } from './is';\nimport type { MemoFunc } from './memo';\nimport { memoBuilder } from './memo';\nimport { convertToPlainObject } from './object';\nimport { getFunctionName } from './stacktrace';\n\ntype Prototype = { constructor: (...args: unknown[]) => unknown };\n// This is a hack to placate TS, relying on the fact that technically, arrays are objects with integer keys. Normally we\n// think of those keys as actual numbers, but `arr['0']` turns out to work just as well as `arr[0]`, and doing it this\n// way lets us use a single type in the places where behave as if we are only dealing with objects, even if some of them\n// might be arrays.\ntype ObjOrArray = { [key: string]: T };\n\n/**\n * Recursively normalizes the given object.\n *\n * - Creates a copy to prevent original input mutation\n * - Skips non-enumerable properties\n * - When stringifying, calls `toJSON` if implemented\n * - Removes circular references\n * - Translates non-serializable values (`undefined`/`NaN`/functions) to serializable format\n * - Translates known global objects/classes to a string representations\n * - Takes care of `Error` object serialization\n * - Optionally limits depth of final output\n * - Optionally limits number of properties/elements included in any single object/array\n *\n * @param input The object to be normalized.\n * @param depth The max depth to which to normalize the object. (Anything deeper stringified whole.)\n * @param maxProperties The max number of elements or properties to be included in any single array or\n * object in the normallized output.\n * @returns A normalized version of the object, or `\"**non-serializable**\"` if any errors are thrown during normalization.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function normalize(input: unknown, depth: number = 100, maxProperties: number = +Infinity): any {\n  try {\n    // since we're at the outermost level, we don't provide a key\n    return visit('', input, depth, maxProperties);\n  } catch (err) {\n    return { ERROR: `**non-serializable** (${err})` };\n  }\n}\n\n/** JSDoc */\nexport function normalizeToSize(\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  object: { [key: string]: any },\n  // Default Node.js REPL depth\n  depth: number = 3,\n  // 100kB, as 200kB is max payload size, so half sounds reasonable\n  maxSize: number = 100 * 1024,\n): T {\n  const normalized = normalize(object, depth);\n\n  if (jsonSize(normalized) > maxSize) {\n    return normalizeToSize(object, depth - 1, maxSize);\n  }\n\n  return normalized as T;\n}\n\n/**\n * Visits a node to perform normalization on it\n *\n * @param key The key corresponding to the given node\n * @param value The node to be visited\n * @param depth Optional number indicating the maximum recursion depth\n * @param maxProperties Optional maximum number of properties/elements included in any single object/array\n * @param memo Optional Memo class handling decycling\n */\nfunction visit(\n  key: string,\n  value: unknown,\n  depth: number = +Infinity,\n  maxProperties: number = +Infinity,\n  memo: MemoFunc = memoBuilder(),\n): Primitive | ObjOrArray {\n  const [memoize, unmemoize] = memo;\n\n  // Get the simple cases out of the way first\n  if (\n    value == null || // this matches null and undefined -> eqeq not eqeqeq\n    (['number', 'boolean', 'string'].includes(typeof value) && !Number.isNaN(value))\n  ) {\n    return value as Primitive;\n  }\n\n  const stringified = stringifyValue(key, value);\n\n  // Anything we could potentially dig into more (objects or arrays) will have come back as `\"[object XXXX]\"`.\n  // Everything else will have already been serialized, so if we don't see that pattern, we're done.\n  if (!stringified.startsWith('[object ')) {\n    return stringified;\n  }\n\n  // From here on, we can assert that `value` is either an object or an array.\n\n  // Do not normalize objects that we know have already been normalized. As a general rule, the\n  // \"__sentry_skip_normalization__\" property should only be used sparingly and only should only be set on objects that\n  // have already been normalized.\n  if ((value as ObjOrArray)['__sentry_skip_normalization__']) {\n    return value as ObjOrArray;\n  }\n\n  // We can set `__sentry_override_normalization_depth__` on an object to ensure that from there\n  // We keep a certain amount of depth.\n  // This should be used sparingly, e.g. we use it for the redux integration to ensure we get a certain amount of state.\n  const remainingDepth =\n    typeof (value as ObjOrArray)['__sentry_override_normalization_depth__'] === 'number'\n      ? ((value as ObjOrArray)['__sentry_override_normalization_depth__'] as number)\n      : depth;\n\n  // We're also done if we've reached the max depth\n  if (remainingDepth === 0) {\n    // At this point we know `serialized` is a string of the form `\"[object XXXX]\"`. Clean it up so it's just `\"[XXXX]\"`.\n    return stringified.replace('object ', '');\n  }\n\n  // If we've already visited this branch, bail out, as it's circular reference. If not, note that we're seeing it now.\n  if (memoize(value)) {\n    return '[Circular ~]';\n  }\n\n  // If the value has a `toJSON` method, we call it to extract more information\n  const valueWithToJSON = value as unknown & { toJSON?: () => unknown };\n  if (valueWithToJSON && typeof valueWithToJSON.toJSON === 'function') {\n    try {\n      const jsonValue = valueWithToJSON.toJSON();\n      // We need to normalize the return value of `.toJSON()` in case it has circular references\n      return visit('', jsonValue, remainingDepth - 1, maxProperties, memo);\n    } catch (err) {\n      // pass (The built-in `toJSON` failed, but we can still try to do it ourselves)\n    }\n  }\n\n  // At this point we know we either have an object or an array, we haven't seen it before, and we're going to recurse\n  // because we haven't yet reached the max depth. Create an accumulator to hold the results of visiting each\n  // property/entry, and keep track of the number of items we add to it.\n  const normalized = (Array.isArray(value) ? [] : {}) as ObjOrArray;\n  let numAdded = 0;\n\n  // Before we begin, convert`Error` and`Event` instances into plain objects, since some of each of their relevant\n  // properties are non-enumerable and otherwise would get missed.\n  const visitable = convertToPlainObject(value as ObjOrArray);\n\n  for (const visitKey in visitable) {\n    // Avoid iterating over fields in the prototype if they've somehow been exposed to enumeration.\n    if (!Object.prototype.hasOwnProperty.call(visitable, visitKey)) {\n      continue;\n    }\n\n    if (numAdded >= maxProperties) {\n      normalized[visitKey] = '[MaxProperties ~]';\n      break;\n    }\n\n    // Recursively visit all the child nodes\n    const visitValue = visitable[visitKey];\n    normalized[visitKey] = visit(visitKey, visitValue, remainingDepth - 1, maxProperties, memo);\n\n    numAdded++;\n  }\n\n  // Once we've visited all the branches, remove the parent from memo storage\n  unmemoize(value);\n\n  // Return accumulated values\n  return normalized;\n}\n\n/* eslint-disable complexity */\n/**\n * Stringify the given value. Handles various known special values and types.\n *\n * Not meant to be used on simple primitives which already have a string representation, as it will, for example, turn\n * the number 1231 into \"[Object Number]\", nor on `null`, as it will throw.\n *\n * @param value The value to stringify\n * @returns A stringified representation of the given value\n */\nfunction stringifyValue(\n  key: unknown,\n  // this type is a tiny bit of a cheat, since this function does handle NaN (which is technically a number), but for\n  // our internal use, it'll do\n  value: Exclude,\n): string {\n  try {\n    if (key === 'domain' && value && typeof value === 'object' && (value as { _events: unknown })._events) {\n      return '[Domain]';\n    }\n\n    if (key === 'domainEmitter') {\n      return '[DomainEmitter]';\n    }\n\n    // It's safe to use `global`, `window`, and `document` here in this manner, as we are asserting using `typeof` first\n    // which won't throw if they are not present.\n\n    if (typeof global !== 'undefined' && value === global) {\n      return '[Global]';\n    }\n\n    // eslint-disable-next-line no-restricted-globals\n    if (typeof window !== 'undefined' && value === window) {\n      return '[Window]';\n    }\n\n    // eslint-disable-next-line no-restricted-globals\n    if (typeof document !== 'undefined' && value === document) {\n      return '[Document]';\n    }\n\n    if (isVueViewModel(value)) {\n      return '[VueViewModel]';\n    }\n\n    // React's SyntheticEvent thingy\n    if (isSyntheticEvent(value)) {\n      return '[SyntheticEvent]';\n    }\n\n    if (typeof value === 'number' && value !== value) {\n      return '[NaN]';\n    }\n\n    if (typeof value === 'function') {\n      return `[Function: ${getFunctionName(value)}]`;\n    }\n\n    if (typeof value === 'symbol') {\n      return `[${String(value)}]`;\n    }\n\n    // stringified BigInts are indistinguishable from regular numbers, so we need to label them to avoid confusion\n    if (typeof value === 'bigint') {\n      return `[BigInt: ${String(value)}]`;\n    }\n\n    // Now that we've knocked out all the special cases and the primitives, all we have left are objects. Simply casting\n    // them to strings means that instances of classes which haven't defined their `toStringTag` will just come out as\n    // `\"[object Object]\"`. If we instead look at the constructor's name (which is the same as the name of the class),\n    // we can make sure that only plain objects come out that way.\n    const objName = getConstructorName(value);\n\n    // Handle HTML Elements\n    if (/^HTML(\\w*)Element$/.test(objName)) {\n      return `[HTMLElement: ${objName}]`;\n    }\n\n    return `[object ${objName}]`;\n  } catch (err) {\n    return `**non-serializable** (${err})`;\n  }\n}\n/* eslint-enable complexity */\n\nfunction getConstructorName(value: unknown): string {\n  const prototype: Prototype | null = Object.getPrototypeOf(value);\n\n  return prototype ? prototype.constructor.name : 'null prototype';\n}\n\n/** Calculates bytes size of input string */\nfunction utf8Length(value: string): number {\n  // eslint-disable-next-line no-bitwise\n  return ~-encodeURI(value).split(/%..|./).length;\n}\n\n/** Calculates bytes size of input object */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction jsonSize(value: any): number {\n  return utf8Length(JSON.stringify(value));\n}\n\n/**\n * Normalizes URLs in exceptions and stacktraces to a base path so Sentry can fingerprint\n * across platforms and working directory.\n *\n * @param url The URL to be normalized.\n * @param basePath The application base path.\n * @returns The normalized URL.\n */\nexport function normalizeUrlToBase(url: string, basePath: string): string {\n  const escapedBase = basePath\n    // Backslash to forward\n    .replace(/\\\\/g, '/')\n    // Escape RegExp special characters\n    .replace(/[|\\\\{}()[\\]^$+*?.]/g, '\\\\$&');\n\n  let newUrl = url;\n  try {\n    newUrl = decodeURI(url);\n  } catch (_Oo) {\n    // Sometime this breaks\n  }\n  return (\n    newUrl\n      .replace(/\\\\/g, '/')\n      .replace(/webpack:\\/?/g, '') // Remove intermediate base path\n      // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor\n      .replace(new RegExp(`(file://)?/*${escapedBase}/*`, 'ig'), 'app:///')\n  );\n}\n"],"names":["memo","memoBuilder","convertToPlainObject","isVueViewModel","isSyntheticEvent","getFunctionName"],"mappings":"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"}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy