summaryrefslogtreecommitdiff
path: root/shared/logger/node_modules/@sentry/utils/esm/normalize.js
diff options
context:
space:
mode:
authorrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
committerrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
commitbce557cc2dc767628bed6aac87301a1be7c5431b (patch)
treeb51a051228d01fe3306cd7626d4a96768aadb944 /shared/logger/node_modules/@sentry/utils/esm/normalize.js
init commit
Diffstat (limited to 'shared/logger/node_modules/@sentry/utils/esm/normalize.js')
-rw-r--r--shared/logger/node_modules/@sentry/utils/esm/normalize.js263
1 files changed, 263 insertions, 0 deletions
diff --git a/shared/logger/node_modules/@sentry/utils/esm/normalize.js b/shared/logger/node_modules/@sentry/utils/esm/normalize.js
new file mode 100644
index 0000000..5d52c58
--- /dev/null
+++ b/shared/logger/node_modules/@sentry/utils/esm/normalize.js
@@ -0,0 +1,263 @@
+import { isNaN, isSyntheticEvent } from './is.js';
+import { memoBuilder } from './memo.js';
+import { convertToPlainObject } from './object.js';
+import { getFunctionName } from './stacktrace.js';
+
+/**
+ * Recursively normalizes the given object.
+ *
+ * - Creates a copy to prevent original input mutation
+ * - Skips non-enumerable properties
+ * - When stringifying, calls `toJSON` if implemented
+ * - Removes circular references
+ * - Translates non-serializable values (`undefined`/`NaN`/functions) to serializable format
+ * - Translates known global objects/classes to a string representations
+ * - Takes care of `Error` object serialization
+ * - Optionally limits depth of final output
+ * - Optionally limits number of properties/elements included in any single object/array
+ *
+ * @param input The object to be normalized.
+ * @param depth The max depth to which to normalize the object. (Anything deeper stringified whole.)
+ * @param maxProperties The max number of elements or properties to be included in any single array or
+ * object in the normallized output.
+ * @returns A normalized version of the object, or `"**non-serializable**"` if any errors are thrown during normalization.
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function normalize(input, depth = 100, maxProperties = +Infinity) {
+ try {
+ // since we're at the outermost level, we don't provide a key
+ return visit('', input, depth, maxProperties);
+ } catch (err) {
+ return { ERROR: `**non-serializable** (${err})` };
+ }
+}
+
+/** JSDoc */
+function normalizeToSize(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ object,
+ // Default Node.js REPL depth
+ depth = 3,
+ // 100kB, as 200kB is max payload size, so half sounds reasonable
+ maxSize = 100 * 1024,
+) {
+ const normalized = normalize(object, depth);
+
+ if (jsonSize(normalized) > maxSize) {
+ return normalizeToSize(object, depth - 1, maxSize);
+ }
+
+ return normalized ;
+}
+
+/**
+ * Visits a node to perform normalization on it
+ *
+ * @param key The key corresponding to the given node
+ * @param value The node to be visited
+ * @param depth Optional number indicating the maximum recursion depth
+ * @param maxProperties Optional maximum number of properties/elements included in any single object/array
+ * @param memo Optional Memo class handling decycling
+ */
+function visit(
+ key,
+ value,
+ depth = +Infinity,
+ maxProperties = +Infinity,
+ memo = memoBuilder(),
+) {
+ const [memoize, unmemoize] = memo;
+
+ // Get the simple cases out of the way first
+ if (
+ value == null || // this matches null and undefined -> eqeq not eqeqeq
+ (['number', 'boolean', 'string'].includes(typeof value) && !isNaN(value))
+ ) {
+ return value ;
+ }
+
+ const stringified = stringifyValue(key, value);
+
+ // Anything we could potentially dig into more (objects or arrays) will have come back as `"[object XXXX]"`.
+ // Everything else will have already been serialized, so if we don't see that pattern, we're done.
+ if (!stringified.startsWith('[object ')) {
+ return stringified;
+ }
+
+ // From here on, we can assert that `value` is either an object or an array.
+
+ // Do not normalize objects that we know have already been normalized. As a general rule, the
+ // "__sentry_skip_normalization__" property should only be used sparingly and only should only be set on objects that
+ // have already been normalized.
+ if ((value )['__sentry_skip_normalization__']) {
+ return value ;
+ }
+
+ // We can set `__sentry_override_normalization_depth__` on an object to ensure that from there
+ // We keep a certain amount of depth.
+ // This should be used sparingly, e.g. we use it for the redux integration to ensure we get a certain amount of state.
+ const remainingDepth =
+ typeof (value )['__sentry_override_normalization_depth__'] === 'number'
+ ? ((value )['__sentry_override_normalization_depth__'] )
+ : depth;
+
+ // We're also done if we've reached the max depth
+ if (remainingDepth === 0) {
+ // At this point we know `serialized` is a string of the form `"[object XXXX]"`. Clean it up so it's just `"[XXXX]"`.
+ return stringified.replace('object ', '');
+ }
+
+ // If we've already visited this branch, bail out, as it's circular reference. If not, note that we're seeing it now.
+ if (memoize(value)) {
+ return '[Circular ~]';
+ }
+
+ // If the value has a `toJSON` method, we call it to extract more information
+ const valueWithToJSON = value ;
+ if (valueWithToJSON && typeof valueWithToJSON.toJSON === 'function') {
+ try {
+ const jsonValue = valueWithToJSON.toJSON();
+ // We need to normalize the return value of `.toJSON()` in case it has circular references
+ return visit('', jsonValue, remainingDepth - 1, maxProperties, memo);
+ } catch (err) {
+ // pass (The built-in `toJSON` failed, but we can still try to do it ourselves)
+ }
+ }
+
+ // 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
+ // because we haven't yet reached the max depth. Create an accumulator to hold the results of visiting each
+ // property/entry, and keep track of the number of items we add to it.
+ const normalized = (Array.isArray(value) ? [] : {}) ;
+ let numAdded = 0;
+
+ // Before we begin, convert`Error` and`Event` instances into plain objects, since some of each of their relevant
+ // properties are non-enumerable and otherwise would get missed.
+ const visitable = convertToPlainObject(value );
+
+ for (const visitKey in visitable) {
+ // Avoid iterating over fields in the prototype if they've somehow been exposed to enumeration.
+ if (!Object.prototype.hasOwnProperty.call(visitable, visitKey)) {
+ continue;
+ }
+
+ if (numAdded >= maxProperties) {
+ normalized[visitKey] = '[MaxProperties ~]';
+ break;
+ }
+
+ // Recursively visit all the child nodes
+ const visitValue = visitable[visitKey];
+ normalized[visitKey] = visit(visitKey, visitValue, remainingDepth - 1, maxProperties, memo);
+
+ numAdded++;
+ }
+
+ // Once we've visited all the branches, remove the parent from memo storage
+ unmemoize(value);
+
+ // Return accumulated values
+ return normalized;
+}
+
+/* eslint-disable complexity */
+/**
+ * Stringify the given value. Handles various known special values and types.
+ *
+ * Not meant to be used on simple primitives which already have a string representation, as it will, for example, turn
+ * the number 1231 into "[Object Number]", nor on `null`, as it will throw.
+ *
+ * @param value The value to stringify
+ * @returns A stringified representation of the given value
+ */
+function stringifyValue(
+ key,
+ // this type is a tiny bit of a cheat, since this function does handle NaN (which is technically a number), but for
+ // our internal use, it'll do
+ value,
+) {
+ try {
+ if (key === 'domain' && value && typeof value === 'object' && (value )._events) {
+ return '[Domain]';
+ }
+
+ if (key === 'domainEmitter') {
+ return '[DomainEmitter]';
+ }
+
+ // It's safe to use `global`, `window`, and `document` here in this manner, as we are asserting using `typeof` first
+ // which won't throw if they are not present.
+
+ if (typeof global !== 'undefined' && value === global) {
+ return '[Global]';
+ }
+
+ // eslint-disable-next-line no-restricted-globals
+ if (typeof window !== 'undefined' && value === window) {
+ return '[Window]';
+ }
+
+ // eslint-disable-next-line no-restricted-globals
+ if (typeof document !== 'undefined' && value === document) {
+ return '[Document]';
+ }
+
+ // React's SyntheticEvent thingy
+ if (isSyntheticEvent(value)) {
+ return '[SyntheticEvent]';
+ }
+
+ if (typeof value === 'number' && value !== value) {
+ return '[NaN]';
+ }
+
+ if (typeof value === 'function') {
+ return `[Function: ${getFunctionName(value)}]`;
+ }
+
+ if (typeof value === 'symbol') {
+ return `[${String(value)}]`;
+ }
+
+ // stringified BigInts are indistinguishable from regular numbers, so we need to label them to avoid confusion
+ if (typeof value === 'bigint') {
+ return `[BigInt: ${String(value)}]`;
+ }
+
+ // Now that we've knocked out all the special cases and the primitives, all we have left are objects. Simply casting
+ // them to strings means that instances of classes which haven't defined their `toStringTag` will just come out as
+ // `"[object Object]"`. If we instead look at the constructor's name (which is the same as the name of the class),
+ // we can make sure that only plain objects come out that way.
+ const objName = getConstructorName(value);
+
+ // Handle HTML Elements
+ if (/^HTML(\w*)Element$/.test(objName)) {
+ return `[HTMLElement: ${objName}]`;
+ }
+
+ return `[object ${objName}]`;
+ } catch (err) {
+ return `**non-serializable** (${err})`;
+ }
+}
+/* eslint-enable complexity */
+
+function getConstructorName(value) {
+ const prototype = Object.getPrototypeOf(value);
+
+ return prototype ? prototype.constructor.name : 'null prototype';
+}
+
+/** Calculates bytes size of input string */
+function utf8Length(value) {
+ // eslint-disable-next-line no-bitwise
+ return ~-encodeURI(value).split(/%..|./).length;
+}
+
+/** Calculates bytes size of input object */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function jsonSize(value) {
+ return utf8Length(JSON.stringify(value));
+}
+
+export { normalize, normalizeToSize, visit as walk };
+//# sourceMappingURL=normalize.js.map