@noggin/elastic-noggin-sdk
Version:
Elastic Noggin SDK
159 lines (141 loc) • 4.1 kB
text/typescript
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);
}