Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tidy-aborts-serialize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@outputai/core": patch
---

- Updated error serialization to keep only `name`, `message`, `code` and `stack` for `DOMException` values, instead of also projecting the 25 legacy numeric constants their prototype carries. Aborted operations, whose reason is usually a `DOMException`, now read like any other error in traces and logs.
10 changes: 9 additions & 1 deletion sdk/core/src/helpers/error_serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ export const flattenPrototypeChain = ( target, depth = 0, result = [], maxDepth
!target || depth >= maxDepth ? result :
flattenPrototypeChain( Object.getPrototypeOf( target ), depth + 1, result.concat( target ), maxDepth );

const shouldIgnoreKey = ( ignoredKeys, key ) => ignoredKeys.some( e => e.test ? e.test( key ) : e === key );

/**
* Define the best "name" for an Error object
* Rules: Assigned .name property > inherited .name if not "Error" > constructor.name
Expand Down Expand Up @@ -129,6 +131,12 @@ const serializeValue = ( target, options, state = { depth: 0, seen: new GlobalCo
return target.toString();
}

if ( target instanceof DOMException ) {
Comment thread
szanata marked this conversation as resolved.
Outdated
return [ 'name', 'stack', 'code', 'message' ]
Comment thread
szanata marked this conversation as resolved.
Outdated
.filter( k => !shouldIgnoreKey( options.ignoredKeys, k ) )
.reduce( ( o, k ) => Object.assign( o, { [k]: serializeValue( tryOrUndefined( () => target[k] ), options, nextState ) } ), {} );
}

if ( typeof target === 'bigint' ) {
return ( target >= MAX_BIGINT || target <= -MAX_BIGINT ) ? Marker.BigIntTooLarge : `${target}n`;
}
Expand Down Expand Up @@ -183,7 +191,7 @@ const serializeValue = ( target, options, state = { depth: 0, seen: new GlobalCo

const keys = Object.getOwnPropertyNames( proto );
for ( const key of keys ) {
if ( options.ignoredKeys.some( e => e.test ? e.test( key ) : e === key ) ) {
if ( shouldIgnoreKey( options.ignoredKeys, key ) ) {
continue;
Comment thread
szanata marked this conversation as resolved.
}

Expand Down
27 changes: 27 additions & 0 deletions sdk/core/src/helpers/error_serializer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,33 @@ describe( 'serializeError', () => {
expect( serializeError( /request-\d+/gi ) ).toBe( '/request-\\d+/gi' );
} );

it( 'serializes DOMException values without the legacy prototype constants', () => {
const error = new DOMException( 'Cancelled by caller', 'AbortError' );

expect( serializeError( error ) ).toEqual( {
name: 'AbortError',
message: 'Cancelled by caller',
code: 20,
stack: expect.stringContaining( 'AbortError: Cancelled by caller' )
} );
expect( serializeError( error, { dropKeys: [ /stack/ ] } ) ).toEqual( {
name: 'AbortError',
message: 'Cancelled by caller',
code: 20
} );
} );

it( 'keeps the readable DOMException fields when one of them throws', () => {
const error = new DOMException( 'aborted', 'AbortError' );
// a name pointing back at the exception makes the stack getter recurse until it throws
Object.defineProperty( error, 'name', { value: error, enumerable: true } );

expect( serializeError( { reason: error, sibling: 'preserved' } ) ).toEqual( {
reason: { name: '[Circular Reference]', message: 'aborted', code: 20 },
sibling: 'preserved'
} );
} );

it( 'serializes bigint values with an n suffix', () => {
expect( serializeError( 42n ) ).toBe( '42n' );
expect( serializeError( -42n ) ).toBe( '-42n' );
Expand Down
Loading