feat(helpers): add deterministic stringHash for stable daily selection

This commit is contained in:
kolaente 2026-04-05 13:29:41 +02:00 committed by kolaente
parent ac76bce5cd
commit b0bc41291e
2 changed files with 32 additions and 0 deletions

View File

@ -0,0 +1,21 @@
import {describe, it, expect} from 'vitest'
import {stringHash} from './stringHash'
describe('stringHash', () => {
it('returns a non-negative integer', () => {
expect(stringHash('hello')).toBeGreaterThanOrEqual(0)
expect(Number.isInteger(stringHash('hello'))).toBe(true)
})
it('is deterministic for the same input', () => {
expect(stringHash('foo')).toBe(stringHash('foo'))
})
it('returns different values for different inputs', () => {
expect(stringHash('foo')).not.toBe(stringHash('bar'))
})
it('handles the empty string', () => {
expect(stringHash('')).toBeGreaterThanOrEqual(0)
})
})

View File

@ -0,0 +1,11 @@
// Deterministic non-cryptographic string hash (djb2 variant).
// Used for stable pseudo-random selection keyed on date + user + bucket.
export function stringHash(input: string): number {
let hash = 5381
for (let i = 0; i < input.length; i++) {
// hash * 33 + char, kept in 32-bit range via `| 0`.
hash = ((hash << 5) + hash + input.charCodeAt(i)) | 0
}
// Ensure non-negative.
return hash >>> 0
}