Files
fluxer/packages/snowflake/src/SnowflakeUtils.ts
T

100 lines
2.4 KiB
TypeScript

// SPDX-License-Identifier: AGPL-3.0-or-later
import {createSnowflake, FLUXER_EPOCH, TIMESTAMP_SHIFT} from '@fluxer/snowflake/src/Snowflake';
const FLUXER_EPOCH_NUMBER = Number(FLUXER_EPOCH);
function extractTimestampWithEpoch(snowflake: bigint, epoch: bigint): number {
return Number((snowflake >> TIMESTAMP_SHIFT) + epoch);
}
function toClampedTimestamp(timestamp: number): number {
if (timestamp <= FLUXER_EPOCH_NUMBER) {
return FLUXER_EPOCH_NUMBER;
}
return timestamp;
}
export function extractTimestamp(snowflake: string): number {
try {
return extractTimestampWithEpoch(BigInt(snowflake), FLUXER_EPOCH);
} catch (_error) {
return Number.NaN;
}
}
export function extractTimestampBigInt(snowflake: bigint): number {
return extractTimestampWithEpoch(snowflake, FLUXER_EPOCH);
}
export function fromTimestamp(timestamp: number): string {
return fromTimestampBigInt(timestamp).toString();
}
export function fromTimestampBigInt(timestamp: number): bigint {
const clampedTimestamp = toClampedTimestamp(timestamp);
if (clampedTimestamp === FLUXER_EPOCH_NUMBER) {
return 0n;
}
return createSnowflake({timestamp: clampedTimestamp});
}
export function atPreviousMillisecond(snowflake: string): string {
return fromTimestamp(extractTimestamp(snowflake) - 1);
}
export function compare(snowflake1: string | null, snowflake2: string | null): number {
if (snowflake1 === snowflake2) {
return 0;
}
if (snowflake2 == null) {
return 1;
}
if (snowflake1 == null) {
return -1;
}
if (snowflake1.length > snowflake2.length) {
return 1;
}
if (snowflake1.length < snowflake2.length) {
return -1;
}
return snowflake1 > snowflake2 ? 1 : -1;
}
export function isProbablyAValidSnowflake(value: string | null | undefined): boolean {
if (value == null) {
return false;
}
try {
const num = BigInt(value);
return num > 0n;
} catch (_error) {
return false;
}
}
export function sortBySnowflakeDesc<
T extends {
id: string;
},
>(items: ReadonlyArray<T>): Array<T> {
return [...items].sort((a, b) => compare(b.id, a.id));
}
export function age(snowflake: string): number {
const timestamp = extractTimestamp(snowflake);
if (Number.isNaN(timestamp)) {
return 0;
}
return Date.now() - timestamp;
}
export function extractTimestampFromSnowflakeAsDate(snowflake: string): Date {
const timestamp = extractTimestamp(snowflake);
if (Number.isNaN(timestamp)) {
return new Date();
}
return new Date(timestamp);
}