@ovencord/util 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/Snowflake.ts +201 -0
- package/src/index.ts +2 -0
package/package.json
CHANGED
package/src/Snowflake.ts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for {@link Snowflake.generate}
|
|
3
|
+
*/
|
|
4
|
+
export interface SnowflakeGenerateOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Timestamp or date of the snowflake to generate
|
|
7
|
+
* @default Date.now()
|
|
8
|
+
*/
|
|
9
|
+
timestamp?: number | bigint | Date;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The increment to use
|
|
13
|
+
* @default 0n
|
|
14
|
+
* @remark keep in mind that this bigint is auto-incremented between generate calls
|
|
15
|
+
*/
|
|
16
|
+
increment?: bigint;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The worker ID to use, will be truncated to 5 bits (0-31)
|
|
20
|
+
* @default 0n
|
|
21
|
+
*/
|
|
22
|
+
workerId?: bigint;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The process ID to use, will be truncated to 5 bits (0-31)
|
|
26
|
+
* @default 1n
|
|
27
|
+
*/
|
|
28
|
+
processId?: bigint;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Object returned by {@link Snowflake.deconstruct}
|
|
33
|
+
*/
|
|
34
|
+
export interface DeconstructedSnowflake {
|
|
35
|
+
/** The id in BigInt form */
|
|
36
|
+
id: bigint;
|
|
37
|
+
/** The timestamp stored in the snowflake */
|
|
38
|
+
timestamp: bigint;
|
|
39
|
+
/** The worker id stored in the snowflake */
|
|
40
|
+
workerId: bigint;
|
|
41
|
+
/** The process id stored in the snowflake */
|
|
42
|
+
processId: bigint;
|
|
43
|
+
/** The increment stored in the snowflake */
|
|
44
|
+
increment: bigint;
|
|
45
|
+
/** The epoch to use in the snowflake */
|
|
46
|
+
epoch: bigint;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const MaximumWorkerId = 0b11111n;
|
|
50
|
+
const MaximumProcessId = 0b11111n;
|
|
51
|
+
const MaximumIncrement = 0b111111111111n;
|
|
52
|
+
const TimestampFieldDivisor = 2 ** 22;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* A class for generating and deconstructing snowflakes.
|
|
56
|
+
*
|
|
57
|
+
* A snowflake is a 64-bit unsigned integer with 4 fields that have a fixed epoch value.
|
|
58
|
+
*
|
|
59
|
+
* ```
|
|
60
|
+
* 64 22 17 12 0
|
|
61
|
+
* 000000111011000111100001101001000101000000 00001 00000 000000000000
|
|
62
|
+
* number of ms since epoch worker pid increment
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export class Snowflake {
|
|
66
|
+
/** Alias for {@link deconstruct} */
|
|
67
|
+
public readonly decode = this.deconstruct.bind(this);
|
|
68
|
+
|
|
69
|
+
#epoch: bigint;
|
|
70
|
+
#epochNumber: number;
|
|
71
|
+
#increment = 0n;
|
|
72
|
+
#processId = 1n;
|
|
73
|
+
#workerId = 0n;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param epoch the epoch to use
|
|
77
|
+
*/
|
|
78
|
+
constructor(epoch: number | bigint | Date) {
|
|
79
|
+
this.#epoch = BigInt(epoch instanceof Date ? epoch.getTime() : epoch);
|
|
80
|
+
this.#epochNumber = Number(this.#epoch);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The epoch for this snowflake, as a bigint */
|
|
84
|
+
get epoch(): bigint {
|
|
85
|
+
return this.#epoch;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The epoch for this snowflake, as a number */
|
|
89
|
+
get epochNumber(): number {
|
|
90
|
+
return this.#epochNumber;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Gets the configured process ID */
|
|
94
|
+
get processId(): bigint {
|
|
95
|
+
return this.#processId;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Sets the process ID (masked with `0b11111n`) */
|
|
99
|
+
set processId(value: number | bigint) {
|
|
100
|
+
this.#processId = BigInt(value) & MaximumProcessId;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Gets the configured worker ID */
|
|
104
|
+
get workerId(): bigint {
|
|
105
|
+
return this.#workerId;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Sets the worker ID (masked with `0b11111n`) */
|
|
109
|
+
set workerId(value: number | bigint) {
|
|
110
|
+
this.#workerId = BigInt(value) & MaximumWorkerId;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Generates a snowflake given an epoch and optionally a timestamp.
|
|
115
|
+
*
|
|
116
|
+
* @param options - Options to pass into the generator
|
|
117
|
+
* @returns A unique snowflake
|
|
118
|
+
*/
|
|
119
|
+
generate({
|
|
120
|
+
increment,
|
|
121
|
+
timestamp = Date.now(),
|
|
122
|
+
workerId = this.#workerId,
|
|
123
|
+
processId = this.#processId,
|
|
124
|
+
}: SnowflakeGenerateOptions = {}): bigint {
|
|
125
|
+
if (timestamp instanceof Date) timestamp = BigInt(timestamp.getTime());
|
|
126
|
+
else if (typeof timestamp === 'number') timestamp = BigInt(timestamp);
|
|
127
|
+
else if (typeof timestamp !== 'bigint') {
|
|
128
|
+
throw new TypeError(`"timestamp" argument must be a number, bigint, or Date (received ${typeof timestamp})`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (typeof increment !== 'bigint') {
|
|
132
|
+
increment = this.#increment;
|
|
133
|
+
this.#increment = (increment + 1n) & MaximumIncrement;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
((timestamp - this.#epoch) << 22n) |
|
|
138
|
+
((workerId & MaximumWorkerId) << 17n) |
|
|
139
|
+
((processId & MaximumProcessId) << 12n) |
|
|
140
|
+
(increment & MaximumIncrement)
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Deconstructs a snowflake given a snowflake ID.
|
|
146
|
+
*
|
|
147
|
+
* @param id - The snowflake to deconstruct
|
|
148
|
+
* @returns A deconstructed snowflake
|
|
149
|
+
*/
|
|
150
|
+
deconstruct(id: string | bigint): DeconstructedSnowflake {
|
|
151
|
+
const bigIntId = BigInt(id);
|
|
152
|
+
const epoch = this.#epoch;
|
|
153
|
+
return {
|
|
154
|
+
id: bigIntId,
|
|
155
|
+
timestamp: (bigIntId >> 22n) + epoch,
|
|
156
|
+
workerId: (bigIntId >> 17n) & MaximumWorkerId,
|
|
157
|
+
processId: (bigIntId >> 12n) & MaximumProcessId,
|
|
158
|
+
increment: bigIntId & MaximumIncrement,
|
|
159
|
+
epoch,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Retrieves the timestamp field's value from a snowflake.
|
|
165
|
+
*
|
|
166
|
+
* @param id - The snowflake to get the timestamp value from
|
|
167
|
+
* @returns The UNIX timestamp that is stored in `id`
|
|
168
|
+
*/
|
|
169
|
+
timestampFrom(id: string | bigint): number {
|
|
170
|
+
return Math.floor(Number(id) / TimestampFieldDivisor) + this.#epochNumber;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Compares two snowflakes.
|
|
175
|
+
*
|
|
176
|
+
* @param a - The first snowflake to compare
|
|
177
|
+
* @param b - The second snowflake to compare
|
|
178
|
+
* @returns `-1` if `a` is older than `b`, `0` if equal, `1` if `a` is newer than `b`
|
|
179
|
+
*/
|
|
180
|
+
static compare(a: string | bigint, b: string | bigint): -1 | 0 | 1 {
|
|
181
|
+
const typeA = typeof a;
|
|
182
|
+
if (typeA === typeof b) {
|
|
183
|
+
if (typeA === 'string') {
|
|
184
|
+
return a === b ? 0 : (a as string).length < (b as string).length ? -1 : (a as string).length > (b as string).length ? 1 : a < b ? -1 : 1;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return a === b ? 0 : (a as bigint) < (b as bigint) ? -1 : 1;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const bigA = BigInt(a);
|
|
191
|
+
const bigB = BigInt(b);
|
|
192
|
+
return bigA === bigB ? 0 : bigA < bigB ? -1 : 1;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* A {@link Snowflake} instance using Discord's epoch (2015-01-01T00:00:00.000Z).
|
|
198
|
+
*
|
|
199
|
+
* @see {@link https://discord.com/developers/docs/reference#snowflakes}
|
|
200
|
+
*/
|
|
201
|
+
export const DiscordSnowflake = new Snowflake(1420070400000n);
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export type * from './types.js';
|
|
2
|
+
export { DiscordSnowflake, Snowflake as SnowflakeClass } from './Snowflake.js';
|
|
3
|
+
export type { DeconstructedSnowflake, SnowflakeGenerateOptions } from './Snowflake.js';
|
|
2
4
|
export * from './functions/index.js';
|
|
3
5
|
export * from './encodables.js';
|
|
4
6
|
export type * from './RawFile.js';
|