-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUUIDv7.ts
More file actions
183 lines (152 loc) · 6.05 KB
/
Copy pathUUIDv7.ts
File metadata and controls
183 lines (152 loc) · 6.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/**
* Copyright 2026 Angus.Fenying <fenying@litert.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as eL from './Errors.js';
import { uuidToString } from './Utils.js';
import { ETimeReversedStrategy } from './Constants.js';
const MAX_TIMESTAMP = 0xFFFF_FFFF_FFFF; // Maximum timestamp for UUIDv7 (48 bits)
const RAND_A_CAP = 4096;
export interface IUuid7GeneratorOptions {
/**
* The strategy for handling time reversal events.
*
* @default ETimeReversedStrategy.THROW_ERROR
*/
onTimeReversed?: ETimeReversedStrategy;
}
/**
* The default strategy for the time reversal event,
* which throws an `E_TIME_REVERSED` error.
*/
export const UUID7_DEFAULT_TIME_REVERSAL_STRATEGY = ETimeReversedStrategy.THROW_ERROR;
/**
* The class for generating UUID in version 7.
*/
export class Uuid7Generator {
private _prevTime: number = 0;
private readonly _onTimeReversedStrategy: ETimeReversedStrategy;
public constructor(opts: IUuid7GeneratorOptions = {}) {
this._onTimeReversedStrategy = opts.onTimeReversed ?? UUID7_DEFAULT_TIME_REVERSAL_STRATEGY;
}
/**
* Generate a UUIDv7 string, which is based on the current timestamp and random values.
*
* @returns A UUIDv7 string.
*
* @example
* ```ts
* const generator = new Uuid7Generator();
* const uuid = generator.generate();
* console.log(uuid);
* ```
*/
public generate(): string {
return uuidToString(this.generateAsBuffer());
}
/**
* Generate a binary UUIDv7 as a 16-byte buffer, which is based on the current timestamp and random values.
*
* @returns A Buffer containing the UUIDv7.
*
* @example
* ```ts
* const generator = new Uuid7Generator();
* const uuidBuffer = generator.generateAsBuffer();
* console.log(uuidBuffer);
* ```
*/
public generateAsBuffer(): Buffer {
let now = Date.now();
if (this._prevTime > now) {
switch (this._onTimeReversedStrategy) {
case ETimeReversedStrategy.THROW_ERROR:
throw new eL.E_TIME_REVERSED({
previous: this._prevTime,
current: now
});
case ETimeReversedStrategy.USE_REVERSED_TIME:
// do nothing, because `timeOffset` is the reversed time.
break;
case ETimeReversedStrategy.USE_PREVIOUS_TIME:
now = this._prevTime;
break;
}
}
return Uuid7Generator.generateBuffer(this._prevTime = now);
}
/**
* Generate a UUIDv7 string based on the provided timestamp and random value.
*
* > This method is implemented in pure JavaScript.
*
* @param timestamp The 48-bit timestamp in milliseconds since the Unix epoch. If not provided, the current time will be used.
* @param randA A 12-bit random value to ensure uniqueness. If not provided, a random value will be generated.
*
* @returns A UUIDv7 string based on the provided timestamp and random value.
*
* @throws {RangeError} If the timestamp is not a non-negative integer or exceeds the maximum allowed value.
*
* @example
* ```ts
* const uuid = Uuid7Generator.generate(Date.parse('2024-01-01T00:00:00Z'), 123456);
* console.log(uuid);
* ```
*/
public static generate(
timestamp: number = Date.now(),
randA: number = Math.floor(Math.random() * RAND_A_CAP),
): string {
return uuidToString(Uuid7Generator.generateBuffer(timestamp, randA));
}
/**
* Generate a binary UUIDv7 based on the provided timestamp and random value, and return it as a Buffer.
*
* > This method is implemented in pure JavaScript.
*
* @param timestamp The 48-bit timestamp in milliseconds since the Unix epoch. If not provided, the current time will be used.
* @param randA A 12-bit random value to ensure uniqueness. If not provided, a random value will be generated.
*
* @returns A Buffer containing the UUIDv7 based on the provided timestamp and random value.
*
* @throws {RangeError} If the timestamp is not a non-negative integer or exceeds the maximum allowed value.
*
* @example
* ```ts
* const uuid = Uuid7Generator.generateBuffer(Date.parse('2024-01-01T00:00:00Z'), 123456);
* console.log(uuid);
* ```
*/
public static generateBuffer(
timestamp: number = Date.now(),
randA: number = Math.floor(Math.random() * RAND_A_CAP),
): Buffer {
if (!Number.isSafeInteger(timestamp) || timestamp < 0 || timestamp > MAX_TIMESTAMP) {
throw new RangeError(`Timestamp must be an integer within the range of 0 to ${MAX_TIMESTAMP}.`);
}
if (!Number.isSafeInteger(randA) || randA < 0 || randA >= RAND_A_CAP) {
throw new RangeError(`randA must be an integer within the range of 0 to ${RAND_A_CAP - 1}.`);
}
const buffer = Buffer.allocUnsafe(16);
// Write the timestamp (48 bits)
buffer.writeUIntBE(timestamp, 0, 6);
// Write the version (4 bits) and the randA (12 bits)
const versionAndRandA = (7 << 12) | (randA & 0x0FFF);
buffer.writeUInt16BE(versionAndRandA, 6);
// Write the variant (2 bits, 0b10) and random bits (62 bits)
buffer.writeUInt32BE(((2 << 30) | Math.floor(Math.random() * 0x4000_0000)) >>> 0, 8);
buffer.writeUInt32BE(Math.floor(Math.random() * 0x1_0000_0000), 12);
return buffer;
}
}