UNPKG

12.3 kBJavaScriptView Raw
1Object.defineProperty(exports, "__esModule", { value: true });
2var tslib_1 = require("tslib");
3var browser_1 = require("./browser");
4var is_1 = require("./is");
5var memo_1 = require("./memo");
6var stacktrace_1 = require("./stacktrace");
7var string_1 = require("./string");
8/**
9 * Replace a method in an object with a wrapped version of itself.
10 *
11 * @param source An object that contains a method to be wrapped.
12 * @param name The name of the method to be wrapped.
13 * @param replacementFactory A higher-order function that takes the original version of the given method and returns a
14 * wrapped version. Note: The function returned by `replacementFactory` needs to be a non-arrow function, in order to
15 * preserve the correct value of `this`, and the original method must be called using `origMethod.call(this, <other
16 * args>)` or `origMethod.apply(this, [<other args>])` (rather than being called directly), again to preserve `this`.
17 * @returns void
18 */
19function fill(source, name, replacementFactory) {
20 if (!(name in source)) {
21 return;
22 }
23 var original = source[name];
24 var wrapped = replacementFactory(original);
25 // Make sure it's a function first, as we need to attach an empty prototype for `defineProperties` to work
26 // otherwise it'll throw "TypeError: Object.defineProperties called on non-object"
27 if (typeof wrapped === 'function') {
28 try {
29 wrapped.prototype = wrapped.prototype || {};
30 Object.defineProperties(wrapped, {
31 __sentry_original__: {
32 enumerable: false,
33 value: original,
34 },
35 });
36 }
37 catch (_Oo) {
38 // This can throw if multiple fill happens on a global object like XMLHttpRequest
39 // Fixes https://github.com/getsentry/sentry-javascript/issues/2043
40 }
41 }
42 source[name] = wrapped;
43}
44exports.fill = fill;
45/**
46 * Encodes given object into url-friendly format
47 *
48 * @param object An object that contains serializable values
49 * @returns string Encoded
50 */
51function urlEncode(object) {
52 return Object.keys(object)
53 .map(function (key) { return encodeURIComponent(key) + "=" + encodeURIComponent(object[key]); })
54 .join('&');
55}
56exports.urlEncode = urlEncode;
57/**
58 * Transforms any object into an object literal with all its attributes
59 * attached to it.
60 *
61 * @param value Initial source that we have to transform in order for it to be usable by the serializer
62 */
63function getWalkSource(value) {
64 if (is_1.isError(value)) {
65 var error = value;
66 var err = {
67 message: error.message,
68 name: error.name,
69 stack: error.stack,
70 };
71 for (var i in error) {
72 if (Object.prototype.hasOwnProperty.call(error, i)) {
73 err[i] = error[i];
74 }
75 }
76 return err;
77 }
78 if (is_1.isEvent(value)) {
79 var event_1 = value;
80 var source = {};
81 source.type = event_1.type;
82 // Accessing event.target can throw (see getsentry/raven-js#838, #768)
83 try {
84 source.target = is_1.isElement(event_1.target)
85 ? browser_1.htmlTreeAsString(event_1.target)
86 : Object.prototype.toString.call(event_1.target);
87 }
88 catch (_oO) {
89 source.target = '<unknown>';
90 }
91 try {
92 source.currentTarget = is_1.isElement(event_1.currentTarget)
93 ? browser_1.htmlTreeAsString(event_1.currentTarget)
94 : Object.prototype.toString.call(event_1.currentTarget);
95 }
96 catch (_oO) {
97 source.currentTarget = '<unknown>';
98 }
99 if (typeof CustomEvent !== 'undefined' && is_1.isInstanceOf(value, CustomEvent)) {
100 source.detail = event_1.detail;
101 }
102 for (var i in event_1) {
103 if (Object.prototype.hasOwnProperty.call(event_1, i)) {
104 source[i] = event_1;
105 }
106 }
107 return source;
108 }
109 return value;
110}
111/** Calculates bytes size of input string */
112function utf8Length(value) {
113 // eslint-disable-next-line no-bitwise
114 return ~-encodeURI(value).split(/%..|./).length;
115}
116/** Calculates bytes size of input object */
117function jsonSize(value) {
118 return utf8Length(JSON.stringify(value));
119}
120/** JSDoc */
121function normalizeToSize(object,
122// Default Node.js REPL depth
123depth,
124// 100kB, as 200kB is max payload size, so half sounds reasonable
125maxSize) {
126 if (depth === void 0) { depth = 3; }
127 if (maxSize === void 0) { maxSize = 100 * 1024; }
128 var serialized = normalize(object, depth);
129 if (jsonSize(serialized) > maxSize) {
130 return normalizeToSize(object, depth - 1, maxSize);
131 }
132 return serialized;
133}
134exports.normalizeToSize = normalizeToSize;
135/**
136 * Transform any non-primitive, BigInt, or Symbol-type value into a string. Acts as a no-op on strings, numbers,
137 * booleans, null, and undefined.
138 *
139 * @param value The value to stringify
140 * @returns For non-primitive, BigInt, and Symbol-type values, a string denoting the value's type, type and value, or
141 * type and `description` property, respectively. For non-BigInt, non-Symbol primitives, returns the original value,
142 * unchanged.
143 */
144function serializeValue(value) {
145 var type = Object.prototype.toString.call(value);
146 // Node.js REPL notation
147 if (typeof value === 'string') {
148 return value;
149 }
150 if (type === '[object Object]') {
151 return '[Object]';
152 }
153 if (type === '[object Array]') {
154 return '[Array]';
155 }
156 var normalized = normalizeValue(value);
157 return is_1.isPrimitive(normalized) ? normalized : type;
158}
159/**
160 * normalizeValue()
161 *
162 * Takes unserializable input and make it serializable friendly
163 *
164 * - translates undefined/NaN values to "[undefined]"/"[NaN]" respectively,
165 * - serializes Error objects
166 * - filter global objects
167 */
168function normalizeValue(value, key) {
169 if (key === 'domain' && value && typeof value === 'object' && value._events) {
170 return '[Domain]';
171 }
172 if (key === 'domainEmitter') {
173 return '[DomainEmitter]';
174 }
175 if (typeof global !== 'undefined' && value === global) {
176 return '[Global]';
177 }
178 if (typeof window !== 'undefined' && value === window) {
179 return '[Window]';
180 }
181 if (typeof document !== 'undefined' && value === document) {
182 return '[Document]';
183 }
184 // React's SyntheticEvent thingy
185 if (is_1.isSyntheticEvent(value)) {
186 return '[SyntheticEvent]';
187 }
188 if (typeof value === 'number' && value !== value) {
189 return '[NaN]';
190 }
191 if (value === void 0) {
192 return '[undefined]';
193 }
194 if (typeof value === 'function') {
195 return "[Function: " + stacktrace_1.getFunctionName(value) + "]";
196 }
197 // symbols and bigints are considered primitives by TS, but aren't natively JSON-serilaizable
198 if (typeof value === 'symbol') {
199 return "[" + String(value) + "]";
200 }
201 if (typeof value === 'bigint') {
202 return "[BigInt: " + String(value) + "]";
203 }
204 return value;
205}
206/**
207 * Walks an object to perform a normalization on it
208 *
209 * @param key of object that's walked in current iteration
210 * @param value object to be walked
211 * @param depth Optional number indicating how deep should walking be performed
212 * @param memo Optional Memo class handling decycling
213 */
214// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
215function walk(key, value, depth, memo) {
216 if (depth === void 0) { depth = +Infinity; }
217 if (memo === void 0) { memo = new memo_1.Memo(); }
218 // If we reach the maximum depth, serialize whatever has left
219 if (depth === 0) {
220 return serializeValue(value);
221 }
222 /* eslint-disable @typescript-eslint/no-unsafe-member-access */
223 // If value implements `toJSON` method, call it and return early
224 if (value !== null && value !== undefined && typeof value.toJSON === 'function') {
225 return value.toJSON();
226 }
227 /* eslint-enable @typescript-eslint/no-unsafe-member-access */
228 // 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
229 var normalized = normalizeValue(value, key);
230 if (is_1.isPrimitive(normalized)) {
231 return normalized;
232 }
233 // Create source that we will use for next itterations, either objectified error object (Error type with extracted keys:value pairs) or the input itself
234 var source = getWalkSource(value);
235 // Create an accumulator that will act as a parent for all future itterations of that branch
236 var acc = Array.isArray(value) ? [] : {};
237 // If we already walked that branch, bail out, as it's circular reference
238 if (memo.memoize(value)) {
239 return '[Circular ~]';
240 }
241 // Walk all keys of the source
242 for (var innerKey in source) {
243 // Avoid iterating over fields in the prototype if they've somehow been exposed to enumeration.
244 if (!Object.prototype.hasOwnProperty.call(source, innerKey)) {
245 continue;
246 }
247 // Recursively walk through all the child nodes
248 acc[innerKey] = walk(innerKey, source[innerKey], depth - 1, memo);
249 }
250 // Once walked through all the branches, remove the parent from memo storage
251 memo.unmemoize(value);
252 // Return accumulated values
253 return acc;
254}
255exports.walk = walk;
256/**
257 * normalize()
258 *
259 * - Creates a copy to prevent original input mutation
260 * - Skip non-enumerablers
261 * - Calls `toJSON` if implemented
262 * - Removes circular references
263 * - Translates non-serializeable values (undefined/NaN/Functions) to serializable format
264 * - Translates known global objects/Classes to a string representations
265 * - Takes care of Error objects serialization
266 * - Optionally limit depth of final output
267 */
268// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
269function normalize(input, depth) {
270 try {
271 return JSON.parse(JSON.stringify(input, function (key, value) { return walk(key, value, depth); }));
272 }
273 catch (_oO) {
274 return '**non-serializable**';
275 }
276}
277exports.normalize = normalize;
278/**
279 * Given any captured exception, extract its keys and create a sorted
280 * and truncated list that will be used inside the event message.
281 * eg. `Non-error exception captured with keys: foo, bar, baz`
282 */
283// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
284function extractExceptionKeysForMessage(exception, maxLength) {
285 if (maxLength === void 0) { maxLength = 40; }
286 var keys = Object.keys(getWalkSource(exception));
287 keys.sort();
288 if (!keys.length) {
289 return '[object has no keys]';
290 }
291 if (keys[0].length >= maxLength) {
292 return string_1.truncate(keys[0], maxLength);
293 }
294 for (var includedKeys = keys.length; includedKeys > 0; includedKeys--) {
295 var serialized = keys.slice(0, includedKeys).join(', ');
296 if (serialized.length > maxLength) {
297 continue;
298 }
299 if (includedKeys === keys.length) {
300 return serialized;
301 }
302 return string_1.truncate(serialized, maxLength);
303 }
304 return '';
305}
306exports.extractExceptionKeysForMessage = extractExceptionKeysForMessage;
307/**
308 * Given any object, return the new object with removed keys that value was `undefined`.
309 * Works recursively on objects and arrays.
310 */
311function dropUndefinedKeys(val) {
312 var e_1, _a;
313 if (is_1.isPlainObject(val)) {
314 var obj = val;
315 var rv = {};
316 try {
317 for (var _b = tslib_1.__values(Object.keys(obj)), _c = _b.next(); !_c.done; _c = _b.next()) {
318 var key = _c.value;
319 if (typeof obj[key] !== 'undefined') {
320 rv[key] = dropUndefinedKeys(obj[key]);
321 }
322 }
323 }
324 catch (e_1_1) { e_1 = { error: e_1_1 }; }
325 finally {
326 try {
327 if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
328 }
329 finally { if (e_1) throw e_1.error; }
330 }
331 return rv;
332 }
333 if (Array.isArray(val)) {
334 return val.map(dropUndefinedKeys);
335 }
336 return val;
337}
338exports.dropUndefinedKeys = dropUndefinedKeys;
339//# sourceMappingURL=object.js.map
\No newline at end of file