@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@ovencord/util",
4
- "version": "1.1.2",
4
+ "version": "1.1.3",
5
5
  "description": "Utilities shared across Discord.js packages",
6
6
  "scripts": {
7
7
  "test": "bun test",
@@ -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';