UNPKG

@winglet/json

Version:

TypeScript library for safe and efficient JSON data manipulation with RFC 6901 (JSON Pointer) and RFC 6902 (JSON Patch) compliance, featuring prototype pollution protection and immutable operations

99 lines (95 loc) 4.22 kB
'use strict'; var filter = require('@winglet/common-utils/filter'); var lib = require('@winglet/common-utils/lib'); var _enum = require('../../../enum.cjs'); var escapeSegment = require('../../escape/escapeSegment.cjs'); var type = require('../type.cjs'); var processValue = require('./utils/processValue.cjs'); const compareRecursive = (source, target, patches, path, strict, immutable) => { if (source === target || (source !== source && target !== target)) return; if ('toJson' in source && typeof source.toJson === 'function') source = source.toJson(); if ('toJson' in target && typeof target.toJson === 'function') target = target.toJson(); const sourceKeys = lib.getKeys(source); const targetKeys = lib.getKeys(target); const sourceIsArray = filter.isArray(source); const targetIsArray = filter.isArray(target); if (sourceIsArray !== targetIsArray) { if (strict) { patches.push({ op: type.Operation.TEST, path, value: source }); } patches.push({ op: type.Operation.REPLACE, path, value: target }); return; } let hasRemoved = false; for (let i = 0, l = sourceKeys.length; i < l; i++) { const key = sourceKeys[i]; const sourceValue = source[key]; if (lib.hasOwnProperty(target, key)) { const targetValue = target[key]; if (sourceValue === targetValue || (sourceValue !== sourceValue && targetValue !== targetValue)) continue; if (targetValue === undefined && sourceValue !== undefined && !targetIsArray) { const targetPath = path + _enum.JSONPointer.Separator + escapeSegment.escapeSegment(key); if (strict) { patches.push({ op: type.Operation.TEST, path: targetPath, value: processValue.processValue(sourceValue, immutable), }); } patches.push({ op: type.Operation.REMOVE, path: targetPath }); hasRemoved = true; continue; } if ((filter.isObject(sourceValue) && filter.isObject(targetValue)) || (filter.isArray(sourceValue) && filter.isArray(targetValue))) { compareRecursive(sourceValue, targetValue, patches, path + _enum.JSONPointer.Separator + escapeSegment.escapeSegment(key), strict, immutable); } else { const targetPath = path + _enum.JSONPointer.Separator + escapeSegment.escapeSegment(key); if (strict) patches.push({ op: type.Operation.TEST, path: targetPath, value: processValue.processValue(sourceValue, immutable), }); patches.push({ op: type.Operation.REPLACE, path: targetPath, value: processValue.processValue(targetValue, immutable), }); } } else { const targetPath = path + _enum.JSONPointer.Separator + escapeSegment.escapeSegment(key); if (strict) patches.push({ op: type.Operation.TEST, path: targetPath, value: processValue.processValue(sourceValue, immutable), }); patches.push({ op: type.Operation.REMOVE, path: targetPath }); hasRemoved = true; } } if (!hasRemoved && targetKeys.length === sourceKeys.length) return; for (let i = 0, l = targetKeys.length; i < l; i++) { const key = targetKeys[i]; const targetValue = target[key]; if (lib.hasOwnProperty(source, key) || targetValue === undefined) continue; patches.push({ op: type.Operation.ADD, path: path + _enum.JSONPointer.Separator + escapeSegment.escapeSegment(key), value: processValue.processValue(targetValue, immutable), }); } }; exports.compareRecursive = compareRecursive;