import { randomInt } from 'crypto'; export type Snowflake = `${bigint}`; export interface SnowflakeConfig { /** Epoch time, e.g. `1577836800000` for 1 Jan 2020 00:00:00 */ epoch: number; /** 6-bit instance ID (0-63) */ instance: number; } export function validate_snowflake_conf(conf: unknown) : asserts conf is SnowflakeConfig { // todo: validate config } /** * Generates a unique 64-bit integer ID. * * Format based on Snowflake IDs (https://en.wikipedia.org/wiki/Snowflake_ID), * with the following modifications / details: * * - Uses a configurable epoch time * - Uses a 45-bit timestamp rather than 41-bit, shortening the "instance" (since this project * is never expected to operate at large scale) to 6-bits (which for now is always 0). */ export function create_snowflake_provider(conf: SnowflakeConfig) { const instance = BigInt(conf.instance) << 12n; return { uid() { const sequence = next_sequence(); const timestamp = BigInt(Date.now() - conf.epoch) & timestamp_mask; return BigInt.asUintN(64, (timestamp << 18n) | instance | sequence); }, uid_str() { return this.uid().toString(10) as Snowflake; } }; } export function is_snowflake(value: string) : value is Snowflake { return /^\d+$/.test(value) && value.length <= 20; } const iterator_mask = 0xfffn; const timestamp_mask = 0x1fffffffffffn; let iterator = BigInt(randomInt(0xfff)); function next_sequence() { const value = iterator++; iterator &= iterator_mask; return value; }