From b1e2c8fd5cb5dfa46bc440a12eafaf56cd844b1c Mon Sep 17 00:00:00 2001 From: Philipp Tanlak Date: Mon, 24 Nov 2025 20:54:57 +0100 Subject: Docs --- node_modules/object-hash/index.js | 453 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 453 insertions(+) create mode 100644 node_modules/object-hash/index.js (limited to 'node_modules/object-hash/index.js') diff --git a/node_modules/object-hash/index.js b/node_modules/object-hash/index.js new file mode 100644 index 0000000..962bf62 --- /dev/null +++ b/node_modules/object-hash/index.js @@ -0,0 +1,453 @@ +'use strict'; + +var crypto = require('crypto'); + +/** + * Exported function + * + * Options: + * + * - `algorithm` hash algo to be used by this instance: *'sha1', 'md5' + * - `excludeValues` {true|*false} hash object keys, values ignored + * - `encoding` hash encoding, supports 'buffer', '*hex', 'binary', 'base64' + * - `ignoreUnknown` {true|*false} ignore unknown object types + * - `replacer` optional function that replaces values before hashing + * - `respectFunctionProperties` {*true|false} consider function properties when hashing + * - `respectFunctionNames` {*true|false} consider 'name' property of functions for hashing + * - `respectType` {*true|false} Respect special properties (prototype, constructor) + * when hashing to distinguish between types + * - `unorderedArrays` {true|*false} Sort all arrays before hashing + * - `unorderedSets` {*true|false} Sort `Set` and `Map` instances before hashing + * * = default + * + * @param {object} object value to hash + * @param {object} options hashing options + * @return {string} hash value + * @api public + */ +exports = module.exports = objectHash; + +function objectHash(object, options){ + options = applyDefaults(object, options); + + return hash(object, options); +} + +/** + * Exported sugar methods + * + * @param {object} object value to hash + * @return {string} hash value + * @api public + */ +exports.sha1 = function(object){ + return objectHash(object); +}; +exports.keys = function(object){ + return objectHash(object, {excludeValues: true, algorithm: 'sha1', encoding: 'hex'}); +}; +exports.MD5 = function(object){ + return objectHash(object, {algorithm: 'md5', encoding: 'hex'}); +}; +exports.keysMD5 = function(object){ + return objectHash(object, {algorithm: 'md5', encoding: 'hex', excludeValues: true}); +}; + +// Internals +var hashes = crypto.getHashes ? crypto.getHashes().slice() : ['sha1', 'md5']; +hashes.push('passthrough'); +var encodings = ['buffer', 'hex', 'binary', 'base64']; + +function applyDefaults(object, sourceOptions){ + sourceOptions = sourceOptions || {}; + + // create a copy rather than mutating + var options = {}; + options.algorithm = sourceOptions.algorithm || 'sha1'; + options.encoding = sourceOptions.encoding || 'hex'; + options.excludeValues = sourceOptions.excludeValues ? true : false; + options.algorithm = options.algorithm.toLowerCase(); + options.encoding = options.encoding.toLowerCase(); + options.ignoreUnknown = sourceOptions.ignoreUnknown !== true ? false : true; // default to false + options.respectType = sourceOptions.respectType === false ? false : true; // default to true + options.respectFunctionNames = sourceOptions.respectFunctionNames === false ? false : true; + options.respectFunctionProperties = sourceOptions.respectFunctionProperties === false ? false : true; + options.unorderedArrays = sourceOptions.unorderedArrays !== true ? false : true; // default to false + options.unorderedSets = sourceOptions.unorderedSets === false ? false : true; // default to false + options.unorderedObjects = sourceOptions.unorderedObjects === false ? false : true; // default to true + options.replacer = sourceOptions.replacer || undefined; + options.excludeKeys = sourceOptions.excludeKeys || undefined; + + if(typeof object === 'undefined') { + throw new Error('Object argument required.'); + } + + // if there is a case-insensitive match in the hashes list, accept it + // (i.e. SHA256 for sha256) + for (var i = 0; i < hashes.length; ++i) { + if (hashes[i].toLowerCase() === options.algorithm.toLowerCase()) { + options.algorithm = hashes[i]; + } + } + + if(hashes.indexOf(options.algorithm) === -1){ + throw new Error('Algorithm "' + options.algorithm + '" not supported. ' + + 'supported values: ' + hashes.join(', ')); + } + + if(encodings.indexOf(options.encoding) === -1 && + options.algorithm !== 'passthrough'){ + throw new Error('Encoding "' + options.encoding + '" not supported. ' + + 'supported values: ' + encodings.join(', ')); + } + + return options; +} + +/** Check if the given function is a native function */ +function isNativeFunction(f) { + if ((typeof f) !== 'function') { + return false; + } + var exp = /^function\s+\w*\s*\(\s*\)\s*{\s+\[native code\]\s+}$/i; + return exp.exec(Function.prototype.toString.call(f)) != null; +} + +function hash(object, options) { + var hashingStream; + + if (options.algorithm !== 'passthrough') { + hashingStream = crypto.createHash(options.algorithm); + } else { + hashingStream = new PassThrough(); + } + + if (typeof hashingStream.write === 'undefined') { + hashingStream.write = hashingStream.update; + hashingStream.end = hashingStream.update; + } + + var hasher = typeHasher(options, hashingStream); + hasher.dispatch(object); + if (!hashingStream.update) { + hashingStream.end(''); + } + + if (hashingStream.digest) { + return hashingStream.digest(options.encoding === 'buffer' ? undefined : options.encoding); + } + + var buf = hashingStream.read(); + if (options.encoding === 'buffer') { + return buf; + } + + return buf.toString(options.encoding); +} + +/** + * Expose streaming API + * + * @param {object} object Value to serialize + * @param {object} options Options, as for hash() + * @param {object} stream A stream to write the serializiation to + * @api public + */ +exports.writeToStream = function(object, options, stream) { + if (typeof stream === 'undefined') { + stream = options; + options = {}; + } + + options = applyDefaults(object, options); + + return typeHasher(options, stream).dispatch(object); +}; + +function typeHasher(options, writeTo, context){ + context = context || []; + var write = function(str) { + if (writeTo.update) { + return writeTo.update(str, 'utf8'); + } else { + return writeTo.write(str, 'utf8'); + } + }; + + return { + dispatch: function(value){ + if (options.replacer) { + value = options.replacer(value); + } + + var type = typeof value; + if (value === null) { + type = 'null'; + } + + //console.log("[DEBUG] Dispatch: ", value, "->", type, " -> ", "_" + type); + + return this['_' + type](value); + }, + _object: function(object) { + var pattern = (/\[object (.*)\]/i); + var objString = Object.prototype.toString.call(object); + var objType = pattern.exec(objString); + if (!objType) { // object type did not match [object ...] + objType = 'unknown:[' + objString + ']'; + } else { + objType = objType[1]; // take only the class name + } + + objType = objType.toLowerCase(); + + var objectNumber = null; + + if ((objectNumber = context.indexOf(object)) >= 0) { + return this.dispatch('[CIRCULAR:' + objectNumber + ']'); + } else { + context.push(object); + } + + if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(object)) { + write('buffer:'); + return write(object); + } + + if(objType !== 'object' && objType !== 'function' && objType !== 'asyncfunction') { + if(this['_' + objType]) { + this['_' + objType](object); + } else if (options.ignoreUnknown) { + return write('[' + objType + ']'); + } else { + throw new Error('Unknown object type "' + objType + '"'); + } + }else{ + var keys = Object.keys(object); + if (options.unorderedObjects) { + keys = keys.sort(); + } + // Make sure to incorporate special properties, so + // Types with different prototypes will produce + // a different hash and objects derived from + // different functions (`new Foo`, `new Bar`) will + // produce different hashes. + // We never do this for native functions since some + // seem to break because of that. + if (options.respectType !== false && !isNativeFunction(object)) { + keys.splice(0, 0, 'prototype', '__proto__', 'constructor'); + } + + if (options.excludeKeys) { + keys = keys.filter(function(key) { return !options.excludeKeys(key); }); + } + + write('object:' + keys.length + ':'); + var self = this; + return keys.forEach(function(key){ + self.dispatch(key); + write(':'); + if(!options.excludeValues) { + self.dispatch(object[key]); + } + write(','); + }); + } + }, + _array: function(arr, unordered){ + unordered = typeof unordered !== 'undefined' ? unordered : + options.unorderedArrays !== false; // default to options.unorderedArrays + + var self = this; + write('array:' + arr.length + ':'); + if (!unordered || arr.length <= 1) { + return arr.forEach(function(entry) { + return self.dispatch(entry); + }); + } + + // the unordered case is a little more complicated: + // since there is no canonical ordering on objects, + // i.e. {a:1} < {a:2} and {a:1} > {a:2} are both false, + // we first serialize each entry using a PassThrough stream + // before sorting. + // also: we can’t use the same context array for all entries + // since the order of hashing should *not* matter. instead, + // we keep track of the additions to a copy of the context array + // and add all of them to the global context array when we’re done + var contextAdditions = []; + var entries = arr.map(function(entry) { + var strm = new PassThrough(); + var localContext = context.slice(); // make copy + var hasher = typeHasher(options, strm, localContext); + hasher.dispatch(entry); + // take only what was added to localContext and append it to contextAdditions + contextAdditions = contextAdditions.concat(localContext.slice(context.length)); + return strm.read().toString(); + }); + context = context.concat(contextAdditions); + entries.sort(); + return this._array(entries, false); + }, + _date: function(date){ + return write('date:' + date.toJSON()); + }, + _symbol: function(sym){ + return write('symbol:' + sym.toString()); + }, + _error: function(err){ + return write('error:' + err.toString()); + }, + _boolean: function(bool){ + return write('bool:' + bool.toString()); + }, + _string: function(string){ + write('string:' + string.length + ':'); + write(string.toString()); + }, + _function: function(fn){ + write('fn:'); + if (isNativeFunction(fn)) { + this.dispatch('[native]'); + } else { + this.dispatch(fn.toString()); + } + + if (options.respectFunctionNames !== false) { + // Make sure we can still distinguish native functions + // by their name, otherwise String and Function will + // have the same hash + this.dispatch("function-name:" + String(fn.name)); + } + + if (options.respectFunctionProperties) { + this._object(fn); + } + }, + _number: function(number){ + return write('number:' + number.toString()); + }, + _xml: function(xml){ + return write('xml:' + xml.toString()); + }, + _null: function() { + return write('Null'); + }, + _undefined: function() { + return write('Undefined'); + }, + _regexp: function(regex){ + return write('regex:' + regex.toString()); + }, + _uint8array: function(arr){ + write('uint8array:'); + return this.dispatch(Array.prototype.slice.call(arr)); + }, + _uint8clampedarray: function(arr){ + write('uint8clampedarray:'); + return this.dispatch(Array.prototype.slice.call(arr)); + }, + _int8array: function(arr){ + write('int8array:'); + return this.dispatch(Array.prototype.slice.call(arr)); + }, + _uint16array: function(arr){ + write('uint16array:'); + return this.dispatch(Array.prototype.slice.call(arr)); + }, + _int16array: function(arr){ + write('int16array:'); + return this.dispatch(Array.prototype.slice.call(arr)); + }, + _uint32array: function(arr){ + write('uint32array:'); + return this.dispatch(Array.prototype.slice.call(arr)); + }, + _int32array: function(arr){ + write('int32array:'); + return this.dispatch(Array.prototype.slice.call(arr)); + }, + _float32array: function(arr){ + write('float32array:'); + return this.dispatch(Array.prototype.slice.call(arr)); + }, + _float64array: function(arr){ + write('float64array:'); + return this.dispatch(Array.prototype.slice.call(arr)); + }, + _arraybuffer: function(arr){ + write('arraybuffer:'); + return this.dispatch(new Uint8Array(arr)); + }, + _url: function(url) { + return write('url:' + url.toString(), 'utf8'); + }, + _map: function(map) { + write('map:'); + var arr = Array.from(map); + return this._array(arr, options.unorderedSets !== false); + }, + _set: function(set) { + write('set:'); + var arr = Array.from(set); + return this._array(arr, options.unorderedSets !== false); + }, + _file: function(file) { + write('file:'); + return this.dispatch([file.name, file.size, file.type, file.lastModfied]); + }, + _blob: function() { + if (options.ignoreUnknown) { + return write('[blob]'); + } + + throw Error('Hashing Blob objects is currently not supported\n' + + '(see https://github.com/puleos/object-hash/issues/26)\n' + + 'Use "options.replacer" or "options.ignoreUnknown"\n'); + }, + _domwindow: function() { return write('domwindow'); }, + _bigint: function(number){ + return write('bigint:' + number.toString()); + }, + /* Node.js standard native objects */ + _process: function() { return write('process'); }, + _timer: function() { return write('timer'); }, + _pipe: function() { return write('pipe'); }, + _tcp: function() { return write('tcp'); }, + _udp: function() { return write('udp'); }, + _tty: function() { return write('tty'); }, + _statwatcher: function() { return write('statwatcher'); }, + _securecontext: function() { return write('securecontext'); }, + _connection: function() { return write('connection'); }, + _zlib: function() { return write('zlib'); }, + _context: function() { return write('context'); }, + _nodescript: function() { return write('nodescript'); }, + _httpparser: function() { return write('httpparser'); }, + _dataview: function() { return write('dataview'); }, + _signal: function() { return write('signal'); }, + _fsevent: function() { return write('fsevent'); }, + _tlswrap: function() { return write('tlswrap'); }, + }; +} + +// Mini-implementation of stream.PassThrough +// We are far from having need for the full implementation, and we can +// make assumptions like "many writes, then only one final read" +// and we can ignore encoding specifics +function PassThrough() { + return { + buf: '', + + write: function(b) { + this.buf += b; + }, + + end: function(b) { + this.buf += b; + }, + + read: function() { + return this.buf; + } + }; +} -- cgit v1.2.3