{"_id":"hashery","_rev":"5-f0150b9ba05d7d6e308573f267b336a4","name":"hashery","dist-tags":{"latest":"1.4.0"},"versions":{"1.0.0":{"name":"hashery","version":"1.0.0","keywords":["webcrypto","crypto.subtle","object-hash","hash","hashing","sha256","sha512","digest","deterministic","stable-hash","json-hash","browser-compatible","native-crypto","secure","standards-based","nodejs","typescript","esmodule","lightweight","fast","modern","object"],"author":{"name":"Jared Wray","email":"me@jaredwray.com"},"license":"MIT","_id":"hashery@1.0.0","maintainers":[{"name":"jaredwray","email":"me@jaredwray.com"}],"homepage":"https://github.com/jaredwray/hashery#readme","bugs":{"url":"https://github.com/jaredwray/hashery/issues"},"dist":{"shasum":"1c24865e6c68874678c379f4ffb84e9153f84e18","tarball":"https://registry.npmjs.org/hashery/-/hashery-1.0.0.tgz","fileCount":11,"integrity":"sha512-ah+O2l/9kuh7tKigRulCT2mxu83US1PEHOiXIAI2h5dHSGurioH9+hWlWIlQOLxr8gtsWA+r7Rr5F+PWTNpb9g==","signatures":[{"sig":"MEUCIQCKQOj5h0NtSwNOLwztAjqiSc0g/FMDTeWhS7djFEKEdAIgHGK/G2YVqYYtHq3ly8cTa7l/kjbEmDnwMHi74ALoaFE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":230448},"main":"dist/node/index.cjs","type":"module","types":"dist/node/index.d.ts","module":"dist/node/index.js","engines":{"node":">=20"},"exports":{".":{"import":"./dist/node/index.js","require":"./dist/node/index.cjs"},"./browser":{"import":"./dist/browser/index.js","default":"./dist/browser/index.global.js"}},"gitHead":"a302ffe0b47be62c596eadd779f2cfba29b353cf","scripts":{"lint":"biome check --write --error-on-warnings","test":"pnpm lint && vitest run --coverage","build":"rimraf ./dist && tsup","clean":"rimraf ./dist ./coverage ./node_modules ./pnpm-lock.yaml ./site/README.md ./site/dist","prepare":"pnpm build","test:ci":"biome check --error-on-warnings && vitest run --coverage","benchmark":"tsx benchmark/hashing.ts","website:build":"rimraf ./site/README.md ./site/dist && pnpm docula","website:serve":"rimraf ./site/README.md ./site/dist && pnpm docula"},"_npmUser":{"name":"jaredwray","email":"me@jaredwray.com"},"repository":{"url":"git+https://github.com/jaredwray/hashery.git","type":"git"},"_npmVersion":"10.9.4","description":"Browser Compatible Object Hashing","directories":{},"_nodeVersion":"22.21.1","dependencies":{"hookified":"^1.13.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","tsup":"^8.5.0","docula":"^0.31.0","rimraf":"^6.1.0","vitest":"^4.0.8","tinybench":"^5.1.0","typescript":"^5.9.3","@types/node":"^24.10.1","@biomejs/biome":"^2.3.5","@faker-js/faker":"^10.1.0","@vitest/coverage-v8":"^4.0.8","@monstermann/tinybench-pretty-printer":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/hashery_1.0.0_1762936875312_0.8174236906004626","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"hashery","version":"1.1.0","keywords":["webcrypto","crypto.subtle","object-hash","hash","hashing","sha256","sha512","digest","deterministic","stable-hash","json-hash","browser-compatible","native-crypto","secure","standards-based","nodejs","typescript","esmodule","lightweight","fast","modern","object"],"author":{"name":"Jared Wray","email":"me@jaredwray.com"},"license":"MIT","_id":"hashery@1.1.0","maintainers":[{"name":"jaredwray","email":"me@jaredwray.com"}],"homepage":"https://github.com/jaredwray/hashery#readme","bugs":{"url":"https://github.com/jaredwray/hashery/issues"},"dist":{"shasum":"e73dc9a91f0c21c0dbe34496b97370bb02e87a0a","tarball":"https://registry.npmjs.org/hashery/-/hashery-1.1.0.tgz","fileCount":11,"integrity":"sha512-EyN4e8SXi0UIMcDedVVzxbhRzMhhiFYZUfZezPV7bgW2Q6U6ODgAKbCrq6ipUa6ZbOf7KXBVQ2Y6DbGkfTCEtA==","signatures":[{"sig":"MEQCIHiO0qfDWjY+nS85M8pldb4ng9iVRqvcvoDkCqhioG5hAiAfquL65cujxUB+/NBMOlEPKkS8vzzFp403JQ+eSkHOlw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":254854},"main":"dist/node/index.cjs","type":"module","types":"dist/node/index.d.ts","module":"dist/node/index.js","engines":{"node":">=20"},"exports":{".":{"import":"./dist/node/index.js","require":"./dist/node/index.cjs"},"./browser":{"import":"./dist/browser/index.js","default":"./dist/browser/index.global.js"}},"gitHead":"60cea5cd2a62b598a852d7869f241f6e4777fee6","scripts":{"lint":"biome check --write --error-on-warnings","test":"pnpm lint && vitest run --coverage","build":"rimraf ./dist && tsup","clean":"rimraf ./dist ./coverage ./node_modules ./pnpm-lock.yaml ./site/README.md ./site/dist","prepare":"pnpm build","test:ci":"biome check --error-on-warnings && vitest run --coverage","benchmark":"tsx benchmark/hashing.ts","website:build":"rimraf ./site/README.md ./site/dist && pnpm docula","website:serve":"rimraf ./site/README.md ./site/dist && pnpm docula"},"_npmUser":{"name":"jaredwray","email":"me@jaredwray.com"},"repository":{"url":"git+https://github.com/jaredwray/hashery.git","type":"git"},"_npmVersion":"10.9.4","description":"Browser Compatible Object Hashing","directories":{},"_nodeVersion":"22.21.1","dependencies":{"hookified":"^1.13.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","tsup":"^8.5.0","docula":"^0.31.0","rimraf":"^6.1.0","vitest":"^4.0.8","tinybench":"^5.1.0","typescript":"^5.9.3","@types/node":"^24.10.1","@biomejs/biome":"^2.3.5","@faker-js/faker":"^10.1.0","@vitest/coverage-v8":"^4.0.8","@monstermann/tinybench-pretty-printer":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/hashery_1.1.0_1762970283221_0.3179244434789823","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"hashery","version":"1.2.0","keywords":["webcrypto","crypto.subtle","object-hash","hash","hashing","sha256","sha512","digest","deterministic","stable-hash","json-hash","browser-compatible","native-crypto","secure","standards-based","nodejs","typescript","esmodule","lightweight","fast","modern","object"],"author":{"name":"Jared Wray","email":"me@jaredwray.com"},"license":"MIT","_id":"hashery@1.2.0","maintainers":[{"name":"jaredwray","email":"me@jaredwray.com"}],"homepage":"https://github.com/jaredwray/hashery#readme","bugs":{"url":"https://github.com/jaredwray/hashery/issues"},"dist":{"shasum":"a5f865f169aedd964d6e986cd56b03128df9af44","tarball":"https://registry.npmjs.org/hashery/-/hashery-1.2.0.tgz","fileCount":11,"integrity":"sha512-43XJKpwle72Ik5Zpam7MuzRWyNdwwdf6XHlh8wCj2PggvWf+v/Dm5B0dxGZOmddidgeO6Ofu9As/o231Ti/9PA==","signatures":[{"sig":"MEUCIBPtI0LLX5vKUgq223sYFpXvHsytESB17Ko3JKP3k6aAAiEAgPx8wRvkg9nFVm3yOcwL+58XBcA1X+950CHtEkqiIXg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":265299},"main":"dist/node/index.cjs","type":"module","types":"dist/node/index.d.ts","module":"dist/node/index.js","engines":{"node":">=20"},"exports":{".":{"import":"./dist/node/index.js","require":"./dist/node/index.cjs"},"./browser":{"import":"./dist/browser/index.js","default":"./dist/browser/index.global.js"}},"gitHead":"7856ec753fc47db68aba3f439995593f5777b71b","scripts":{"lint":"biome check --write --error-on-warnings","test":"pnpm lint && vitest run --coverage","build":"rimraf ./dist && tsup","clean":"rimraf ./dist ./coverage ./node_modules ./pnpm-lock.yaml ./site/README.md ./site/dist","prepare":"pnpm build","test:ci":"biome check --error-on-warnings && vitest run --coverage","benchmark":"tsx benchmark/hashing.ts","website:build":"rimraf ./site/README.md ./site/dist && pnpm docula","website:serve":"rimraf ./site/README.md ./site/dist && pnpm docula"},"_npmUser":{"name":"jaredwray","email":"me@jaredwray.com"},"repository":{"url":"git+https://github.com/jaredwray/hashery.git","type":"git"},"_npmVersion":"10.9.4","description":"Browser Compatible Object Hashing","directories":{},"_nodeVersion":"22.21.1","dependencies":{"hookified":"^1.13.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","tsup":"^8.5.0","docula":"^0.31.0","rimraf":"^6.1.0","vitest":"^4.0.8","tinybench":"^5.1.0","typescript":"^5.9.3","@types/node":"^24.10.1","@biomejs/biome":"^2.3.5","@faker-js/faker":"^10.1.0","@vitest/coverage-v8":"^4.0.8","@monstermann/tinybench-pretty-printer":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/hashery_1.2.0_1763058884690_0.8101342782736329","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"hashery","version":"1.3.0","keywords":["webcrypto","object-hash","hash","hashing","sha256","sha384","sha512","djb2","murmur","fnv1","crc32","digest","deterministic","stable-hash","json-hash","browser-compatible","native-crypto","secure","standards-based","esmodule","lightweight","fast","modern"],"author":{"name":"Jared Wray","email":"me@jaredwray.com"},"license":"MIT","_id":"hashery@1.3.0","maintainers":[{"name":"jaredwray","email":"me@jaredwray.com"}],"homepage":"https://github.com/jaredwray/hashery#readme","bugs":{"url":"https://github.com/jaredwray/hashery/issues"},"dist":{"shasum":"2af169f1698efc92bf9235c4880eec92cc5b5107","tarball":"https://registry.npmjs.org/hashery/-/hashery-1.3.0.tgz","fileCount":11,"integrity":"sha512-fWltioiy5zsSAs9ouEnvhsVJeAXRybGCNNv0lvzpzNOSDbULXRy7ivFWwCCv4I5Am6kSo75hmbsCduOoc2/K4w==","signatures":[{"sig":"MEUCIFpEWFWo4s4fquAFW72HdHk4kFAlSG6QUmy9zL/vQy9TAiEAmrEy53SW7i8PSp+9mSetN7L6I9xctxFLVPfK8xIpIaY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":299472},"main":"dist/node/index.js","type":"module","types":"dist/node/index.d.ts","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js"},"require":{"types":"./dist/node/index.d.cts","default":"./dist/node/index.cjs"}},"./browser":{"import":"./dist/browser/index.js","default":"./dist/browser/index.global.js"}},"gitHead":"39d58a7c76e04a21a193c6d26fd27713b178d42e","scripts":{"lint":"biome check --write --error-on-warnings","test":"pnpm lint && vitest run --coverage","build":"rimraf ./dist && tsup","clean":"rimraf ./dist ./coverage ./node_modules ./pnpm-lock.yaml ./site/README.md ./site/dist","prepare":"pnpm build","test:ci":"biome check --error-on-warnings && vitest run --coverage","benchmark":"tsx benchmark/hashing.ts && tsx benchmark/hashing-no-cache.ts","website:build":"rimraf ./site/README.md ./site/dist && pnpm docula","website:serve":"rimraf ./site/README.md ./site/dist && pnpm docula"},"_npmUser":{"name":"jaredwray","email":"me@jaredwray.com"},"repository":{"url":"git+https://github.com/jaredwray/hashery.git","type":"git"},"_npmVersion":"10.9.4","description":"Browser Compatible Object Hashing","directories":{},"_nodeVersion":"22.21.1","dependencies":{"hookified":"^1.13.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","tsup":"^8.5.0","docula":"^0.31.0","rimraf":"^6.1.0","vitest":"^4.0.8","tinybench":"^5.1.0","typescript":"^5.9.3","@types/node":"^24.10.1","@biomejs/biome":"^2.3.5","@faker-js/faker":"^10.1.0","@vitest/coverage-v8":"^4.0.8","@monstermann/tinybench-pretty-printer":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/hashery_1.3.0_1764361432549_0.32058022479733306","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"name":"hashery","version":"1.4.0","description":"Browser Compatible Object Hashing","type":"module","main":"dist/node/index.js","types":"dist/node/index.d.ts","exports":{".":{"import":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js"},"require":{"types":"./dist/node/index.d.cts","default":"./dist/node/index.cjs"}},"./browser":{"import":"./dist/browser/index.js","default":"./dist/browser/index.global.js"}},"repository":{"type":"git","url":"git+https://github.com/jaredwray/hashery.git"},"author":{"name":"Jared Wray","email":"me@jaredwray.com"},"engines":{"node":">=20"},"license":"MIT","scripts":{"clean":"rimraf ./dist ./coverage ./node_modules ./pnpm-lock.yaml ./site/README.md ./site/dist","build":"rimraf ./dist && tsup","prepare":"pnpm build","benchmark":"pnpm benchmark:main && pnpm benchmark:no-cache && pnpm benchmark:node","benchmark:main":"tsx benchmark/hashing.ts","benchmark:no-cache":"tsx benchmark/hashing-no-cache.ts","benchmark:node":"tsx benchmark/hashing-vs-node.ts","lint":"biome check --write --error-on-warnings","test":"pnpm lint && vitest run --coverage","test:ci":"biome check --error-on-warnings && vitest run --coverage","website:build":"rimraf ./site/README.md ./site/dist && pnpm docula","website:serve":"rimraf ./site/README.md ./site/dist && pnpm docula"},"dependencies":{"hookified":"^1.14.0"},"devDependencies":{"@biomejs/biome":"^2.3.10","@faker-js/faker":"^10.1.0","@monstermann/tinybench-pretty-printer":"^0.3.0","@types/node":"^25.0.3","@vitest/coverage-v8":"^4.0.16","docula":"^0.31.0","rimraf":"^6.1.2","tinybench":"^6.0.0","tsup":"^8.5.0","tsx":"^4.21.0","typescript":"^5.9.3","vitest":"^4.0.16"},"keywords":["webcrypto","object-hash","hash","hashing","sha256","sha384","sha512","djb2","murmur","fnv1","crc32","digest","deterministic","stable-hash","json-hash","browser-compatible","native-crypto","secure","standards-based","esmodule","lightweight","fast","modern"],"_id":"hashery@1.4.0","gitHead":"ad980151f07ed9667ddd5c18dc1d0f11919981c1","bugs":{"url":"https://github.com/jaredwray/hashery/issues"},"homepage":"https://github.com/jaredwray/hashery#readme","_nodeVersion":"22.21.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-Wn2i1In6XFxl8Az55kkgnFRiAlIAushzh26PTjL2AKtQcEfXrcLa7Hn5QOWGZEf3LU057P9TwwZjFyxfS1VuvQ==","shasum":"3af04d9af0c63ff2f15a353ee9c2d11fdef7919f","tarball":"https://registry.npmjs.org/hashery/-/hashery-1.4.0.tgz","fileCount":11,"unpackedSize":313384,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEoFODH0Wmf4d2nGlLLzFvT12UdUCg+GAW1cPUY8eKfVAiACH9hxRlEOS1nNTAL8wwfFpeaPr+Up2ZRbwLiH/Y8FLw=="}]},"_npmUser":{"name":"jaredwray","email":"me@jaredwray.com"},"directories":{},"maintainers":[{"name":"jaredwray","email":"me@jaredwray.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/hashery_1.4.0_1767117766663_0.5414122447999803"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-12T08:41:15.311Z","modified":"2025-12-30T18:02:47.030Z","1.0.0":"2025-11-12T08:41:15.488Z","1.1.0":"2025-11-12T17:58:03.396Z","1.2.0":"2025-11-13T18:34:44.875Z","1.3.0":"2025-11-28T20:23:52.737Z","1.4.0":"2025-12-30T18:02:46.835Z"},"bugs":{"url":"https://github.com/jaredwray/hashery/issues"},"author":{"name":"Jared Wray","email":"me@jaredwray.com"},"license":"MIT","homepage":"https://github.com/jaredwray/hashery#readme","keywords":["webcrypto","object-hash","hash","hashing","sha256","sha384","sha512","djb2","murmur","fnv1","crc32","digest","deterministic","stable-hash","json-hash","browser-compatible","native-crypto","secure","standards-based","esmodule","lightweight","fast","modern"],"repository":{"type":"git","url":"git+https://github.com/jaredwray/hashery.git"},"description":"Browser Compatible Object Hashing","maintainers":[{"name":"jaredwray","email":"me@jaredwray.com"}],"readme":"\n\n# hashery\nBrowser / Nodejs Compatible Object Hashing\n\n[![tests](https://github.com/jaredwray/hashery/actions/workflows/tests.yml/badge.svg)](https://github.com/jaredwray/hashery/actions/workflows/tests.yml)\n[![codecov](https://codecov.io/gh/jaredwray/hashery/graph/badge.svg?token=JTuDzWoTRn)](https://codecov.io/gh/jaredwray/hashery)\n[![GitHub license](https://img.shields.io/github/license/jaredwray/hashery)](https://github.com/jaredwray/hashery/blob/master/LICENSE)\n[![npm](https://img.shields.io/npm/dm/hashery)](https://npmjs.com/package/hashery)\n[![jsDelivr](https://data.jsdelivr.com/v1/package/npm/hashery/badge)](https://www.jsdelivr.com/package/npm/hashery)\n[![npm](https://img.shields.io/npm/v/hashery)](https://npmjs.com/package/hashery)\n\n# Features\n- **Simple and Easy Object Hashing** - Object hashing based on multiple algorithms.\n- **Browser and Node.js Compatible** - Built using `WebCrypto` API for both environments\n- **Multiple Hash Algorithms** - Supports SHA-256, SHA-384, SHA-512 (WebCrypto), plus DJB2, FNV1, Murmer, and CRC32\n- **Synchronous & Asynchronous** - Both sync and async methods for flexible integration\n- **Custom Serialization** - Easily replace JSON `parse` and `stringify` with custom functions\n- **Deterministic Hashing** - Generate consistent hashes for the same input\n- **Hash to Number** - Convert hashes to deterministic numbers within a specified range. Great for slot management\n- **Provider System** - Extensible hash provider architecture for custom algorithms\n- **Fuzzy Provider Matching** - Case-insensitive and dash-tolerant algorithm name matching\n- **Hooks Support** - Extends Hookified for event-based functionality\n- **Maintained on a Regular Basis** - Active maintenance and updates\n\n# Table of Contents\n\n- [Features](#features)\n- [Installation](#installation)\n- [Usage](#usage)\n - [Basic Hashing](#basic-hashing)\n - [Synchronous Hashing](#synchronous-hashing)\n - [Using Different Hash Algorithms](#using-different-hash-algorithms)\n - [Setting a Default Algorithm](#setting-a-default-algorithm)\n - [Truncating Hash Output](#truncating-hash-output)\n - [Hash to Number (Great for Slot Management)](#hash-to-number-great-for-slot-management)\n - [Hash to Number Synchronous](#hash-to-number-synchronous)\n - [Browser Usage](#browser-usage)\n- [Hooks](#hooks)\n - [Warning Events for Invalid Algorithms](#warning-events-for-invalid-algorithms)\n- [Caching](#caching)\n- [Web Crypto](#web-crypto)\n - [Browser Support](#browser-support)\n - [Node.js Support](#nodejs-support)\n- [DJB2 Hashing](#djb2-hashing)\n- [FNV1 Hashing](#fnv1-hashing)\n- [CRC Hashing](#crc-hashing)\n- [API - Properties](#api---properties)\n - [parse](#parse)\n - [stringify](#stringify)\n - [providers](#providers)\n - [names](#names)\n - [defaultAlgorithm](#defaultalgorithm)\n - [defaultAlgorithmSync](#defaultalgorithmsync)\n- [API - Functions](#api---functions)\n - [toHash(data, options?)](#toHashdata-options)\n - [toHashSync(data, options?)](#toHashsyncdata-options)\n - [toNumber(data, options?)](#tonumberdata-options)\n - [toNumberSync(data, options?)](#tonumbersyncdata-options)\n - [loadProviders(providers?, options?)](#loadprovidersproviders-options)\n- [Benchmarks](#benchmarks)\n- [Code of Conduct and Contributing](#code-of-conduct-and-contributing)\n- [License and Copyright](#license-and-copyright)\n\n# Installation\n\n```bash\nnpm install hashery\n```\n\n# Usage\n\n## Basic Hashing\n\n```typescript\nimport { Hashery } from 'hashery';\n\nconst hashery = new Hashery();\n\n// Hash an object (defaults to SHA-256)\nconst hash = await hashery.toHash({ name: 'John', age: 30 });\nconsole.log(hash); // SHA-256 hash string\n\n// Hash a string\nconst stringHash = await hashery.toHash('hello world');\n\n// Hash any value (numbers, arrays, etc.)\nconst numberHash = await hashery.toHash(42);\nconst arrayHash = await hashery.toHash([1, 2, 3, 4, 5]);\n```\n\n## Synchronous Hashing\n\nFor 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.\n\n```typescript\nimport { Hashery } from 'hashery';\n\nconst hashery = new Hashery();\n\n// Synchronous hash (defaults to djb2)\nconst hash = hashery.toHashSync({ name: 'John', age: 30 });\nconsole.log(hash); // djb2 hash string (8 hex characters)\n\n// Sync with specific algorithm\nconst fnv1Hash = hashery.toHashSync({ data: 'example' }, { algorithm: 'fnv1' });\nconst murmerHash = hashery.toHashSync({ data: 'example' }, { algorithm: 'murmer' });\nconst crcHash = hashery.toHashSync({ data: 'example' }, { algorithm: 'crc32' });\n\n// Note: WebCrypto algorithms (SHA-256, SHA-384, SHA-512) are NOT supported in sync mode\n// This will throw an error:\n// hashery.toHashSync({ data: 'example' }, { algorithm: 'SHA-256' }); // ❌ Error!\n```\n\n## Using Different Hash Algorithms\n\n```typescript\nimport { Hashery } from 'hashery';\n\nconst hashery = new Hashery();\n\n// Use SHA-384\nconst hash384 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-384' });\n\n// Use SHA-512\nconst hash512 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-512' });\n\n// Use non-crypto hash algorithms\nconst fastHash = await hashery.toHash({ data: 'example' }, { algorithm: 'djb2' });\n```\n\n## Setting a Default Algorithm\n\nYou can set a default algorithm for all hash operations via constructor or property:\n\n```typescript\nimport { Hashery } from 'hashery';\n\n// Set default algorithm via constructor\nconst hashery = new Hashery({ defaultAlgorithm: 'SHA-512' });\n\n// Now all hashes use SHA-512 by default\nconst hash1 = await hashery.toHash({ data: 'example' }); // Uses SHA-512\nconsole.log(hash1.length); // 128 (SHA-512 produces 128 hex characters)\n\n// You can still override it per call\nconst hash2 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-256' });\nconsole.log(hash2.length); // 64 (SHA-256 produces 64 hex characters)\n\n// Change default algorithm at runtime\nhashery.defaultAlgorithm = 'djb2';\nconst hash3 = await hashery.toHash({ data: 'example' }); // Uses djb2\n```\n\n## Truncating Hash Output\n\nYou can limit the length of the hash output using the `maxLength` option:\n\n```typescript\nimport { Hashery } from 'hashery';\n\nconst hashery = new Hashery();\n\n// Get a shorter hash (16 characters instead of 64)\nconst shortHash = await hashery.toHash(\n { data: 'example' },\n { algorithm: 'SHA-256', maxLength: 16 }\n);\nconsole.log(shortHash); // \"3f79bb7b435b0518\" (16 chars)\n\n// Full hash for comparison\nconst fullHash = await hashery.toHash({ data: 'example' });\nconsole.log(fullHash); // \"3f79bb7b435b05181e4ccf0d4e8...\" (64 chars)\n```\n\n## Hash to Number (Great for Slot Management)\n\n```typescript\nimport { Hashery } from 'hashery';\n\nconst hashery = new Hashery();\n\n// Convert hash to a number within a range\nconst slot = await hashery.toNumber({ userId: 123 }, { min: 0, max: 100 });\nconsole.log(slot); // Deterministic number between 0-100\n\n// Use for consistent slot assignment\nconst userSlot = await hashery.toNumber({ userId: 'user@example.com' }, { min: 0, max: 9 });\n// Same user will always get the same slot number\n```\n\n## Hash to Number Synchronous\n\nGenerate deterministic numbers synchronously for high-performance scenarios. Perfect for A/B testing, sharding, and load balancing without async overhead.\n\n```typescript\nimport { Hashery } from 'hashery';\n\nconst hashery = new Hashery();\n\n// Synchronous number generation (defaults to djb2)\nconst slot = hashery.toNumberSync({ userId: 123 }, { min: 0, max: 100 });\nconsole.log(slot); // Deterministic number between 0-100\n\n// A/B testing without async/await\nconst variant = hashery.toNumberSync({ userId: 'user123' }, { min: 0, max: 1 });\nconsole.log(variant === 0 ? 'Group A' : 'Group B');\n\n// Load balancing across servers\nconst serverIndex = hashery.toNumberSync(\n { requestId: 'req_abc123' },\n { min: 0, max: 9, algorithm: 'fnv1' } // 10 servers\n);\n\n// Sharding assignment\nconst shardId = hashery.toNumberSync(\n { customerId: 'cust_xyz' },\n { min: 0, max: 15, algorithm: 'murmer' } // 16 shards\n);\n\n// Set default sync algorithm for all sync operations\nconst hashery2 = new Hashery({ defaultAlgorithmSync: 'fnv1' });\nconst num = hashery2.toNumberSync({ data: 'test' }); // Uses fnv1 by default\n```\n\n## Browser Usage\n\nHashery works seamlessly in the browser using the Web Crypto API. You can include it via CDN or bundle it with your application.\n\n### Using via CDN (jsDelivr)\n\n```html\n\n\n\n Hashery Browser Example\n\n\n \n\n\n```\n\n# Hooks\n\nHashery extends [Hookified](https://github.com/jaredwray/hookified) to provide event-based functionality through hooks. Hooks allow you to intercept and modify behavior during the hashing process.\n\n## Available Hooks\n\n### Asynchronous Method Hooks\n\n#### `before:toHash`\n\nFired before hashing occurs. This hook receives a context object containing:\n- `data` - The data to be hashed (can be modified)\n- `algorithm` - The hash algorithm to use (can be modified)\n- `maxLength` - Optional maximum length for the hash output\n\n#### `after:toHash`\n\nFired after hashing completes. This hook receives a result object containing:\n- `hash` - The generated hash (can be modified)\n- `data` - The data that was hashed\n- `algorithm` - The algorithm that was used\n\n### Synchronous Method Hooks\n\n#### `before:toHashSync`\n\nFired before synchronous hashing occurs. This hook receives a context object containing:\n- `data` - The data to be hashed (can be modified)\n- `algorithm` - The hash algorithm to use (can be modified)\n- `maxLength` - Optional maximum length for the hash output\n\n**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.\n\n#### `after:toHashSync`\n\nFired after synchronous hashing completes. This hook receives a result object containing:\n- `hash` - The generated hash (can be modified)\n- `data` - The data that was hashed\n- `algorithm` - The algorithm that was used\n\n**Note:** This hook fires asynchronously (non-blocking) to maintain the synchronous nature of `toHashSync()`. Hook execution happens in the background.\n\n## Basic Hook Usage\n\n```typescript\nimport { Hashery } from 'hashery';\n\nconst hashery = new Hashery();\n\n// Listen to before:toHash hook\nhashery.onHook('before:toHash', async (context) => {\n console.log('About to hash:', context.data);\n console.log('Using algorithm:', context.algorithm);\n});\n\n// Listen to after:toHash hook\nhashery.onHook('after:toHash', async (result) => {\n console.log('Hash generated:', result.hash);\n console.log('Original data:', result.data);\n});\n\nawait hashery.toHash({ name: 'John', age: 30 });\n```\n\n## Modifying Data with Hooks\n\nYou can modify the data before it's hashed:\n\n```typescript\nconst hashery = new Hashery();\n\n// Add a timestamp to all hashed data\nhashery.onHook('before:toHash', async (context) => {\n context.data = {\n original: context.data,\n timestamp: new Date().toISOString()\n };\n});\n\nconst hash = await hashery.toHash({ userId: 123 });\n// Data will be hashed with timestamp included\n```\n\n## Modifying Algorithms with Hooks\n\nYou can force a specific algorithm regardless of what's requested:\n\n```typescript\nconst hashery = new Hashery();\n\n// Force all hashes to use SHA-512\nhashery.onHook('before:toHash', async (context) => {\n context.algorithm = 'SHA-512';\n});\n\n// Even though we request SHA-256, it will use SHA-512\nconst hash = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-256' });\nconsole.log(hash.length); // 128 (SHA-512 hash length)\n```\n\n## Modifying Hash Results\n\nYou can transform the hash after it's generated:\n\n```typescript\nconst hashery = new Hashery();\n\n// Convert all hashes to uppercase\nhashery.onHook('after:toHash', async (result) => {\n result.hash = result.hash.toUpperCase();\n});\n\nconst hash = await hashery.toHash({ data: 'example' });\nconsole.log(hash); // Hash will be in uppercase\n```\n\n## Implementing Caching with Hooks\n\nUse hooks to implement a caching layer:\n\n```typescript\nconst hashery = new Hashery();\nconst cache = new Map();\n\n// Store hashes in cache after generation\nhashery.onHook('after:toHash', async (result) => {\n const cacheKey = `${result.algorithm}:${JSON.stringify(result.data)}`;\n cache.set(cacheKey, result.hash);\n});\n\n// Later you can check the cache before hashing\n// (Note: You would need to implement cache lookup logic in your application)\n```\n\n## Logging and Debugging\n\nHooks are perfect for logging and debugging:\n\n```typescript\nconst hashery = new Hashery();\n\nhashery.onHook('before:toHash', async (context) => {\n console.log(`[DEBUG] Hashing data with ${context.algorithm}:`, context.data);\n});\n\nhashery.onHook('after:toHash', async (result) => {\n console.log(`[DEBUG] Hash generated: ${result.hash.substring(0, 8)}...`);\n});\n\nawait hashery.toHash({ userId: 'user123' });\n```\n\n## Multiple Hooks\n\nYou can register multiple hooks, and they will execute in the order they were registered:\n\n```typescript\nconst hashery = new Hashery();\n\nhashery.onHook('before:toHash', async (context) => {\n console.log('First hook');\n context.data = { step: 1, original: context.data };\n});\n\nhashery.onHook('before:toHash', async (context) => {\n console.log('Second hook');\n context.data = { step: 2, previous: context.data };\n});\n\nawait hashery.toHash({ name: 'test' });\n// Output: \"First hook\" then \"Second hook\"\n// Data will be wrapped twice\n```\n\n## Synchronous Method Hooks\n\nSynchronous methods (`toHashSync`, `toNumberSync`) support hooks, but they fire asynchronously in the background to maintain the synchronous nature of the methods.\n\n```typescript\nconst hashery = new Hashery();\n\n// Listen to synchronous hash hooks\nhashery.onHook('before:toHashSync', async (context) => {\n console.log('About to hash synchronously:', context.data);\n console.log('Using algorithm:', context.algorithm);\n});\n\nhashery.onHook('after:toHashSync', async (result) => {\n console.log('Sync hash generated:', result.hash);\n});\n\n// Call synchronous method - hooks fire in background\nconst hash = hashery.toHashSync({ name: 'John', age: 30 });\nconsole.log('Method returned:', hash);\n// Method returns immediately, hooks execute asynchronously\n```\n\n**Important Notes:**\n- Synchronous method hooks fire asynchronously (non-blocking)\n- The method returns immediately without waiting for hooks to complete\n- Hook modifications to context/result may not affect the returned value\n- For guaranteed hook execution, use async methods (`toHash`, `toNumber`)\n\n### Why Are Sync Hooks Asynchronous?\n\nSynchronous methods are designed for maximum performance. Making hooks blocking would defeat this purpose. The async hook execution allows:\n- Logging and monitoring without performance impact\n- Side effects (like caching) without blocking\n- Maintaining the synchronous contract of the method\n\nIf you need hooks that modify data or behavior, use the async methods (`toHash`, `toNumber`).\n\n## Warning Events for Invalid Algorithms\n\nWhen 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.\n\n### Listening to Warnings\n\n```typescript\nimport { Hashery } from 'hashery';\n\nconst hashery = new Hashery();\n\n// Listen for warning events\nhashery.on('warn', (message: string) => {\n console.log('Warning:', message);\n});\n\n// Using an invalid algorithm will trigger the warning\nconst hash = await hashery.toHash({ data: 'test' }, { algorithm: 'invalid-algo' });\n// Warning: Invalid algorithm 'invalid-algo' not found. Falling back to default algorithm 'SHA-256'.\n\n// Hash is still generated using SHA-256 (the default)\nconsole.log(hash); // Valid SHA-256 hash\n```\n\n### Behavior\n\n**For async methods (`toHash`, `toNumber`):**\n- Emits 'warn' event with descriptive message\n- Falls back to `defaultAlgorithm` (SHA-256 by default)\n- Returns a valid hash using the fallback algorithm\n\n**For sync methods (`toHashSync`, `toNumberSync`):**\n- Emits 'warn' event with descriptive message\n- Falls back to `defaultAlgorithmSync` (djb2 by default)\n- Returns a valid hash using the fallback algorithm\n- **Note:** If the default sync algorithm is also not found, an error will be thrown\n\n### Warning Message Format\n\nThe warning message includes both the invalid algorithm name and the fallback algorithm being used:\n\n```\nInvalid algorithm '' not found. Falling back to default algorithm ''.\n```\n\n### Example Use Cases\n\n**Development/Debugging:**\n```typescript\nconst hashery = new Hashery();\n\nhashery.on('warn', (message) => {\n console.error('[Hashery Warning]', message);\n // Log to monitoring service, etc.\n});\n```\n\n**Production Monitoring:**\n```typescript\nconst hashery = new Hashery();\n\nhashery.on('warn', (message) => {\n // Send to error tracking service\n errorTracker.captureMessage(message, 'warning');\n});\n```\n\n**Graceful Degradation:**\n```typescript\nconst hashery = new Hashery();\nlet hasWarnings = false;\n\nhashery.on('warn', () => {\n hasWarnings = true;\n});\n\nconst hash = await hashery.toHash(userData, { algorithm: userPreferredAlgo });\n\nif (hasWarnings) {\n // Notify user that their preferred algorithm is not available\n console.log('Using default algorithm instead of your preference');\n}\n```\n\n## Removing Hooks\n\nYou can remove hooks when they're no longer needed:\n\n```typescript\nconst hashery = new Hashery();\n\nconst myHook = async (context: any) => {\n console.log('Hook called');\n};\n\n// Add the hook\nhashery.onHook('before:toHash', myHook);\n\n// Remove the hook\nhashery.offHook('before:toHash', myHook);\n\n// Same works for sync hooks\nhashery.onHook('before:toHashSync', myHook);\nhashery.offHook('before:toHashSync', myHook);\n```\n\n## Error Handling in Hooks\n\nControl how errors in hooks are handled using the `throwOnEmitError` option:\n\n```typescript\n// Throw errors that occur in hooks\nconst hashery1 = new Hashery({ throwOnEmitError: true });\n\nhashery1.onHook('before:toHash', async (context) => {\n throw new Error('Hook error');\n});\n\n// This will throw the error\nawait hashery1.toHash({ data: 'example' }); // Throws Error: Hook error\n\n// Silently handle errors in hooks\nconst hashery2 = new Hashery({ throwOnEmitError: false });\n\nhashery2.onHook('before:toHash', async (context) => {\n throw new Error('Hook error');\n});\n\n// This will not throw, hashing continues\nconst hash = await hashery2.toHash({ data: 'example' }); // Returns hash successfully\n```\n\n# Caching\n\nHashery 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.\n\n- **Enabled by Default** - Caching works out of the box\n- **FIFO Eviction** - Oldest entries removed when `maxSize` is reached (default: 4000)\n- **Shared Cache** - Both sync and async methods share the same cache\n\n## Configuration\n\n```typescript\nimport { Hashery } from 'hashery';\n\n// Default: cache enabled with maxSize of 4000\nconst hashery = new Hashery();\n\n// Custom configuration\nconst hashery2 = new Hashery({\n cache: {\n enabled: true, // default: true\n maxSize: 10000 // default: 4000\n }\n});\n\n// Disable cache\nconst hashery3 = new Hashery({ cache: { enabled: false } });\n\n// Toggle at runtime\nhashery.cache.enabled = false;\nhashery.cache.enabled = true;\n```\n\n## Cache Properties and Methods\n\n```typescript\nconst hashery = new Hashery();\n\nhashery.cache.enabled; // boolean - is caching enabled\nhashery.cache.maxSize; // number - maximum entries (default: 4000)\nhashery.cache.size; // number - current cached entries\nhashery.cache.store; // Map - underlying store\nhashery.cache.clear(); // Clear all cached entries\n```\n\n## Memory Considerations\n\nThe 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.\n\n# Web Crypto\n\nHashery 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.\n\n## Browser Support\n\nThe Web Crypto API is supported in all modern browsers:\n- Chrome 37+\n- Firefox 34+\n- Safari 11+\n- Edge 12+\n\n## Node.js Support\n\nWeb 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.\n\n## Available Algorithms\n\n### Web Crypto Algorithms (Async Only)\nThese algorithms use the Web Crypto API and are only available asynchronously:\n- **SHA-256** - Secure Hash Algorithm 256-bit (default for async methods)\n- **SHA-384** - Secure Hash Algorithm 384-bit\n- **SHA-512** - Secure Hash Algorithm 512-bit\n\nThese are cryptographically secure and suitable for security-sensitive applications.\n\n### Non-Crypto Algorithms (Async & Sync)\nThese algorithms support both synchronous and asynchronous operation:\n- **djb2** - Fast hash function by Daniel J. Bernstein (default for sync methods)\n- **fnv1** - Fowler-Noll-Vo hash function\n- **murmer** - MurmurHash algorithm\n- **crc32** - Cyclic Redundancy Check 32-bit\n\n**Async methods** (`toHash`, `toNumber`):\n- Default to `SHA-256`\n- Can use any algorithm (WebCrypto or non-crypto)\n- Return Promises\n\n**Sync methods** (`toHashSync`, `toNumberSync`):\n- Default to `djb2`\n- Only work with non-crypto algorithms (djb2, fnv1, murmer, crc32)\n- Return values immediately\n- Throw an error if you try to use WebCrypto algorithms\n\n## Example: Using Web Crypto\n\n```typescript\nimport { Hashery } from 'hashery';\n\nconst hashery = new Hashery();\n\n// Web Crypto algorithms\nconst sha256 = await hashery.toHash({ data: 'example' }); // Default SHA-256\nconst sha384 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-384' });\nconst sha512 = await hashery.toHash({ data: 'example' }, { algorithm: 'SHA-512' });\n\n// Non-crypto algorithms (faster, but not cryptographically secure)\nconst djb2Hash = await hashery.toHash({ data: 'example' }, { algorithm: 'djb2' });\nconst fnv1Hash = await hashery.toHash({ data: 'example' }, { algorithm: 'fnv1' });\n```\n\n# DJB2 Hashing\n\nDJB2 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.\n\n## Why Use DJB2?\n\n- **Fast Performance** - Significantly faster than cryptographic hash functions\n- **Good Distribution** - Provides good hash distribution for most data\n- **Simple Algorithm** - Easy to understand and implement\n- **Low Collision Rate** - Works well for hash tables and data structures\n- **Deterministic** - Same input always produces the same output\n\n## When to Use DJB2\n\n**Good for:**\n- Hash tables and data structures\n- Non-security checksums\n- Fast data lookups\n- Cache keys\n- General-purpose hashing where security isn't a concern\n\n**Not suitable for:**\n- Password hashing\n- Cryptographic signatures\n- Security-sensitive applications\n- Data integrity verification where tampering is a concern\n\n## DJB2 vs Cryptographic Hashes\n\n| Feature | DJB2 | SHA-256 |\n|---------|------|---------|\n| Speed | Very Fast | Slower |\n| Security | Not Secure | Cryptographically Secure |\n| Hash Length | 32-bit | 256-bit |\n| Collision Resistance | Good | Excellent |\n| Use Case | General Purpose | Security |\n\n## Example: Using DJB2\n\n```typescript\nimport { Hashery } from 'hashery';\n\nconst hashery = new Hashery();\n\n// Hash with DJB2 (fast, non-cryptographic)\nconst djb2Hash = await hashery.toHash({ userId: 123, action: 'login' }, { algorithm: 'djb2' });\n\n// Use for cache keys\nconst cacheKey = await hashery.toHash({\n endpoint: '/api/users',\n params: { page: 1, limit: 10 }\n}, { algorithm: 'djb2' });\n\n// Generate slot numbers with DJB2\nconst slot = await hashery.toNumber({ userId: 'user123' }, { min: 0, max: 99, algorithm: 'djb2' });\n```\n\n## Algorithm Details\n\nDJB2 uses a simple formula:\n```\nhash = 5381\nfor each character c:\n hash = ((hash << 5) + hash) + c\n```\n\nThis translates to: `hash * 33 + c`, where 5381 is the magic initial value chosen by Daniel J. Bernstein for its distribution properties.\n\n# FNV1 Hashing\n\nFNV1 (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.\n\n## Why Use FNV1?\n\n- **Excellent Distribution** - Superior hash distribution reduces collisions\n- **Fast Performance** - Very fast computation with minimal operations\n- **Simple Implementation** - Easy to understand and implement\n- **Public Domain** - No licensing restrictions\n- **Well-Tested** - Extensively used and tested in production systems\n- **Deterministic** - Same input always produces the same output\n\n## When to Use FNV1\n\n**Good for:**\n- Hash tables and associative arrays\n- Checksums and fingerprints\n- Data deduplication\n- Bloom filters\n- Fast lookups and indexing\n- Non-cryptographic applications\n\n**Not suitable for:**\n- Password hashing\n- Cryptographic signatures\n- Security-critical applications\n- Digital signatures\n- Data integrity in adversarial environments\n\n## FNV1 vs Other Hash Functions\n\n| Feature | FNV1 | DJB2 | SHA-256 |\n|---------|------|------|---------|\n| Speed | Very Fast | Very Fast | Slower |\n| Distribution | Excellent | Good | Excellent |\n| Security | Not Secure | Not Secure | Cryptographically Secure |\n| Collision Resistance | Good | Good | Excellent |\n| Use Case | Hash Tables | General Purpose | Security |\n\n## Example: Using FNV1\n\n```typescript\nimport { Hashery } from 'hashery';\n\nconst hashery = new Hashery();\n\n// Hash with FNV1 (fast, excellent distribution)\nconst fnv1Hash = await hashery.toHash({ productId: 'ABC123', variant: 'red' }, { algorithm: 'fnv1' });\n\n// Use for hash table keys\nconst tableKey = await hashery.toHash({\n userId: 'user@example.com',\n resource: 'profile'\n}, { algorithm: 'fnv1' });\n\n// Generate distributed slot numbers with FNV1\nconst slot = await hashery.toNumber({ sessionId: 'sess_xyz789' }, { min: 0, max: 999, algorithm: 'fnv1' });\n\n// Use for data deduplication\nconst fingerprint = await hashery.toHash({\n content: 'document content here',\n metadata: { author: 'John', date: '2024-01-01' }\n}, { algorithm: 'fnv1' });\n```\n\n## Algorithm Details\n\nFNV1 uses the following formula:\n```\nhash = FNV_offset_basis\nfor each byte b:\n hash = hash * FNV_prime\n hash = hash XOR b\n```\n\nWhere:\n- **FNV_offset_basis**: Initial hash value (different for 32-bit, 64-bit, etc.)\n- **FNV_prime**: A carefully chosen prime number for good distribution\n- **XOR**: Bitwise exclusive OR operation\n\nThe 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.\n\n# CRC Hashing\n\nCRC (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.\n\n## Why Use CRC?\n\n- **Error Detection** - Excellent at detecting accidental data corruption\n- **Industry Standard** - Widely used in ZIP, PNG, Ethernet, and many other standards\n- **Fast Performance** - Very efficient computation using lookup tables\n- **Hardware Support** - Often implemented in hardware for maximum speed\n- **Well-Understood** - Decades of use and mathematical analysis\n- **Deterministic** - Same input always produces the same output\n\n## When to Use CRC\n\n**Good for:**\n- Data integrity verification\n- Error detection in network protocols\n- File format checksums (ZIP, PNG, etc.)\n- Storage integrity checks\n- Detecting accidental corruption\n- Quick data validation\n\n**Not suitable for:**\n- Cryptographic applications\n- Password hashing\n- Digital signatures\n- Security-sensitive checksums\n- Protection against intentional tampering\n- Hash tables (not designed for this use case)\n\n## CRC vs Other Hash Functions\n\n| Feature | CRC32 | DJB2 | FNV1 | SHA-256 |\n|---------|-------|------|------|---------|\n| Primary Use | Error Detection | Hash Tables | Hash Tables | Security |\n| Speed | Very Fast | Very Fast | Very Fast | Slower |\n| Security | Not Secure | Not Secure | Not Secure | Cryptographically Secure |\n| Hash Length | 32-bit | 32-bit | 32-bit/64-bit | 256-bit |\n| Error Detection | Excellent | Poor | Poor | Excellent |\n| Use Case | Data Integrity | General Purpose | Hash Tables | Security |\n\n## Example: Using CRC\n\n```typescript\nimport { Hashery } from 'hashery';\n\nconst hashery = new Hashery();\n\n// Hash with CRC32 for data integrity\nconst crcHash = await hashery.toHash({ fileData: 'content here' }, { algorithm: 'crc32' });\n\n// Verify file integrity\nconst fileChecksum = await hashery.toHash({\n filename: 'document.pdf',\n size: 1024000,\n modified: '2024-01-01'\n}, { algorithm: 'crc32' });\n\n// Network packet validation\nconst packetChecksum = await hashery.toHash({\n header: { type: 'data', seq: 123 },\n payload: 'packet payload data'\n}, { algorithm: 'crc32' });\n\n// Quick data validation\nconst dataIntegrity = await hashery.toHash({\n recordId: 'rec_123',\n data: { field1: 'value1', field2: 'value2' }\n}, { algorithm: 'crc32' });\n```\n\n## Algorithm Details\n\nCRC32 uses polynomial division in a finite field (GF(2)):\n\n```\nCRC32 polynomial: 0x04C11DB7 (IEEE 802.3 standard)\n\nfor each byte b:\n crc = (crc >> 8) XOR table[(crc XOR b) & 0xFF]\n```\n\nKey characteristics:\n- **Polynomial**: Uses a standardized polynomial for consistent results\n- **Lookup Table**: Pre-computed table for fast calculation\n- **Bit Shifting**: Efficient XOR and shift operations\n- **Finite Field**: Mathematical properties ensure good error detection\n\n## Important Notes\n\n⚠️ **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.\n\n✅ **Best Practice**: Use CRC32 for checksums and error detection in non-adversarial environments. Use cryptographic hashes (SHA-256, SHA-512) when security matters.\n\n# API - Properties\n\n## `parse`\n\nGets or sets the parse function used to deserialize stored values.\n\n**Type:** `ParseFn`\n\n**Default:** `JSON.parse`\n\n```typescript\nconst hashery = new Hashery();\nhashery.parse = customParseFunction;\n```\n\n## `stringify`\n\nGets or sets the stringify function used to serialize values for storage.\n\n**Type:** `StringifyFn`\n\n**Default:** `JSON.stringify`\n\n```typescript\nconst hashery = new Hashery();\nhashery.stringify = customStringifyFunction;\n```\n\n## `providers`\n\nGets or sets the HashProviders instance used to manage hash providers.\n\n**Type:** `HashProviders`\n\n```typescript\nconst hashery = new Hashery();\nconsole.log(hashery.providers);\n```\n\n## `names`\n\nGets the names of all registered hash algorithm providers.\n\n**Type:** `Array`\n\n**Returns:** An array of provider names (e.g., ['SHA-256', 'SHA-384', 'SHA-512', 'djb2', 'fnv1', 'murmer', 'crc32'])\n\n```typescript\nconst hashery = new Hashery();\nconsole.log(hashery.names); // ['SHA-256', 'SHA-384', 'SHA-512', 'djb2', 'fnv1', 'murmer', 'crc32']\n```\n\n## `defaultAlgorithm`\n\nGets or sets the default hash algorithm to use when none is specified for async methods.\n\n**Type:** `string`\n\n**Default:** `'SHA-256'`\n\n```typescript\nconst hashery = new Hashery();\n\n// Get default algorithm\nconsole.log(hashery.defaultAlgorithm); // 'SHA-256'\n\n// Set default algorithm\nhashery.defaultAlgorithm = 'SHA-512';\n\n// Now all async hashes use SHA-512 by default\nconst hash = await hashery.toHash({ data: 'example' });\nconsole.log(hash.length); // 128 (SHA-512 produces 128 hex characters)\n```\n\n## `defaultAlgorithmSync`\n\nGets or sets the default hash algorithm to use when none is specified for synchronous methods.\n\n**Type:** `string`\n\n**Default:** `'djb2'`\n\n```typescript\nconst hashery = new Hashery();\n\n// Get default sync algorithm\nconsole.log(hashery.defaultAlgorithmSync); // 'djb2'\n\n// Set default sync algorithm\nhashery.defaultAlgorithmSync = 'fnv1';\n\n// Now all sync hashes use fnv1 by default\nconst hash = hashery.toHashSync({ data: 'example' });\n\n// You can also set it in the constructor\nconst hashery2 = new Hashery({ defaultAlgorithmSync: 'murmer' });\nconst hash2 = hashery2.toHashSync({ data: 'test' }); // Uses murmer\n```\n\n# API - Functions\n\n## `toHash(data, options?)`\n\nGenerates a cryptographic hash of the provided data using the specified algorithm (async). The data is first stringified using the configured stringify function, then hashed.\n\n**Parameters:**\n- `data` (unknown) - The data to hash (will be stringified before hashing)\n- `options` (object, optional) - Configuration options\n - `algorithm` (string, optional) - The hash algorithm to use (defaults to 'SHA-256')\n - `maxLength` (number, optional) - Maximum length for the hash output (truncates from the start)\n\n**Returns:** `Promise` - A Promise that resolves to the hexadecimal string representation of the hash\n\n**Example:**\n\n```typescript\nconst hashery = new Hashery();\n\n// Using default SHA-256\nconst hash = await hashery.toHash({ name: 'John', age: 30 });\n\n// Using a different algorithm\nconst hash512 = await hashery.toHash({ name: 'John' }, { algorithm: 'SHA-512' });\nconst fastHash = await hashery.toHash({ name: 'John' }, { algorithm: 'djb2' });\n\n// Truncating hash output\nconst shortHash = await hashery.toHash(\n { name: 'John' },\n { algorithm: 'SHA-256', maxLength: 16 }\n);\n```\n\n## `toHashSync(data, options?)`\n\nGenerates 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.\n\n**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.\n\n**Parameters:**\n- `data` (unknown) - The data to hash (will be stringified before hashing)\n- `options` (object, optional) - Configuration options\n - `algorithm` (string, optional) - The hash algorithm to use (defaults to 'djb2')\n - `maxLength` (number, optional) - Maximum length for the hash output (truncates from the start)\n\n**Returns:** `string` - The hexadecimal string representation of the hash\n\n**Throws:** `Error` if the specified algorithm does not support synchronous hashing\n\n**Example:**\n\n```typescript\nconst hashery = new Hashery();\n\n// Using default djb2\nconst hash = hashery.toHashSync({ name: 'John', age: 30 });\n\n// Using a different algorithm\nconst hashFnv1 = hashery.toHashSync({ name: 'John' }, { algorithm: 'fnv1' });\nconst hashMurmer = hashery.toHashSync({ name: 'John' }, { algorithm: 'murmer' });\nconst hashCrc = hashery.toHashSync({ name: 'John' }, { algorithm: 'crc32' });\n\n// Truncating hash output\nconst shortHash = hashery.toHashSync(\n { name: 'John' },\n { algorithm: 'djb2', maxLength: 4 }\n);\n\n// This will throw an error (WebCrypto not supported in sync mode)\n// const invalid = hashery.toHashSync({ name: 'John' }, { algorithm: 'SHA-256' }); // ❌\n```\n\n## `toNumber(data, options?)`\n\nGenerates 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).\n\n**Parameters:**\n- `data` (unknown) - The data to hash (will be stringified before hashing)\n- `options` (object, optional) - Configuration options\n - `min` (number, optional) - The minimum value of the range (inclusive, defaults to 0)\n - `max` (number, optional) - The maximum value of the range (inclusive, defaults to 100)\n - `algorithm` (string, optional) - The hash algorithm to use (defaults to 'SHA-256')\n - `hashLength` (number, optional) - Number of characters from hash to use for conversion (defaults to 16)\n\n**Returns:** `Promise` - A Promise that resolves to a number between min and max (inclusive)\n\n**Throws:** Error if min is greater than max\n\n**Example:**\n\n```typescript\nconst hashery = new Hashery();\n\n// Generate a number between 0 and 100 (default range)\nconst num = await hashery.toNumber({ user: 'john' });\n\n// Generate a number with custom range\nconst num2 = await hashery.toNumber({ user: 'john' }, { min: 0, max: 100 });\n\n// Using a different algorithm\nconst num512 = await hashery.toNumber({ user: 'john' }, { min: 0, max: 255, algorithm: 'SHA-512' });\n```\n\n## `toNumberSync(data, options?)`\n\nGenerates 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).\n\n**Important:** This method only works with synchronous hash providers (djb2, fnv1, murmer, crc32).\n\n**Parameters:**\n- `data` (unknown) - The data to hash (will be stringified before hashing)\n- `options` (object, optional) - Configuration options\n - `min` (number, optional) - The minimum value of the range (inclusive, defaults to 0)\n - `max` (number, optional) - The maximum value of the range (inclusive, defaults to 100)\n - `algorithm` (string, optional) - The hash algorithm to use (defaults to 'djb2')\n - `hashLength` (number, optional) - Number of characters from hash to use for conversion (defaults to 16)\n\n**Returns:** `number` - A number between min and max (inclusive)\n\n**Throws:**\n- Error if min is greater than max\n- Error if the specified algorithm does not support synchronous hashing\n\n**Example:**\n\n```typescript\nconst hashery = new Hashery();\n\n// Generate a number between 0 and 100 (default range)\nconst num = hashery.toNumberSync({ user: 'john' });\n\n// Generate a number with custom range\nconst slot = hashery.toNumberSync({ user: 'john' }, { min: 0, max: 9 });\n\n// Using a different algorithm\nconst numFnv1 = hashery.toNumberSync({ user: 'john' }, { min: 0, max: 255, algorithm: 'fnv1' });\n\n// A/B testing\nconst variant = hashery.toNumberSync({ userId: 'user123' }, { min: 0, max: 1 });\nconsole.log(variant === 0 ? 'Group A' : 'Group B');\n\n// Load balancing\nconst serverId = hashery.toNumberSync(\n { requestId: 'req_abc' },\n { min: 0, max: 9, algorithm: 'murmer' } // 10 servers\n);\n\n// This will throw an error (WebCrypto not supported in sync mode)\n// const invalid = hashery.toNumberSync({ user: 'john' }, { algorithm: 'SHA-256' }); // ❌\n```\n\n## `loadProviders(providers?, options?)`\n\nLoads hash providers into the Hashery instance. This allows you to add custom hash providers or replace the default ones.\n\n**Parameters:**\n- `providers` (Array, optional) - Array of hash providers to add\n- `options` (HasheryLoadProviderOptions, optional) - Options object\n - `includeBase` (boolean) - Whether to include base providers (default: true)\n\n**Returns:** `void`\n\n**Example:**\n\n```typescript\nconst hashery = new Hashery();\n\n// Add a custom provider\nconst customProvider = {\n name: 'custom',\n toHash: async (data: BufferSource) => 'custom-hash'\n};\n\nhashery.loadProviders([customProvider]);\n\n// Load without base providers\nhashery.loadProviders([customProvider], { includeBase: false });\n```\n\n# Benchmarks\n\nOverall 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`.\n\n**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.**\n\n## Hashing\n| name | summary | ops/sec | time/op | margin | samples |\n|-----------------|:---------:|----------:|----------:|:--------:|----------:|\n| MURMER Sync | 🥇 | 726K | 1µs | ±0.02% | 707K |\n| CRC32 Sync | -1.9% | 713K | 1µs | ±0.02% | 672K |\n| DJB2 Sync | -2.7% | 707K | 1µs | ±0.03% | 672K |\n| FNV1 Sync | -2.9% | 705K | 1µs | ±0.03% | 670K |\n| CRC32 Async | -8.8% | 663K | 2µs | ±0.02% | 647K |\n| MURMER Async | -11% | 649K | 2µs | ±0.03% | 620K |\n| SHA-512 Async | -11% | 647K | 2µs | ±0.03% | 596K |\n| SHA-384 Async | -11% | 645K | 2µs | ±0.03% | 589K |\n| SHA-256 Async | -12% | 642K | 2µs | ±0.03% | 583K |\n| FNV1 Async | -12% | 641K | 2µs | ±0.03% | 615K |\n| DJB2 Async | -12% | 639K | 2µs | ±0.03% | 613K |\n\n## Hashing without Caching\n| name | summary | ops/sec | time/op | margin | samples |\n|-----------------|:---------:|----------:|----------:|:--------:|----------:|\n| DJB2 Sync | 🥇 | 507K | 2µs | ±0.04% | 475K |\n| MURMER Sync | -0.6% | 504K | 2µs | ±0.04% | 476K |\n| DJB2 Async | -3.6% | 489K | 2µs | ±0.04% | 463K |\n| MURMER Async | -4.4% | 484K | 2µs | ±0.04% | 457K |\n| FNV1 Sync | -6.6% | 474K | 2µs | ±0.04% | 447K |\n| FNV1 Async | -14% | 436K | 2µs | ±0.04% | 412K |\n| CRC32 Sync | -31% | 349K | 3µs | ±0.04% | 334K |\n| CRC32 Async | -31% | 347K | 3µs | ±0.04% | 336K |\n| SHA-256 Async | -86% | 69K | 15µs | ±0.11% | 66K |\n| SHA-384 Async | -87% | 64K | 17µs | ±0.14% | 60K |\n| SHA-512 Async | -88% | 61K | 17µs | ±0.12% | 58K |\n\nCaching 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.\n\n## Hashery Web Crypto vs Node Crypto\n| name | summary | ops/sec | time/op | margin | samples |\n|---------------------------|:---------:|----------:|----------:|:--------:|----------:|\n| Hashery SHA-384 (Cache) | 🥇 | 646K | 2µs | ±0.03% | 596K |\n| Hashery SHA-256 (Cache) | -0.42% | 643K | 2µs | ±0.03% | 594K |\n| Hashery SHA-512 (Cache) | -0.71% | 641K | 2µs | ±0.03% | 589K |\n| node:crypto SHA-256 | -1.2% | 638K | 2µs | ±0.02% | 588K |\n| node:crypto SHA-384 | -6.9% | 602K | 2µs | ±0.03% | 507K |\n| node:crypto SHA-512 | -7.3% | 599K | 2µs | ±0.03% | 525K |\n| Hashery SHA-256 | -89% | 69K | 15µs | ±0.10% | 66K |\n| Hashery SHA-384 | -90% | 65K | 16µs | ±0.11% | 62K |\n| Hashery SHA-512 | -91% | 61K | 17µs | ±0.11% | 59K |\n\nIn 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.\n\n# Code of Conduct and Contributing\nPlease use our [Code of Conduct](CODE_OF_CONDUCT.md) and [Contributing](CONTRIBUTING.md) guidelines for development and testing. We appreciate your contributions!\n\n# License and Copyright\n\n[MIT](LICENSE) & © [Jared Wray](https://jaredwray.com)","readmeFilename":"README.md"}