@@ -235,3 +235,74 @@ export function difference(array1: string[], array2: string[]): string[] {
235235export function removeSlashes ( input : string ) : string {
236236 return input . replace ( / \/ / g, '' )
237237}
238+
239+ /**
240+ * Traverses the merged document starting from given obj to the bottom and aggregates the diffs with rollup from the bottom up.
241+ * Each object in the tree will have aggregatedDiffProperty only if there are diffs in the object or in the children,
242+ * otherwise the aggregatedDiffProperty is not added.
243+ * Note, that adding/removing the object itself is not included in the aggregation for this object,
244+ * you need retrieve this diffs from parent object if you need them.
245+ * Supports cycled JSO, nested objects and arrays.
246+ * @param obj - The object to aggregate the diffs of.
247+ * @param diffProperty - The property of the object to aggregate the diffs of.
248+ * @param aggregatedDiffProperty - The property of the object to store the aggregated diffs in.
249+ * @returns The aggregated diffs of the given object.
250+ */
251+
252+ // TODO: generalize to other use cases (like collecting deprecated)
253+ export function aggregateDiffsWithRollup ( obj : any , diffProperty : any , aggregatedDiffProperty : any ) : Set < Diff > | undefined {
254+
255+ const visited = new Set < any > ( )
256+
257+ function _aggregateDiffsWithRollup ( obj : any ) : Set < Diff > | undefined {
258+ if ( obj === null || typeof obj !== 'object' ) {
259+ return undefined
260+ }
261+
262+ if ( visited . has ( obj ) ) {
263+ return obj [ aggregatedDiffProperty ]
264+ }
265+
266+ visited . add ( obj )
267+
268+ // Process all children and collect their diffs
269+ const childrenDiffs = new Array < Set < Diff > > ( )
270+ if ( Array . isArray ( obj ) ) {
271+ for ( const item of obj ) {
272+ const childDiffs = _aggregateDiffsWithRollup ( item )
273+ childDiffs && childDiffs . size > 0 && childrenDiffs . push ( childDiffs )
274+ }
275+ } else {
276+ for ( const [ _ , value ] of Object . entries ( obj ) ) {
277+ const childDiffs = _aggregateDiffsWithRollup ( value )
278+ childDiffs && childDiffs . size > 0 && childrenDiffs . push ( childDiffs )
279+ }
280+ }
281+
282+ const hasOwnDiffs = diffProperty in obj
283+
284+ if ( hasOwnDiffs || childrenDiffs . length > 1 ) {
285+ // obj aggregated diffs are different from children diffs
286+ const aggregatedDiffs = new Set < Diff > ( )
287+ for ( const childDiffs of childrenDiffs ) {
288+ childDiffs . forEach ( diff => aggregatedDiffs . add ( diff ) )
289+ }
290+ const diffs = obj [ diffProperty ]
291+ for ( const key in diffs ) {
292+ aggregatedDiffs . add ( diffs [ key ] )
293+ }
294+ // Store the aggregated diffs in the object
295+ obj [ aggregatedDiffProperty ] = aggregatedDiffs
296+ } else if ( childrenDiffs . length === 1 ) {
297+ // could reuse a child diffs if there is only one
298+ [ obj [ aggregatedDiffProperty ] ] = childrenDiffs
299+ } else {
300+ // no diffs- no aggregated diffs get assigned
301+ }
302+
303+ return obj [ aggregatedDiffProperty ]
304+ }
305+
306+ return _aggregateDiffsWithRollup ( obj )
307+ }
308+
0 commit comments