json-canonicalize
Advanced tools
Comparing version
@@ -1,4 +0,15 @@ | ||
function canonicalize(obj, allowCircular) { | ||
function _serialize(obj, options) { | ||
let buffer = ''; | ||
const vInclude = options && options.include; | ||
let vExclude = options && options.exclude; | ||
if (vExclude) { | ||
if (typeof vExclude === 'string') | ||
vExclude = [vExclude]; | ||
} | ||
if (vInclude) | ||
vInclude.sort(); | ||
const visited = new WeakMap(); | ||
const allowCircular = options && options.allowCircular; | ||
const filterUndefined = options && options.filterUndefined; | ||
const undefinedInArrayToNull = options && options.undefinedInArrayToNull; | ||
serialize(obj, ''); | ||
@@ -14,4 +25,2 @@ return buffer; | ||
buffer += JSON.stringify(object); | ||
// } else if (object instanceof Date) { | ||
// buffer += JSON.stringify(object); | ||
} | ||
@@ -40,3 +49,3 @@ else if (Array.isArray(object)) { | ||
next = true; | ||
if (element === undefined) { | ||
if (undefinedInArrayToNull && element === undefined) { | ||
element = null; | ||
@@ -67,66 +76,7 @@ } | ||
buffer += '{'; | ||
const vKeys = Object.keys(object).filter((k) => object[k] !== undefined).sort(); | ||
vKeys.forEach((property, index) => addProp(object, property, index, path)); | ||
buffer += '}'; | ||
} | ||
} | ||
function addProp(object, property, index, path) { | ||
if (index > 0) { | ||
buffer += ','; | ||
} | ||
/////////////////////////////////////////////// | ||
// Property names are strings - Use ES6/JSON // | ||
/////////////////////////////////////////////// | ||
buffer += JSON.stringify(property); | ||
buffer += ':'; | ||
////////////////////////////////////////// | ||
// Property value - Recursive expansion // | ||
////////////////////////////////////////// | ||
serialize(object[property], `${path}.${property}`); | ||
} | ||
} | ||
function canonicalizeEx(obj, options) { | ||
let buffer = ''; | ||
const vInclude = options && options.include; | ||
let vExclude = options && options.exclude; | ||
if (vExclude) { | ||
if (typeof vExclude === 'string') | ||
vExclude = [vExclude]; | ||
} | ||
if (vInclude) | ||
vInclude.sort(); | ||
const visited = new WeakMap(); | ||
const allowCircular = options && (options === null || options === void 0 ? void 0 : options.allowCircular); | ||
serialize(obj, ''); | ||
return buffer; | ||
function serialize(object, path) { | ||
if (object === null || | ||
typeof object !== 'object' || | ||
object.toJSON != null) { | ||
///////////////////////////////////////////////// | ||
// Primitive data type - Use ES6/JSON // | ||
///////////////////////////////////////////////// | ||
buffer += JSON.stringify(object); | ||
// } else if (object instanceof Date) { | ||
// buffer += JSON.stringify(object); | ||
} | ||
else if (Array.isArray(object)) { | ||
///////////////////////////////////////////////// | ||
// Array - Maintain element order // | ||
///////////////////////////////////////////////// | ||
const visitedPath = visited.get(object); | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected'); | ||
} | ||
buffer += '"[Circular]"'; | ||
let next = false; | ||
const addProp = (property) => { | ||
if (vExclude && vExclude.includes(property)) { | ||
return; | ||
} | ||
} | ||
visited.set(object, path); | ||
buffer += '['; | ||
let next = false; | ||
object.forEach((element, index) => { | ||
if (next) { | ||
@@ -136,35 +86,28 @@ buffer += ','; | ||
next = true; | ||
///////////////////////////////////////// | ||
// Array element - Recursive expansion // | ||
///////////////////////////////////////// | ||
serialize(element, `${path}[${index}]`); | ||
}); | ||
buffer += ']'; | ||
} | ||
else { | ||
///////////////////////////////////////////////// | ||
// Object - Sort properties before serializing // | ||
///////////////////////////////////////////////// | ||
const visitedPath = visited.get(object); | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected'); | ||
/////////////////////////////////////////////// | ||
// Property names are strings - Use ES6/JSON // | ||
/////////////////////////////////////////////// | ||
buffer += JSON.stringify(property); | ||
buffer += ':'; | ||
////////////////////////////////////////// | ||
// Property value - Recursive expansion // | ||
////////////////////////////////////////// | ||
serialize(object[property], `${path}.${property}`); | ||
}; | ||
if (path === '' && vInclude) { | ||
vInclude.forEach((property) => { | ||
if (object.hasOwnProperty(property)) { | ||
addProp(property); | ||
} | ||
buffer += '"[Circular]"'; | ||
return; | ||
} | ||
} | ||
visited.set(object, path); | ||
buffer += '{'; | ||
if (path === '' && vInclude) { | ||
vInclude.forEach((property, index) => { | ||
if (!object.hasOwnProperty(property)) | ||
return; | ||
addProp(object, property, index, path); | ||
}); | ||
} | ||
else { | ||
const vKeys = Object.keys(object).sort(); | ||
vKeys.forEach((property, index) => addProp(object, property, index, path)); | ||
let vKeys = Object.keys(object); | ||
if (filterUndefined) { | ||
vKeys = vKeys.filter((k) => object[k] !== undefined); | ||
} | ||
vKeys.sort(); | ||
vKeys.forEach((property) => { | ||
addProp(property); | ||
}); | ||
} | ||
@@ -174,25 +117,36 @@ buffer += '}'; | ||
} | ||
function addProp(object, property, index, path) { | ||
if (vExclude && vExclude.length) { | ||
for (const v of vExclude) { | ||
if (v === property) | ||
return; | ||
} | ||
} | ||
if (index > 0) { | ||
buffer += ','; | ||
} | ||
/////////////////////////////////////////////// | ||
// Property names are strings - Use ES6/JSON // | ||
/////////////////////////////////////////////// | ||
buffer += JSON.stringify(property); | ||
buffer += ':'; | ||
////////////////////////////////////////// | ||
// Property value - Recursive expansion // | ||
////////////////////////////////////////// | ||
serialize(object[property], `${path}.${property}`); | ||
} | ||
} | ||
/** | ||
* The main function for JSON canonicalization. It takes a JavaScript object and returns its canonical string representation. | ||
* | ||
* @param obj The JavaScript object to canonicalize. | ||
* @param allowCircular If `true`, the function will handle circular references in the object by replacing them with `null`. Defaults to `false`. | ||
* @returns The canonical string representation of the object. | ||
*/ | ||
function canonicalize(obj, allowCircular) { | ||
return _serialize(obj, { | ||
allowCircular, | ||
filterUndefined: true, | ||
undefinedInArrayToNull: true, | ||
}); | ||
} | ||
/** | ||
* The extended canonicalization function, offering more granular control over the serialization process. | ||
* | ||
* @param obj The JavaScript object to canonicalize. | ||
* @param options An object with the following properties: | ||
* - `allowCircular` (boolean, optional): Same as in `canonicalize`. | ||
* - `filterUndefined` (boolean, optional): If `true`, `undefined` values in objects will be filtered out. Defaults to `true`. | ||
* - `undefinedInArrayToNull` (boolean, optional): If `true`, `undefined` values in arrays will be converted to `null`. Defaults to `true`. | ||
* - `include` (string[], optional): An array of property names to include in the canonicalization. | ||
* - `exclude` (string[], optional): An array of property names to exclude from the canonicalization. | ||
* @returns The canonical string representation of the object. | ||
*/ | ||
function canonicalizeEx(obj, options) { | ||
return _serialize(obj, options); | ||
} | ||
export { canonicalize, canonicalizeEx }; | ||
//# sourceMappingURL=index.esm.js.map |
@@ -1,2 +0,2 @@ | ||
function r(r,e){let t="";const i=new WeakMap;return o(r,""),t;function o(r,n){if(null===r||"object"!=typeof r||null!=r.toJSON)t+=JSON.stringify(r);else if(Array.isArray(r)){const c=i.get(r);if(void 0!==c&&n.startsWith(c)){if(!e)throw new Error("Circular reference detected");return void(t+='"[Circular]"')}i.set(r,n),t+="[";let f=!1;r.forEach(((r,e)=>{f&&(t+=","),f=!0,void 0===r&&(r=null),o(r,`${n}[${e}]`)})),t+="]"}else{const c=i.get(r);if(void 0!==c&&n.startsWith(c)){if(!e)throw new Error("Circular reference detected");return void(t+='"[Circular]"')}i.set(r,n),t+="{";Object.keys(r).filter((e=>void 0!==r[e])).sort().forEach(((e,i)=>function(r,e,i,n){i>0&&(t+=",");t+=JSON.stringify(e),t+=":",o(r[e],`${n}.${e}`)}(r,e,i,n))),t+="}"}}}function e(r,e){let t="";const i=e&&e.include;let o=e&&e.exclude;o&&"string"==typeof o&&(o=[o]),i&&i.sort();const n=new WeakMap,c=e&&(null==e?void 0:e.allowCircular);return f(r,""),t;function f(r,e){if(null===r||"object"!=typeof r||null!=r.toJSON)t+=JSON.stringify(r);else if(Array.isArray(r)){const i=n.get(r);if(void 0!==i&&e.startsWith(i)){if(!c)throw new Error("Circular reference detected");return void(t+='"[Circular]"')}n.set(r,e),t+="[";let o=!1;r.forEach(((r,i)=>{o&&(t+=","),o=!0,f(r,`${e}[${i}]`)})),t+="]"}else{const o=n.get(r);if(void 0!==o&&e.startsWith(o)){if(!c)throw new Error("Circular reference detected");return void(t+='"[Circular]"')}if(n.set(r,e),t+="{",""===e&&i)i.forEach(((t,i)=>{r.hasOwnProperty(t)&&s(r,t,i,e)}));else{Object.keys(r).sort().forEach(((t,i)=>s(r,t,i,e)))}t+="}"}}function s(r,e,i,n){if(o&&o.length)for(const r of o)if(r===e)return;i>0&&(t+=","),t+=JSON.stringify(e),t+=":",f(r[e],`${n}.${e}`)}}export{r as canonicalize,e as canonicalizeEx}; | ||
function e(e,r){let t="";const n=r&&r.include;let i=r&&r.exclude;i&&"string"==typeof i&&(i=[i]),n&&n.sort();const l=new WeakMap,o=r&&r.allowCircular,c=r&&r.filterUndefined,f=r&&r.undefinedInArrayToNull;return function e(r,u){if(null===r||"object"!=typeof r||null!=r.toJSON)t+=JSON.stringify(r);else if(Array.isArray(r)){const n=l.get(r);if(void 0!==n&&u.startsWith(n)){if(!o)throw new Error("Circular reference detected");return void(t+='"[Circular]"')}l.set(r,u),t+="[";let i=!1;r.forEach(((r,n)=>{i&&(t+=","),i=!0,f&&void 0===r&&(r=null),e(r,`${u}[${n}]`)})),t+="]"}else{const f=l.get(r);if(void 0!==f&&u.startsWith(f)){if(!o)throw new Error("Circular reference detected");return void(t+='"[Circular]"')}l.set(r,u),t+="{";let s=!1;const d=n=>{i&&i.includes(n)||(s&&(t+=","),s=!0,t+=JSON.stringify(n),t+=":",e(r[n],`${u}.${n}`))};if(""===u&&n)n.forEach((e=>{r.hasOwnProperty(e)&&d(e)}));else{let e=Object.keys(r);c&&(e=e.filter((e=>void 0!==r[e]))),e.sort(),e.forEach((e=>{d(e)}))}t+="}"}}(e,""),t}function r(r,t){return e(r,{allowCircular:t,filterUndefined:!0,undefinedInArrayToNull:!0})}function t(r,t){return e(r,t)}export{r as canonicalize,t as canonicalizeEx}; | ||
//# sourceMappingURL=index.esm.min.js.map |
@@ -7,89 +7,4 @@ (function (global, factory) { | ||
function canonicalize(obj, allowCircular) { | ||
function _serialize(obj, options) { | ||
var buffer = ''; | ||
var visited = new WeakMap(); | ||
serialize(obj, ''); | ||
return buffer; | ||
function serialize(object, path) { | ||
if (object === null || | ||
typeof object !== 'object' || | ||
object.toJSON != null) { | ||
///////////////////////////////////////////////// | ||
// Primitive data type - Use ES6/JSON // | ||
///////////////////////////////////////////////// | ||
buffer += JSON.stringify(object); | ||
// } else if (object instanceof Date) { | ||
// buffer += JSON.stringify(object); | ||
} | ||
else if (Array.isArray(object)) { | ||
///////////////////////////////////////////////// | ||
// Array - Maintain element order // | ||
///////////////////////////////////////////////// | ||
var visitedPath = visited.get(object); | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected'); | ||
} | ||
buffer += '"[Circular]"'; | ||
return; | ||
} | ||
} | ||
visited.set(object, path); | ||
buffer += '['; | ||
var next_1 = false; | ||
object.forEach(function (element, index) { | ||
if (next_1) { | ||
buffer += ','; | ||
} | ||
next_1 = true; | ||
if (element === undefined) { | ||
element = null; | ||
} | ||
///////////////////////////////////////// | ||
// Array element - Recursive expansion // | ||
///////////////////////////////////////// | ||
serialize(element, path + "[" + index + "]"); | ||
}); | ||
buffer += ']'; | ||
} | ||
else { | ||
///////////////////////////////////////////////// | ||
// Object - Sort properties before serializing // | ||
///////////////////////////////////////////////// | ||
var visitedPath = visited.get(object); | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected'); | ||
} | ||
buffer += '"[Circular]"'; | ||
return; | ||
} | ||
} | ||
visited.set(object, path); | ||
buffer += '{'; | ||
var vKeys = Object.keys(object).filter(function (k) { return object[k] !== undefined; }).sort(); | ||
vKeys.forEach(function (property, index) { return addProp(object, property, index, path); }); | ||
buffer += '}'; | ||
} | ||
} | ||
function addProp(object, property, index, path) { | ||
if (index > 0) { | ||
buffer += ','; | ||
} | ||
/////////////////////////////////////////////// | ||
// Property names are strings - Use ES6/JSON // | ||
/////////////////////////////////////////////// | ||
buffer += JSON.stringify(property); | ||
buffer += ':'; | ||
////////////////////////////////////////// | ||
// Property value - Recursive expansion // | ||
////////////////////////////////////////// | ||
serialize(object[property], path + "." + property); | ||
} | ||
} | ||
function canonicalizeEx(obj, options) { | ||
var buffer = ''; | ||
var vInclude = options && options.include; | ||
@@ -104,3 +19,5 @@ var vExclude = options && options.exclude; | ||
var visited = new WeakMap(); | ||
var allowCircular = options && (options === null || options === void 0 ? void 0 : options.allowCircular); | ||
var allowCircular = options && options.allowCircular; | ||
var filterUndefined = options && options.filterUndefined; | ||
var undefinedInArrayToNull = options && options.undefinedInArrayToNull; | ||
serialize(obj, ''); | ||
@@ -116,4 +33,2 @@ return buffer; | ||
buffer += JSON.stringify(object); | ||
// } else if (object instanceof Date) { | ||
// buffer += JSON.stringify(object); | ||
} | ||
@@ -142,2 +57,5 @@ else if (Array.isArray(object)) { | ||
next_1 = true; | ||
if (undefinedInArrayToNull && element === undefined) { | ||
element = null; | ||
} | ||
///////////////////////////////////////// | ||
@@ -166,12 +84,37 @@ // Array element - Recursive expansion // | ||
buffer += '{'; | ||
var next_2 = false; | ||
var addProp_1 = function (property) { | ||
if (vExclude && vExclude.includes(property)) { | ||
return; | ||
} | ||
if (next_2) { | ||
buffer += ','; | ||
} | ||
next_2 = true; | ||
/////////////////////////////////////////////// | ||
// Property names are strings - Use ES6/JSON // | ||
/////////////////////////////////////////////// | ||
buffer += JSON.stringify(property); | ||
buffer += ':'; | ||
////////////////////////////////////////// | ||
// Property value - Recursive expansion // | ||
////////////////////////////////////////// | ||
serialize(object[property], path + "." + property); | ||
}; | ||
if (path === '' && vInclude) { | ||
vInclude.forEach(function (property, index) { | ||
if (!object.hasOwnProperty(property)) | ||
return; | ||
addProp(object, property, index, path); | ||
vInclude.forEach(function (property) { | ||
if (object.hasOwnProperty(property)) { | ||
addProp_1(property); | ||
} | ||
}); | ||
} | ||
else { | ||
var vKeys = Object.keys(object).sort(); | ||
vKeys.forEach(function (property, index) { return addProp(object, property, index, path); }); | ||
var vKeys = Object.keys(object); | ||
if (filterUndefined) { | ||
vKeys = vKeys.filter(function (k) { return object[k] !== undefined; }); | ||
} | ||
vKeys.sort(); | ||
vKeys.forEach(function (property) { | ||
addProp_1(property); | ||
}); | ||
} | ||
@@ -181,25 +124,35 @@ buffer += '}'; | ||
} | ||
function addProp(object, property, index, path) { | ||
if (vExclude && vExclude.length) { | ||
for (var _i = 0, vExclude_1 = vExclude; _i < vExclude_1.length; _i++) { | ||
var v = vExclude_1[_i]; | ||
if (v === property) | ||
return; | ||
} | ||
} | ||
if (index > 0) { | ||
buffer += ','; | ||
} | ||
/////////////////////////////////////////////// | ||
// Property names are strings - Use ES6/JSON // | ||
/////////////////////////////////////////////// | ||
buffer += JSON.stringify(property); | ||
buffer += ':'; | ||
////////////////////////////////////////// | ||
// Property value - Recursive expansion // | ||
////////////////////////////////////////// | ||
serialize(object[property], path + "." + property); | ||
} | ||
} | ||
/** | ||
* The main function for JSON canonicalization. It takes a JavaScript object and returns its canonical string representation. | ||
* | ||
* @param obj The JavaScript object to canonicalize. | ||
* @param allowCircular If `true`, the function will handle circular references in the object by replacing them with `null`. Defaults to `false`. | ||
* @returns The canonical string representation of the object. | ||
*/ | ||
function canonicalize(obj, allowCircular) { | ||
return _serialize(obj, { | ||
allowCircular: allowCircular, | ||
filterUndefined: true, | ||
undefinedInArrayToNull: true, | ||
}); | ||
} | ||
/** | ||
* The extended canonicalization function, offering more granular control over the serialization process. | ||
* | ||
* @param obj The JavaScript object to canonicalize. | ||
* @param options An object with the following properties: | ||
* - `allowCircular` (boolean, optional): Same as in `canonicalize`. | ||
* - `filterUndefined` (boolean, optional): If `true`, `undefined` values in objects will be filtered out. Defaults to `true`. | ||
* - `undefinedInArrayToNull` (boolean, optional): If `true`, `undefined` values in arrays will be converted to `null`. Defaults to `true`. | ||
* - `include` (string[], optional): An array of property names to include in the canonicalization. | ||
* - `exclude` (string[], optional): An array of property names to exclude from the canonicalization. | ||
* @returns The canonical string representation of the object. | ||
*/ | ||
function canonicalizeEx(obj, options) { | ||
return _serialize(obj, options); | ||
} | ||
exports.canonicalize = canonicalize; | ||
@@ -206,0 +159,0 @@ exports.canonicalizeEx = canonicalizeEx; |
@@ -7,6 +7,8 @@ (function (global, factory) { | ||
function canonicalize(r,e){var t="",i=new WeakMap;return n(r,""),t;function n(r,o){if(null===r||"object"!=typeof r||null!=r.toJSON)t+=JSON.stringify(r);else if(Array.isArray(r)){if(void 0!==(f=i.get(r))&&o.startsWith(f)){if(!e)throw new Error("Circular reference detected");return void(t+='"[Circular]"')}i.set(r,o),t+="[";var c=!1;r.forEach((function(r,e){c&&(t+=","),c=!0,void 0===r&&(r=null),n(r,o+"["+e+"]");})),t+="]";}else {var f;if(void 0!==(f=i.get(r))&&o.startsWith(f)){if(!e)throw new Error("Circular reference detected");return void(t+='"[Circular]"')}i.set(r,o),t+="{",Object.keys(r).filter((function(e){return void 0!==r[e]})).sort().forEach((function(e,i){return function(r,e,i,o){i>0&&(t+=",");t+=JSON.stringify(e),t+=":",n(r[e],o+"."+e);}(r,e,i,o)})),t+="}";}}} | ||
function _serialize(r,e){var i="",t=e&&e.include,n=e&&e.exclude;n&&"string"==typeof n&&(n=[n]),t&&t.sort();var o=new WeakMap,f=e&&e.allowCircular,a=e&&e.filterUndefined,c=e&&e.undefinedInArrayToNull;return function r(e,l){if(null===e||"object"!=typeof e||null!=e.toJSON)i+=JSON.stringify(e);else if(Array.isArray(e)){if(void 0!==(s=o.get(e))&&l.startsWith(s)){if(!f)throw new Error("Circular reference detected");return void(i+='"[Circular]"')}o.set(e,l),i+="[";var u=!1;e.forEach((function(e,t){u&&(i+=","),u=!0,c&&void 0===e&&(e=null),r(e,l+"["+t+"]");})),i+="]";}else {var s;if(void 0!==(s=o.get(e))&&l.startsWith(s)){if(!f)throw new Error("Circular reference detected");return void(i+='"[Circular]"')}o.set(e,l),i+="{";var d=!1,v=function(t){n&&n.includes(t)||(d&&(i+=","),d=!0,i+=JSON.stringify(t),i+=":",r(e[t],l+"."+t));};if(""===l&&t)t.forEach((function(r){e.hasOwnProperty(r)&&v(r);}));else {var y=Object.keys(e);a&&(y=y.filter((function(r){return void 0!==e[r]}))),y.sort(),y.forEach((function(r){v(r);}));}i+="}";}}(r,""),i} | ||
function canonicalizeEx(r,e){var t="",i=e&&e.include,n=e&&e.exclude;n&&"string"==typeof n&&(n=[n]),i&&i.sort();var o=new WeakMap,f=e&&(null==e?void 0:e.allowCircular);return c(r,""),t;function c(r,e){if(null===r||"object"!=typeof r||null!=r.toJSON)t+=JSON.stringify(r);else if(Array.isArray(r)){if(void 0!==(l=o.get(r))&&e.startsWith(l)){if(!f)throw new Error("Circular reference detected");return void(t+='"[Circular]"')}o.set(r,e),t+="[";var n=!1;r.forEach((function(r,i){n&&(t+=","),n=!0,c(r,e+"["+i+"]");})),t+="]";}else {var l;if(void 0!==(l=o.get(r))&&e.startsWith(l)){if(!f)throw new Error("Circular reference detected");return void(t+='"[Circular]"')}if(o.set(r,e),t+="{",""===e&&i)i.forEach((function(t,i){r.hasOwnProperty(t)&&a(r,t,i,e);}));else Object.keys(r).sort().forEach((function(t,i){return a(r,t,i,e)}));t+="}";}}function a(r,e,i,o){if(n&&n.length)for(var f=0,a=n;f<a.length;f++){if(a[f]===e)return}i>0&&(t+=","),t+=JSON.stringify(e),t+=":",c(r[e],o+"."+e);}} | ||
function canonicalize(e,i){return _serialize(e,{allowCircular:i,filterUndefined:!0,undefinedInArrayToNull:!0})} | ||
function canonicalizeEx(i,e){return _serialize(i,e)} | ||
exports.canonicalize = canonicalize; | ||
@@ -13,0 +15,0 @@ exports.canonicalizeEx = canonicalizeEx; |
@@ -47,3 +47,3 @@ // tslint:disable max-line-length | ||
obj.arr.push(obj.arr); | ||
expect(() => canonicalize(obj)).toThrowError('Circular reference detected'); | ||
expect(() => canonicalize(obj)).toThrow('Circular reference detected'); | ||
}); | ||
@@ -65,3 +65,3 @@ it('should allow canonicalize array item circular ref', () => { | ||
obj.cir = obj; | ||
expect(() => canonicalize(obj)).toThrowError('Circular reference detected'); | ||
expect(() => canonicalize(obj)).toThrow('Circular reference detected'); | ||
}); | ||
@@ -68,0 +68,0 @@ it('should allow canonicalize obj item circular ref', () => { |
@@ -34,3 +34,3 @@ // tslint:disable max-line-length | ||
obj.arr.push(obj.arr); | ||
expect(() => canonicalize(obj)).toThrowError('Circular reference detected'); | ||
expect(() => canonicalize(obj)).toThrow('Circular reference detected'); | ||
}); | ||
@@ -55,3 +55,3 @@ it('should allow canonicalize array item circular ref', () => { | ||
// ) | ||
expect(() => canonicalize(obj)).toThrowError('Circular reference detected'); | ||
expect(() => canonicalize(obj)).toThrow('Circular reference detected'); | ||
}); | ||
@@ -68,2 +68,13 @@ it('should allow canonicalize obj item circular ref', () => { | ||
}); | ||
it('should allow canonicalize obj item circular ref2', () => { | ||
const obj = { | ||
text: undefined, | ||
num: 47734.12, | ||
dt: new Date('2018-12-17T01:08:19.719Z'), | ||
arr: [56, 'a', '12', { t: '455A', a: 123 }], | ||
}; | ||
obj.cir = obj; | ||
obj.arr.push(obj); | ||
expect(canonicalize(obj, true)).toEqual('{"arr":[56,"a","12",{"a":123,"t":"455A"},"[Circular]"],"cir":"[Circular]","dt":"2018-12-17T01:08:19.719Z","num":47734.12}'); | ||
}); | ||
it('should not treat two references to the same sub-object as a circular reference', () => { | ||
@@ -92,3 +103,11 @@ const sharedObject = { key: 'value' }; | ||
}); | ||
it('should not treat two references to the same sub-array as a circular reference2', () => { | ||
const sharedArray = [1, 2]; | ||
const obj = { | ||
a: [sharedArray], | ||
b: sharedArray | ||
}; | ||
expect(canonicalize(obj)).toEqual('{"a":[[1,2]],"b":[1,2]}'); | ||
}); | ||
}); | ||
//# sourceMappingURL=canonicalize.spec.js.map |
@@ -0,106 +1,17 @@ | ||
import { _serialize } from './serializer'; | ||
/** | ||
* The extended canonicalization function, offering more granular control over the serialization process. | ||
* | ||
* @param obj The JavaScript object to canonicalize. | ||
* @param options An object with the following properties: | ||
* - `allowCircular` (boolean, optional): Same as in `canonicalize`. | ||
* - `filterUndefined` (boolean, optional): If `true`, `undefined` values in objects will be filtered out. Defaults to `true`. | ||
* - `undefinedInArrayToNull` (boolean, optional): If `true`, `undefined` values in arrays will be converted to `null`. Defaults to `true`. | ||
* - `include` (string[], optional): An array of property names to include in the canonicalization. | ||
* - `exclude` (string[], optional): An array of property names to exclude from the canonicalization. | ||
* @returns The canonical string representation of the object. | ||
*/ | ||
export function canonicalizeEx(obj, options) { | ||
let buffer = ''; | ||
const vInclude = options && options.include; | ||
let vExclude = options && options.exclude; | ||
if (vExclude) { | ||
if (typeof vExclude === 'string') | ||
vExclude = [vExclude]; | ||
} | ||
if (vInclude) | ||
vInclude.sort(); | ||
const visited = new WeakMap(); | ||
const allowCircular = options && (options === null || options === void 0 ? void 0 : options.allowCircular); | ||
serialize(obj, ''); | ||
return buffer; | ||
function serialize(object, path) { | ||
if (object === null || | ||
typeof object !== 'object' || | ||
object.toJSON != null) { | ||
///////////////////////////////////////////////// | ||
// Primitive data type - Use ES6/JSON // | ||
///////////////////////////////////////////////// | ||
buffer += JSON.stringify(object); | ||
// } else if (object instanceof Date) { | ||
// buffer += JSON.stringify(object); | ||
} | ||
else if (Array.isArray(object)) { | ||
///////////////////////////////////////////////// | ||
// Array - Maintain element order // | ||
///////////////////////////////////////////////// | ||
const visitedPath = visited.get(object); | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected'); | ||
} | ||
buffer += '"[Circular]"'; | ||
return; | ||
} | ||
} | ||
visited.set(object, path); | ||
buffer += '['; | ||
let next = false; | ||
object.forEach((element, index) => { | ||
if (next) { | ||
buffer += ','; | ||
} | ||
next = true; | ||
///////////////////////////////////////// | ||
// Array element - Recursive expansion // | ||
///////////////////////////////////////// | ||
serialize(element, `${path}[${index}]`); | ||
}); | ||
buffer += ']'; | ||
} | ||
else { | ||
///////////////////////////////////////////////// | ||
// Object - Sort properties before serializing // | ||
///////////////////////////////////////////////// | ||
const visitedPath = visited.get(object); | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected'); | ||
} | ||
buffer += '"[Circular]"'; | ||
return; | ||
} | ||
} | ||
visited.set(object, path); | ||
buffer += '{'; | ||
if (path === '' && vInclude) { | ||
vInclude.forEach((property, index) => { | ||
if (!object.hasOwnProperty(property)) | ||
return; | ||
addProp(object, property, index, path); | ||
}); | ||
} | ||
else { | ||
const vKeys = Object.keys(object).sort(); | ||
vKeys.forEach((property, index) => addProp(object, property, index, path)); | ||
} | ||
buffer += '}'; | ||
} | ||
} | ||
function addProp(object, property, index, path) { | ||
if (vExclude && vExclude.length) { | ||
for (const v of vExclude) { | ||
if (v === property) | ||
return; | ||
} | ||
} | ||
if (index > 0) { | ||
buffer += ','; | ||
} | ||
/////////////////////////////////////////////// | ||
// Property names are strings - Use ES6/JSON // | ||
/////////////////////////////////////////////// | ||
buffer += JSON.stringify(property); | ||
buffer += ':'; | ||
////////////////////////////////////////// | ||
// Property value - Recursive expansion // | ||
////////////////////////////////////////// | ||
serialize(object[property], `${path}.${property}`); | ||
} | ||
return _serialize(obj, options); | ||
} | ||
//# sourceMappingURL=canonicalize-ex.js.map |
@@ -0,85 +1,16 @@ | ||
import { _serialize } from './serializer'; | ||
/** | ||
* The main function for JSON canonicalization. It takes a JavaScript object and returns its canonical string representation. | ||
* | ||
* @param obj The JavaScript object to canonicalize. | ||
* @param allowCircular If `true`, the function will handle circular references in the object by replacing them with `null`. Defaults to `false`. | ||
* @returns The canonical string representation of the object. | ||
*/ | ||
export function canonicalize(obj, allowCircular) { | ||
let buffer = ''; | ||
const visited = new WeakMap(); | ||
serialize(obj, ''); | ||
return buffer; | ||
function serialize(object, path) { | ||
if (object === null || | ||
typeof object !== 'object' || | ||
object.toJSON != null) { | ||
///////////////////////////////////////////////// | ||
// Primitive data type - Use ES6/JSON // | ||
///////////////////////////////////////////////// | ||
buffer += JSON.stringify(object); | ||
// } else if (object instanceof Date) { | ||
// buffer += JSON.stringify(object); | ||
} | ||
else if (Array.isArray(object)) { | ||
///////////////////////////////////////////////// | ||
// Array - Maintain element order // | ||
///////////////////////////////////////////////// | ||
const visitedPath = visited.get(object); | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected'); | ||
} | ||
buffer += '"[Circular]"'; | ||
return; | ||
} | ||
} | ||
visited.set(object, path); | ||
buffer += '['; | ||
let next = false; | ||
object.forEach((element, index) => { | ||
if (next) { | ||
buffer += ','; | ||
} | ||
next = true; | ||
if (element === undefined) { | ||
element = null; | ||
} | ||
///////////////////////////////////////// | ||
// Array element - Recursive expansion // | ||
///////////////////////////////////////// | ||
serialize(element, `${path}[${index}]`); | ||
}); | ||
buffer += ']'; | ||
} | ||
else { | ||
///////////////////////////////////////////////// | ||
// Object - Sort properties before serializing // | ||
///////////////////////////////////////////////// | ||
const visitedPath = visited.get(object); | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected'); | ||
} | ||
buffer += '"[Circular]"'; | ||
return; | ||
} | ||
} | ||
visited.set(object, path); | ||
buffer += '{'; | ||
const vKeys = Object.keys(object).filter((k) => object[k] !== undefined).sort(); | ||
vKeys.forEach((property, index) => addProp(object, property, index, path)); | ||
buffer += '}'; | ||
} | ||
} | ||
function addProp(object, property, index, path) { | ||
if (index > 0) { | ||
buffer += ','; | ||
} | ||
/////////////////////////////////////////////// | ||
// Property names are strings - Use ES6/JSON // | ||
/////////////////////////////////////////////// | ||
buffer += JSON.stringify(property); | ||
buffer += ':'; | ||
////////////////////////////////////////// | ||
// Property value - Recursive expansion // | ||
////////////////////////////////////////// | ||
serialize(object[property], `${path}.${property}`); | ||
} | ||
return _serialize(obj, { | ||
allowCircular, | ||
filterUndefined: true, | ||
undefinedInArrayToNull: true, | ||
}); | ||
} | ||
//# sourceMappingURL=canonicalize.js.map |
@@ -47,3 +47,3 @@ // tslint:disable max-line-length | ||
obj.arr.push(obj.arr); | ||
expect(function () { return canonicalize(obj); }).toThrowError('Circular reference detected'); | ||
expect(function () { return canonicalize(obj); }).toThrow('Circular reference detected'); | ||
}); | ||
@@ -65,3 +65,3 @@ it('should allow canonicalize array item circular ref', function () { | ||
obj.cir = obj; | ||
expect(function () { return canonicalize(obj); }).toThrowError('Circular reference detected'); | ||
expect(function () { return canonicalize(obj); }).toThrow('Circular reference detected'); | ||
}); | ||
@@ -68,0 +68,0 @@ it('should allow canonicalize obj item circular ref', function () { |
@@ -34,3 +34,3 @@ // tslint:disable max-line-length | ||
obj.arr.push(obj.arr); | ||
expect(function () { return canonicalize(obj); }).toThrowError('Circular reference detected'); | ||
expect(function () { return canonicalize(obj); }).toThrow('Circular reference detected'); | ||
}); | ||
@@ -55,3 +55,3 @@ it('should allow canonicalize array item circular ref', function () { | ||
// ) | ||
expect(function () { return canonicalize(obj); }).toThrowError('Circular reference detected'); | ||
expect(function () { return canonicalize(obj); }).toThrow('Circular reference detected'); | ||
}); | ||
@@ -68,2 +68,13 @@ it('should allow canonicalize obj item circular ref', function () { | ||
}); | ||
it('should allow canonicalize obj item circular ref2', function () { | ||
var obj = { | ||
text: undefined, | ||
num: 47734.12, | ||
dt: new Date('2018-12-17T01:08:19.719Z'), | ||
arr: [56, 'a', '12', { t: '455A', a: 123 }], | ||
}; | ||
obj.cir = obj; | ||
obj.arr.push(obj); | ||
expect(canonicalize(obj, true)).toEqual('{"arr":[56,"a","12",{"a":123,"t":"455A"},"[Circular]"],"cir":"[Circular]","dt":"2018-12-17T01:08:19.719Z","num":47734.12}'); | ||
}); | ||
it('should not treat two references to the same sub-object as a circular reference', function () { | ||
@@ -92,3 +103,11 @@ var sharedObject = { key: 'value' }; | ||
}); | ||
it('should not treat two references to the same sub-array as a circular reference2', function () { | ||
var sharedArray = [1, 2]; | ||
var obj = { | ||
a: [sharedArray], | ||
b: sharedArray | ||
}; | ||
expect(canonicalize(obj)).toEqual('{"a":[[1,2]],"b":[1,2]}'); | ||
}); | ||
}); | ||
//# sourceMappingURL=canonicalize.spec.js.map |
@@ -0,107 +1,17 @@ | ||
import { _serialize } from './serializer'; | ||
/** | ||
* The extended canonicalization function, offering more granular control over the serialization process. | ||
* | ||
* @param obj The JavaScript object to canonicalize. | ||
* @param options An object with the following properties: | ||
* - `allowCircular` (boolean, optional): Same as in `canonicalize`. | ||
* - `filterUndefined` (boolean, optional): If `true`, `undefined` values in objects will be filtered out. Defaults to `true`. | ||
* - `undefinedInArrayToNull` (boolean, optional): If `true`, `undefined` values in arrays will be converted to `null`. Defaults to `true`. | ||
* - `include` (string[], optional): An array of property names to include in the canonicalization. | ||
* - `exclude` (string[], optional): An array of property names to exclude from the canonicalization. | ||
* @returns The canonical string representation of the object. | ||
*/ | ||
export function canonicalizeEx(obj, options) { | ||
var buffer = ''; | ||
var vInclude = options && options.include; | ||
var vExclude = options && options.exclude; | ||
if (vExclude) { | ||
if (typeof vExclude === 'string') | ||
vExclude = [vExclude]; | ||
} | ||
if (vInclude) | ||
vInclude.sort(); | ||
var visited = new WeakMap(); | ||
var allowCircular = options && (options === null || options === void 0 ? void 0 : options.allowCircular); | ||
serialize(obj, ''); | ||
return buffer; | ||
function serialize(object, path) { | ||
if (object === null || | ||
typeof object !== 'object' || | ||
object.toJSON != null) { | ||
///////////////////////////////////////////////// | ||
// Primitive data type - Use ES6/JSON // | ||
///////////////////////////////////////////////// | ||
buffer += JSON.stringify(object); | ||
// } else if (object instanceof Date) { | ||
// buffer += JSON.stringify(object); | ||
} | ||
else if (Array.isArray(object)) { | ||
///////////////////////////////////////////////// | ||
// Array - Maintain element order // | ||
///////////////////////////////////////////////// | ||
var visitedPath = visited.get(object); | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected'); | ||
} | ||
buffer += '"[Circular]"'; | ||
return; | ||
} | ||
} | ||
visited.set(object, path); | ||
buffer += '['; | ||
var next_1 = false; | ||
object.forEach(function (element, index) { | ||
if (next_1) { | ||
buffer += ','; | ||
} | ||
next_1 = true; | ||
///////////////////////////////////////// | ||
// Array element - Recursive expansion // | ||
///////////////////////////////////////// | ||
serialize(element, path + "[" + index + "]"); | ||
}); | ||
buffer += ']'; | ||
} | ||
else { | ||
///////////////////////////////////////////////// | ||
// Object - Sort properties before serializing // | ||
///////////////////////////////////////////////// | ||
var visitedPath = visited.get(object); | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected'); | ||
} | ||
buffer += '"[Circular]"'; | ||
return; | ||
} | ||
} | ||
visited.set(object, path); | ||
buffer += '{'; | ||
if (path === '' && vInclude) { | ||
vInclude.forEach(function (property, index) { | ||
if (!object.hasOwnProperty(property)) | ||
return; | ||
addProp(object, property, index, path); | ||
}); | ||
} | ||
else { | ||
var vKeys = Object.keys(object).sort(); | ||
vKeys.forEach(function (property, index) { return addProp(object, property, index, path); }); | ||
} | ||
buffer += '}'; | ||
} | ||
} | ||
function addProp(object, property, index, path) { | ||
if (vExclude && vExclude.length) { | ||
for (var _i = 0, vExclude_1 = vExclude; _i < vExclude_1.length; _i++) { | ||
var v = vExclude_1[_i]; | ||
if (v === property) | ||
return; | ||
} | ||
} | ||
if (index > 0) { | ||
buffer += ','; | ||
} | ||
/////////////////////////////////////////////// | ||
// Property names are strings - Use ES6/JSON // | ||
/////////////////////////////////////////////// | ||
buffer += JSON.stringify(property); | ||
buffer += ':'; | ||
////////////////////////////////////////// | ||
// Property value - Recursive expansion // | ||
////////////////////////////////////////// | ||
serialize(object[property], path + "." + property); | ||
} | ||
return _serialize(obj, options); | ||
} | ||
//# sourceMappingURL=canonicalize-ex.js.map |
@@ -0,85 +1,16 @@ | ||
import { _serialize } from './serializer'; | ||
/** | ||
* The main function for JSON canonicalization. It takes a JavaScript object and returns its canonical string representation. | ||
* | ||
* @param obj The JavaScript object to canonicalize. | ||
* @param allowCircular If `true`, the function will handle circular references in the object by replacing them with `null`. Defaults to `false`. | ||
* @returns The canonical string representation of the object. | ||
*/ | ||
export function canonicalize(obj, allowCircular) { | ||
var buffer = ''; | ||
var visited = new WeakMap(); | ||
serialize(obj, ''); | ||
return buffer; | ||
function serialize(object, path) { | ||
if (object === null || | ||
typeof object !== 'object' || | ||
object.toJSON != null) { | ||
///////////////////////////////////////////////// | ||
// Primitive data type - Use ES6/JSON // | ||
///////////////////////////////////////////////// | ||
buffer += JSON.stringify(object); | ||
// } else if (object instanceof Date) { | ||
// buffer += JSON.stringify(object); | ||
} | ||
else if (Array.isArray(object)) { | ||
///////////////////////////////////////////////// | ||
// Array - Maintain element order // | ||
///////////////////////////////////////////////// | ||
var visitedPath = visited.get(object); | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected'); | ||
} | ||
buffer += '"[Circular]"'; | ||
return; | ||
} | ||
} | ||
visited.set(object, path); | ||
buffer += '['; | ||
var next_1 = false; | ||
object.forEach(function (element, index) { | ||
if (next_1) { | ||
buffer += ','; | ||
} | ||
next_1 = true; | ||
if (element === undefined) { | ||
element = null; | ||
} | ||
///////////////////////////////////////// | ||
// Array element - Recursive expansion // | ||
///////////////////////////////////////// | ||
serialize(element, path + "[" + index + "]"); | ||
}); | ||
buffer += ']'; | ||
} | ||
else { | ||
///////////////////////////////////////////////// | ||
// Object - Sort properties before serializing // | ||
///////////////////////////////////////////////// | ||
var visitedPath = visited.get(object); | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected'); | ||
} | ||
buffer += '"[Circular]"'; | ||
return; | ||
} | ||
} | ||
visited.set(object, path); | ||
buffer += '{'; | ||
var vKeys = Object.keys(object).filter(function (k) { return object[k] !== undefined; }).sort(); | ||
vKeys.forEach(function (property, index) { return addProp(object, property, index, path); }); | ||
buffer += '}'; | ||
} | ||
} | ||
function addProp(object, property, index, path) { | ||
if (index > 0) { | ||
buffer += ','; | ||
} | ||
/////////////////////////////////////////////// | ||
// Property names are strings - Use ES6/JSON // | ||
/////////////////////////////////////////////// | ||
buffer += JSON.stringify(property); | ||
buffer += ':'; | ||
////////////////////////////////////////// | ||
// Property value - Recursive expansion // | ||
////////////////////////////////////////// | ||
serialize(object[property], path + "." + property); | ||
} | ||
return _serialize(obj, { | ||
allowCircular: allowCircular, | ||
filterUndefined: true, | ||
undefinedInArrayToNull: true, | ||
}); | ||
} | ||
//# sourceMappingURL=canonicalize.js.map |
{ | ||
"name": "json-canonicalize", | ||
"version": "1.1.1", | ||
"version": "1.2.0", | ||
"description": "JSON canonicalize function ", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -28,2 +28,3 @@ # json-canonicalize | ||
- Published IETF Draft: https://tools.ietf.org/html/draft-rundgren-json-canonicalization-scheme-05 | ||
- Published RFC 8785: https://www.rfc-editor.org/rfc/rfc8785 | ||
@@ -38,2 +39,15 @@ ## ✨ Features | ||
## RFC 8785 Compatibility | ||
This implementation is compatible with JCS / RFC 8785, with a couple of key differences in the default `canonicalize` function: | ||
- **Handling of `undefined` in arrays:** When a value in an array is `undefined`, the `canonicalize` function treats it as `null`. RFC 8785 specifies that it should be treated as `undefined`, which can lead to different outputs. | ||
- **Recursive References:** This implementation supports recursive object references, which is an enhancement not covered by the standard. | ||
To be fully compatible with RFC 8785, you can use the `canonicalizeEx` function with the `undefinedInArrayToNull` option set to `false`: | ||
```ts | ||
canonicalizeEx(obj, { undefinedInArrayToNull: false }); | ||
``` | ||
## 🔧 Installation | ||
@@ -51,5 +65,5 @@ | ||
import { canonicalize, canonicalizeEx } from 'json-canonicalize'; | ||
canonicalize(obj} | ||
canonicalize(obj) | ||
// Add `include` and `exclude` options to `canonicalizeEx`. | ||
canonicalizeEx(obj, {exclude:['num', 'dt']} | ||
canonicalizeEx(obj, {exclude:['num', 'dt']}) | ||
@@ -63,2 +77,23 @@ // add canonicalize to JSON directly. | ||
## API | ||
### `canonicalize(obj, allowCircular)` | ||
This is the main function for JSON canonicalization. It takes a JavaScript object and returns its canonical string representation. | ||
- `obj` (any): The JavaScript object to canonicalize. | ||
- `allowCircular` (boolean, optional): If `true`, the function will handle circular references in the object by replacing them with `null`. Defaults to `false`. | ||
### `canonicalizeEx(obj, options)` | ||
This is the extended canonicalization function, offering more granular control over the serialization process. | ||
- `obj` (any): The JavaScript object to canonicalize. | ||
- `options` (ISerializeOptions, optional): An object with the following properties: | ||
- `allowCircular` (boolean, optional): Same as in `canonicalize`. | ||
- `filterUndefined` (boolean, optional): If `true`, `undefined` values in objects will be filtered out. Defaults to `true`. | ||
- `undefinedInArrayToNull` (boolean, optional): If `true`, `undefined` values in arrays will be converted to `null`. Defaults to `true`. | ||
- `include` (string[], optional): An array of property names to include in the canonicalization. | ||
- `exclude` (string[], optional): An array of property names to exclude from the canonicalization. | ||
## 🥂 License | ||
@@ -65,0 +100,0 @@ |
@@ -58,3 +58,3 @@ // tslint:disable max-line-length | ||
obj.arr.push(obj.arr); | ||
expect(() => canonicalize(obj)).toThrowError('Circular reference detected') | ||
expect(() => canonicalize(obj)).toThrow('Circular reference detected') | ||
}) | ||
@@ -80,3 +80,3 @@ | ||
obj.cir = obj; | ||
expect(() => canonicalize(obj)).toThrowError('Circular reference detected') | ||
expect(() => canonicalize(obj)).toThrow('Circular reference detected') | ||
}) | ||
@@ -83,0 +83,0 @@ |
@@ -42,3 +42,3 @@ // tslint:disable max-line-length | ||
obj.arr.push(obj.arr); | ||
expect(() => canonicalize(obj)).toThrowError('Circular reference detected') | ||
expect(() => canonicalize(obj)).toThrow('Circular reference detected') | ||
}) | ||
@@ -68,3 +68,3 @@ | ||
expect(() => canonicalize(obj)).toThrowError('Circular reference detected') | ||
expect(() => canonicalize(obj)).toThrow('Circular reference detected') | ||
}) | ||
@@ -86,2 +86,17 @@ | ||
it('should allow canonicalize obj item circular ref2', () => { | ||
const obj: any = { | ||
text: undefined, | ||
num: 47734.12, | ||
dt: new Date('2018-12-17T01:08:19.719Z'), | ||
arr: [56, 'a', '12', { t: '455A', a: 123 }], | ||
} | ||
obj.cir = obj; | ||
obj.arr.push(obj) | ||
expect(canonicalize(obj, true)).toEqual( | ||
'{"arr":[56,"a","12",{"a":123,"t":"455A"},"[Circular]"],"cir":"[Circular]","dt":"2018-12-17T01:08:19.719Z","num":47734.12}' | ||
) | ||
}) | ||
it('should not treat two references to the same sub-object as a circular reference', () => { | ||
@@ -113,2 +128,11 @@ const sharedObject = { key: 'value' }; | ||
it('should not treat two references to the same sub-array as a circular reference2', () => { | ||
const sharedArray = [1, 2]; | ||
const obj = { | ||
a: [sharedArray], | ||
b: sharedArray | ||
}; | ||
expect(canonicalize(obj)).toEqual('{"a":[[1,2]],"b":[1,2]}'); | ||
}); | ||
}) |
@@ -1,115 +0,17 @@ | ||
export interface IOptions { | ||
exclude?: string | string[] | ||
include?: string[] | ||
allowCircular?: boolean | ||
} | ||
import { ISerializeOptions, _serialize } from './serializer'; | ||
export function canonicalizeEx(obj: any, options?: IOptions) { | ||
let buffer = '' | ||
const vInclude = options && options.include | ||
let vExclude = options && options.exclude | ||
if (vExclude) { | ||
if (typeof vExclude === 'string') vExclude = [vExclude] | ||
} | ||
if (vInclude) vInclude.sort() | ||
const visited = new WeakMap<object, string>() | ||
const allowCircular = options && options?.allowCircular | ||
serialize(obj, '') | ||
return buffer | ||
function serialize(object: any, path: string) { | ||
if ( | ||
object === null || | ||
typeof object !== 'object' || | ||
object.toJSON != null | ||
) { | ||
///////////////////////////////////////////////// | ||
// Primitive data type - Use ES6/JSON // | ||
///////////////////////////////////////////////// | ||
buffer += JSON.stringify(object) | ||
// } else if (object instanceof Date) { | ||
// buffer += JSON.stringify(object); | ||
} else if (Array.isArray(object)) { | ||
///////////////////////////////////////////////// | ||
// Array - Maintain element order // | ||
///////////////////////////////////////////////// | ||
const visitedPath = visited.get(object) | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected') | ||
} | ||
buffer += '"[Circular]"' | ||
return | ||
} | ||
} | ||
visited.set(object, path) | ||
buffer += '[' | ||
let next = false | ||
object.forEach((element, index) => { | ||
if (next) { | ||
buffer += ',' | ||
} | ||
next = true | ||
///////////////////////////////////////// | ||
// Array element - Recursive expansion // | ||
///////////////////////////////////////// | ||
serialize(element, `${path}[${index}]`) | ||
}) | ||
buffer += ']' | ||
} else { | ||
///////////////////////////////////////////////// | ||
// Object - Sort properties before serializing // | ||
///////////////////////////////////////////////// | ||
const visitedPath = visited.get(object) | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected') | ||
} | ||
buffer += '"[Circular]"' | ||
return | ||
} | ||
} | ||
visited.set(object, path) | ||
buffer += '{' | ||
if (path === '' && vInclude) { | ||
vInclude.forEach((property, index) => { | ||
if (!object.hasOwnProperty(property)) return | ||
addProp(object, property, index, path) | ||
}) | ||
} else { | ||
const vKeys = Object.keys(object).sort() | ||
vKeys.forEach((property, index) => addProp(object, property, index, path)) | ||
} | ||
buffer += '}' | ||
} | ||
} | ||
function addProp(object: any, property: string, index: number, path: string) { | ||
if (vExclude && vExclude.length) { | ||
for (const v of vExclude) { | ||
if (v === property) return | ||
} | ||
} | ||
if (index > 0) { | ||
buffer += ',' | ||
} | ||
/////////////////////////////////////////////// | ||
// Property names are strings - Use ES6/JSON // | ||
/////////////////////////////////////////////// | ||
buffer += JSON.stringify(property) | ||
buffer += ':' | ||
////////////////////////////////////////// | ||
// Property value - Recursive expansion // | ||
////////////////////////////////////////// | ||
serialize(object[property], `${path}.${property}`) | ||
} | ||
/** | ||
* The extended canonicalization function, offering more granular control over the serialization process. | ||
* | ||
* @param obj The JavaScript object to canonicalize. | ||
* @param options An object with the following properties: | ||
* - `allowCircular` (boolean, optional): Same as in `canonicalize`. | ||
* - `filterUndefined` (boolean, optional): If `true`, `undefined` values in objects will be filtered out. Defaults to `true`. | ||
* - `undefinedInArrayToNull` (boolean, optional): If `true`, `undefined` values in arrays will be converted to `null`. Defaults to `true`. | ||
* - `include` (string[], optional): An array of property names to include in the canonicalization. | ||
* - `exclude` (string[], optional): An array of property names to exclude from the canonicalization. | ||
* @returns The canonical string representation of the object. | ||
*/ | ||
export function canonicalizeEx(obj: any, options?: ISerializeOptions) { | ||
return _serialize(obj, options); | ||
} |
@@ -0,92 +1,16 @@ | ||
import { _serialize } from './serializer'; | ||
/** | ||
* The main function for JSON canonicalization. It takes a JavaScript object and returns its canonical string representation. | ||
* | ||
* @param obj The JavaScript object to canonicalize. | ||
* @param allowCircular If `true`, the function will handle circular references in the object by replacing them with `null`. Defaults to `false`. | ||
* @returns The canonical string representation of the object. | ||
*/ | ||
export function canonicalize(obj: any, allowCircular?: boolean) { | ||
let buffer = '' | ||
const visited = new WeakMap<object, string>() | ||
serialize(obj, '') | ||
return buffer | ||
function serialize(object: any, path: string) { | ||
if ( | ||
object === null || | ||
typeof object !== 'object' || | ||
object.toJSON != null | ||
) { | ||
///////////////////////////////////////////////// | ||
// Primitive data type - Use ES6/JSON // | ||
///////////////////////////////////////////////// | ||
buffer += JSON.stringify(object) | ||
// } else if (object instanceof Date) { | ||
// buffer += JSON.stringify(object); | ||
} else if (Array.isArray(object)) { | ||
///////////////////////////////////////////////// | ||
// Array - Maintain element order // | ||
///////////////////////////////////////////////// | ||
const visitedPath = visited.get(object) | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected') | ||
} | ||
buffer += '"[Circular]"' | ||
return | ||
} | ||
} | ||
visited.set(object, path) | ||
buffer += '[' | ||
let next = false | ||
object.forEach((element, index) => { | ||
if (next) { | ||
buffer += ',' | ||
} | ||
next = true | ||
if (element === undefined) {element = null} | ||
///////////////////////////////////////// | ||
// Array element - Recursive expansion // | ||
///////////////////////////////////////// | ||
serialize(element, `${path}[${index}]`) | ||
}) | ||
buffer += ']' | ||
} else { | ||
///////////////////////////////////////////////// | ||
// Object - Sort properties before serializing // | ||
///////////////////////////////////////////////// | ||
const visitedPath = visited.get(object) | ||
if (visitedPath !== undefined) { | ||
if (path.startsWith(visitedPath)) { | ||
if (!allowCircular) { | ||
throw new Error('Circular reference detected') | ||
} | ||
buffer += '"[Circular]"' | ||
return | ||
} | ||
} | ||
visited.set(object, path) | ||
buffer += '{' | ||
const vKeys = Object.keys(object).filter((k)=> object[k] !== undefined).sort() | ||
vKeys.forEach((property, index) => addProp(object, property, index, path)) | ||
buffer += '}' | ||
} | ||
} | ||
function addProp(object: any, property: string, index: number, path: string) { | ||
if (index > 0) { | ||
buffer += ',' | ||
} | ||
/////////////////////////////////////////////// | ||
// Property names are strings - Use ES6/JSON // | ||
/////////////////////////////////////////////// | ||
buffer += JSON.stringify(property) | ||
buffer += ':' | ||
////////////////////////////////////////// | ||
// Property value - Recursive expansion // | ||
////////////////////////////////////////// | ||
serialize(object[property], `${path}.${property}`) | ||
} | ||
return _serialize(obj, { | ||
allowCircular, | ||
filterUndefined: true, | ||
undefinedInArrayToNull: true, | ||
}); | ||
} |
@@ -1,7 +0,15 @@ | ||
export interface IOptions { | ||
exclude?: string | string[]; | ||
include?: string[]; | ||
allowCircular?: boolean; | ||
} | ||
export declare function canonicalizeEx(obj: any, options?: IOptions): string; | ||
import { ISerializeOptions } from './serializer'; | ||
/** | ||
* The extended canonicalization function, offering more granular control over the serialization process. | ||
* | ||
* @param obj The JavaScript object to canonicalize. | ||
* @param options An object with the following properties: | ||
* - `allowCircular` (boolean, optional): Same as in `canonicalize`. | ||
* - `filterUndefined` (boolean, optional): If `true`, `undefined` values in objects will be filtered out. Defaults to `true`. | ||
* - `undefinedInArrayToNull` (boolean, optional): If `true`, `undefined` values in arrays will be converted to `null`. Defaults to `true`. | ||
* - `include` (string[], optional): An array of property names to include in the canonicalization. | ||
* - `exclude` (string[], optional): An array of property names to exclude from the canonicalization. | ||
* @returns The canonical string representation of the object. | ||
*/ | ||
export declare function canonicalizeEx(obj: any, options?: ISerializeOptions): string; | ||
//# sourceMappingURL=canonicalize-ex.d.ts.map |
@@ -0,2 +1,9 @@ | ||
/** | ||
* The main function for JSON canonicalization. It takes a JavaScript object and returns its canonical string representation. | ||
* | ||
* @param obj The JavaScript object to canonicalize. | ||
* @param allowCircular If `true`, the function will handle circular references in the object by replacing them with `null`. Defaults to `false`. | ||
* @returns The canonical string representation of the object. | ||
*/ | ||
export declare function canonicalize(obj: any, allowCircular?: boolean): string; | ||
//# sourceMappingURL=canonicalize.d.ts.map |
@@ -1,2 +0,2 @@ | ||
// Type definitions for json-canonicalize 1.1.1 | ||
// Type definitions for json-canonicalize 1.2.0 | ||
// Project: https://github.com/snowyu/json-canonicalize.ts | ||
@@ -3,0 +3,0 @@ // Definitions by: Riceball LEE <snowyu.lee@gmail.com> <https://github.com/snowyu> |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
150768
5.08%74
23.33%1719
5.46%102
52.24%1
Infinity%