@ -14,21 +14,19 @@ function getLeanDiff(
showOnly : ObjectDiffOptions [ "showOnly" ] = DEFAULT_OBJECT_DIFF_OPTIONS . showOnly ,
showOnly : ObjectDiffOptions [ "showOnly" ] = DEFAULT_OBJECT_DIFF_OPTIONS . showOnly ,
) : ObjectDiff [ "diff" ] {
) : ObjectDiff [ "diff" ] {
const { statuses , granularity } = showOnly ;
const { statuses , granularity } = showOnly ;
return diff . reduce (
const res : ObjectDiff [ "diff" ] = [ ] ;
( acc , value ) = > {
for ( let i = 0 ; i < diff . length ; i ++ ) {
const value = diff [ i ] ;
if ( granularity === GRANULARITY . DEEP && value . diff ) {
if ( granularity === GRANULARITY . DEEP && value . diff ) {
const leanDiff = getLeanDiff ( value . diff , showOnly ) ;
const leanDiff = getLeanDiff ( value . diff , showOnly ) ;
if ( leanDiff . length > 0 ) {
if ( leanDiff . length > 0 ) {
return [ . . . acc , { . . . value , diff : leanDiff } ] ;
res . push ( { . . . value , diff : leanDiff } ) ;
}
}
} else if ( statuses . includes ( value . status ) ) {
res . push ( value ) ;
}
}
if ( statuses . includes ( value . status ) ) {
return [ . . . acc , value ] ;
}
}
return acc ;
return res ;
} ,
[ ] as ObjectDiff [ "diff" ] ,
) ;
}
}
function getObjectStatus ( diff : ObjectDiff [ "diff" ] ) : OBJECT_STATUS {
function getObjectStatus ( diff : ObjectDiff [ "diff" ] ) : OBJECT_STATUS {
@ -50,18 +48,19 @@ function formatSingleObjectDiff(
} ;
} ;
}
}
const diff : ObjectDiff [ "diff" ] = [ ] ;
const diff : ObjectDiff [ "diff" ] = [ ] ;
Object . entries ( data ) . forEach ( ( [ property , value ] ) = > {
for ( const [ property , value ] of Object . entries ( data ) ) {
if ( isObject ( value ) ) {
if ( isObject ( value ) ) {
const subPropertiesDiff : Diff [ ] = [ ] ;
const subPropertiesDiff : Diff [ ] = [ ] ;
Object . entries ( value ) . forEach ( ( [ subProperty , subValue ] ) = > {
for ( const [ subProperty , subValue ] of Object . entries ( value ) ) {
subPropertiesDiff . push ( {
subPropertiesDiff . push ( {
property : subProperty ,
property : subProperty ,
previousValue : status === OBJECT_STATUS . ADDED ? undefined : subValue ,
previousValue : status === OBJECT_STATUS . ADDED ? undefined : subValue ,
currentValue : status === OBJECT_STATUS . ADDED ? subValue : undefined ,
currentValue : status === OBJECT_STATUS . ADDED ? subValue : undefined ,
status ,
status ,
} ) ;
} ) ;
} ) ;
}
return diff . push ( {
diff . push ( {
property ,
property ,
previousValue :
previousValue :
status === OBJECT_STATUS . ADDED ? undefined : data [ property ] ,
status === OBJECT_STATUS . ADDED ? undefined : data [ property ] ,
@ -69,15 +68,17 @@ function formatSingleObjectDiff(
status ,
status ,
diff : subPropertiesDiff ,
diff : subPropertiesDiff ,
} ) ;
} ) ;
}
} else {
return diff . push ( {
diff . push ( {
property ,
property ,
previousValue :
previousValue :
status === OBJECT_STATUS . ADDED ? undefined : data [ property ] ,
status === OBJECT_STATUS . ADDED ? undefined : data [ property ] ,
currentValue : status === OBJECT_STATUS . ADDED ? value : undefined ,
currentValue : status === OBJECT_STATUS . ADDED ? value : undefined ,
status ,
status ,
} ) ;
} ) ;
} ) ;
}
}
if ( options . showOnly && options . showOnly . statuses . length > 0 ) {
if ( options . showOnly && options . showOnly . statuses . length > 0 ) {
return {
return {
type : "object" ,
type : "object" ,
@ -92,20 +93,6 @@ function formatSingleObjectDiff(
} ;
} ;
}
}
function getPreviousMatch (
previousValue : unknown | undefined ,
nextSubProperty : unknown ,
options? : ObjectDiffOptions ,
) : unknown | undefined {
if ( ! previousValue ) {
return undefined ;
}
const previousMatch = Object . entries ( previousValue ) . find ( ( [ subPreviousKey ] ) = >
isEqual ( subPreviousKey , nextSubProperty , options ) ,
) ;
return previousMatch ? previousMatch [ 1 ] : undefined ;
}
function getValueStatus (
function getValueStatus (
previousValue : unknown ,
previousValue : unknown ,
nextValue : unknown ,
nextValue : unknown ,
@ -117,92 +104,61 @@ function getValueStatus(
return OBJECT_STATUS . UPDATED ;
return OBJECT_STATUS . UPDATED ;
}
}
function getPropertyStatus ( subPropertiesDiff : Diff [ ] ) : OBJECT_STATUS {
function getDiff (
return subPropertiesDiff . some (
previousValue : Record < string , unknown > | undefined = { } ,
( property ) = > property . status !== OBJECT_STATUS . EQUAL ,
)
? OBJECT_STATUS . UPDATED
: OBJECT_STATUS . EQUAL ;
}
function getDeletedProperties (
previousValue : Record < string , unknown > | undefined ,
nextValue : Record < string , unknown > ,
) : { property : string ; value : unknown } [ ] | undefined {
if ( ! previousValue ) return undefined ;
const prevKeys = Object . keys ( previousValue ) ;
const nextKeys = Object . keys ( nextValue ) ;
const deletedKeys = prevKeys . filter ( ( prevKey ) = > ! nextKeys . includes ( prevKey ) ) ;
if ( deletedKeys . length > 0 ) {
return deletedKeys . map ( ( deletedKey ) = > ( {
property : deletedKey ,
value : previousValue [ deletedKey ] ,
} ) ) ;
}
return undefined ;
}
function getSubPropertiesDiff (
previousValue : Record < string , unknown > | undefined ,
nextValue : Record < string , unknown > ,
nextValue : Record < string , unknown > ,
options? : ObjectDiffOptions ,
options? : ObjectDiffOptions ,
) : Diff [ ] {
) : Diff [ ] {
const subPropertiesDiff : Diff [ ] = [ ] ;
const diff : Diff [ ] = [ ] ;
let subDiff : Diff [ ] ;
const allKeys = new Set ( [
const deletedMainSubProperties = getDeletedProperties (
. . . Object . keys ( previousValue ) ,
previousValue ,
. . . Object . keys ( nextValue ) ,
nextValue ,
] ) ;
) ;
if ( deletedMainSubProperties ) {
for ( const property of allKeys ) {
deletedMainSubProperties . forEach ( ( deletedProperty ) = > {
const prevSubValue = previousValue [ property ] ;
subPropertiesDiff . push ( {
const nextSubValue = nextValue [ property ] ;
property : deletedProperty.property ,
if ( ! ( property in nextValue ) ) {
previousValue : deletedProperty.value ,
diff . push ( {
property ,
previousValue : prevSubValue ,
currentValue : undefined ,
currentValue : undefined ,
status : OBJECT_STATUS.DELETED ,
status : OBJECT_STATUS.DELETED ,
} ) ;
} ) ;
} ) ;
continue ;
}
}
Object . entries ( nextValue ) . forEach ( ( [ nextSubProperty , nextSubValue ] ) = > {
if ( ! ( property in previousValue ) ) {
const previousMatch = getPreviousMatch (
diff . push ( {
previousValue ,
property ,
nextSubProperty ,
previousValue : undefined ,
options ,
) ;
if ( ! previousMatch ) {
return subPropertiesDiff . push ( {
property : nextSubProperty ,
previousValue : previousMatch ,
currentValue : nextSubValue ,
currentValue : nextSubValue ,
status :
status : OBJECT_STATUS.ADDED ,
! previousValue || ! ( nextSubProperty in previousValue )
? OBJECT_STATUS . ADDED
: previousMatch === nextSubValue
? OBJECT_STATUS . EQUAL
: OBJECT_STATUS . UPDATED ,
} ) ;
} ) ;
continue ;
}
}
if ( isObject ( nextSubValue ) ) {
if ( isObject ( nextSubValue ) && isObject ( prevSubValue ) ) {
const data : Diff [ ] = getSubPropertiesDiff (
const subDiff = getDiff ( prevSubValue , nextSubValue , options ) ;
previousMatch as Record < string , unknown > ,
const isUpdated = subDiff . some (
nextSubValue ,
( entry ) = > entry . status !== OBJECT_STATUS . EQUAL ,
options ,
) ;
) ;
if ( data && data . length > 0 ) {
diff . push ( {
subDiff = data ;
property ,
}
previousValue : prevSubValue ,
}
if ( previousMatch ) {
subPropertiesDiff . push ( {
property : nextSubProperty ,
previousValue : previousMatch ,
currentValue : nextSubValue ,
currentValue : nextSubValue ,
status : getValueStatus ( previousMatch , nextSubValue , options ) ,
status : isUpdated ? OBJECT_STATUS.UPDATED : OBJECT_STATUS.EQUAL ,
. . . ( ! ! subDiff && { diff : subDiff } ) ,
. . . ( isUpdated && { diff : subDiff } ) ,
} ) ;
} ) ;
}
} else {
const status = getValueStatus ( prevSubValue , nextSubValue , options ) ;
diff . push ( {
property ,
previousValue : prevSubValue ,
currentValue : nextSubValue ,
status ,
} ) ;
} ) ;
return subPropertiesDiff ;
}
}
return diff ;
}
}
/ * *
/ * *
@ -234,66 +190,12 @@ export function getObjectDiff(
if ( ! nextData ) {
if ( ! nextData ) {
return formatSingleObjectDiff ( prevData , OBJECT_STATUS . DELETED , options ) ;
return formatSingleObjectDiff ( prevData , OBJECT_STATUS . DELETED , options ) ;
}
}
const diff : ObjectDiff [ "diff" ] = [ ] ;
const diff : ObjectDiff [ "diff" ] = getDiff ( prevData , nextData , options ) ;
Object . entries ( nextData ) . forEach ( ( [ nextProperty , nextValue ] ) = > {
const status = getObjectStatus ( diff ) ;
const previousValue = prevData [ nextProperty ] ;
const showLeanDiff = ( options ? . showOnly ? . statuses ? . length || 0 ) > 0 ;
if ( ! previousValue ) {
return diff . push ( {
property : nextProperty ,
previousValue ,
currentValue : nextValue ,
status : ! ( nextProperty in prevData )
? OBJECT_STATUS . ADDED
: previousValue === nextValue
? OBJECT_STATUS . EQUAL
: OBJECT_STATUS . UPDATED ,
} ) ;
}
if ( isObject ( nextValue ) ) {
const subPropertiesDiff : Diff [ ] = getSubPropertiesDiff (
previousValue as Record < string , unknown > ,
nextValue ,
options ,
) ;
const subPropertyStatus = getPropertyStatus ( subPropertiesDiff ) ;
return diff . push ( {
property : nextProperty ,
previousValue ,
currentValue : nextValue ,
status : subPropertyStatus ,
. . . ( subPropertyStatus !== OBJECT_STATUS . EQUAL && {
diff : subPropertiesDiff ,
} ) ,
} ) ;
}
return diff . push ( {
property : nextProperty ,
previousValue ,
currentValue : nextValue ,
status : getValueStatus ( previousValue , nextValue , options ) ,
} ) ;
} ) ;
const deletedProperties = getDeletedProperties ( prevData , nextData ) ;
if ( deletedProperties ) {
deletedProperties . forEach ( ( deletedProperty ) = > {
diff . push ( {
property : deletedProperty.property ,
previousValue : deletedProperty.value ,
currentValue : undefined ,
status : OBJECT_STATUS.DELETED ,
} ) ;
} ) ;
}
if ( options . showOnly && options . showOnly . statuses . length > 0 ) {
return {
type : "object" ,
status : getObjectStatus ( diff ) ,
diff : getLeanDiff ( diff , options . showOnly ) ,
} ;
}
return {
return {
type : "object" ,
type : "object" ,
status : getObjectStatus ( diff ) ,
status ,
diff ,
diff : showLeanDiff ? getLeanDiff ( diff , options . showOnly ) : diff ,
} ;
} ;
}
}