mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-20 09:45:21 +03:00
feat: Sync to OSS repo
This commit is contained in:
80
packages/core/src/id.ts
Normal file
80
packages/core/src/id.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { randomBytes } from 'crypto';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
export namespace Identifier {
|
||||
export const prefixes = {
|
||||
user: 'usr',
|
||||
linkedAccount: 'lac',
|
||||
team: 'tem',
|
||||
teamMember: 'mem',
|
||||
verification: 'ver',
|
||||
userFingerprint: 'ufp',
|
||||
pairingCode: 'pai',
|
||||
machine: 'mch',
|
||||
accessToken: 'pat',
|
||||
game: 'gam',
|
||||
userLibrary: 'ulb',
|
||||
gameDepot: 'gdp',
|
||||
gameDownload: 'gdl'
|
||||
} as const;
|
||||
|
||||
export function schema(prefix: keyof typeof prefixes) {
|
||||
return z.string().startsWith(prefixes[prefix]);
|
||||
}
|
||||
|
||||
const LENGTH = 26;
|
||||
|
||||
let lastTimestamp = 0;
|
||||
let counter = 0;
|
||||
|
||||
export function ascending(prefix: keyof typeof prefixes, given?: string) {
|
||||
return generateID(prefix, false, given);
|
||||
}
|
||||
|
||||
export function descending(prefix: keyof typeof prefixes, given?: string) {
|
||||
return generateID(prefix, true, given);
|
||||
}
|
||||
|
||||
function generateID(prefix: keyof typeof prefixes, descending: boolean, given?: string): string {
|
||||
if (!given) {
|
||||
return generateNewID(prefix, descending);
|
||||
}
|
||||
|
||||
if (!given.startsWith(prefixes[prefix])) {
|
||||
throw new Error(`ID ${given} does not start with ${prefixes[prefix]}`);
|
||||
}
|
||||
return given;
|
||||
}
|
||||
|
||||
function randomBase62(length: number): string {
|
||||
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
let result = '';
|
||||
const bytes = randomBytes(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += chars[bytes[i]! % 62];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function generateNewID(prefix: keyof typeof prefixes, descending: boolean): string {
|
||||
const currentTimestamp = Date.now();
|
||||
|
||||
if (currentTimestamp !== lastTimestamp) {
|
||||
lastTimestamp = currentTimestamp;
|
||||
counter = 0;
|
||||
}
|
||||
counter++;
|
||||
|
||||
let now = BigInt(currentTimestamp) * BigInt(0x1000) + BigInt(counter);
|
||||
|
||||
now = descending ? ~now : now;
|
||||
|
||||
const timeBytes = Buffer.alloc(6);
|
||||
for (let i = 0; i < 6; i++) {
|
||||
timeBytes[i] = Number((now >> BigInt(40 - 8 * i)) & BigInt(0xff));
|
||||
}
|
||||
|
||||
return prefixes[prefix] + '_' + timeBytes.toString('hex') + randomBase62(LENGTH - 12);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user