@outputai/core 0.13.1-next.318b2e6.0 → 0.13.1-next.38c895c.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -61,6 +61,18 @@ export const flattenPrototypeChain = ( target, depth = 0, result = [], maxDepth
|
|
|
61
61
|
!target || depth >= maxDepth ? result :
|
|
62
62
|
flattenPrototypeChain( Object.getPrototypeOf( target ), depth + 1, result.concat( target ), maxDepth );
|
|
63
63
|
|
|
64
|
+
// Realm agnostic check for DOMException, works for AbortController errors even on environments without DOMExceptions
|
|
65
|
+
export const isDomException = target => Object.prototype.toString.call( target ) === '[object DOMException]';
|
|
66
|
+
|
|
67
|
+
const evaluateSpecialObjectsKeysList = target => {
|
|
68
|
+
// DOMException has a special serialization because it carries a bunch of legacy constant fields
|
|
69
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/DOMException
|
|
70
|
+
if ( isDomException( target ) ) {
|
|
71
|
+
return [ 'name', 'message', 'code', 'cause', 'stack' ];
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
};
|
|
75
|
+
|
|
64
76
|
/**
|
|
65
77
|
* Define the best "name" for an Error object
|
|
66
78
|
* Rules: Assigned .name property > inherited .name if not "Error" > constructor.name
|
|
@@ -176,6 +188,8 @@ const serializeValue = ( target, options, state = { depth: 0, seen: new GlobalCo
|
|
|
176
188
|
const prototypes = flattenPrototypeChain( target );
|
|
177
189
|
const receiver = target;
|
|
178
190
|
|
|
191
|
+
const strictKeysList = evaluateSpecialObjectsKeysList( target );
|
|
192
|
+
|
|
179
193
|
const props = prototypes.reduce( ( projection, proto ) => {
|
|
180
194
|
if ( Object.keys( projection ).length > MAX_OBJECT_KEYS ) {
|
|
181
195
|
return projection;
|
|
@@ -187,6 +201,10 @@ const serializeValue = ( target, options, state = { depth: 0, seen: new GlobalCo
|
|
|
187
201
|
continue;
|
|
188
202
|
}
|
|
189
203
|
|
|
204
|
+
if ( Array.isArray( strictKeysList ) && !strictKeysList.includes( key ) ) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
190
208
|
const sanitizedKey = truncateString( key, MAX_OBJECT_KEY_LEN );
|
|
191
209
|
if ( Object.hasOwn( projection, sanitizedKey ) ) { // do not overwrite similar keys
|
|
192
210
|
continue;
|
|
@@ -212,7 +230,7 @@ const serializeValue = ( target, options, state = { depth: 0, seen: new GlobalCo
|
|
|
212
230
|
if ( !options.ignoredKeys.includes( 'name' ) && target instanceof Error ) {
|
|
213
231
|
const name = resolveErrorName( target );
|
|
214
232
|
if ( name !== undefined ) {
|
|
215
|
-
props.name = name;
|
|
233
|
+
props.name = typeof name === 'string' ? truncateString( name ) : serializeValue( name, options, nextState );
|
|
216
234
|
}
|
|
217
235
|
}
|
|
218
236
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import {
|
|
3
3
|
flattenPrototypeChain,
|
|
4
|
+
isDomException,
|
|
4
5
|
resolveErrorName,
|
|
5
6
|
serializeError,
|
|
6
7
|
truncateArray,
|
|
@@ -43,6 +44,33 @@ describe( 'error serializer helpers', () => {
|
|
|
43
44
|
} );
|
|
44
45
|
} );
|
|
45
46
|
|
|
47
|
+
describe( 'isDomException', () => {
|
|
48
|
+
afterEach( () => {
|
|
49
|
+
vi.unstubAllGlobals();
|
|
50
|
+
} );
|
|
51
|
+
|
|
52
|
+
it( 'detects DOMExceptions, including abort reasons, and rejects lookalikes', () => {
|
|
53
|
+
expect( isDomException( new DOMException( 'aborted', 'AbortError' ) ) ).toBe( true );
|
|
54
|
+
expect( isDomException( AbortSignal.abort().reason ) ).toBe( true );
|
|
55
|
+
expect( isDomException( new Error( 'aborted' ) ) ).toBe( false );
|
|
56
|
+
expect( isDomException( { name: 'AbortError', code: 20 } ) ).toBe( false );
|
|
57
|
+
expect( isDomException( undefined ) ).toBe( false );
|
|
58
|
+
expect( isDomException( null ) ).toBe( false );
|
|
59
|
+
} );
|
|
60
|
+
|
|
61
|
+
it( 'detects DOMExceptions where the global is unavailable, as in the workflow sandbox', () => {
|
|
62
|
+
const error = new DOMException( 'aborted', 'AbortError' );
|
|
63
|
+
vi.stubGlobal( 'DOMException', undefined );
|
|
64
|
+
|
|
65
|
+
expect( isDomException( error ) ).toBe( true );
|
|
66
|
+
expect( serializeError( error, { dropKeys: [ 'stack' ] } ) ).toEqual( {
|
|
67
|
+
name: 'AbortError',
|
|
68
|
+
message: 'aborted',
|
|
69
|
+
code: 20
|
|
70
|
+
} );
|
|
71
|
+
} );
|
|
72
|
+
} );
|
|
73
|
+
|
|
46
74
|
describe( 'resolveErrorName', () => {
|
|
47
75
|
it( 'uses explicit, inherited, and constructor names in precedence order', () => {
|
|
48
76
|
class ConstructorNamedError extends Error {}
|
|
@@ -194,6 +222,33 @@ describe( 'serializeError', () => {
|
|
|
194
222
|
expect( serializeError( /request-\d+/gi ) ).toBe( '/request-\\d+/gi' );
|
|
195
223
|
} );
|
|
196
224
|
|
|
225
|
+
it( 'serializes DOMException values without the legacy prototype constants', () => {
|
|
226
|
+
const error = new DOMException( 'Cancelled by caller', 'AbortError' );
|
|
227
|
+
|
|
228
|
+
expect( serializeError( error ) ).toEqual( {
|
|
229
|
+
name: 'AbortError',
|
|
230
|
+
message: 'Cancelled by caller',
|
|
231
|
+
code: 20,
|
|
232
|
+
stack: expect.stringContaining( 'AbortError: Cancelled by caller' )
|
|
233
|
+
} );
|
|
234
|
+
expect( serializeError( error, { dropKeys: [ /stack/ ] } ) ).toEqual( {
|
|
235
|
+
name: 'AbortError',
|
|
236
|
+
message: 'Cancelled by caller',
|
|
237
|
+
code: 20
|
|
238
|
+
} );
|
|
239
|
+
} );
|
|
240
|
+
|
|
241
|
+
it( 'marks circular DOMException fields and drops properties outside the allowed list', () => {
|
|
242
|
+
const error = new DOMException( 'aborted', 'AbortError' );
|
|
243
|
+
Object.defineProperty( error, 'message', { value: error, enumerable: true } );
|
|
244
|
+
error.attempt = 2;
|
|
245
|
+
|
|
246
|
+
expect( serializeError( { reason: error, sibling: 'preserved' }, { dropKeys: [ 'stack' ] } ) ).toEqual( {
|
|
247
|
+
reason: { name: 'AbortError', message: '[Circular Reference]', code: 20 },
|
|
248
|
+
sibling: 'preserved'
|
|
249
|
+
} );
|
|
250
|
+
} );
|
|
251
|
+
|
|
197
252
|
it( 'serializes bigint values with an n suffix', () => {
|
|
198
253
|
expect( serializeError( 42n ) ).toBe( '42n' );
|
|
199
254
|
expect( serializeError( -42n ) ).toBe( '-42n' );
|
|
@@ -298,6 +353,17 @@ describe( 'serializeError', () => {
|
|
|
298
353
|
expect( serialized['...'] ).toBe( '[Maximum Object Properties Reached]' );
|
|
299
354
|
} );
|
|
300
355
|
|
|
356
|
+
it( 'serializes the resolved error name instead of returning it raw', () => {
|
|
357
|
+
const circular = new Error( 'boom' );
|
|
358
|
+
circular.name = circular;
|
|
359
|
+
const oversized = new Error( 'boom' );
|
|
360
|
+
oversized.name = 'n'.repeat( 16_385 );
|
|
361
|
+
|
|
362
|
+
expect( serializeError( circular, { dropKeys: [ 'stack' ] } ).name ).toBe( '[Circular Reference]' );
|
|
363
|
+
expect( serializeError( oversized, { dropKeys: [ 'stack' ] } ).name )
|
|
364
|
+
.toBe( `${'n'.repeat( 16_384 )}... [1 more characters omitted]` );
|
|
365
|
+
} );
|
|
366
|
+
|
|
301
367
|
it( 'retains error identity when properties reach the object limit', () => {
|
|
302
368
|
class DetailedError extends Error {}
|
|
303
369
|
const error = new DetailedError( 'failed' );
|