NestJS 11 + Fastify 统一错误体与 trace_id,/health 真实探活 DB/Redis,OpenAPI 3.1 契约含 20 个错误码;shared 包提供错误码、权限点、ULID、分页与通用 schema;worker 完成队列进程骨架。
57 lines
1.8 KiB
TypeScript
57 lines
1.8 KiB
TypeScript
/**
|
||
* ULID 生成与解析(Crockford Base32,26 字符 = 48bit 时间 + 80bit 随机)
|
||
* 主键统一由应用侧生成 char(26) ULID(工程规范 05)。
|
||
*/
|
||
import { ULID_LENGTH } from './constants.js';
|
||
|
||
const ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
||
const ENCODING_LEN = 32;
|
||
|
||
export const ULID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/;
|
||
|
||
function randomBytes(length: number): Uint8Array {
|
||
const bytes = new Uint8Array(length);
|
||
globalThis.crypto.getRandomValues(bytes);
|
||
return bytes;
|
||
}
|
||
|
||
/** 生成 ULID;同一毫秒内依靠 80bit 随机数避免碰撞 */
|
||
export function ulid(timestamp: number = Date.now()): string {
|
||
if (!Number.isInteger(timestamp) || timestamp < 0 || timestamp > 0xffffffffffff) {
|
||
throw new RangeError('ULID 时间戳必须是 48bit 非负整数');
|
||
}
|
||
let time = timestamp;
|
||
const timeChars: string[] = new Array<string>(10);
|
||
for (let i = 9; i >= 0; i -= 1) {
|
||
timeChars[i] = ENCODING[time % ENCODING_LEN] ?? '0';
|
||
time = Math.floor(time / ENCODING_LEN);
|
||
}
|
||
const randomChars = Array.from(randomBytes(16), (byte) => ENCODING[byte & 31] ?? '0');
|
||
return `${timeChars.join('')}${randomChars.join('')}`;
|
||
}
|
||
|
||
export function isUlid(value: string): boolean {
|
||
return value.length === ULID_LENGTH && ULID_PATTERN.test(value);
|
||
}
|
||
|
||
/** 解析 ULID 前 10 位得到毫秒时间戳 */
|
||
export function ulidToTimestamp(id: string): number {
|
||
if (!isUlid(id)) {
|
||
throw new TypeError(`不是合法 ULID:${id}`);
|
||
}
|
||
let timestamp = 0;
|
||
for (let i = 0; i < 10; i += 1) {
|
||
const char = id[i] ?? '';
|
||
const value = ENCODING.indexOf(char);
|
||
if (value < 0) {
|
||
throw new TypeError(`ULID 含非法字符:${char}`);
|
||
}
|
||
timestamp = timestamp * ENCODING_LEN + value;
|
||
}
|
||
return timestamp;
|
||
}
|
||
|
||
export function ulidToDate(id: string): Date {
|
||
return new Date(ulidToTimestamp(id));
|
||
}
|