Files
TREK/shared/src/book/book-ids.ts
T

42 lines
1.7 KiB
TypeScript

/**
* Ids for the things a book is made of.
*
* Every panel that places an element mints one, and they were four copies of
* the same line: a prefix, `Math.random().toString(36)` and a slice. Copies of
* a line drift, and this one had already drifted in its slice length. Moved
* here from the Studio (client/src/components/Studio/bookIds.ts) so the editor,
* the layouts and the server's book tools mint them the same way.
*
* `getRandomValues` rather than `Math.random`: TREK's own quickstart is plain
* http on a LAN address, where `crypto.randomUUID` is undefined but
* `getRandomValues` still works, and every Node TREK supports has it as a
* global too. A book document is shared over a websocket and merged by whoever
* saves last, so two editors minting the same id for two different elements is
* a collision that shows up as one of them losing their work. A runtime without
* Web Crypto at all (none TREK supports) still gets an id that is unique within
* its own session, from the clock and a counter.
*
* Seven characters, as before: 36^7 is about 78 billion, against a document
* that holds a few hundred elements.
*/
const ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789';
interface RandomSource {
getRandomValues?: (bytes: Uint8Array) => Uint8Array;
}
let fallbackSeq = 0;
export function bookElementId(prefix: string): string {
const c = (globalThis as { crypto?: RandomSource }).crypto;
if (c && typeof c.getRandomValues === 'function') {
const bytes = new Uint8Array(7);
c.getRandomValues(bytes);
let out = '';
for (const b of bytes) out += ALPHABET[b % ALPHABET.length];
return `${prefix}-${out}`;
}
fallbackSeq += 1;
return `${prefix}-${(Date.now() * 1000 + fallbackSeq).toString(36).slice(-7)}`;
}