hashery
Browser / Nodejs Compatible Object Hashing

Features
- Simple and Easy Object Hashing - Object hashing based on multiple algorithms.
- Browser and Node.js Compatible - Built using
WebCrypto API for both environments
- Multiple Hash Algorithms - Supports SHA-256, SHA-384, SHA-512 (WebCrypto), plus DJB2, FNV1, Murmer, and CRC32
- Synchronous & Asynchronous - Both sync and async methods for flexible integration
- Custom Serialization - Easily replace JSON
parse and stringify with custom functions
- Deterministic Hashing - Generate consistent hashes for the same input
- Hash to Number - Convert hashes to deterministic numbers within a specified range. Great for slot management
- Provider System - Extensible hash provider architecture for custom algorithms
- Fuzzy Provider Matching - Case-insensitive and dash-tolerant algorithm name matching
- Hooks Support - Extends Hookified for event-based functionality
- Maintained on a Regular Basis - Active maintenance and updates
Table of Contents
Installation
npm install hashery
Usage
Basic Hashing
import { Hashery } from 'hashery';
const hashery = new Hashery();
const hash = await hashery.toHash({ name: 'John', age: 30 });
console.log(hash);
const stringHash = await hashery.toHash('hello world');
const numberHash = await hashery.toHash(42);
const arrayHash = await hashery.toHash([1, 2, 3, 4, 5]);
Synchronous Hashing
For performance-critical applications or when you need to avoid async/await, use the synchronous hashing methods. These work with non-cryptographic hash algorithms (djb2, fnv1, murmer, crc32) and are significantly faster than WebCrypto methods.
import { Hashery } from 'hashery';
const hashery = new Hashery();
const hash = hashery.toHashSync({ name: 'John', age: 30 });
console.log(hash);
const fnv1Hash = hashery.toHashSync({ data: 'example' }, { algorithm: 'fnv1' });
const murmerHash = hashery.toHashSync({ data: 'example' }, { algorithm: 'murmer' });
const crcHash = hashery.toHashSync({ data: 'example' }, { algorithm: 'crc32' });
Using Different Hash Algorithms
import { Hashery } from 'hashery';
const hashery = new Hashery();
const hash384 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-384' });
const hash512 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-512' });
const fastHash = await hashery.toHash({ data: 'example' }, { algorithm: 'djb2' });
Setting a Default Algorithm
You can set a default algorithm for all hash operations via constructor or property:
import { Hashery } from 'hashery';
const hashery = new Hashery({ defaultAlgorithm: 'SHA-512' });
const hash1 = await hashery.toHash({ data: 'example' });
console.log(hash1.length);
const hash2 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-256' });
console.log(hash2.length);
hashery.defaultAlgorithm = 'djb2';
const hash3 = await hashery.toHash({ data: 'example' });
Truncating Hash Output
You can limit the length of the hash output using the maxLength option:
import { Hashery } from 'hashery';
const hashery = new Hashery();
const shortHash = await hashery.toHash(
{ data: 'example' },
{ algorithm: 'SHA-256', maxLength: 16 }
);
console.log(shortHash);
const fullHash = await hashery.toHash({ data: 'example' });
console.log(fullHash);
Hash to Number (Great for Slot Management)
import { Hashery } from 'hashery';
const hashery = new Hashery();
const slot = await hashery.toNumber({ userId: 123 }, { min: 0, max: 100 });
console.log(slot);
const userSlot = await hashery.toNumber({ userId: 'user@example.com' }, { min: 0, max: 9 });
Hash to Number Synchronous
Generate deterministic numbers synchronously for high-performance scenarios. Perfect for A/B testing, sharding, and load balancing without async overhead.
import { Hashery } from 'hashery';
const hashery = new Hashery();
const slot = hashery.toNumberSync({ userId: 123 }, { min: 0, max: 100 });
console.log(slot);
const variant = hashery.toNumberSync({ userId: 'user123' }, { min: 0, max: 1 });
console.log(variant === 0 ? 'Group A' : 'Group B');
const serverIndex = hashery.toNumberSync(
{ requestId: 'req_abc123' },
{ min: 0, max: 9, algorithm: 'fnv1' }
);
const shardId = hashery.toNumberSync(
{ customerId: 'cust_xyz' },
{ min: 0, max: 15, algorithm: 'murmer' }
);
const hashery2 = new Hashery({ defaultAlgorithmSync: 'fnv1' });
const num = hashery2.toNumberSync({ data: 'test' });
Browser Usage
Hashery works seamlessly in the browser using the Web Crypto API. You can include it via CDN or bundle it with your application.
Using via CDN (jsDelivr)
<!DOCTYPE html>
<html>
<head>
<title>Hashery Browser Example</title>
</head>
<body>
<script type="module">
import { Hashery } from 'https://cdn.jsdelivr.net/npm/hashery@latest/dist/browser/index.js';
const hashery = new Hashery();
const hash = await hashery.toHash({ page: 'home', userId: 123 });
console.log('Hash:', hash);
const variant = await hashery.toNumber({ userId: 'user123' }, { min: 0, max: 1 });
console.log('A/B Test Variant:', variant === 0 ? 'A' : 'B');
</script>
</body>
</html>
Hooks
Hashery extends Hookified to provide event-based functionality through hooks. Hooks allow you to intercept and modify behavior during the hashing process.
Available Hooks
Asynchronous Method Hooks
before:toHash
Fired before hashing occurs. This hook receives a context object containing:
data - The data to be hashed (can be modified)
algorithm - The hash algorithm to use (can be modified)
maxLength - Optional maximum length for the hash output
after:toHash
Fired after hashing completes. This hook receives a result object containing:
hash - The generated hash (can be modified)
data - The data that was hashed
algorithm - The algorithm that was used
Synchronous Method Hooks
before:toHashSync
Fired before synchronous hashing occurs. This hook receives a context object containing:
data - The data to be hashed (can be modified)
algorithm - The hash algorithm to use (can be modified)
maxLength - Optional maximum length for the hash output
Note: This hook fires asynchronously (non-blocking) to maintain the synchronous nature of toHashSync(). Hook execution happens in the background and won't delay the method's return.
after:toHashSync
Fired after synchronous hashing completes. This hook receives a result object containing:
hash - The generated hash (can be modified)
data - The data that was hashed
algorithm - The algorithm that was used
Note: This hook fires asynchronously (non-blocking) to maintain the synchronous nature of toHashSync(). Hook execution happens in the background.
Basic Hook Usage
import { Hashery } from 'hashery';
const hashery = new Hashery();
hashery.onHook('before:toHash', async (context) => {
console.log('About to hash:', context.data);
console.log('Using algorithm:', context.algorithm);
});
hashery.onHook('after:toHash', async (result) => {
console.log('Hash generated:', result.hash);
console.log('Original data:', result.data);
});
await hashery.toHash({ name: 'John', age: 30 });
Modifying Data with Hooks
You can modify the data before it's hashed:
const hashery = new Hashery();
hashery.onHook('before:toHash', async (context) => {
context.data = {
original: context.data,
timestamp: new Date().toISOString()
};
});
const hash = await hashery.toHash({ userId: 123 });
Modifying Algorithms with Hooks
You can force a specific algorithm regardless of what's requested:
const hashery = new Hashery();
hashery.onHook('before:toHash', async (context) => {
context.algorithm = 'SHA-512';
});
const hash = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-256' });
console.log(hash.length);
Modifying Hash Results
You can transform the hash after it's generated:
const hashery = new Hashery();
hashery.onHook('after:toHash', async (result) => {
result.hash = result.hash.toUpperCase();
});
const hash = await hashery.toHash({ data: 'example' });
console.log(hash);
Implementing Caching with Hooks
Use hooks to implement a caching layer:
const hashery = new Hashery();
const cache = new Map<string, string>();
hashery.onHook('after:toHash', async (result) => {
const cacheKey = `${result.algorithm}:${JSON.stringify(result.data)}`;
cache.set(cacheKey, result.hash);
});
Logging and Debugging
Hooks are perfect for logging and debugging:
const hashery = new Hashery();
hashery.onHook('before:toHash', async (context) => {
console.log(`[DEBUG] Hashing data with ${context.algorithm}:`, context.data);
});
hashery.onHook('after:toHash', async (result) => {
console.log(`[DEBUG] Hash generated: ${result.hash.substring(0, 8)}...`);
});
await hashery.toHash({ userId: 'user123' });
Multiple Hooks
You can register multiple hooks, and they will execute in the order they were registered:
const hashery = new Hashery();
hashery.onHook('before:toHash', async (context) => {
console.log('First hook');
context.data = { step: 1, original: context.data };
});
hashery.onHook('before:toHash', async (context) => {
console.log('Second hook');
context.data = { step: 2, previous: context.data };
});
await hashery.toHash({ name: 'test' });
Synchronous Method Hooks
Synchronous methods (toHashSync, toNumberSync) support hooks, but they fire asynchronously in the background to maintain the synchronous nature of the methods.
const hashery = new Hashery();
hashery.onHook('before:toHashSync', async (context) => {
console.log('About to hash synchronously:', context.data);
console.log('Using algorithm:', context.algorithm);
});
hashery.onHook('after:toHashSync', async (result) => {
console.log('Sync hash generated:', result.hash);
});
const hash = hashery.toHashSync({ name: 'John', age: 30 });
console.log('Method returned:', hash);
Important Notes:
- Synchronous method hooks fire asynchronously (non-blocking)
- The method returns immediately without waiting for hooks to complete
- Hook modifications to context/result may not affect the returned value
- For guaranteed hook execution, use async methods (
toHash, toNumber)
Why Are Sync Hooks Asynchronous?
Synchronous methods are designed for maximum performance. Making hooks blocking would defeat this purpose. The async hook execution allows:
- Logging and monitoring without performance impact
- Side effects (like caching) without blocking
- Maintaining the synchronous contract of the method
If you need hooks that modify data or behavior, use the async methods (toHash, toNumber).
Warning Events for Invalid Algorithms
When an invalid or unknown hash algorithm is provided to toHash() or toHashSync(), Hashery emits a 'warn' event and automatically falls back to the default algorithm instead of throwing an error. This ensures your application continues to work even when invalid algorithms are specified.
Listening to Warnings
import { Hashery } from 'hashery';
const hashery = new Hashery();
hashery.on('warn', (message: string) => {
console.log('Warning:', message);
});
const hash = await hashery.toHash({ data: 'test' }, { algorithm: 'invalid-algo' });
console.log(hash);
Behavior
For async methods (toHash, toNumber):
- Emits 'warn' event with descriptive message
- Falls back to
defaultAlgorithm (SHA-256 by default)
- Returns a valid hash using the fallback algorithm
For sync methods (toHashSync, toNumberSync):
- Emits 'warn' event with descriptive message
- Falls back to
defaultAlgorithmSync (djb2 by default)
- Returns a valid hash using the fallback algorithm
- Note: If the default sync algorithm is also not found, an error will be thrown
Warning Message Format
The warning message includes both the invalid algorithm name and the fallback algorithm being used:
Invalid algorithm '<requested-algorithm>' not found. Falling back to default algorithm '<default-algorithm>'.
Example Use Cases
Development/Debugging:
const hashery = new Hashery();
hashery.on('warn', (message) => {
console.error('[Hashery Warning]', message);
});
Production Monitoring:
const hashery = new Hashery();
hashery.on('warn', (message) => {
errorTracker.captureMessage(message, 'warning');
});
Graceful Degradation:
const hashery = new Hashery();
let hasWarnings = false;
hashery.on('warn', () => {
hasWarnings = true;
});
const hash = await hashery.toHash(userData, { algorithm: userPreferredAlgo });
if (hasWarnings) {
console.log('Using default algorithm instead of your preference');
}
Removing Hooks
You can remove hooks when they're no longer needed:
const hashery = new Hashery();
const myHook = async (context: any) => {
console.log('Hook called');
};
hashery.onHook('before:toHash', myHook);
hashery.offHook('before:toHash', myHook);
hashery.onHook('before:toHashSync', myHook);
hashery.offHook('before:toHashSync', myHook);
Error Handling in Hooks
Control how errors in hooks are handled using the throwOnEmitError option:
const hashery1 = new Hashery({ throwOnEmitError: true });
hashery1.onHook('before:toHash', async (context) => {
throw new Error('Hook error');
});
await hashery1.toHash({ data: 'example' });
const hashery2 = new Hashery({ throwOnEmitError: false });
hashery2.onHook('before:toHash', async (context) => {
throw new Error('Hook error');
});
const hash = await hashery2.toHash({ data: 'example' });
Caching
Hashery includes a built-in FIFO (First In, First Out) cache that stores computed hash values. When the same data is hashed with the same algorithm, the cached result is returned instead of recomputing.
- Enabled by Default - Caching works out of the box
- FIFO Eviction - Oldest entries removed when
maxSize is reached (default: 4000)
- Shared Cache - Both sync and async methods share the same cache
Configuration
import { Hashery } from 'hashery';
const hashery = new Hashery();
const hashery2 = new Hashery({
cache: {
enabled: true,
maxSize: 10000
}
});
const hashery3 = new Hashery({ cache: { enabled: false } });
hashery.cache.enabled = false;
hashery.cache.enabled = true;
Cache Properties and Methods
const hashery = new Hashery();
hashery.cache.enabled;
hashery.cache.maxSize;
hashery.cache.size;
hashery.cache.store;
hashery.cache.clear();
Memory Considerations
The default maxSize of 4000 provides a good balance (~1-2 MB memory). JavaScript Maps can hold up to 2^24 (~16.7 million) entries, but practical limits depend on available memory. For most use cases, 4000-10000 entries is sufficient.
Web Crypto
Hashery is built on top of the Web Crypto API, which provides cryptographic operations in both browser and Node.js environments. This ensures consistent, secure hashing across all platforms.
Browser Support
The Web Crypto API is supported in all modern browsers:
- Chrome 37+
- Firefox 34+
- Safari 11+
- Edge 12+
Node.js Support
Web Crypto API was introduced in Node.js 15.0.0. Hashery is tested against Node.js LTS 20+ and automatically detects and uses the appropriate crypto implementation for your environment via the crypto.webcrypto global.
Available Algorithms
Web Crypto Algorithms (Async Only)
These algorithms use the Web Crypto API and are only available asynchronously:
- SHA-256 - Secure Hash Algorithm 256-bit (default for async methods)
- SHA-384 - Secure Hash Algorithm 384-bit
- SHA-512 - Secure Hash Algorithm 512-bit
These are cryptographically secure and suitable for security-sensitive applications.
Non-Crypto Algorithms (Async & Sync)
These algorithms support both synchronous and asynchronous operation:
- djb2 - Fast hash function by Daniel J. Bernstein (default for sync methods)
- fnv1 - Fowler-Noll-Vo hash function
- murmer - MurmurHash algorithm
- crc32 - Cyclic Redundancy Check 32-bit
Async methods (toHash, toNumber):
- Default to
SHA-256
- Can use any algorithm (WebCrypto or non-crypto)
- Return Promises
Sync methods (toHashSync, toNumberSync):
- Default to
djb2
- Only work with non-crypto algorithms (djb2, fnv1, murmer, crc32)
- Return values immediately
- Throw an error if you try to use WebCrypto algorithms
Example: Using Web Crypto
import { Hashery } from 'hashery';
const hashery = new Hashery();
const sha256 = await hashery.toHash({ data: 'example' });
const sha384 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-384' });
const sha512 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-512' });
const djb2Hash = await hashery.toHash({ data: 'example' }, { algorithm: 'djb2' });
const fnv1Hash = await hashery.toHash({ data: 'example' }, { algorithm: 'fnv1' });
DJB2 Hashing
DJB2 is a non-cryptographic hash function created by Daniel J. Bernstein. It's known for its simplicity and speed, making it ideal for hash tables, checksums, and other non-security applications.
Why Use DJB2?
- Fast Performance - Significantly faster than cryptographic hash functions
- Good Distribution - Provides good hash distribution for most data
- Simple Algorithm - Easy to understand and implement
- Low Collision Rate - Works well for hash tables and data structures
- Deterministic - Same input always produces the same output
When to Use DJB2
Good for:
- Hash tables and data structures
- Non-security checksums
- Fast data lookups
- Cache keys
- General-purpose hashing where security isn't a concern
Not suitable for:
- Password hashing
- Cryptographic signatures
- Security-sensitive applications
- Data integrity verification where tampering is a concern
DJB2 vs Cryptographic Hashes
| Speed | Very Fast | Slower |
| Security | Not Secure | Cryptographically Secure |
| Hash Length | 32-bit | 256-bit |
| Collision Resistance | Good | Excellent |
| Use Case | General Purpose | Security |
Example: Using DJB2
import { Hashery } from 'hashery';
const hashery = new Hashery();
const djb2Hash = await hashery.toHash({ userId: 123, action: 'login' }, { algorithm: 'djb2' });
const cacheKey = await hashery.toHash({
endpoint: '/api/users',
params: { page: 1, limit: 10 }
}, { algorithm: 'djb2' });
const slot = await hashery.toNumber({ userId: 'user123' }, { min: 0, max: 99, algorithm: 'djb2' });
Algorithm Details
DJB2 uses a simple formula:
hash = 5381
for each character c:
hash = ((hash << 5) + hash) + c
This translates to: hash * 33 + c, where 5381 is the magic initial value chosen by Daniel J. Bernstein for its distribution properties.
FNV1 Hashing
FNV1 (Fowler-Noll-Vo) is a non-cryptographic hash function designed for fast hash table and checksum use. Created by Glenn Fowler, Landon Curt Noll, and Kiem-Phong Vo, it's known for its excellent distribution properties and simplicity.
Why Use FNV1?
- Excellent Distribution - Superior hash distribution reduces collisions
- Fast Performance - Very fast computation with minimal operations
- Simple Implementation - Easy to understand and implement
- Public Domain - No licensing restrictions
- Well-Tested - Extensively used and tested in production systems
- Deterministic - Same input always produces the same output
When to Use FNV1
Good for:
- Hash tables and associative arrays
- Checksums and fingerprints
- Data deduplication
- Bloom filters
- Fast lookups and indexing
- Non-cryptographic applications
Not suitable for:
- Password hashing
- Cryptographic signatures
- Security-critical applications
- Digital signatures
- Data integrity in adversarial environments
FNV1 vs Other Hash Functions
| Speed | Very Fast | Very Fast | Slower |
| Distribution | Excellent | Good | Excellent |
| Security | Not Secure | Not Secure | Cryptographically Secure |
| Collision Resistance | Good | Good | Excellent |
| Use Case | Hash Tables | General Purpose | Security |
Example: Using FNV1
import { Hashery } from 'hashery';
const hashery = new Hashery();
const fnv1Hash = await hashery.toHash({ productId: 'ABC123', variant: 'red' }, { algorithm: 'fnv1' });
const tableKey = await hashery.toHash({
userId: 'user@example.com',
resource: 'profile'
}, { algorithm: 'fnv1' });
const slot = await hashery.toNumber({ sessionId: 'sess_xyz789' }, { min: 0, max: 999, algorithm: 'fnv1' });
const fingerprint = await hashery.toHash({
content: 'document content here',
metadata: { author: 'John', date: '2024-01-01' }
}, { algorithm: 'fnv1' });
Algorithm Details
FNV1 uses the following formula:
hash = FNV_offset_basis
for each byte b:
hash = hash * FNV_prime
hash = hash XOR b
Where:
- FNV_offset_basis: Initial hash value (different for 32-bit, 64-bit, etc.)
- FNV_prime: A carefully chosen prime number for good distribution
- XOR: Bitwise exclusive OR operation
The algorithm multiplies by a prime and XORs with each input byte, creating excellent avalanche properties where small input changes result in very different hash values.
CRC Hashing
CRC (Cyclic Redundancy Check) is a non-cryptographic hash function designed primarily for detecting accidental changes to data. CRC32 is a 32-bit variant widely used in network protocols, file formats, and data integrity verification.
Why Use CRC?
- Error Detection - Excellent at detecting accidental data corruption
- Industry Standard - Widely used in ZIP, PNG, Ethernet, and many other standards
- Fast Performance - Very efficient computation using lookup tables
- Hardware Support - Often implemented in hardware for maximum speed
- Well-Understood - Decades of use and mathematical analysis
- Deterministic - Same input always produces the same output
When to Use CRC
Good for:
- Data integrity verification
- Error detection in network protocols
- File format checksums (ZIP, PNG, etc.)
- Storage integrity checks
- Detecting accidental corruption
- Quick data validation
Not suitable for:
- Cryptographic applications
- Password hashing
- Digital signatures
- Security-sensitive checksums
- Protection against intentional tampering
- Hash tables (not designed for this use case)
CRC vs Other Hash Functions
| Primary Use | Error Detection | Hash Tables | Hash Tables | Security |
| Speed | Very Fast | Very Fast | Very Fast | Slower |
| Security | Not Secure | Not Secure | Not Secure | Cryptographically Secure |
| Hash Length | 32-bit | 32-bit | 32-bit/64-bit | 256-bit |
| Error Detection | Excellent | Poor | Poor | Excellent |
| Use Case | Data Integrity | General Purpose | Hash Tables | Security |
Example: Using CRC
import { Hashery } from 'hashery';
const hashery = new Hashery();
const crcHash = await hashery.toHash({ fileData: 'content here' }, { algorithm: 'crc32' });
const fileChecksum = await hashery.toHash({
filename: 'document.pdf',
size: 1024000,
modified: '2024-01-01'
}, { algorithm: 'crc32' });
const packetChecksum = await hashery.toHash({
header: { type: 'data', seq: 123 },
payload: 'packet payload data'
}, { algorithm: 'crc32' });
const dataIntegrity = await hashery.toHash({
recordId: 'rec_123',
data: { field1: 'value1', field2: 'value2' }
}, { algorithm: 'crc32' });
Algorithm Details
CRC32 uses polynomial division in a finite field (GF(2)):
CRC32 polynomial: 0x04C11DB7 (IEEE 802.3 standard)
for each byte b:
crc = (crc >> 8) XOR table[(crc XOR b) & 0xFF]
Key characteristics:
- Polynomial: Uses a standardized polynomial for consistent results
- Lookup Table: Pre-computed table for fast calculation
- Bit Shifting: Efficient XOR and shift operations
- Finite Field: Mathematical properties ensure good error detection
Important Notes
⚠️ Security Warning: CRC is NOT cryptographically secure. It's designed to detect accidental errors, not intentional tampering. For security applications, use SHA-256 or other cryptographic hash functions.
✅ Best Practice: Use CRC32 for checksums and error detection in non-adversarial environments. Use cryptographic hashes (SHA-256, SHA-512) when security matters.
API - Properties
parse
Gets or sets the parse function used to deserialize stored values.
Type: ParseFn
Default: JSON.parse
const hashery = new Hashery();
hashery.parse = customParseFunction;
stringify
Gets or sets the stringify function used to serialize values for storage.
Type: StringifyFn
Default: JSON.stringify
const hashery = new Hashery();
hashery.stringify = customStringifyFunction;
providers
Gets or sets the HashProviders instance used to manage hash providers.
Type: HashProviders
const hashery = new Hashery();
console.log(hashery.providers);
names
Gets the names of all registered hash algorithm providers.
Type: Array<string>
Returns: An array of provider names (e.g., ['SHA-256', 'SHA-384', 'SHA-512', 'djb2', 'fnv1', 'murmer', 'crc32'])
const hashery = new Hashery();
console.log(hashery.names);
defaultAlgorithm
Gets or sets the default hash algorithm to use when none is specified for async methods.
Type: string
Default: 'SHA-256'
const hashery = new Hashery();
console.log(hashery.defaultAlgorithm);
hashery.defaultAlgorithm = 'SHA-512';
const hash = await hashery.toHash({ data: 'example' });
console.log(hash.length);
defaultAlgorithmSync
Gets or sets the default hash algorithm to use when none is specified for synchronous methods.
Type: string
Default: 'djb2'
const hashery = new Hashery();
console.log(hashery.defaultAlgorithmSync);
hashery.defaultAlgorithmSync = 'fnv1';
const hash = hashery.toHashSync({ data: 'example' });
const hashery2 = new Hashery({ defaultAlgorithmSync: 'murmer' });
const hash2 = hashery2.toHashSync({ data: 'test' });
API - Functions
toHash(data, options?)
Generates a cryptographic hash of the provided data using the specified algorithm (async). The data is first stringified using the configured stringify function, then hashed.
Parameters:
data (unknown) - The data to hash (will be stringified before hashing)
options (object, optional) - Configuration options
algorithm (string, optional) - The hash algorithm to use (defaults to 'SHA-256')
maxLength (number, optional) - Maximum length for the hash output (truncates from the start)
Returns: Promise<string> - A Promise that resolves to the hexadecimal string representation of the hash
Example:
const hashery = new Hashery();
const hash = await hashery.toHash({ name: 'John', age: 30 });
const hash512 = await hashery.toHash({ name: 'John' }, { algorithm: 'SHA-512' });
const fastHash = await hashery.toHash({ name: 'John' }, { algorithm: 'djb2' });
const shortHash = await hashery.toHash(
{ name: 'John' },
{ algorithm: 'SHA-256', maxLength: 16 }
);
toHashSync(data, options?)
Generates a hash of the provided data synchronously using a non-cryptographic hash algorithm. The data is first stringified using the configured stringify function, then hashed.
Important: This method only works with synchronous hash providers (djb2, fnv1, murmer, crc32). WebCrypto algorithms (SHA-256, SHA-384, SHA-512) are not supported and will throw an error.
Parameters:
data (unknown) - The data to hash (will be stringified before hashing)
options (object, optional) - Configuration options
algorithm (string, optional) - The hash algorithm to use (defaults to 'djb2')
maxLength (number, optional) - Maximum length for the hash output (truncates from the start)
Returns: string - The hexadecimal string representation of the hash
Throws: Error if the specified algorithm does not support synchronous hashing
Example:
const hashery = new Hashery();
const hash = hashery.toHashSync({ name: 'John', age: 30 });
const hashFnv1 = hashery.toHashSync({ name: 'John' }, { algorithm: 'fnv1' });
const hashMurmer = hashery.toHashSync({ name: 'John' }, { algorithm: 'murmer' });
const hashCrc = hashery.toHashSync({ name: 'John' }, { algorithm: 'crc32' });
const shortHash = hashery.toHashSync(
{ name: 'John' },
{ algorithm: 'djb2', maxLength: 4 }
);
toNumber(data, options?)
Generates a deterministic number within a specified range based on the hash of the provided data (async). This method uses the toHash function to create a consistent hash, then maps it to a number between min and max (inclusive).
Parameters:
data (unknown) - The data to hash (will be stringified before hashing)
options (object, optional) - Configuration options
min (number, optional) - The minimum value of the range (inclusive, defaults to 0)
max (number, optional) - The maximum value of the range (inclusive, defaults to 100)
algorithm (string, optional) - The hash algorithm to use (defaults to 'SHA-256')
hashLength (number, optional) - Number of characters from hash to use for conversion (defaults to 16)
Returns: Promise<number> - A Promise that resolves to a number between min and max (inclusive)
Throws: Error if min is greater than max
Example:
const hashery = new Hashery();
const num = await hashery.toNumber({ user: 'john' });
const num2 = await hashery.toNumber({ user: 'john' }, { min: 0, max: 100 });
const num512 = await hashery.toNumber({ user: 'john' }, { min: 0, max: 255, algorithm: 'SHA-512' });
toNumberSync(data, options?)
Generates a deterministic number within a specified range based on the hash of the provided data synchronously. This method uses the toHashSync function to create a consistent hash, then maps it to a number between min and max (inclusive).
Important: This method only works with synchronous hash providers (djb2, fnv1, murmer, crc32).
Parameters:
data (unknown) - The data to hash (will be stringified before hashing)
options (object, optional) - Configuration options
min (number, optional) - The minimum value of the range (inclusive, defaults to 0)
max (number, optional) - The maximum value of the range (inclusive, defaults to 100)
algorithm (string, optional) - The hash algorithm to use (defaults to 'djb2')
hashLength (number, optional) - Number of characters from hash to use for conversion (defaults to 16)
Returns: number - A number between min and max (inclusive)
Throws:
- Error if min is greater than max
- Error if the specified algorithm does not support synchronous hashing
Example:
const hashery = new Hashery();
const num = hashery.toNumberSync({ user: 'john' });
const slot = hashery.toNumberSync({ user: 'john' }, { min: 0, max: 9 });
const numFnv1 = hashery.toNumberSync({ user: 'john' }, { min: 0, max: 255, algorithm: 'fnv1' });
const variant = hashery.toNumberSync({ userId: 'user123' }, { min: 0, max: 1 });
console.log(variant === 0 ? 'Group A' : 'Group B');
const serverId = hashery.toNumberSync(
{ requestId: 'req_abc' },
{ min: 0, max: 9, algorithm: 'murmer' }
);
loadProviders(providers?, options?)
Loads hash providers into the Hashery instance. This allows you to add custom hash providers or replace the default ones.
Parameters:
providers (Array, optional) - Array of hash providers to add
options (HasheryLoadProviderOptions, optional) - Options object
includeBase (boolean) - Whether to include base providers (default: true)
Returns: void
Example:
const hashery = new Hashery();
const customProvider = {
name: 'custom',
toHash: async (data: BufferSource) => 'custom-hash'
};
hashery.loadProviders([customProvider]);
hashery.loadProviders([customProvider], { includeBase: false });
Benchmarks
Overall view of the current algorithm's and their performance using simple hashing with random data. Sync is when we use toHashSync and Async is the toHash function which requires await.
NOTE: Many of these are not secure and should be used only for object hashing. Read about each one in the documentation and pick what works best for your use case.
Hashing
| MURMER Sync | 🥇 | 726K | 1µs | ±0.02% | 707K |
| CRC32 Sync | -1.9% | 713K | 1µs | ±0.02% | 672K |
| DJB2 Sync | -2.7% | 707K | 1µs | ±0.03% | 672K |
| FNV1 Sync | -2.9% | 705K | 1µs | ±0.03% | 670K |
| CRC32 Async | -8.8% | 663K | 2µs | ±0.02% | 647K |
| MURMER Async | -11% | 649K | 2µs | ±0.03% | 620K |
| SHA-512 Async | -11% | 647K | 2µs | ±0.03% | 596K |
| SHA-384 Async | -11% | 645K | 2µs | ±0.03% | 589K |
| SHA-256 Async | -12% | 642K | 2µs | ±0.03% | 583K |
| FNV1 Async | -12% | 641K | 2µs | ±0.03% | 615K |
| DJB2 Async | -12% | 639K | 2µs | ±0.03% | 613K |
Hashing without Caching
| DJB2 Sync | 🥇 | 507K | 2µs | ±0.04% | 475K |
| MURMER Sync | -0.6% | 504K | 2µs | ±0.04% | 476K |
| DJB2 Async | -3.6% | 489K | 2µs | ±0.04% | 463K |
| MURMER Async | -4.4% | 484K | 2µs | ±0.04% | 457K |
| FNV1 Sync | -6.6% | 474K | 2µs | ±0.04% | 447K |
| FNV1 Async | -14% | 436K | 2µs | ±0.04% | 412K |
| CRC32 Sync | -31% | 349K | 3µs | ±0.04% | 334K |
| CRC32 Async | -31% | 347K | 3µs | ±0.04% | 336K |
| SHA-256 Async | -86% | 69K | 15µs | ±0.11% | 66K |
| SHA-384 Async | -87% | 64K | 17µs | ±0.14% | 60K |
| SHA-512 Async | -88% | 61K | 17µs | ±0.12% | 58K |
Caching is enabled by default and is a simple FIFO with default max keys at 4000. The performance gain is greater than 20%+ on average. Some of the biggest gains are on SHA hashing which 10x when caching is enabled.
Hashery Web Crypto vs Node Crypto
| Hashery SHA-384 (Cache) | 🥇 | 646K | 2µs | ±0.03% | 596K |
| Hashery SHA-256 (Cache) | -0.42% | 643K | 2µs | ±0.03% | 594K |
| Hashery SHA-512 (Cache) | -0.71% | 641K | 2µs | ±0.03% | 589K |
| node:crypto SHA-256 | -1.2% | 638K | 2µs | ±0.02% | 588K |
| node:crypto SHA-384 | -6.9% | 602K | 2µs | ±0.03% | 507K |
| node:crypto SHA-512 | -7.3% | 599K | 2µs | ±0.03% | 525K |
| Hashery SHA-256 | -89% | 69K | 15µs | ±0.10% | 66K |
| Hashery SHA-384 | -90% | 65K | 16µs | ±0.11% | 62K |
| Hashery SHA-512 | -91% | 61K | 17µs | ±0.11% | 59K |
In this benchmark it shows the performance gap that happens between web crypto and the standard node:crypto. By default node:crypto has significant performance natively and doesnt use async/await to perform its hash. With caching enabled we start to see the performance become more similar.
Code of Conduct and Contributing
Please use our Code of Conduct and Contributing guidelines for development and testing. We appreciate your contributions!
License and Copyright
MIT & © Jared Wray