/ utils / taggedId.ts
taggedId.ts
 1  /**
 2   * Tagged ID encoding compatible with the API's tagged_id.py format.
 3   *
 4   * Produces IDs like "user_01PaGUP2rbg1XDh7Z9W1CEpd" from a UUID string.
 5   * The format is: {tag}_{version}{base58(uuid_as_128bit_int)}
 6   *
 7   * This must stay in sync with api/api/common/utils/tagged_id.py.
 8   */
 9  
10  const BASE_58_CHARS =
11    '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
12  const VERSION = '01'
13  // ceil(128 / log2(58)) = 22
14  const ENCODED_LENGTH = 22
15  
16  /**
17   * Encode a 128-bit unsigned integer as a fixed-length base58 string.
18   */
19  function base58Encode(n: bigint): string {
20    const base = BigInt(BASE_58_CHARS.length)
21    const result = new Array<string>(ENCODED_LENGTH).fill(BASE_58_CHARS[0]!)
22    let i = ENCODED_LENGTH - 1
23    let value = n
24    while (value > 0n) {
25      const rem = Number(value % base)
26      result[i] = BASE_58_CHARS[rem]!
27      value = value / base
28      i--
29    }
30    return result.join('')
31  }
32  
33  /**
34   * Parse a UUID string (with or without hyphens) into a 128-bit bigint.
35   */
36  function uuidToBigInt(uuid: string): bigint {
37    const hex = uuid.replace(/-/g, '')
38    if (hex.length !== 32) {
39      throw new Error(`Invalid UUID hex length: ${hex.length}`)
40    }
41    return BigInt('0x' + hex)
42  }
43  
44  /**
45   * Convert an account UUID to a tagged ID in the API's format.
46   *
47   * @param tag - The tag prefix (e.g. "user", "org")
48   * @param uuid - A UUID string (with or without hyphens)
49   * @returns Tagged ID string like "user_01PaGUP2rbg1XDh7Z9W1CEpd"
50   */
51  export function toTaggedId(tag: string, uuid: string): string {
52    const n = uuidToBigInt(uuid)
53    return `${tag}_${VERSION}${base58Encode(n)}`
54  }