@@ -4,6 +4,7 @@ import { WrappedFunction } from '@sentry/types';
4
4
5
5
import { htmlTreeAsString } from './browser' ;
6
6
import { isElement , isError , isEvent , isInstanceOf , isPlainObject , isPrimitive } from './is' ;
7
+ import { memoBuilder , MemoFunc } from './memo' ;
7
8
import { truncate } from './string' ;
8
9
9
10
/**
@@ -205,20 +206,37 @@ export function extractExceptionKeysForMessage(exception: Record<string, unknown
205
206
/**
206
207
* Given any object, return the new object with removed keys that value was `undefined`.
207
208
* Works recursively on objects and arrays.
209
+ *
210
+ * Attention: This function keeps circular references in the returned object.
208
211
*/
209
212
export function dropUndefinedKeys < T > ( val : T ) : T {
213
+ // This function just proxies `_dropUndefinedKeys` to keep the `memoBuilder` out of this function's API
214
+ return _dropUndefinedKeys ( val , memoBuilder ( ) ) ;
215
+ }
216
+
217
+ function _dropUndefinedKeys < T > ( val : T , memo : MemoFunc ) : T {
218
+ const [ memoize ] = memo ; // we don't need unmemoize because we don't need to visit nodes twice
219
+
210
220
if ( isPlainObject ( val ) ) {
221
+ if ( memoize ( val ) ) {
222
+ return val ;
223
+ }
211
224
const rv : { [ key : string ] : any } = { } ;
212
225
for ( const key of Object . keys ( val ) ) {
213
226
if ( typeof val [ key ] !== 'undefined' ) {
214
- rv [ key ] = dropUndefinedKeys ( val [ key ] ) ;
227
+ rv [ key ] = _dropUndefinedKeys ( val [ key ] , memo ) ;
215
228
}
216
229
}
217
230
return rv as T ;
218
231
}
219
232
220
233
if ( Array . isArray ( val ) ) {
221
- return ( val as any [ ] ) . map ( dropUndefinedKeys ) as any ;
234
+ if ( memoize ( val ) ) {
235
+ return val ;
236
+ }
237
+ return ( val as any [ ] ) . map ( item => {
238
+ return _dropUndefinedKeys ( item , memo ) ;
239
+ } ) as any ;
222
240
}
223
241
224
242
return val ;
0 commit comments