|
| 1 | +# [uuidv7-js](https://github.com/TheEdoRan/uuidv7-js) |
| 2 | + |
| 3 | +UUIDv7 generator library for Node.js. |
| 4 | + |
| 5 | +## Installation |
| 6 | + |
| 7 | +```sh |
| 8 | +npm i uuidv7-js |
| 9 | +``` |
| 10 | + |
| 11 | +## Usage |
| 12 | + |
| 13 | +```typescript |
| 14 | +import { UUIDv7, uuidv7, encodeUUIDv7, decodeUUIDv7 } from "uuidv7-js"; |
| 15 | + |
| 16 | +// Initialize a new UUIDv7 generator. |
| 17 | +// You can pass a custom encoding alphabet here. |
| 18 | +const uuidv7 = new UUIDv7(); |
| 19 | + |
| 20 | +const id = uuidv7.gen(); // 018ef3e8-90e2-7be4-b4ea-4be3bf8803b7 |
| 21 | +const encoded = uuidv7.encode(id); // CANANjseoigQthQMd1VwC |
| 22 | +const decoded = uuidv7.decode(encoded); // 018ef3e8-90e2-7be4-b4ea-4be3bf8803b7 |
| 23 | +const isValid = UUIDv7.isValid(id); // true |
| 24 | +const timestamp = UUIDv7.timestamp(id); // 1713489088738 |
| 25 | +const date = UUIDv7.date(id); // 2024-04-19T01:11:28.738Z |
| 26 | + |
| 27 | +// You can also use convenient function aliases if you don't need to use a custom alphabet. |
| 28 | +const id = uuidv7(); // 018ef3e8-90e2-7be4-b4ea-4be3bf8803b7 |
| 29 | +const encoded = encodeUUIDv7(id); // CANANjseoigQthQMd1VwC |
| 30 | +const decoded = decodeUUIDv7(encoded); // // 018ef3e8-90e2-7be4-b4ea-4be3bf8803b7 |
| 31 | +``` |
| 32 | + |
| 33 | +## Create a new instance |
| 34 | + |
| 35 | +```typescript |
| 36 | +new UUIDv7(opts?: { encodeAlphabet: string }) |
| 37 | +``` |
| 38 | + |
| 39 | +Creates a new `UUIDv7` instance. By default it uses the [Base58](https://www.cs.utexas.edu/users/moore/acl2/manuals/current/manual/index-seo.php/BITCOIN_____A2BASE58-CHARACTERS_A2) alphabet to `encode` and `decode` UUIDs, but you can pass a custom alphabet (16-64 characters). |
| 40 | + |
| 41 | +### Instance methods |
| 42 | + |
| 43 | +#### `gen` |
| 44 | + |
| 45 | +```typescript |
| 46 | +gen() => string |
| 47 | +``` |
| 48 | + |
| 49 | +Generates a new UUIDv7. |
| 50 | + |
| 51 | +#### `genMany` |
| 52 | + |
| 53 | +```typescript |
| 54 | +genMany(amount: number) => string |
| 55 | +``` |
| 56 | + |
| 57 | +Generates a custom amount of UUIDv7s. |
| 58 | + |
| 59 | +#### `encode` |
| 60 | + |
| 61 | +```typescript |
| 62 | +encode(id: string) => string |
| 63 | +``` |
| 64 | + |
| 65 | +Encodes a UUIDv7 using the alphabet passed to the constructor or the default one. |
| 66 | + |
| 67 | +#### `decode` |
| 68 | + |
| 69 | +```typescript |
| 70 | +decode(encodedId: string) => string | null |
| 71 | +``` |
| 72 | + |
| 73 | +Decodes an encoded UUIDv7 using the alphabet passed to the constuctor or the default one. If the UUIDv7 is not valid, `null` is returned. |
| 74 | + |
| 75 | +#### `decodeOrThrow` |
| 76 | + |
| 77 | +```typescript |
| 78 | +decodeOrThrow(encodedId: string) => string |
| 79 | +``` |
| 80 | + |
| 81 | +Decodes an encoded UUIDv7 using the alphabet passed to the constuctor or the default one. If the UUIDv7 is not valid, an error is thrown. |
| 82 | + |
| 83 | +### Static methods |
| 84 | + |
| 85 | +#### `UUIDv7.isValid` |
| 86 | + |
| 87 | +```typescript |
| 88 | +UUIDv7.isValid(id: string) => boolean |
| 89 | +``` |
| 90 | + |
| 91 | +Checks if the UUIDv7 is valid. |
| 92 | + |
| 93 | +#### `UUIDv7.timestamp` |
| 94 | + |
| 95 | +```typescript |
| 96 | +UUIDv7.timestamp(id: string) => number | null |
| 97 | +``` |
| 98 | + |
| 99 | +Returns the timestamp part of the UUIDv7. If the UUIDv7 is not valid, `null` is returned. |
| 100 | + |
| 101 | +#### `UUIDv7.date` |
| 102 | + |
| 103 | +```typescript |
| 104 | +UUIDv7.date(id: string) => Date | null |
| 105 | +``` |
| 106 | + |
| 107 | +Returns the timestamp part of the UUIDv7 converted to `Date`. If the UUIDv7 is not valid, `null` is returned. |
| 108 | + |
| 109 | +### Function aliases |
| 110 | + |
| 111 | +The library provides a few function aliases for convenience. You can use them without creating a new `UUIDv7` instance: |
| 112 | + |
| 113 | +| Function name | Instance method | Description | |
| 114 | +| --------------------- | --------------- | --------------------------------------------------------------------------------------------- | |
| 115 | +| `uuidv7` | `gen` | Generates a new UUIDv7. | |
| 116 | +| `encodeUUIDv7` | `encode` | Encodes a UUIDv7 with the default Base58 alphabet. | |
| 117 | +| `decodeUUIDv7` | `decode` | Decodes an encoded UUIDv7 from Base58 alphabet. Returns null if the encoded ID is invalid. | |
| 118 | +| `decodeOrThrowUUIDv7` | `decodeOrThrow` | Decodes an encoded UUIDv7 from Base58 alphabet. Throws an error if the encoded ID is invalid. | |
| 119 | + |
| 120 | +## Implementation details |
| 121 | + |
| 122 | +This library follows the [draft-ietf-uuidrev-rfc4122bis-11](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-11.html#name-uuid-version-7) draft to generate UUIDv7s: |
| 123 | + |
| 124 | +- if the current timestamp is ahead of the last stored one, it generates new [rand_a] and [rand_b] parts; |
| 125 | +- if the current timestamp is behind the last stored one, it waits for the next valid timestamp to return a UUIDv7 with newly generated random parts; |
| 126 | +- if the current timestamp is the same as the last stored one: |
| 127 | + - it uses `rand_b` and then `rand_a` as randomly seeded counters, in that order. `rand_b` is the primary counter, and `rand_a` is used as the secondary one, when `rand_b` overflows its 62 bits (rare case). When used as a counter, `rand_b` increments its previous random value by a random integer between 2^6 (64) and 2^16 - 1 (65535), and `rand_a` increments its previous random value by 1, while generating a new `rand_b` part. |
| 128 | + - if both counters overflow their bit sizes, the generation function waits for the next millisecond to return a UUIDv7 with newly generated random parts. |
| 129 | + |
| 130 | +This approach follows the [method 2](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-11.html#monotonicity_counters) of the "Monotonicity and Counters" section of the draft. It guarantees monotonicity and uniqueness per instance, and always keeps timestamp the same as `Date.now()` value. |
| 131 | + |
| 132 | +## Field and Bit Layout |
| 133 | + |
| 134 | +This is the UUIDv7 Field and Bit Layout, took from the draft linked above: |
| 135 | + |
| 136 | +``` |
| 137 | + 0 1 2 3 |
| 138 | + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
| 139 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 140 | +| unix_ts_ms | |
| 141 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 142 | +| unix_ts_ms | ver | rand_a | |
| 143 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 144 | +|var| rand_b | |
| 145 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 146 | +| rand_b | |
| 147 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 148 | +``` |
| 149 | + |
| 150 | +### Description |
| 151 | + |
| 152 | +#### unix_ts_ms |
| 153 | + |
| 154 | +48 bit big-endian unsigned number of Unix epoch timestamp in milliseconds as per [Section 6.1](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-11.html#timestamp_considerations). Occupies bits 0 through 47 (octets 0-5). |
| 155 | + |
| 156 | +#### ver |
| 157 | + |
| 158 | +The 4 bit version field as defined by [Section 4.2](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-11.html#version_field), set to 0b0111 (7). Occupies bits 48 through 51 of octet 6. |
| 159 | + |
| 160 | +#### rand_a |
| 161 | + |
| 162 | +12 bits pseudo-random data to provide uniqueness as per [Section 6.8](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-11.html#unguessability) and/or optional constructs to guarantee additional monotonicity as per [Section 6.2](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-11.html#monotonicity_counters). Occupies bits 52 through 63 (octets 6-7). |
| 163 | + |
| 164 | +#### var |
| 165 | + |
| 166 | +The 2 bit variant field as defined by [Section 4.1](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-11.html#variant_field), set to 0b10. Occupies bits 64 and 65 of octet 8. |
| 167 | + |
| 168 | +#### rand_b |
| 169 | + |
| 170 | +The final 62 bits of pseudo-random data to provide uniqueness as per [Section 6.8](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-11.html#unguessability) and/or an optional counter to guarantee additional monotonicity as per [Section 6.2](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-11.html#monotonicity_counters). Occupies bits 66 through 127 (octets 8-15). |
| 171 | + |
| 172 | +## Feedback |
| 173 | + |
| 174 | +If you found a bug in the implementation, please open a new [issue](https://github.com/TheEdoRan/uuidv7/issues/new). |
| 175 | + |
| 176 | +## Alternatives |
| 177 | + |
| 178 | +- [uuidv7](https://www.npmjs.com/package/uuidv7) by [LiosK](https://github.com/LiosK) |
| 179 | + |
| 180 | +## License |
| 181 | + |
| 182 | +This project is licensed under the MIT License. |
0 commit comments