-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathidUtils.ts
90 lines (69 loc) · 2.34 KB
/
idUtils.ts
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
import { v4 } from 'uuid';
const decoder = new TextDecoder('utf8');
function fromUint8ArrayToUrlSafeBase64(bytes: Uint8Array) {
const base64 =
typeof Buffer === 'undefined'
? btoa(decoder.decode(bytes))
: Buffer.from(bytes).toString('base64');
// Convert to URL-safe format (see RFC 4648, sec. 5)
return (
base64
// Replace + with -
.replace(/\+/g, '-')
// Replace / with _
.replace(/\//g, '_')
// Drop '==' padding
.substring(0, 22)
);
}
function fromUrlSafeBase64toUint8Array(urlSafeBase64: string): Uint8Array {
// Convert from URL-safe format (see RFC 4648, sec. 5)
const base64 = urlSafeBase64
// Replace - with +
.replace(/-/g, '+')
// Replace _ with /
.replace(/_/g, '/');
return typeof Buffer === 'undefined'
? Uint8Array.from(atob(base64), (c) => c.charCodeAt(0))
: new Uint8Array(Buffer.from(base64, 'base64'));
}
export function isValidId(id: string) {
// For backward compatibility, first check to see if this is an older-style integer ID formerly used by Dato
if (/^\d+$/.test(id)) {
const intId = BigInt(id);
const maxDatoIntegerId = 281474976710655; // Max 6-byte/48-bit unsigned int
return intId <= maxDatoIntegerId;
}
const bytes = fromUrlSafeBase64toUint8Array(id);
// UUIDs are 16 bytes
if (bytes.length !== 16) {
return false;
}
// The variant field determines the layout of the UUID
// (see RFC 4122, sec. 4.1.1)
const variant = bytes.at(8)!;
// Variant must be the one described in RFC 4122
if ((variant & 0b11000000) !== 0b10000000) {
return false;
}
// The version number is in the most significant 4 bits
// of the time stamp (see RFC 4122, sec. 4.1.3)
const version = bytes.at(6)! >> 4;
// Version number must be 4 (randomly generated)
if (version !== 0x4) {
return false;
}
return true;
}
export function generateId() {
const bytes = v4(null, new Uint8Array(16));
// Here we unset the first bit to ensure [A-Za-f] as the first char.
//
// If we didn't do this, we would generate IDs that, once encoded
// in base64, could start with a '+' or a '/'. This makes them less
// easy to copy/paste, with bad DX.
// This choice is purely aesthetic: definitely non-mandatory!
bytes[0] = bytes[0]! & 0x7f;
const base64 = fromUint8ArrayToUrlSafeBase64(bytes);
return base64;
}