UNPKG

@krishnadaspc/tiny-json

Version:

Lightweight JSON compressor** with reversible key/value renaming, built for API response optimization and payload shrinking.

122 lines (103 loc) 3.2 kB
// json-mini/index.js export function compress(obj) { const keyToShort = {}; const shortToKey = {}; const valToShort = {}; const shortToVal = {}; let counter = 1; let vcount = 1; const valueCount = {}; // First pass to count value occurrences function countValues(value) { if (Array.isArray(value)) { value.forEach(countValues); } else if (value && typeof value === 'object') { Object.entries(value).forEach(([_, v]) => countValues(v)); } else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { valueCount[value] = (valueCount[value] || 0) + 1; } } countValues(obj); function getShortKey(key) { if (!keyToShort[key]) { const shortKey = `k${counter++}`; keyToShort[key] = shortKey; shortToKey[shortKey] = key; } return keyToShort[key]; } function getShortVal(val) { const isCompressible = typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean'; if (!isCompressible) return val; if (valueCount[val] > 1) { if (!valToShort[val]) { const shortVal = `v${vcount++}`; valToShort[val] = shortVal; shortToVal[shortVal] = val; } return valToShort[val]; } return val; } function _compress(value) { if (Array.isArray(value)) { return value.map(_compress); } if (value && typeof value === 'object') { const newObj = {}; for (const [key, val] of Object.entries(value)) { const shortKey = getShortKey(key); newObj[shortKey] = _compress(val); } return newObj; } return getShortVal(value); } const compressed = { _map: shortToKey, _data: _compress(obj) }; if (Object.keys(shortToVal).length > 0) { compressed._valmap = shortToVal; } return compressed; } export function decompress(compressed) { const keyMap = compressed._map; const valMap = compressed._valmap || {}; function _decompress(value) { if (Array.isArray(value)) { return value.map(_decompress); } if (value && typeof value === 'object') { const newObj = {}; for (const [key, val] of Object.entries(value)) { const originalKey = keyMap[key]; newObj[originalKey] = _decompress(val); } return newObj; } return valMap[value] ?? value; } return _decompress(compressed._data); } export function analyze(original, unit = 'kb') { const compressed = compress(original); const originalSize = Buffer.byteLength(JSON.stringify(original), 'utf8'); const compressedSize = Buffer.byteLength(JSON.stringify(compressed), 'utf8'); const reductionPercent = originalSize === 0 ? 0 : ((originalSize - compressedSize) / originalSize) * 100; function formatSize(size) { switch (unit.toLowerCase()) { case 'kb': return (size / 1024).toFixed(2) + ' KB'; case 'mb': return (size / (1024 * 1024)).toFixed(4) + ' MB'; default: return size + ' bytes'; } } return { originalSize: formatSize(originalSize), compressedSize: formatSize(compressedSize), reductionPercent: Math.round(reductionPercent * 100) / 100 }; }