Suite of AT Protocol TypeScript libraries built on web standards
1import { s32decode, s32encode } from "./util.ts";
2
3const TID_LEN = 13;
4
5let lastTimestamp = 0;
6let timestampCount = 0;
7let clockid: number | null = null;
8
9function dedash(str: string): string {
10 return str.replaceAll("-", "");
11}
12
13export class TID {
14 str: string;
15
16 constructor(str: string) {
17 const noDashes = dedash(str);
18 if (noDashes.length !== TID_LEN) {
19 throw new Error(`Poorly formatted TID: ${noDashes.length} length`);
20 }
21 this.str = noDashes;
22 }
23
24 static next(prev?: TID): TID {
25 const time = Math.max(Date.now(), lastTimestamp);
26 if (time === lastTimestamp) {
27 timestampCount++;
28 }
29 lastTimestamp = time;
30 const timestamp = time * 1000 + timestampCount;
31 if (clockid === null) {
32 clockid = Math.floor(Math.random() * 32);
33 }
34 const tid = TID.fromTime(timestamp, clockid);
35 if (!prev || tid.newerThan(prev)) {
36 return tid;
37 }
38 return TID.fromTime(prev.timestamp() + 1, clockid);
39 }
40
41 static nextStr(prev?: string): string {
42 return TID.next(prev ? new TID(prev) : undefined).toString();
43 }
44
45 static fromTime(timestamp: number, clockid: number): TID {
46 const str = `${s32encode(timestamp)}${s32encode(clockid).padStart(2, "2")}`;
47 return new TID(str);
48 }
49
50 static fromStr(str: string): TID {
51 return new TID(str);
52 }
53
54 static oldestFirst(a: TID, b: TID): number {
55 return a.compareTo(b);
56 }
57
58 static newestFirst(a: TID, b: TID): number {
59 return b.compareTo(a);
60 }
61
62 static is(str: string): boolean {
63 return dedash(str).length === TID_LEN;
64 }
65
66 timestamp(): number {
67 return s32decode(this.str.slice(0, 11));
68 }
69
70 clockid(): number {
71 return s32decode(this.str.slice(11, 13));
72 }
73
74 formatted(): string {
75 const str = this.toString();
76 return `${str.slice(0, 4)}-${str.slice(4, 7)}-${
77 str.slice(
78 7,
79 11,
80 )
81 }-${str.slice(11, 13)}`;
82 }
83
84 toString(): string {
85 return this.str;
86 }
87
88 compareTo(other: TID): number {
89 if (this.str > other.str) return 1;
90 if (this.str < other.str) return -1;
91 return 0;
92 }
93
94 equals(other: TID): boolean {
95 return this.str === other.str;
96 }
97
98 newerThan(other: TID): boolean {
99 return this.compareTo(other) > 0;
100 }
101
102 olderThan(other: TID): boolean {
103 return this.compareTo(other) < 0;
104 }
105}