UNPKG

@noggin/elastic-noggin-sdk

Version:
159 lines (141 loc) 4.1 kB
import shajs from "sha.js"; import { Hash } from "crypto"; /** * Returns a deterministic JSON string of the given object. The object is compacted as it goes to ensure that the * absence of a properties (for example) is the same as null or an empty array. */ const sortArrays = { field: comparisonFunctionFactory("tip"), i18n: comparisonFunctionFactory("lang"), parent: undefined, // Unicode sort }; function comparisonFunctionFactory(key: string) { return function (a: any, b: any) { if ( typeof a === "object" && typeof b === "object" && a[key] !== undefined && b[key] !== undefined ) { return a[key] < b[key] ? -1 : a[key] > b[key] ? 1 : 0; } return 0; }; } function RFCJSONEncode(obj: any): string { const json = JSON.stringify(obj); return json.replace(/[\u007F-\uFFFF]/g, function (chr) { return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4); }); } export function sidStringify(obj: any): string { if (obj === undefined || obj === null) { return "null"; } if (obj instanceof Array) { return "[" + obj.map(sidStringify) + "]"; } if (typeof obj === "object") { return ( "{" + Object.keys(obj) .filter(function (key) { return !( obj[key] === undefined || obj[key] === null || (typeof obj[key] === "object" && Object.keys(obj[key]).length === 0) ); // return true; }) .sort() .map(function (key) { if (obj[key] instanceof Array && sortArrays.hasOwnProperty(key)) { return ( RFCJSONEncode(key) + ":[" + obj[key] .slice() .sort((<any>sortArrays)[key]) .map(sidStringify) + "]" ); } return RFCJSONEncode(key) + ":" + sidStringify(obj[key]); }) + "}" ); } return RFCJSONEncode(obj); } const hashChunkSize = 1024; const hashUnicodeRegExp = /[\u007F-\uFFFF]/g; const hashUnicodeReplacer = (chr: string): string => { return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4); }; function hashRFCJSONEncode(sha256: Hash, obj: any): Hash { if (typeof obj === "string") { const l = obj.length; sha256.update('"'); for (let i = 0; i < l; i += hashChunkSize) { sha256.update( JSON.stringify(obj.substring(i, i + hashChunkSize)) .slice(1, -1) .replace(hashUnicodeRegExp, hashUnicodeReplacer) ); } sha256.update('"'); return sha256; } return sha256.update(JSON.stringify(obj)); } export function hashSidStringify(sha256: Hash, obj: any): Hash { if (obj === undefined || obj === null) { return sha256.update("null"); } if (obj instanceof Array) { sha256.update("["); obj.forEach((elem, i) => { if (i > 0) { sha256.update(","); } hashSidStringify(sha256, elem); }); return sha256.update("]"); } if (typeof obj === "object") { sha256.update("{"); Object.keys(obj) .filter((key: string) => { return !( obj[key] === undefined || obj[key] === null || (typeof obj[key] === "object" && Object.keys(obj[key]).length === 0) ); }) .sort() .forEach((key: string, i: number) => { if (i > 0) { sha256.update(","); } if (obj[key] instanceof Array && sortArrays.hasOwnProperty(key)) { hashRFCJSONEncode(sha256, key); sha256.update(":["); obj[key] .slice() .sort((<any>sortArrays)[key]) .forEach((elem, i) => { if (i > 0) { sha256.update(","); } hashSidStringify(sha256, elem); }); return sha256.update("]"); } hashRFCJSONEncode(sha256, key); sha256.update(":"); hashSidStringify(sha256, obj[key]); }); return sha256.update("}"); } return hashRFCJSONEncode(sha256, obj); }