@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@outputai/core",
3
- "version": "0.13.1-next.318b2e6.0",
3
+ "version": "0.13.1-next.38c895c.0",
4
4
  "description": "The core module of the output framework",
5
5
  "type": "module",
6
6
  "engines": {
@@ -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' );