/** * AT Protocol TID (timestamp identifier) generation. * * We mint record keys client-side rather than letting the PDS assign them so * that writes are idempotent. A create with a server-assigned rkey turns every * replayed request into a brand new record — that is how a single gallery ended * up with the same photo at position 2 three times, ~20s apart, from one * `await`. With the rkey fixed up front a replay collides with the record it * already wrote and the PDS rejects it instead of duplicating it. * * A TID is 13 chars of base32-sortable encoding a 64-bit integer: one * always-zero high bit, 53 bits of microseconds since the UNIX epoch, then a * 10-bit clock id that keeps concurrent writers on different devices apart. */ const S32 = "234567abcdefghijklmnopqrstuvwxyz"; /** Random per-session, per the TID spec's clock identifier. */ const CLOCK_ID = BigInt(Math.floor(Math.random() * 1024)); let lastMicros = 0n; /** Encode microseconds + clock id as a 13-char base32-sortable TID. */ export function encodeTid(micros: bigint, clockId: bigint): string { let n = (micros << 10n) | (clockId & 1023n); let out = ""; for (let i = 0; i < 13; i++) { out = S32[Number(n & 31n)] + out; n >>= 5n; } return out; } /** * Mint the next TID. Strictly increasing, so a batch of writes minted in a * loop sorts in call order. */ export function nextTid(): string { // Date.now() is millisecond-granular and a whole gallery's worth of rkeys is // minted inside one tick, so step forward by hand rather than handing back // the same rkey 30 times. const micros = BigInt(Date.now()) * 1000n; lastMicros = micros > lastMicros ? micros : lastMicros + 1n; return encodeTid(lastMicros, CLOCK_ID); }