index.js 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. /**
  2. * Node.js constant object hash module.
  3. * @exports node-object-hash
  4. * @type {Function}
  5. */
  6. 'use strict';
  7. const crypto = require('crypto');
  8. /**
  9. * Sorts object fields
  10. * @param {Object|Array|string|function} obj Initial object
  11. * @param {Object} options Options
  12. * @param {boolean} [options.coerce=true] Perform coercion
  13. * @param {boolean} [options.sort=true] Perform Array, Set, Object sorting
  14. * @returns {string}
  15. */
  16. exports.sortedObjectString = (obj, options) => {
  17. const coerce = typeof options.coerce == 'undefined' ? true : options.coerce;
  18. const sort = typeof options.sort == 'undefined' ? true : options.sort;
  19. if (typeof obj == 'object') {
  20. if (Array.isArray(obj)) {
  21. let tmp = [...obj];
  22. tmp.forEach((it, idx) => {
  23. tmp[idx] = exports.sortedObjectString(it, {coerce, sort})
  24. });
  25. return sort ? `[${tmp.sort().toString()}]` : `[${tmp.toString()}]`;
  26. }
  27. if (obj === null) {
  28. return `null`;
  29. }
  30. if (obj instanceof Map) {
  31. return `Map[${[...obj].toString()}]`;
  32. }
  33. if (obj instanceof Set) {
  34. return sort ? `Set[${[...obj].sort().toString()}]` : `Set[${[...obj].toString()}]`;
  35. }
  36. const sortedObj = new Map();
  37. const keys = sort ? Object.keys(obj).sort() : Object.keys(obj);
  38. keys.forEach((key) => {
  39. sortedObj.set(key, exports.sortedObjectString(obj[key], {coerce, sort}));
  40. });
  41. return `{${[...sortedObj].toString()}}`;
  42. }
  43. if (typeof obj == 'function') {
  44. return `${obj.name}=>${obj.toString()}`;
  45. }
  46. if (coerce) {
  47. if (typeof obj == 'boolean') {
  48. return `${Number(obj)}`;
  49. }
  50. } else {
  51. if (typeof obj == 'string') {
  52. return `"${obj}"`
  53. }
  54. }
  55. if (coerce && typeof obj == 'boolean') {
  56. return `${Number(obj)}`;
  57. }
  58. return obj;
  59. };
  60. /**
  61. * Calculates object hash
  62. * @param {Object} obj Object to hash
  63. * @param {Object} [options] Options
  64. * @param {string} [options.alg="sha256"] Crypto algorithm to use
  65. * @param {string} [options.enc="hex"] Hash string encoding
  66. * @param {boolean} [options.coerce=true] Perform coercion
  67. * @param {boolean} [options.sort=true] Perform Array, Set, Object sorting
  68. * @returns {string} Hash string
  69. */
  70. exports.hash = (obj, options) => {
  71. options = options || {};
  72. const alg = options.alg || 'sha256';
  73. const enc = options.enc || 'hex';
  74. const coerce = typeof options.coerce == 'undefined' ? true : options.coerce;
  75. const sort = typeof options.sort == 'undefined' ? true : options.sort;
  76. if (~crypto.getHashes().indexOf(alg)) {
  77. const sorted = exports.sortedObjectString(obj, {coerce, sort});
  78. return crypto.createHash(alg)
  79. .update(sorted)
  80. .digest(enc);
  81. } else {
  82. throw new Error(`Algorithm ${alg} not supported by "ctypto" module`);
  83. }
  84. };