@@ -92,38 +92,89 @@ export const contentMediaTypeMappingResolver: MappingResolver<string> = (before,
9292 const result : MapKeysResult < string > = { added : [ ] , removed : [ ] , mapped : { } }
9393
9494 const beforeKeys = objectKeys ( before )
95- const _beforeKeys = beforeKeys . map ( ( key ) => key . split ( ';' ) [ 0 ] ?? '' )
9695 const afterKeys = objectKeys ( after )
97- const _afterKeys = afterKeys . map ( ( key ) => key . split ( ';' ) [ 0 ] ?? '' )
9896
99- const mappedIndex = new Set ( afterKeys . keys ( ) )
100-
101- for ( let i = 0 ; i < beforeKeys . length ; i ++ ) {
102- const _afterIndex = _afterKeys . findIndex ( ( key ) => {
103- const [ afterType , afterSubType ] = key . split ( '/' )
104- const [ beforeType , beforeSubType ] = _beforeKeys [ i ] . split ( '/' )
97+ const unmappedAfterIndices = new Set ( afterKeys . keys ( ) )
98+ const unmappedBeforeIndices = new Set ( beforeKeys . keys ( ) )
99+
100+ function mapExactMatches (
101+ getComparisonKey : ( key : string ) => string
102+ ) : void {
103+
104+ for ( const beforeIndex of unmappedBeforeIndices ) {
105+ const beforeKey = getComparisonKey ( beforeKeys [ beforeIndex ] )
106+
107+ // Find matching after index by iterating over the after indices set
108+ let matchingAfterIndex : number | undefined
109+ for ( const afterIndex of unmappedAfterIndices ) {
110+ const afterKey = getComparisonKey ( afterKeys [ afterIndex ] )
111+ if ( afterKey === beforeKey ) {
112+ matchingAfterIndex = afterIndex
113+ break
114+ }
115+ }
105116
106- if ( afterType !== beforeType && afterType !== '*' && beforeType !== '*' ) { return false }
107- if ( afterSubType !== beforeSubType && afterSubType !== '*' && beforeSubType !== '*' ) { return false }
108- return true
109- } )
117+ if ( matchingAfterIndex !== undefined ) {
118+ // Match found - create mapping and remove from unmapped sets
119+ result . mapped [ beforeKeys [ beforeIndex ] ] = afterKeys [ matchingAfterIndex ]
120+ unmappedAfterIndices . delete ( matchingAfterIndex )
121+ unmappedBeforeIndices . delete ( beforeIndex )
122+ }
123+ }
124+ }
110125
111- if ( _afterIndex < 0 || ! mappedIndex . has ( _afterIndex ) ) {
112- // removed item
113- result . removed . push ( beforeKeys [ i ] )
114- } else {
115- // mapped items
116- result . mapped [ beforeKeys [ i ] ] = afterKeys [ _afterIndex ]
117- mappedIndex . delete ( _afterIndex )
126+ // First, map exact matches for full media type
127+ mapExactMatches ( ( key ) => key )
128+
129+ // After that, try to map media types by base type for remaining unmapped keys
130+ mapExactMatches ( getMediaTypeBase )
131+
132+ // If exactly one unmapped item in both before and after, try wildcard matching
133+ if ( unmappedBeforeIndices . size === 1 && unmappedAfterIndices . size === 1 ) {
134+ const beforeIndex = Array . from ( unmappedBeforeIndices ) [ 0 ]
135+ const afterIndex = Array . from ( unmappedAfterIndices ) [ 0 ]
136+ const beforeKey = beforeKeys [ beforeIndex ]
137+ const afterKey = afterKeys [ afterIndex ]
138+ const beforeBaseType = getMediaTypeBase ( beforeKey )
139+ const afterBaseType = getMediaTypeBase ( afterKey )
140+
141+ // Check if they are compatible using wildcard matching
142+ if ( isWildcardCompatible ( beforeBaseType , afterBaseType ) ) {
143+ // Map them together
144+ result . mapped [ beforeKeys [ beforeIndex ] ] = afterKeys [ afterIndex ]
145+ unmappedAfterIndices . delete ( afterIndex )
146+ unmappedBeforeIndices . delete ( beforeIndex )
118147 }
119148 }
120149
121- // added items
122- mappedIndex . forEach ( ( i ) => result . added . push ( afterKeys [ i ] ) )
150+ // Mark remaining unmapped items as removed/added
151+ unmappedBeforeIndices . forEach ( ( index ) => result . removed . push ( beforeKeys [ index ] ) )
152+ unmappedAfterIndices . forEach ( ( index ) => result . added . push ( afterKeys [ index ] ) )
123153
124154 return result
125155}
126156
157+ function getMediaTypeBase ( mediaType : string ) : string {
158+ return mediaType . split ( ';' ) [ 0 ] ?? ''
159+ }
160+
161+ function isWildcardCompatible ( beforeType : string , afterType : string ) : boolean {
162+ const [ beforeMainType , beforeSubType ] = beforeType . split ( '/' )
163+ const [ afterMainType , afterSubType ] = afterType . split ( '/' )
164+
165+ // Check main type compatibility
166+ if ( beforeMainType !== afterMainType && beforeMainType !== '*' && afterMainType !== '*' ) {
167+ return false
168+ }
169+
170+ // Check sub type compatibility
171+ if ( beforeSubType !== afterSubType && beforeSubType !== '*' && afterSubType !== '*' ) {
172+ return false
173+ }
174+
175+ return true
176+ }
177+
127178export function hidePathParamNames ( path : string ) : string {
128179 return path . replace ( PATH_PARAMETER_REGEXP , PATH_PARAM_UNIFIED_PLACEHOLDER )
129180}
0 commit comments