Skip to content

Function: toStableKey()

toStableKey(value, seen?): string

Canonical string for a structured value, for deriving a STABLE IDENTITY from it - an advisory-lock id, a channel name, a cache key.

The guarantee is two-way, which is what makes it safe to hash: values that should share an identity produce the same string (object key order does not matter), and values that should not are never conflated. Every token carries its type, so 1 and "1" differ; a string carries its length, so ["a", "bc"] and ["ab", "c"] differ; arrays keep order while object keys are sorted.

JSON.stringify cannot do this job - key order leaks in, undefined vanishes, 1 and "1" collide after quoting is stripped, and a cycle throws a TypeError naming neither the value nor the caller’s intent.

Deliberately strict where a silent answer would be a WRONG identity rather than a missing one, since two callers disagreeing about a lock or a channel is invisible until it corrupts something. Throws TypeError on a cycle, on a non-finite number (NaN is not equal to itself, so it cannot have a stable identity), and on a function or symbol (no meaningful value identity). undefined and null are accepted as distinct tokens.

Date is canonicalized by instant, unlike the hash canonicalizer in ./hash.ts, which folds every Date onto one token. Prefer this function when distinctness is a correctness requirement; prefer hash.fnvHash when a short, collision-tolerant digest is enough.

unknown

The value to canonicalize.

Set<object> = ...

Cycle-detection set for the recursive walk. Internal; callers pass one value.

string

toStableKey({ a: 1, b: 2 }) === toStableKey({ b: 2, a: 1 }); // true
toStableKey(1) !== toStableKey("1"); // true