2121'use strict' ; 
2222
2323const  { 
24+   ArrayFrom, 
25+   ArrayIsArray, 
2426  ArrayPrototypeIndexOf, 
2527  ArrayPrototypeJoin, 
2628  ArrayPrototypePush, 
2729  ArrayPrototypeSlice, 
2830  Error, 
31+   FunctionPrototypeCall, 
32+   MapPrototypeDelete, 
33+   MapPrototypeGet, 
34+   MapPrototypeHas, 
35+   MapPrototypeSet, 
2936  NumberIsNaN, 
3037  ObjectAssign, 
3138  ObjectIs, 
3239  ObjectKeys, 
3340  ObjectPrototypeIsPrototypeOf, 
3441  ReflectApply, 
42+   ReflectHas, 
43+   ReflectOwnKeys, 
3544  RegExpPrototypeExec, 
45+   SafeMap, 
46+   SafeSet, 
47+   SafeWeakSet, 
3648  String, 
3749  StringPrototypeIndexOf, 
3850  StringPrototypeSlice, 
3951  StringPrototypeSplit, 
52+   SymbolIterator, 
4053}  =  primordials ; 
4154
4255const  { 
@@ -50,8 +63,18 @@ const {
5063}  =  require ( 'internal/errors' ) ; 
5164const  AssertionError  =  require ( 'internal/assert/assertion_error' ) ; 
5265const  {  inspect }  =  require ( 'internal/util/inspect' ) ; 
53- const  {  isPromise,  isRegExp }  =  require ( 'internal/util/types' ) ; 
54- const  {  isError,  deprecate }  =  require ( 'internal/util' ) ; 
66+ const  {  Buffer }  =  require ( 'buffer' ) ; 
67+ const  { 
68+   isKeyObject, 
69+   isPromise, 
70+   isRegExp, 
71+   isMap, 
72+   isSet, 
73+   isDate, 
74+   isWeakSet, 
75+   isWeakMap, 
76+ }  =  require ( 'internal/util/types' ) ; 
77+ const  {  isError,  deprecate,  emitExperimentalWarning }  =  require ( 'internal/util' ) ; 
5578const  {  innerOk }  =  require ( 'internal/assert/utils' ) ; 
5679
5780const  CallTracker  =  require ( 'internal/assert/calltracker' ) ; 
@@ -341,6 +364,191 @@ assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
341364  } 
342365} ; 
343366
367+ function  isSpecial ( obj )  { 
368+   return  obj  ==  null  ||  typeof  obj  !==  'object'  ||  isError ( obj )  ||  isRegExp ( obj )  ||  isDate ( obj ) ; 
369+ } 
370+ 
371+ const  typesToCallDeepStrictEqualWith  =  [ 
372+   isKeyObject ,  isWeakSet ,  isWeakMap ,  Buffer . isBuffer , 
373+ ] ; 
374+ 
375+ /** 
376+  * Compares two objects or values recursively to check if they are equal. 
377+  * @param  {any } actual - The actual value to compare. 
378+  * @param  {any } expected - The expected value to compare. 
379+  * @param  {Set } [comparedObjects=new Set()] - Set to track compared objects for handling circular references. 
380+  * @returns  {boolean } - Returns `true` if the actual value matches the expected value, otherwise `false`. 
381+  * @example  
382+  * compareBranch({a: 1, b: 2, c: 3}, {a: 1, b: 2}); // true 
383+  */ 
384+ function  compareBranch ( 
385+   actual , 
386+   expected , 
387+   comparedObjects , 
388+ )  { 
389+   // Check for Map object equality 
390+   if  ( isMap ( actual )  &&  isMap ( expected ) )  { 
391+     if  ( actual . size  !==  expected . size )  { 
392+       return  false ; 
393+     } 
394+     const  safeIterator  =  FunctionPrototypeCall ( SafeMap . prototype [ SymbolIterator ] ,  actual ) ; 
395+ 
396+     comparedObjects  ??=  new  SafeWeakSet ( ) ; 
397+ 
398+     for  ( const  {  0 : key ,  1 : val  }  of  safeIterator )  { 
399+       if  ( ! MapPrototypeHas ( expected ,  key ) )  { 
400+         return  false ; 
401+       } 
402+       if  ( ! compareBranch ( val ,  MapPrototypeGet ( expected ,  key ) ,  comparedObjects ) )  { 
403+         return  false ; 
404+       } 
405+     } 
406+     return  true ; 
407+   } 
408+ 
409+   for  ( const  type  of  typesToCallDeepStrictEqualWith )  { 
410+     if  ( type ( actual )  ||  type ( expected ) )  { 
411+       if  ( isDeepStrictEqual  ===  undefined )  lazyLoadComparison ( ) ; 
412+       return  isDeepStrictEqual ( actual ,  expected ) ; 
413+     } 
414+   } 
415+ 
416+   // Check for Set object equality 
417+   // TODO(aduh95): switch to `SetPrototypeIsSubsetOf` when it's available 
418+   if  ( isSet ( actual )  &&  isSet ( expected ) )  { 
419+     if  ( expected . size  >  actual . size )  { 
420+       return  false ;  // `expected` can't be a subset if it has more elements 
421+     } 
422+ 
423+     if  ( isDeepEqual  ===  undefined )  lazyLoadComparison ( ) ; 
424+ 
425+     const  actualArray  =  ArrayFrom ( actual ) ; 
426+     const  expectedArray  =  ArrayFrom ( expected ) ; 
427+     const  usedIndices  =  new  SafeSet ( ) ; 
428+ 
429+     for  ( let  expectedIdx  =  0 ;  expectedIdx  <  expectedArray . length ;  expectedIdx ++ )  { 
430+       const  expectedItem  =  expectedArray [ expectedIdx ] ; 
431+       let  found  =  false ; 
432+ 
433+       for  ( let  actualIdx  =  0 ;  actualIdx  <  actualArray . length ;  actualIdx ++ )  { 
434+         if  ( ! usedIndices . has ( actualIdx )  &&  isDeepStrictEqual ( actualArray [ actualIdx ] ,  expectedItem ) )  { 
435+           usedIndices . add ( actualIdx ) ; 
436+           found  =  true ; 
437+           break ; 
438+         } 
439+       } 
440+ 
441+       if  ( ! found )  { 
442+         return  false ; 
443+       } 
444+     } 
445+ 
446+     return  true ; 
447+   } 
448+ 
449+   // Check if expected array is a subset of actual array 
450+   if  ( ArrayIsArray ( actual )  &&  ArrayIsArray ( expected ) )  { 
451+     if  ( expected . length  >  actual . length )  { 
452+       return  false ; 
453+     } 
454+ 
455+     if  ( isDeepEqual  ===  undefined )  lazyLoadComparison ( ) ; 
456+ 
457+     // Create a map to count occurrences of each element in the expected array 
458+     const  expectedCounts  =  new  SafeMap ( ) ; 
459+     for  ( const  expectedItem  of  expected )  { 
460+       let  found  =  false ; 
461+       for  ( const  {  0 : key ,  1 : count  }  of  expectedCounts )  { 
462+         if  ( isDeepStrictEqual ( key ,  expectedItem ) )  { 
463+           MapPrototypeSet ( expectedCounts ,  key ,  count  +  1 ) ; 
464+           found  =  true ; 
465+           break ; 
466+         } 
467+       } 
468+       if  ( ! found )  { 
469+         MapPrototypeSet ( expectedCounts ,  expectedItem ,  1 ) ; 
470+       } 
471+     } 
472+ 
473+     // Create a map to count occurrences of relevant elements in the actual array 
474+     for  ( const  actualItem  of  actual )  { 
475+       for  ( const  {  0 : key ,  1 : count  }  of  expectedCounts )  { 
476+         if  ( isDeepStrictEqual ( key ,  actualItem ) )  { 
477+           if  ( count  ===  1 )  { 
478+             MapPrototypeDelete ( expectedCounts ,  key ) ; 
479+           }  else  { 
480+             MapPrototypeSet ( expectedCounts ,  key ,  count  -  1 ) ; 
481+           } 
482+           break ; 
483+         } 
484+       } 
485+     } 
486+ 
487+     return  ! expectedCounts . size ; 
488+   } 
489+ 
490+   // Comparison done when at least one of the values is not an object 
491+   if  ( isSpecial ( actual )  ||  isSpecial ( expected ) )  { 
492+     if  ( isDeepEqual  ===  undefined )  { 
493+       lazyLoadComparison ( ) ; 
494+     } 
495+     return  isDeepStrictEqual ( actual ,  expected ) ; 
496+   } 
497+ 
498+   // Use Reflect.ownKeys() instead of Object.keys() to include symbol properties 
499+   const  keysExpected  =  ReflectOwnKeys ( expected ) ; 
500+ 
501+   comparedObjects  ??=  new  SafeWeakSet ( ) ; 
502+ 
503+   // Handle circular references 
504+   if  ( comparedObjects . has ( actual ) )  { 
505+     return  true ; 
506+   } 
507+   comparedObjects . add ( actual ) ; 
508+ 
509+   // Check if all expected keys and values match 
510+   for  ( let  i  =  0 ;  i  <  keysExpected . length ;  i ++ )  { 
511+     const  key  =  keysExpected [ i ] ; 
512+     assert ( 
513+       ReflectHas ( actual ,  key ) , 
514+       new  AssertionError ( {  message : `Expected key ${ String ( key ) }   not found in actual object`  } ) , 
515+     ) ; 
516+     if  ( ! compareBranch ( actual [ key ] ,  expected [ key ] ,  comparedObjects ) )  { 
517+       return  false ; 
518+     } 
519+   } 
520+ 
521+   return  true ; 
522+ } 
523+ 
524+ /** 
525+  * The strict equivalence assertion test between two objects 
526+  * @param  {any } actual 
527+  * @param  {any } expected 
528+  * @param  {string | Error } [message] 
529+  * @returns  {void } 
530+  */ 
531+ assert . partialDeepStrictEqual  =  function  partialDeepStrictEqual ( 
532+   actual , 
533+   expected , 
534+   message , 
535+ )  { 
536+   emitExperimentalWarning ( 'assert.partialDeepStrictEqual' ) ; 
537+   if  ( arguments . length  <  2 )  { 
538+     throw  new  ERR_MISSING_ARGS ( 'actual' ,  'expected' ) ; 
539+   } 
540+ 
541+   if  ( ! compareBranch ( actual ,  expected ) )  { 
542+     innerFail ( { 
543+       actual, 
544+       expected, 
545+       message, 
546+       operator : 'partialDeepStrictEqual' , 
547+       stackStartFn : partialDeepStrictEqual , 
548+     } ) ; 
549+   } 
550+ } ; 
551+ 
344552class  Comparison  { 
345553  constructor ( obj ,  keys ,  actual )  { 
346554    for  ( const  key  of  keys )  { 
0 commit comments