[READ-ONLY] Mirror of https://github.com/danielroe/fnv1a-64. Tiny, fast, dependency-free 64-bit FNV-1a string hash for Node and the browser.
64-bit fnv fnv-1a fnv1a hash non-cryptographic
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: 64-bit FNV-1a hash in two 32-bit lanes

Daniel Roe (Jul 15, 2026, 11:55 AM +0100) 5f28329f 66b8bb82

+511 -329
+1 -1
.github/ISSUE_TEMPLATE/---bug-report.yml
··· 13 13 required: true 14 14 attributes: 15 15 label: 🛠️ To reproduce 16 - description: A reproduction of the bug via https://stackblitz.com/github/danielroe/package-name/tree/main/playground 16 + description: A reproduction of the bug via https://stackblitz.com/github/danielroe/fnv1a-64/tree/main/playground 17 17 placeholder: https://stackblitz.com/[...] 18 18 - type: textarea 19 19 validations:
+68 -17
README.md
··· 1 - # package-name 1 + # fnv1a-64 2 2 3 3 [![npm version][npm-version-src]][npm-version-href] 4 4 [![npm downloads][npm-downloads-src]][npm-downloads-href] 5 5 [![Github Actions][github-actions-src]][github-actions-href] 6 6 [![Codecov][codecov-src]][codecov-href] 7 7 8 - > Package description 8 + > Tiny, fast, dependency-free 64-bit [FNV-1a](http://www.isthe.com/chongo/tech/comp/fnv/) string hash for Node and the browser. 9 + 10 + The core computes a true 64-bit hash in two 32-bit lanes with plain JS numbers. It's ~320B gzipped (minified) and hashes a short key in ~36ns. 11 + 12 + ## Why 13 + 14 + I created this for [Nuxt](https://github.com/nuxt/nuxt) due to the following issues with other FNV packages. These are all good packages and you should consider them if you don't have the same constraints I did: 15 + 16 + | Package | Width | Gzip | Notes | 17 + | --- | --- | --- | --- | 18 + | `fnv1a`, `object-code` | 32-bit | tiny | collides at ~50k distinct keys | 19 + | `@sindresorhus/fnv1a` | 64-bit | ~1KB | BigInt, ~20x slower | 20 + | `fnv-lite` | 128-bit | ~1.4KB | byte arrays, ~40x slower | 21 + | `fnv-plus` | multi | ~9KB | _excellent_ package, but contains lots of utilities | 22 + | `fnv-hash` | 64-bit | native | can't run in the browser | 9 23 10 24 ## Usage 11 25 12 - Install package: 26 + ```sh 27 + npm install fnv1a-64 28 + ``` 13 29 14 - ```sh 15 - # npm 16 - npm install package-name 30 + ```js 31 + import { fnv1a64, fnv1a64Base36, fnv1a64BigInt, fnv1a64Hex } from 'fnv1a-64' 17 32 18 - # pnpm 19 - pnpm install package-name 33 + fnv1a64Hex('hello world') // => '779a65e7023cd2e7' (16-char zero-padded hex) 34 + fnv1a64Base36('hello world') // => '1th7cxzlyc0dj' (shortest, good for cache keys) 35 + fnv1a64BigInt('hello world') // => 8618312879776256743n 36 + fnv1a64('hello world') // => { high: 2006607335, low: 37540583 } (fast core, no BigInt) 20 37 ``` 21 38 39 + - **`fnv1a64Hex`** and **`fnv1a64Base36`** are what you usually want as a map or cache key. 40 + - **`fnv1a64`** returns the raw 32-bit lanes and is the fastest path if you never need a string. 41 + - **`fnv1a64BigInt`** composes the lanes into a `bigint` when you want a single comparable number. 42 + 43 + All outputs are deterministic: the same input always produces the same result. Hex is always exactly 16 characters; base36 length varies with the value but is stable per value. 44 + 45 + ## Non-ASCII: UTF-16 code units, not UTF-8 bytes 46 + 47 + The hash iterates `str.charCodeAt(i)`, so it hashes UTF-16 code units rather than UTF-8 bytes. For **ASCII input this is bit-for-bit identical to a canonical FNV-1a-64**. For non-ASCII (accents, CJK, emoji) the output is stable and collision-resistant but will **not** match an FNV-1a-64 computed over the UTF-8 encoding of the same string. 48 + 49 + If you need to interoperate with a hash produced elsewhere over UTF-8 bytes, encode first: 50 + 22 51 ```js 23 - import {} from 'package-name' 52 + const bytes = new TextEncoder().encode(str) 53 + // then hash the bytes with a UTF-8-aware FNV-1a-64 implementation 24 54 ``` 25 55 56 + > [!IMPORTANT] 57 + 58 + > FNV-1a is a fast, **non-cryptographic** hash. It's useful for hash tables, cache keys, checksums, and bucketing, but do not use it for anything security-sensitive: it is not collision-resistant against an adversary and offers no preimage resistance. 59 + 60 + ## Benchmark 61 + 62 + Run `pnpm bench` (uses [mitata](https://github.com/evanwashere/mitata)). Indicative numbers on Node 22 (Apple silicon), lower is better: 63 + 64 + | | width | short key | 1 KB string | 65 + | --- | --- | --- | --- | 66 + | `fnv1a64` (lanes) | 64 | **~42 ns** | ~2.7 µs | 67 + | `fnv1a64Base36` | 64 | ~98 ns | ~2.8 µs | 68 + | `fnv1a64Hex` | 64 | ~339 ns | ~3.0 µs | 69 + | `fnv-plus` `fast1a64` | 64 | ~63 ns | **~1.7 µs** | 70 + | `murmurhash` v3 | 32 | ~257 ns | ~1.7 µs | 71 + | `@sindresorhus/fnv1a` | 64 | ~714 ns | ~29 µs | 72 + | `fnv-lite` `hex` | 128 | ~5.6 µs | ~297 µs | 73 + | `xxhashjs` `h64` | 64 | ~34 µs | ~59 µs | 74 + 75 + The `fnv1a64` core wins short keys outright. `fnv-plus` is competitive (and faster on long strings) but ships ~9 KB gzipped for a whole multi-width toolkit rather than one function. `murmurhash` is faster than 64-bit alternatives but is 32-bit, so it collides. `fnv-lite` and `xxhashjs` pay a large constant cost for their byte-array / `cuint` internals. 76 + 26 77 ## 💻 Development 27 78 28 79 - Clone this repository ··· 38 89 39 90 <!-- Badges --> 40 91 41 - [npm-version-src]: https://npmx.dev/api/registry/badge/version/package-name 42 - [npm-version-href]: https://npmx.dev/package/package-name 43 - [npm-downloads-src]: https://npmx.dev/api/registry/badge/downloads/package-name 44 - [npm-downloads-href]: https://npm.chart.dev/package-name 45 - [github-actions-src]: https://img.shields.io/github/actions/workflow/status/danielroe/package-name/ci.yml?branch=main&style=flat-square 46 - [github-actions-href]: https://github.com/danielroe/package-name/actions?query=workflow%3Aci 47 - [codecov-src]: https://img.shields.io/codecov/c/gh/danielroe/package-name/main?style=flat-square 48 - [codecov-href]: https://codecov.io/gh/danielroe/package-name 92 + [npm-version-src]: https://npmx.dev/api/registry/badge/version/fnv1a-64 93 + [npm-version-href]: https://npmx.dev/package/fnv1a-64 94 + [npm-downloads-src]: https://npmx.dev/api/registry/badge/downloads/fnv1a-64 95 + [npm-downloads-href]: https://npm.chart.dev/fnv1a-64 96 + [github-actions-src]: https://img.shields.io/github/actions/workflow/status/danielroe/fnv1a-64/ci.yml?branch=main&style=flat-square 97 + [github-actions-href]: https://github.com/danielroe/fnv1a-64/actions?query=workflow%3Aci 98 + [codecov-src]: https://img.shields.io/codecov/c/gh/danielroe/fnv1a-64/main?style=flat-square 99 + [codecov-href]: https://codecov.io/gh/danielroe/fnv1a-64
+25
bench.mjs
··· 1 + import sindresorhusFnv1a from '@sindresorhus/fnv1a' 2 + import fnvLite from 'fnv-lite' 3 + import fnvPlus from 'fnv-plus' 4 + import { bench, group, run } from 'mitata' 5 + import murmurhash from 'murmurhash' 6 + import xxhash from 'xxhashjs' 7 + import { fnv1a64, fnv1a64Base36, fnv1a64Hex } from './src/index.ts' 8 + 9 + const short = 'user:12345:profile' 10 + const long = 'a'.repeat(1024) 11 + 12 + for (const [label, input] of [['short key', short], ['1KB string', long]]) { 13 + group(label, () => { 14 + bench('fnv1a-64 (lanes)', () => fnv1a64(input)) 15 + bench('fnv1a-64 (hex)', () => fnv1a64Hex(input)) 16 + bench('fnv1a-64 (base36)', () => fnv1a64Base36(input)) 17 + bench('@sindresorhus/fnv1a size:64', () => sindresorhusFnv1a(input, { size: 64 })) 18 + bench('fnv-plus fast1a64 (64-bit)', () => fnvPlus.fast1a64(input)) 19 + bench('fnv-lite hex (128-bit)', () => fnvLite.hex(input)) 20 + bench('xxhashjs h64', () => xxhash.h64(input, 0).toString(16)) 21 + bench('murmurhash v3 (32-bit)', () => murmurhash.v3(input)) 22 + }) 23 + } 24 + 25 + run()
+43
collisions.mjs
··· 1 + /* eslint-disable no-console */ 2 + import process from 'node:process' 3 + import sindresorhusFnv1a from '@sindresorhus/fnv1a' 4 + import fnvLite from 'fnv-lite' 5 + import fnvPlus from 'fnv-plus' 6 + import murmurhash from 'murmurhash' 7 + import xxhash from 'xxhashjs' 8 + import { fnv1a64Hex } from './src/index.ts' 9 + 10 + const N = Number(process.argv[2] ?? 1_000_000) 11 + 12 + const prefixes = ['user', 'session', 'cache', 'route', 'asset', 'chunk', 'node', 'query'] 13 + function key(i) { 14 + return `${prefixes[i % prefixes.length]}:${i}:${((i * 2654435761) >>> 0).toString(36)}` 15 + } 16 + 17 + const hashers = { 18 + 'fnv1a-64': i => fnv1a64Hex(key(i)), 19 + '@sindresorhus/fnv1a size:64': i => sindresorhusFnv1a(key(i), { size: 64 }).toString(), 20 + 'fnv-plus fast1a64 (64-bit)': i => fnvPlus.fast1a64(key(i)), 21 + 'fnv-lite hex (128-bit)': i => fnvLite.hex(key(i)), 22 + 'xxhashjs h64': i => xxhash.h64(key(i), 0).toString(16), 23 + 'murmurhash v3 (32-bit)': i => String(murmurhash.v3(key(i))), 24 + } 25 + 26 + console.log(`Hashing ${N.toLocaleString()} distinct realistic keys\n`) 27 + console.log('hasher'.padEnd(32), 'collisions') 28 + console.log('-'.repeat(46)) 29 + 30 + for (const [name, fn] of Object.entries(hashers)) { 31 + const seen = new Set() 32 + let collisions = 0 33 + for (let i = 0; i < N; i++) { 34 + const h = fn(i) 35 + if (seen.has(h)) { 36 + collisions++ 37 + } 38 + else { 39 + seen.add(h) 40 + } 41 + } 42 + console.log(name.padEnd(32), collisions.toLocaleString()) 43 + }
+21 -5
package.json
··· 1 1 { 2 - "name": "package-name", 2 + "name": "fnv1a-64", 3 3 "type": "module", 4 4 "version": "0.0.0", 5 5 "packageManager": "pnpm@11.10.0", 6 - "description": "", 6 + "description": "Tiny, fast, dependency-free 64-bit FNV-1a string hash for Node and the browser", 7 7 "license": "MIT", 8 - "repository": "danielroe/package-name", 8 + "repository": "danielroe/fnv1a-64", 9 + "keywords": [ 10 + "fnv", 11 + "fnv1a", 12 + "fnv-1a", 13 + "hash", 14 + "64-bit", 15 + "non-cryptographic" 16 + ], 9 17 "sideEffects": false, 10 18 "exports": { 11 19 ".": "./src/index.ts", ··· 38 46 "test:unit": "vitest", 39 47 "test:knip": "knip", 40 48 "test:versions": "installed-check -d --no-workspaces", 49 + "bench": "node bench.mjs", 50 + "collisions": "node collisions.mjs", 41 51 "test:types": "tsc --noEmit" 42 52 }, 43 53 "devDependencies": { 44 54 "@antfu/eslint-config": "latest", 55 + "@sindresorhus/fnv1a": "^3.1.0", 45 56 "@vitest/coverage-v8": "latest", 46 57 "eslint": "latest", 58 + "fnv-lite": "^1.2.0", 59 + "fnv-plus": "^1.3.1", 47 60 "installed-check": "latest", 48 61 "knip": "latest", 62 + "mitata": "^1.0.34", 63 + "murmurhash": "^2.0.1", 49 64 "nano-staged": "1.0.2", 50 65 "simple-git-hooks": "latest", 51 66 "tsdown": "0.22.3", 52 - "typescript": "latest", 53 - "vitest": "latest" 67 + "typescript": "6.0.3", 68 + "vitest": "latest", 69 + "xxhashjs": "^0.2.2" 54 70 }, 55 71 "simple-git-hooks": { 56 72 "pre-commit": "npx nano-staged"
+4 -3
playground/index.js
··· 1 1 import assert from 'node:assert' 2 - import * as pkg from 'package-name' 2 + import { fnv1a64, fnv1a64Base36, fnv1a64Hex } from 'fnv1a-64' 3 3 4 4 // eslint-disable-next-line no-console 5 - console.log(pkg.welcome()) 5 + console.log(fnv1a64Hex('hello world'), fnv1a64Base36('hello world')) 6 6 7 - assert.strictEqual(pkg.welcome(), 'hello world') 7 + assert.strictEqual(fnv1a64Hex('hello world'), '779a65e7023cd2e7') 8 + assert.deepStrictEqual(fnv1a64(''), { high: 0xCBF29CE4, low: 0x84222325 })
+1 -1
playground/package.json
··· 5 5 "dev": "node index.js" 6 6 }, 7 7 "dependencies": { 8 - "package-name": "latest" 8 + "fnv1a-64": "latest" 9 9 } 10 10 }
+146 -291
pnpm-lock.yaml
··· 5 5 excludeLinksFromLockfile: false 6 6 7 7 overrides: 8 - package-name: link:. 8 + fnv1a-64: link:. 9 9 10 10 importers: 11 11 ··· 13 13 devDependencies: 14 14 '@antfu/eslint-config': 15 15 specifier: latest 16 - version: 9.1.0(@typescript-eslint/rule-tester@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(@typescript-eslint/typescript-estree@8.62.0(typescript@7.0.2))(@typescript-eslint/utils@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(@vue/compiler-sfc@3.5.35)(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2)(vitest@4.1.10) 16 + version: 9.1.0(@typescript-eslint/rule-tester@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(@typescript-eslint/typescript-estree@8.62.0(typescript@6.0.3))(@typescript-eslint/utils@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(@vue/compiler-sfc@3.5.35)(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.10) 17 + '@sindresorhus/fnv1a': 18 + specifier: ^3.1.0 19 + version: 3.1.0 17 20 '@vitest/coverage-v8': 18 21 specifier: latest 19 22 version: 4.1.10(vitest@4.1.10) 20 23 eslint: 21 24 specifier: latest 22 25 version: 10.7.0(jiti@2.7.0) 26 + fnv-lite: 27 + specifier: ^1.2.0 28 + version: 1.2.0 29 + fnv-plus: 30 + specifier: ^1.3.1 31 + version: 1.3.1 23 32 installed-check: 24 33 specifier: latest 25 34 version: 10.0.1 26 35 knip: 27 36 specifier: latest 28 37 version: 6.26.0 38 + mitata: 39 + specifier: ^1.0.34 40 + version: 1.0.34 41 + murmurhash: 42 + specifier: ^2.0.1 43 + version: 2.0.1 29 44 nano-staged: 30 45 specifier: 1.0.2 31 46 version: 1.0.2 ··· 34 49 version: 2.13.1 35 50 tsdown: 36 51 specifier: 0.22.3 37 - version: 0.22.3(oxc-resolver@11.21.3)(typescript@7.0.2) 52 + version: 0.22.3(oxc-resolver@11.21.3)(typescript@6.0.3) 38 53 typescript: 39 - specifier: latest 40 - version: 7.0.2 54 + specifier: 6.0.3 55 + version: 6.0.3 41 56 vitest: 42 57 specifier: latest 43 58 version: 4.1.10(@vitest/coverage-v8@4.1.10)(vite@8.0.16(jiti@2.7.0)(yaml@2.9.0)) 59 + xxhashjs: 60 + specifier: ^0.2.2 61 + version: 0.2.2 44 62 45 63 playground: 46 64 dependencies: 47 - package-name: 65 + fnv1a-64: 48 66 specifier: link:.. 49 67 version: link:.. 50 68 ··· 779 797 resolution: {integrity: sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==} 780 798 engines: {node: '>=18'} 781 799 800 + '@sindresorhus/fnv1a@3.1.0': 801 + resolution: {integrity: sha512-KV321z5m/0nuAg83W1dPLy85HpHDk7Sdi4fJbwvacWsEhAh+rZUW4ZfGcXmUIvjZg4ss2bcwNlRhJ7GBEUG08w==} 802 + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 803 + 782 804 '@standard-schema/spec@1.1.0': 783 805 resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} 784 806 ··· 940 962 resolution: {integrity: sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==} 941 963 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 942 964 943 - '@typescript/typescript-aix-ppc64@7.0.2': 944 - resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} 945 - engines: {node: '>=16.20.0'} 946 - cpu: [ppc64] 947 - os: [aix] 948 - 949 - '@typescript/typescript-darwin-arm64@7.0.2': 950 - resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} 951 - engines: {node: '>=16.20.0'} 952 - cpu: [arm64] 953 - os: [darwin] 954 - 955 - '@typescript/typescript-darwin-x64@7.0.2': 956 - resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} 957 - engines: {node: '>=16.20.0'} 958 - cpu: [x64] 959 - os: [darwin] 960 - 961 - '@typescript/typescript-freebsd-arm64@7.0.2': 962 - resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} 963 - engines: {node: '>=16.20.0'} 964 - cpu: [arm64] 965 - os: [freebsd] 966 - 967 - '@typescript/typescript-freebsd-x64@7.0.2': 968 - resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} 969 - engines: {node: '>=16.20.0'} 970 - cpu: [x64] 971 - os: [freebsd] 972 - 973 - '@typescript/typescript-linux-arm64@7.0.2': 974 - resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} 975 - engines: {node: '>=16.20.0'} 976 - cpu: [arm64] 977 - os: [linux] 978 - 979 - '@typescript/typescript-linux-arm@7.0.2': 980 - resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} 981 - engines: {node: '>=16.20.0'} 982 - cpu: [arm] 983 - os: [linux] 984 - 985 - '@typescript/typescript-linux-loong64@7.0.2': 986 - resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} 987 - engines: {node: '>=16.20.0'} 988 - cpu: [loong64] 989 - os: [linux] 990 - 991 - '@typescript/typescript-linux-mips64el@7.0.2': 992 - resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} 993 - engines: {node: '>=16.20.0'} 994 - cpu: [mips64el] 995 - os: [linux] 996 - 997 - '@typescript/typescript-linux-ppc64@7.0.2': 998 - resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} 999 - engines: {node: '>=16.20.0'} 1000 - cpu: [ppc64] 1001 - os: [linux] 1002 - 1003 - '@typescript/typescript-linux-riscv64@7.0.2': 1004 - resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} 1005 - engines: {node: '>=16.20.0'} 1006 - cpu: [riscv64] 1007 - os: [linux] 1008 - 1009 - '@typescript/typescript-linux-s390x@7.0.2': 1010 - resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} 1011 - engines: {node: '>=16.20.0'} 1012 - cpu: [s390x] 1013 - os: [linux] 1014 - 1015 - '@typescript/typescript-linux-x64@7.0.2': 1016 - resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} 1017 - engines: {node: '>=16.20.0'} 1018 - cpu: [x64] 1019 - os: [linux] 1020 - 1021 - '@typescript/typescript-netbsd-arm64@7.0.2': 1022 - resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} 1023 - engines: {node: '>=16.20.0'} 1024 - cpu: [arm64] 1025 - os: [netbsd] 1026 - 1027 - '@typescript/typescript-netbsd-x64@7.0.2': 1028 - resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} 1029 - engines: {node: '>=16.20.0'} 1030 - cpu: [x64] 1031 - os: [netbsd] 1032 - 1033 - '@typescript/typescript-openbsd-arm64@7.0.2': 1034 - resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} 1035 - engines: {node: '>=16.20.0'} 1036 - cpu: [arm64] 1037 - os: [openbsd] 1038 - 1039 - '@typescript/typescript-openbsd-x64@7.0.2': 1040 - resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} 1041 - engines: {node: '>=16.20.0'} 1042 - cpu: [x64] 1043 - os: [openbsd] 1044 - 1045 - '@typescript/typescript-sunos-x64@7.0.2': 1046 - resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} 1047 - engines: {node: '>=16.20.0'} 1048 - cpu: [x64] 1049 - os: [sunos] 1050 - 1051 - '@typescript/typescript-win32-arm64@7.0.2': 1052 - resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} 1053 - engines: {node: '>=16.20.0'} 1054 - cpu: [arm64] 1055 - os: [win32] 1056 - 1057 - '@typescript/typescript-win32-x64@7.0.2': 1058 - resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} 1059 - engines: {node: '>=16.20.0'} 1060 - cpu: [x64] 1061 - os: [win32] 1062 - 1063 965 '@vitest/coverage-v8@4.1.10': 1064 966 resolution: {integrity: sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==} 1065 967 peerDependencies: ··· 1297 1199 resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} 1298 1200 engines: {node: '>=4'} 1299 1201 hasBin: true 1202 + 1203 + cuint@0.2.2: 1204 + resolution: {integrity: sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==} 1300 1205 1301 1206 debug@4.4.3: 1302 1207 resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} ··· 1646 1551 flatted@3.4.2: 1647 1552 resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} 1648 1553 1554 + fnv-lite@1.2.0: 1555 + resolution: {integrity: sha512-bWOFDuEm1ffmavTjpE8ep+3i1f3X+zy5DcO8Um2rPyWNZn+vV6Pdo1tAnYhTISHxgtF6M8tG1QwWXh+llMUOjQ==} 1556 + 1557 + fnv-plus@1.3.1: 1558 + resolution: {integrity: sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw==} 1559 + 1649 1560 foreground-child@3.3.1: 1650 1561 resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} 1651 1562 engines: {node: '>=14'} ··· 2090 2001 resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} 2091 2002 engines: {node: '>=16 || 14 >=14.17'} 2092 2003 2004 + mitata@1.0.34: 2005 + resolution: {integrity: sha512-Mc3zrtNBKIMeHSCQ0XqRLo1vbdIx1wvFV9c8NJAiyho6AjNfMY8bVhbS12bwciUdd1t4rj8099CH3N3NFahaUA==} 2006 + 2093 2007 mlly@1.8.2: 2094 2008 resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} 2095 2009 ··· 2098 2012 2099 2013 ms@2.1.3: 2100 2014 resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 2015 + 2016 + murmurhash@2.0.1: 2017 + resolution: {integrity: sha512-5vQEh3y+DG/lMPM0mCGPDnyV8chYg/g7rl6v3Gd8WMF9S429ox3Xk8qrk174kWhG767KQMqqxLD1WnGd77hiew==} 2101 2018 2102 2019 nano-staged@1.0.2: 2103 2020 resolution: {integrity: sha512-Fytar3zHLY99nlMfqPPbraxZodqQAHPpdPRyYaplL+lB9DCR6pUrafxbG+Btz4+7fO5Rm/+DO4ZeDO/nLSUMhw==} ··· 2487 2404 resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} 2488 2405 engines: {node: '>=16'} 2489 2406 2490 - typescript@7.0.2: 2491 - resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} 2492 - engines: {node: '>=16.20.0'} 2407 + typescript@6.0.3: 2408 + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} 2409 + engines: {node: '>=14.17'} 2493 2410 hasBin: true 2494 2411 2495 2412 ufo@1.6.4: ··· 2660 2577 resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} 2661 2578 engines: {node: '>=12'} 2662 2579 2580 + xxhashjs@0.2.2: 2581 + resolution: {integrity: sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==} 2582 + 2663 2583 yaml-eslint-parser@2.0.0: 2664 2584 resolution: {integrity: sha512-h0uDm97wvT2bokfwwTmY6kJ1hp6YDFL0nRHwNKz8s/VD1FH/vvZjAKoMUE+un0eaYBSG7/c6h+lJTP+31tjgTw==} 2665 2585 engines: {node: ^20.19.0 || ^22.13.0 || >=24} ··· 2681 2601 2682 2602 snapshots: 2683 2603 2684 - '@antfu/eslint-config@9.1.0(@typescript-eslint/rule-tester@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(@typescript-eslint/typescript-estree@8.62.0(typescript@7.0.2))(@typescript-eslint/utils@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(@vue/compiler-sfc@3.5.35)(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2)(vitest@4.1.10)': 2604 + '@antfu/eslint-config@9.1.0(@typescript-eslint/rule-tester@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(@typescript-eslint/typescript-estree@8.62.0(typescript@6.0.3))(@typescript-eslint/utils@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(@vue/compiler-sfc@3.5.35)(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.10)': 2685 2605 dependencies: 2686 2606 '@antfu/install-pkg': 1.1.0 2687 2607 '@clack/prompts': 1.6.0 ··· 2689 2609 '@eslint-community/eslint-plugin-eslint-comments': 4.7.2(eslint@10.7.0(jiti@2.7.0)) 2690 2610 '@eslint/markdown': 8.0.2 2691 2611 '@stylistic/eslint-plugin': 5.10.0(eslint@10.7.0(jiti@2.7.0)) 2692 - '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 2693 - '@typescript-eslint/parser': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 2694 - '@vitest/eslint-plugin': 1.6.20(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2)(vitest@4.1.10) 2612 + '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 2613 + '@typescript-eslint/parser': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 2614 + '@vitest/eslint-plugin': 1.6.20(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.10) 2695 2615 ansis: 4.3.1 2696 2616 cac: 7.0.0 2697 2617 eslint: 10.7.0(jiti@2.7.0) ··· 2699 2619 eslint-flat-config-utils: 3.2.0 2700 2620 eslint-merge-processors: 2.0.0(eslint@10.7.0(jiti@2.7.0)) 2701 2621 eslint-plugin-antfu: 3.2.3(eslint@10.7.0(jiti@2.7.0)) 2702 - eslint-plugin-command: 3.5.2(@typescript-eslint/rule-tester@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(@typescript-eslint/typescript-estree@8.62.0(typescript@7.0.2))(@typescript-eslint/utils@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0)) 2622 + eslint-plugin-command: 3.5.2(@typescript-eslint/rule-tester@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(@typescript-eslint/typescript-estree@8.62.0(typescript@6.0.3))(@typescript-eslint/utils@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0)) 2703 2623 eslint-plugin-import-lite: 0.6.0(eslint@10.7.0(jiti@2.7.0)) 2704 2624 eslint-plugin-jsdoc: 63.0.10(eslint@10.7.0(jiti@2.7.0)) 2705 2625 eslint-plugin-jsonc: 3.2.0(eslint@10.7.0(jiti@2.7.0)) 2706 - eslint-plugin-n: 18.2.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 2626 + eslint-plugin-n: 18.2.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 2707 2627 eslint-plugin-no-only-tests: 3.4.0 2708 - eslint-plugin-perfectionist: 5.9.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 2628 + eslint-plugin-perfectionist: 5.9.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 2709 2629 eslint-plugin-pnpm: 1.6.1(eslint@10.7.0(jiti@2.7.0)) 2710 2630 eslint-plugin-regexp: 3.1.0(eslint@10.7.0(jiti@2.7.0)) 2711 2631 eslint-plugin-toml: 1.4.0(eslint@10.7.0(jiti@2.7.0)) 2712 2632 eslint-plugin-unicorn: 68.0.0(eslint@10.7.0(jiti@2.7.0)) 2713 - eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0)) 2714 - eslint-plugin-vue: 10.9.2(@stylistic/eslint-plugin@5.10.0(eslint@10.7.0(jiti@2.7.0)))(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@10.7.0(jiti@2.7.0))) 2633 + eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0)) 2634 + eslint-plugin-vue: 10.9.2(@stylistic/eslint-plugin@5.10.0(eslint@10.7.0(jiti@2.7.0)))(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@10.7.0(jiti@2.7.0))) 2715 2635 eslint-plugin-yml: 3.5.0(eslint@10.7.0(jiti@2.7.0)) 2716 2636 eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.35)(eslint@10.7.0(jiti@2.7.0)) 2717 2637 globals: 17.7.0 ··· 3246 3166 3247 3167 '@sindresorhus/base62@1.0.0': {} 3248 3168 3169 + '@sindresorhus/fnv1a@3.1.0': {} 3170 + 3249 3171 '@standard-schema/spec@1.1.0': {} 3250 3172 3251 3173 '@stylistic/eslint-plugin@5.10.0(eslint@10.7.0(jiti@2.7.0))': ··· 3298 3220 3299 3221 '@types/unist@3.0.3': {} 3300 3222 3301 - '@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2)': 3223 + '@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)': 3302 3224 dependencies: 3303 3225 '@eslint-community/regexpp': 4.12.2 3304 - '@typescript-eslint/parser': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3226 + '@typescript-eslint/parser': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3305 3227 '@typescript-eslint/scope-manager': 8.62.0 3306 - '@typescript-eslint/type-utils': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3307 - '@typescript-eslint/utils': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3228 + '@typescript-eslint/type-utils': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3229 + '@typescript-eslint/utils': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3308 3230 '@typescript-eslint/visitor-keys': 8.62.0 3309 3231 eslint: 10.7.0(jiti@2.7.0) 3310 3232 ignore: 7.0.5 3311 3233 natural-compare: 1.4.0 3312 - ts-api-utils: 2.5.0(typescript@7.0.2) 3313 - typescript: 7.0.2 3234 + ts-api-utils: 2.5.0(typescript@6.0.3) 3235 + typescript: 6.0.3 3314 3236 transitivePeerDependencies: 3315 3237 - supports-color 3316 3238 3317 - '@typescript-eslint/parser@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2)': 3239 + '@typescript-eslint/parser@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)': 3318 3240 dependencies: 3319 3241 '@typescript-eslint/scope-manager': 8.60.1 3320 3242 '@typescript-eslint/types': 8.60.1 3321 - '@typescript-eslint/typescript-estree': 8.60.1(typescript@7.0.2) 3243 + '@typescript-eslint/typescript-estree': 8.60.1(typescript@6.0.3) 3322 3244 '@typescript-eslint/visitor-keys': 8.60.1 3323 3245 debug: 4.4.3 3324 3246 eslint: 10.7.0(jiti@2.7.0) 3325 - typescript: 7.0.2 3247 + typescript: 6.0.3 3326 3248 transitivePeerDependencies: 3327 3249 - supports-color 3328 3250 3329 - '@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2)': 3251 + '@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)': 3330 3252 dependencies: 3331 3253 '@typescript-eslint/scope-manager': 8.62.0 3332 3254 '@typescript-eslint/types': 8.62.0 3333 - '@typescript-eslint/typescript-estree': 8.62.0(typescript@7.0.2) 3255 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) 3334 3256 '@typescript-eslint/visitor-keys': 8.62.0 3335 3257 debug: 4.4.3 3336 3258 eslint: 10.7.0(jiti@2.7.0) 3337 - typescript: 7.0.2 3259 + typescript: 6.0.3 3338 3260 transitivePeerDependencies: 3339 3261 - supports-color 3340 3262 3341 - '@typescript-eslint/project-service@8.60.1(typescript@7.0.2)': 3263 + '@typescript-eslint/project-service@8.60.1(typescript@6.0.3)': 3342 3264 dependencies: 3343 - '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@7.0.2) 3265 + '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@6.0.3) 3344 3266 '@typescript-eslint/types': 8.62.0 3345 3267 debug: 4.4.3 3346 - typescript: 7.0.2 3268 + typescript: 6.0.3 3347 3269 transitivePeerDependencies: 3348 3270 - supports-color 3349 3271 3350 - '@typescript-eslint/project-service@8.62.0(typescript@7.0.2)': 3272 + '@typescript-eslint/project-service@8.62.0(typescript@6.0.3)': 3351 3273 dependencies: 3352 - '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@7.0.2) 3274 + '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@6.0.3) 3353 3275 '@typescript-eslint/types': 8.62.0 3354 3276 debug: 4.4.3 3355 - typescript: 7.0.2 3277 + typescript: 6.0.3 3356 3278 transitivePeerDependencies: 3357 3279 - supports-color 3358 3280 3359 - '@typescript-eslint/rule-tester@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2)': 3281 + '@typescript-eslint/rule-tester@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)': 3360 3282 dependencies: 3361 - '@typescript-eslint/parser': 8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3362 - '@typescript-eslint/typescript-estree': 8.60.1(typescript@7.0.2) 3363 - '@typescript-eslint/utils': 8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3283 + '@typescript-eslint/parser': 8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3284 + '@typescript-eslint/typescript-estree': 8.60.1(typescript@6.0.3) 3285 + '@typescript-eslint/utils': 8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3364 3286 ajv: 6.15.0 3365 3287 eslint: 10.7.0(jiti@2.7.0) 3366 3288 json-stable-stringify-without-jsonify: 1.0.1 3367 3289 lodash.merge: 4.6.2 3368 3290 semver: 7.8.5 3369 - typescript: 7.0.2 3291 + typescript: 6.0.3 3370 3292 transitivePeerDependencies: 3371 3293 - supports-color 3372 3294 ··· 3380 3302 '@typescript-eslint/types': 8.62.0 3381 3303 '@typescript-eslint/visitor-keys': 8.62.0 3382 3304 3383 - '@typescript-eslint/tsconfig-utils@8.60.1(typescript@7.0.2)': 3305 + '@typescript-eslint/tsconfig-utils@8.60.1(typescript@6.0.3)': 3384 3306 dependencies: 3385 - typescript: 7.0.2 3307 + typescript: 6.0.3 3386 3308 3387 - '@typescript-eslint/tsconfig-utils@8.62.0(typescript@7.0.2)': 3309 + '@typescript-eslint/tsconfig-utils@8.62.0(typescript@6.0.3)': 3388 3310 dependencies: 3389 - typescript: 7.0.2 3311 + typescript: 6.0.3 3390 3312 3391 - '@typescript-eslint/type-utils@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2)': 3313 + '@typescript-eslint/type-utils@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)': 3392 3314 dependencies: 3393 3315 '@typescript-eslint/types': 8.62.0 3394 - '@typescript-eslint/typescript-estree': 8.62.0(typescript@7.0.2) 3395 - '@typescript-eslint/utils': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3316 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) 3317 + '@typescript-eslint/utils': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3396 3318 debug: 4.4.3 3397 3319 eslint: 10.7.0(jiti@2.7.0) 3398 - ts-api-utils: 2.5.0(typescript@7.0.2) 3399 - typescript: 7.0.2 3320 + ts-api-utils: 2.5.0(typescript@6.0.3) 3321 + typescript: 6.0.3 3400 3322 transitivePeerDependencies: 3401 3323 - supports-color 3402 3324 ··· 3404 3326 3405 3327 '@typescript-eslint/types@8.62.0': {} 3406 3328 3407 - '@typescript-eslint/typescript-estree@8.60.1(typescript@7.0.2)': 3329 + '@typescript-eslint/typescript-estree@8.60.1(typescript@6.0.3)': 3408 3330 dependencies: 3409 - '@typescript-eslint/project-service': 8.60.1(typescript@7.0.2) 3410 - '@typescript-eslint/tsconfig-utils': 8.60.1(typescript@7.0.2) 3331 + '@typescript-eslint/project-service': 8.60.1(typescript@6.0.3) 3332 + '@typescript-eslint/tsconfig-utils': 8.60.1(typescript@6.0.3) 3411 3333 '@typescript-eslint/types': 8.60.1 3412 3334 '@typescript-eslint/visitor-keys': 8.60.1 3413 3335 debug: 4.4.3 3414 3336 minimatch: 10.2.5 3415 3337 semver: 7.8.5 3416 3338 tinyglobby: 0.2.17 3417 - ts-api-utils: 2.5.0(typescript@7.0.2) 3418 - typescript: 7.0.2 3339 + ts-api-utils: 2.5.0(typescript@6.0.3) 3340 + typescript: 6.0.3 3419 3341 transitivePeerDependencies: 3420 3342 - supports-color 3421 3343 3422 - '@typescript-eslint/typescript-estree@8.62.0(typescript@7.0.2)': 3344 + '@typescript-eslint/typescript-estree@8.62.0(typescript@6.0.3)': 3423 3345 dependencies: 3424 - '@typescript-eslint/project-service': 8.62.0(typescript@7.0.2) 3425 - '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@7.0.2) 3346 + '@typescript-eslint/project-service': 8.62.0(typescript@6.0.3) 3347 + '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@6.0.3) 3426 3348 '@typescript-eslint/types': 8.62.0 3427 3349 '@typescript-eslint/visitor-keys': 8.62.0 3428 3350 debug: 4.4.3 3429 3351 minimatch: 10.2.5 3430 3352 semver: 7.8.5 3431 3353 tinyglobby: 0.2.17 3432 - ts-api-utils: 2.5.0(typescript@7.0.2) 3433 - typescript: 7.0.2 3354 + ts-api-utils: 2.5.0(typescript@6.0.3) 3355 + typescript: 6.0.3 3434 3356 transitivePeerDependencies: 3435 3357 - supports-color 3436 3358 3437 - '@typescript-eslint/utils@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2)': 3359 + '@typescript-eslint/utils@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)': 3438 3360 dependencies: 3439 3361 '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.0)) 3440 3362 '@typescript-eslint/scope-manager': 8.60.1 3441 3363 '@typescript-eslint/types': 8.60.1 3442 - '@typescript-eslint/typescript-estree': 8.60.1(typescript@7.0.2) 3364 + '@typescript-eslint/typescript-estree': 8.60.1(typescript@6.0.3) 3443 3365 eslint: 10.7.0(jiti@2.7.0) 3444 - typescript: 7.0.2 3366 + typescript: 6.0.3 3445 3367 transitivePeerDependencies: 3446 3368 - supports-color 3447 3369 3448 - '@typescript-eslint/utils@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2)': 3370 + '@typescript-eslint/utils@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)': 3449 3371 dependencies: 3450 3372 '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.0)) 3451 3373 '@typescript-eslint/scope-manager': 8.62.0 3452 3374 '@typescript-eslint/types': 8.62.0 3453 - '@typescript-eslint/typescript-estree': 8.62.0(typescript@7.0.2) 3375 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) 3454 3376 eslint: 10.7.0(jiti@2.7.0) 3455 - typescript: 7.0.2 3377 + typescript: 6.0.3 3456 3378 transitivePeerDependencies: 3457 3379 - supports-color 3458 3380 ··· 3466 3388 '@typescript-eslint/types': 8.62.0 3467 3389 eslint-visitor-keys: 5.0.1 3468 3390 3469 - '@typescript/typescript-aix-ppc64@7.0.2': 3470 - optional: true 3471 - 3472 - '@typescript/typescript-darwin-arm64@7.0.2': 3473 - optional: true 3474 - 3475 - '@typescript/typescript-darwin-x64@7.0.2': 3476 - optional: true 3477 - 3478 - '@typescript/typescript-freebsd-arm64@7.0.2': 3479 - optional: true 3480 - 3481 - '@typescript/typescript-freebsd-x64@7.0.2': 3482 - optional: true 3483 - 3484 - '@typescript/typescript-linux-arm64@7.0.2': 3485 - optional: true 3486 - 3487 - '@typescript/typescript-linux-arm@7.0.2': 3488 - optional: true 3489 - 3490 - '@typescript/typescript-linux-loong64@7.0.2': 3491 - optional: true 3492 - 3493 - '@typescript/typescript-linux-mips64el@7.0.2': 3494 - optional: true 3495 - 3496 - '@typescript/typescript-linux-ppc64@7.0.2': 3497 - optional: true 3498 - 3499 - '@typescript/typescript-linux-riscv64@7.0.2': 3500 - optional: true 3501 - 3502 - '@typescript/typescript-linux-s390x@7.0.2': 3503 - optional: true 3504 - 3505 - '@typescript/typescript-linux-x64@7.0.2': 3506 - optional: true 3507 - 3508 - '@typescript/typescript-netbsd-arm64@7.0.2': 3509 - optional: true 3510 - 3511 - '@typescript/typescript-netbsd-x64@7.0.2': 3512 - optional: true 3513 - 3514 - '@typescript/typescript-openbsd-arm64@7.0.2': 3515 - optional: true 3516 - 3517 - '@typescript/typescript-openbsd-x64@7.0.2': 3518 - optional: true 3519 - 3520 - '@typescript/typescript-sunos-x64@7.0.2': 3521 - optional: true 3522 - 3523 - '@typescript/typescript-win32-arm64@7.0.2': 3524 - optional: true 3525 - 3526 - '@typescript/typescript-win32-x64@7.0.2': 3527 - optional: true 3528 - 3529 3391 '@vitest/coverage-v8@4.1.10(vitest@4.1.10)': 3530 3392 dependencies: 3531 3393 '@bcoe/v8-coverage': 1.0.2 ··· 3540 3402 tinyrainbow: 3.1.0 3541 3403 vitest: 4.1.10(@vitest/coverage-v8@4.1.10)(vite@8.0.16(jiti@2.7.0)(yaml@2.9.0)) 3542 3404 3543 - '@vitest/eslint-plugin@1.6.20(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2)(vitest@4.1.10)': 3405 + '@vitest/eslint-plugin@1.6.20(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.10)': 3544 3406 dependencies: 3545 3407 '@typescript-eslint/scope-manager': 8.62.0 3546 - '@typescript-eslint/utils': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3408 + '@typescript-eslint/utils': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3547 3409 eslint: 10.7.0(jiti@2.7.0) 3548 3410 optionalDependencies: 3549 - '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3550 - typescript: 7.0.2 3411 + '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3412 + typescript: 6.0.3 3551 3413 vitest: 4.1.10(@vitest/coverage-v8@4.1.10)(vite@8.0.16(jiti@2.7.0)(yaml@2.9.0)) 3552 3414 transitivePeerDependencies: 3553 3415 - supports-color ··· 3754 3616 3755 3617 cssesc@3.0.0: {} 3756 3618 3619 + cuint@0.2.2: {} 3620 + 3757 3621 debug@4.4.3: 3758 3622 dependencies: 3759 3623 ms: 2.1.3 ··· 3836 3700 dependencies: 3837 3701 eslint: 10.7.0(jiti@2.7.0) 3838 3702 3839 - eslint-plugin-command@3.5.2(@typescript-eslint/rule-tester@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(@typescript-eslint/typescript-estree@8.62.0(typescript@7.0.2))(@typescript-eslint/utils@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0)): 3703 + eslint-plugin-command@3.5.2(@typescript-eslint/rule-tester@8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(@typescript-eslint/typescript-estree@8.62.0(typescript@6.0.3))(@typescript-eslint/utils@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0)): 3840 3704 dependencies: 3841 3705 '@es-joy/jsdoccomment': 0.84.0 3842 - '@typescript-eslint/rule-tester': 8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3843 - '@typescript-eslint/typescript-estree': 8.62.0(typescript@7.0.2) 3844 - '@typescript-eslint/utils': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3706 + '@typescript-eslint/rule-tester': 8.60.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3707 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) 3708 + '@typescript-eslint/utils': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3845 3709 eslint: 10.7.0(jiti@2.7.0) 3846 3710 3847 3711 eslint-plugin-es-x@7.8.0(eslint@10.7.0(jiti@2.7.0)): ··· 3890 3754 transitivePeerDependencies: 3891 3755 - '@eslint/json' 3892 3756 3893 - eslint-plugin-n@18.2.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2): 3757 + eslint-plugin-n@18.2.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3): 3894 3758 dependencies: 3895 3759 '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.0)) 3896 3760 enhanced-resolve: 5.23.0 ··· 3902 3766 ignore: 5.3.2 3903 3767 semver: 7.8.5 3904 3768 optionalDependencies: 3905 - typescript: 7.0.2 3769 + typescript: 6.0.3 3906 3770 3907 3771 eslint-plugin-no-only-tests@3.4.0: {} 3908 3772 3909 - eslint-plugin-perfectionist@5.9.1(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2): 3773 + eslint-plugin-perfectionist@5.9.1(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3): 3910 3774 dependencies: 3911 - '@typescript-eslint/utils': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3775 + '@typescript-eslint/utils': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3912 3776 eslint: 10.7.0(jiti@2.7.0) 3913 3777 natural-orderby: 5.0.0 3914 3778 transitivePeerDependencies: ··· 3968 3832 semver: 7.8.5 3969 3833 strip-indent: 4.1.1 3970 3834 3971 - eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0)): 3835 + eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0)): 3972 3836 dependencies: 3973 3837 eslint: 10.7.0(jiti@2.7.0) 3974 3838 optionalDependencies: 3975 - '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3839 + '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3976 3840 3977 - eslint-plugin-vue@10.9.2(@stylistic/eslint-plugin@5.10.0(eslint@10.7.0(jiti@2.7.0)))(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2))(eslint@10.7.0(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@10.7.0(jiti@2.7.0))): 3841 + eslint-plugin-vue@10.9.2(@stylistic/eslint-plugin@5.10.0(eslint@10.7.0(jiti@2.7.0)))(@typescript-eslint/parser@8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.7.0(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@10.7.0(jiti@2.7.0))): 3978 3842 dependencies: 3979 3843 '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.0)) 3980 3844 eslint: 10.7.0(jiti@2.7.0) ··· 3986 3850 xml-name-validator: 4.0.0 3987 3851 optionalDependencies: 3988 3852 '@stylistic/eslint-plugin': 5.10.0(eslint@10.7.0(jiti@2.7.0)) 3989 - '@typescript-eslint/parser': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@7.0.2) 3853 + '@typescript-eslint/parser': 8.62.0(eslint@10.7.0(jiti@2.7.0))(typescript@6.0.3) 3990 3854 3991 3855 eslint-plugin-yml@3.5.0(eslint@10.7.0(jiti@2.7.0)): 3992 3856 dependencies: ··· 4133 3997 keyv: 4.5.4 4134 3998 4135 3999 flatted@3.4.2: {} 4000 + 4001 + fnv-lite@1.2.0: {} 4002 + 4003 + fnv-plus@1.3.1: {} 4136 4004 4137 4005 foreground-child@3.3.1: 4138 4006 dependencies: ··· 4744 4612 4745 4613 minipass@7.1.3: {} 4746 4614 4615 + mitata@1.0.34: {} 4616 + 4747 4617 mlly@1.8.2: 4748 4618 dependencies: 4749 4619 acorn: 8.16.0 ··· 4754 4624 module-replacements@3.0.0-beta.8: {} 4755 4625 4756 4626 ms@2.1.3: {} 4627 + 4628 + murmurhash@2.0.1: {} 4757 4629 4758 4630 nano-staged@1.0.2: {} 4759 4631 ··· 4962 4834 4963 4835 resolve-workspace-root@2.0.1: {} 4964 4836 4965 - rolldown-plugin-dts@0.26.0(oxc-resolver@11.21.3)(rolldown@1.1.2)(typescript@7.0.2): 4837 + rolldown-plugin-dts@0.26.0(oxc-resolver@11.21.3)(rolldown@1.1.2)(typescript@6.0.3): 4966 4838 dependencies: 4967 4839 '@babel/generator': 8.0.0 4968 4840 '@babel/helper-validator-identifier': 8.0.2 ··· 4974 4846 obug: 2.1.3 4975 4847 rolldown: 1.1.2 4976 4848 optionalDependencies: 4977 - typescript: 7.0.2 4849 + typescript: 6.0.3 4978 4850 transitivePeerDependencies: 4979 4851 - oxc-resolver 4980 4852 ··· 5127 4999 5128 5000 tree-kill@1.2.2: {} 5129 5001 5130 - ts-api-utils@2.5.0(typescript@7.0.2): 5002 + ts-api-utils@2.5.0(typescript@6.0.3): 5131 5003 dependencies: 5132 - typescript: 7.0.2 5004 + typescript: 6.0.3 5133 5005 5134 - tsdown@0.22.3(oxc-resolver@11.21.3)(typescript@7.0.2): 5006 + tsdown@0.22.3(oxc-resolver@11.21.3)(typescript@6.0.3): 5135 5007 dependencies: 5136 5008 ansis: 4.3.1 5137 5009 cac: 7.0.0 ··· 5142 5014 obug: 2.1.3 5143 5015 picomatch: 4.0.4 5144 5016 rolldown: 1.1.2 5145 - rolldown-plugin-dts: 0.26.0(oxc-resolver@11.21.3)(rolldown@1.1.2)(typescript@7.0.2) 5017 + rolldown-plugin-dts: 0.26.0(oxc-resolver@11.21.3)(rolldown@1.1.2)(typescript@6.0.3) 5146 5018 semver: 7.8.5 5147 5019 tinyexec: 1.2.4 5148 5020 tinyglobby: 0.2.17 5149 5021 tree-kill: 1.2.2 5150 5022 unconfig-core: 7.5.0 5151 5023 optionalDependencies: 5152 - typescript: 7.0.2 5024 + typescript: 6.0.3 5153 5025 transitivePeerDependencies: 5154 5026 - '@ts-macro/tsc' 5155 5027 - '@typescript/native-preview' ··· 5165 5037 5166 5038 type-fest@4.41.0: {} 5167 5039 5168 - typescript@7.0.2: 5169 - optionalDependencies: 5170 - '@typescript/typescript-aix-ppc64': 7.0.2 5171 - '@typescript/typescript-darwin-arm64': 7.0.2 5172 - '@typescript/typescript-darwin-x64': 7.0.2 5173 - '@typescript/typescript-freebsd-arm64': 7.0.2 5174 - '@typescript/typescript-freebsd-x64': 7.0.2 5175 - '@typescript/typescript-linux-arm': 7.0.2 5176 - '@typescript/typescript-linux-arm64': 7.0.2 5177 - '@typescript/typescript-linux-loong64': 7.0.2 5178 - '@typescript/typescript-linux-mips64el': 7.0.2 5179 - '@typescript/typescript-linux-ppc64': 7.0.2 5180 - '@typescript/typescript-linux-riscv64': 7.0.2 5181 - '@typescript/typescript-linux-s390x': 7.0.2 5182 - '@typescript/typescript-linux-x64': 7.0.2 5183 - '@typescript/typescript-netbsd-arm64': 7.0.2 5184 - '@typescript/typescript-netbsd-x64': 7.0.2 5185 - '@typescript/typescript-openbsd-arm64': 7.0.2 5186 - '@typescript/typescript-openbsd-x64': 7.0.2 5187 - '@typescript/typescript-sunos-x64': 7.0.2 5188 - '@typescript/typescript-win32-arm64': 7.0.2 5189 - '@typescript/typescript-win32-x64': 7.0.2 5040 + typescript@6.0.3: {} 5190 5041 5191 5042 ufo@1.6.4: {} 5192 5043 ··· 5319 5170 strip-ansi: 7.2.0 5320 5171 5321 5172 xml-name-validator@4.0.0: {} 5173 + 5174 + xxhashjs@0.2.2: 5175 + dependencies: 5176 + cuint: 0.2.2 5322 5177 5323 5178 yaml-eslint-parser@2.0.0: 5324 5179 dependencies:
+5 -1
pnpm-workspace.yaml
··· 1 + shellEmulator: true 2 + 3 + trustPolicy: no-downgrade 4 + 1 5 packages: 2 6 - playground 3 7 ··· 6 10 simple-git-hooks: true 7 11 8 12 overrides: 9 - package-name: "link:." 13 + fnv1a-64: 'link:.'
+83 -1
src/index.ts
··· 1 - export const welcome = () => 'hello world' 1 + /** 2 + * The two 32-bit lanes of a 64-bit FNV-1a hash. 3 + * 4 + * `high` is the most-significant 32 bits, `low` the least-significant. Both are 5 + * unsigned integers in the range `0` to `2^32 - 1`. 6 + */ 7 + export interface Fnv1a64Lanes { 8 + high: number 9 + low: number 10 + } 11 + 12 + /** 13 + * Compute the 64-bit FNV-1a hash of a string as two 32-bit lanes. 14 + * 15 + * This is the fast core: no BigInt, no allocations, plain `Math.imul`-free 16 + * 32-bit arithmetic. Prefer {@link fnv1a64Hex} or {@link fnv1a64Base36} for a 17 + * usable key; use this directly only when you want to avoid string formatting. 18 + * 19 + * The hash is computed over UTF-16 code units (`str.charCodeAt(i)`), not UTF-8 20 + * bytes. For ASCII input this matches a canonical FNV-1a-64; for non-ASCII it 21 + * does not. See the README for details. 22 + * 23 + * @param str - The string to hash. 24 + * @returns The `{ high, low }` 32-bit lanes of the 64-bit hash. 25 + */ 26 + export function fnv1a64(str: string): Fnv1a64Lanes { 27 + let low = 0x84222325 28 + let high = 0xCBF29CE4 29 + for (let i = 0; i < str.length; i++) { 30 + low ^= str.charCodeAt(i) 31 + const lowByLow = (low & 0xFFFF) * 0x1B3 32 + const highOfLow = (low >>> 16) * 0x1B3 33 + const highByHigh = (high & 0xFFFF) * 0x1B3 + ((high >>> 16) * 0x1B3 << 16) 34 + const carry = (lowByLow >>> 16) + highOfLow 35 + // The 64-bit prime 0x100000001B3's 2^40 bit folds `low * 2^8` into the high lane. 36 + high = (highByHigh + (carry >>> 16) + low * 0x100) >>> 0 37 + low = ((lowByLow & 0xFFFF) | ((carry & 0xFFFF) << 16)) >>> 0 38 + } 39 + return { high: high >>> 0, low: low >>> 0 } 40 + } 41 + 42 + /** 43 + * Compute the 64-bit FNV-1a hash of a string as a `bigint`. 44 + * 45 + * Ergonomic and comparable, at the cost of composing the two lanes into a 46 + * `bigint`. For a compact string key, prefer {@link fnv1a64Base36}. 47 + * 48 + * @param str - The string to hash. 49 + * @returns The 64-bit hash as an unsigned `bigint`. 50 + */ 51 + export function fnv1a64BigInt(str: string): bigint { 52 + const { high, low } = fnv1a64(str) 53 + return (BigInt(high) << 32n) | BigInt(low) 54 + } 55 + 56 + /** 57 + * Compute the 64-bit FNV-1a hash of a string as a 16-character zero-padded 58 + * lowercase hex string. 59 + * 60 + * The output is always exactly 16 characters, so equal-length comparison and 61 + * fixed-width storage are safe. 62 + * 63 + * @param str - The string to hash. 64 + * @returns A 16-character hex string. 65 + */ 66 + export function fnv1a64Hex(str: string): string { 67 + const { high, low } = fnv1a64(str) 68 + return high.toString(16).padStart(8, '0') + low.toString(16).padStart(8, '0') 69 + } 70 + 71 + /** 72 + * Compute the 64-bit FNV-1a hash of a string as a base36 string. 73 + * 74 + * This is the shortest textual form (up to 13 characters) and is ideal for 75 + * cache keys. The length varies with the value; it is not zero-padded. Equal 76 + * inputs always produce identical strings. 77 + * 78 + * @param str - The string to hash. 79 + * @returns A base36 string of the 64-bit hash. 80 + */ 81 + export function fnv1a64Base36(str: string): string { 82 + return fnv1a64BigInt(str).toString(36) 83 + }
+111 -4
test/index.test.ts
··· 1 1 import { describe, expect, it } from 'vitest' 2 - import { welcome } from '../src' 2 + import { fnv1a64, fnv1a64Base36, fnv1a64BigInt, fnv1a64Hex } from '../src' 3 + 4 + function reference(str: string): { high: number, low: number, value: bigint } { 5 + let h = 0xCBF29CE484222325n 6 + const mask = (1n << 64n) - 1n 7 + for (let i = 0; i < str.length; i++) { 8 + h ^= BigInt(str.charCodeAt(i)) 9 + h = (h * 0x100000001B3n) & mask 10 + } 11 + return { high: Number(h >> 32n), low: Number(h & 0xFFFFFFFFn), value: h } 12 + } 13 + 14 + const cases = ['', 'a', 'abc', 'hello world', 'x'.repeat(200)] 15 + 16 + describe('fnv1a64', () => { 17 + it('matches a bigint reference bit-for-bit', () => { 18 + for (const input of cases) { 19 + const ref = reference(input) 20 + const { high, low } = fnv1a64(input) 21 + expect({ high, low }, `lanes for ${JSON.stringify(input)}`).toEqual({ 22 + high: ref.high, 23 + low: ref.low, 24 + }) 25 + } 26 + }) 27 + 28 + it('returns the offset basis for the empty string', () => { 29 + expect(fnv1a64('')).toEqual({ high: 0xCBF29CE4, low: 0x84222325 }) 30 + }) 31 + 32 + it('returns unsigned lanes', () => { 33 + for (const input of cases) { 34 + const { high, low } = fnv1a64(input) 35 + expect(high).toBeGreaterThanOrEqual(0) 36 + expect(low).toBeGreaterThanOrEqual(0) 37 + expect(high).toBeLessThanOrEqual(0xFFFFFFFF) 38 + expect(low).toBeLessThanOrEqual(0xFFFFFFFF) 39 + } 40 + }) 41 + 42 + it('is stable across calls', () => { 43 + for (const input of cases) { 44 + expect(fnv1a64(input)).toEqual(fnv1a64(input)) 45 + } 46 + }) 47 + }) 48 + 49 + describe('fnv1a64BigInt', () => { 50 + it('matches a bigint reference', () => { 51 + for (const input of cases) { 52 + expect(fnv1a64BigInt(input)).toBe(reference(input).value) 53 + } 54 + }) 55 + }) 56 + 57 + describe('fnv1a64Hex', () => { 58 + it('is 16 zero-padded characters', () => { 59 + for (const input of cases) { 60 + const hex = fnv1a64Hex(input) 61 + expect(hex).toHaveLength(16) 62 + expect(hex).toMatch(/^[0-9a-f]{16}$/) 63 + expect(BigInt(`0x${hex}`)).toBe(reference(input).value) 64 + } 65 + }) 66 + 67 + it('matches known snapshots', () => { 68 + expect(fnv1a64Hex('')).toBe('cbf29ce484222325') 69 + expect(fnv1a64Hex('a')).toBe('af63dc4c8601ec8c') 70 + expect(fnv1a64Hex('hello world')).toBe('779a65e7023cd2e7') 71 + }) 72 + }) 73 + 74 + describe('fnv1a64Base36', () => { 75 + it('round-trips to the bigint value', () => { 76 + for (const input of cases) { 77 + expect(parseBase36(fnv1a64Base36(input))).toBe(reference(input).value) 78 + } 79 + }) 80 + 81 + it('is stable and deterministic', () => { 82 + expect(fnv1a64Base36('abc')).toBe(fnv1a64Base36('abc')) 83 + }) 84 + }) 85 + 86 + describe('non-ascii behaviour', () => { 87 + it('hashes utf-16 code units, not utf-8 bytes', () => { 88 + for (const input of ['café', '日本語', '\u{1F600}emoji']) { 89 + const ref = reference(input) 90 + expect(fnv1a64(input)).toEqual({ high: ref.high, low: ref.low }) 91 + } 92 + }) 93 + }) 3 94 4 - describe('package-name', () => { 5 - it('works', () => { 6 - expect(welcome()).toMatchInlineSnapshot('"hello world"') 95 + describe('collision smoke test', () => { 96 + it('has no collisions across 50k realistic keys', () => { 97 + const seen = new Set<string>() 98 + const prefixes = ['user', 'session', 'cache', 'route', 'asset', 'chunk', 'node', 'query'] 99 + let count = 0 100 + for (let i = 0; i < 50_000; i++) { 101 + const key = `${prefixes[i % prefixes.length]}:${i}:${(i * 2654435761 >>> 0).toString(36)}` 102 + seen.add(fnv1a64Hex(key)) 103 + count++ 104 + } 105 + expect(seen.size).toBe(count) 7 106 }) 8 107 }) 108 + 109 + function parseBase36(value: string): bigint { 110 + let result = 0n 111 + for (const char of value) { 112 + result = result * 36n + BigInt(Number.parseInt(char, 36)) 113 + } 114 + return result 115 + }
+2 -4
tsconfig.json
··· 18 18 "verbatimModuleSyntax": true, 19 19 "skipLibCheck": true 20 20 }, 21 - "include": [ 22 - "src", 23 - "test", 24 - "playground" 21 + "exclude": [ 22 + "dist" 25 23 ] 26 24 }
+1 -1
vitest.config.ts
··· 4 4 export default defineConfig({ 5 5 resolve: { 6 6 alias: { 7 - 'package-name': fileURLToPath( 7 + 'fnv1a-64': fileURLToPath( 8 8 new URL('./src/index.ts', import.meta.url).href, 9 9 ), 10 10 },