Last active
October 24, 2025 10:07
-
-
Save robinpokorny/dec8623c91422292a6a8978d60450d80 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const generator = new UUIDv7Generator(); | |
| // Single UUID | |
| console.log(generator.generate()); | |
| // Batch generation | |
| const batch = generator.generateBatch(1000); | |
| console.log(`Generated ${batch.length} UUIDs`); | |
| console.log('First:', batch[0]); | |
| console.log('Last:', batch[batch.length - 1]); | |
| // Error handling | |
| try { | |
| generator.generateBatch(200000); // exceeds limit | |
| } catch (e) { | |
| console.error(e.message); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class UUIDv7Generator { | |
| // constants | |
| #MAX_COUNTER = 0xFFFF_FFFF_FFFF; | |
| #BATCH_LIMIT = 10_000; | |
| // timestamp state | |
| #lastTimestamp = -1; | |
| #timestampHex1 = ``; // 8 chars | |
| #timestampHex2 = ``; // 4 chars | |
| // random hex state | |
| #randomHex1 = ``; // 4 chars | |
| #randomHex2 = ``; // 4 chars | |
| // counter state | |
| #counter = 0; | |
| // random pool state | |
| #randomPool = new Uint8Array(1024); | |
| #poolView; | |
| #poolIndex = 0; | |
| constructor() { | |
| this.#poolView = new DataView(this.#randomPool.buffer); | |
| crypto.getRandomValues(this.#randomPool); | |
| } | |
| #ensurePoolBytes(count) { | |
| if (this.#poolIndex + count > this.#randomPool.length) { | |
| crypto.getRandomValues(this.#randomPool); | |
| this.#poolIndex = 0; | |
| } | |
| } | |
| #readUint32() { | |
| this.#ensurePoolBytes(4); | |
| const value = this.#poolView.getUint32(this.#poolIndex); | |
| this.#poolIndex += 4; | |
| return value; | |
| } | |
| #updateTimestamp(timestamp) { | |
| this.#lastTimestamp = timestamp; | |
| const hex = timestamp.toString(16).padStart(12, `0`); | |
| this.#timestampHex1 = hex.slice(0, 8); | |
| this.#timestampHex2 = hex.slice(8, 12); | |
| } | |
| #resetRandomState() { | |
| this.#ensurePoolBytes(10); | |
| // read uint32 and apply version/variant | |
| let randomValue = this.#poolView.getUint32(this.#poolIndex); | |
| randomValue = (randomValue & 0x0fffffff) | 0x70000000; // version 7 | |
| randomValue = (randomValue & 0xffff3fff) | 0x00008000; // variant | |
| const hex = randomValue.toString(16).padStart(8, `0`); | |
| this.#randomHex1 = hex.slice(0, 4); | |
| this.#randomHex2 = hex.slice(4, 8); | |
| this.#poolIndex += 4; | |
| // read counter | |
| this.#counter = this.#poolView.getUint16(this.#poolIndex) * 0x100000000 + | |
| this.#poolView.getUint32(this.#poolIndex + 2); | |
| this.#poolIndex += 6; | |
| } | |
| generate() { | |
| const timestamp = Date.now(); | |
| // new millisecond - reset random state | |
| if (timestamp > this.#lastTimestamp) { | |
| this.#updateTimestamp(timestamp); | |
| this.#resetRandomState(); | |
| } | |
| // same millisecond or clock regression - increment counter | |
| else { | |
| const randomIncrement = this.#readUint32(); | |
| this.#counter += Math.max(randomIncrement, 2); | |
| if (this.#counter >= this.#MAX_COUNTER) { | |
| this.#updateTimestamp(timestamp + 1); | |
| this.#resetRandomState(); | |
| } | |
| } | |
| // counter hex (only thing that changes within a millisecond) | |
| const counterHex = this.#counter.toString(16).padStart(12, `0`); | |
| // format without any slices | |
| return `${this.#timestampHex1}-${this.#timestampHex2}-${this.#randomHex1}-${this.#randomHex2}-${counterHex}`; | |
| } | |
| generateBatch(count) { | |
| if (count <= 0) { | |
| throw new Error(`Batch count must be positive`); | |
| } | |
| if (count > this.#BATCH_LIMIT) { | |
| throw new Error(`Requested batch count ${count} exceeds limit of ${this.#BATCH_LIMIT}`); | |
| } | |
| const uuids = new Array(count); | |
| for (let i = 0; i < count; i++) { | |
| uuids[i] = this.generate(); | |
| } | |
| return uuids; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment