UNPKG

@thi.ng/ksuid

Version:

Configurable K-sortable unique IDs, ULIDs, binary & base-N encoded, 32/48/64bit time resolutions

64 lines (63 loc) 1.75 kB
import { BASE62 } from "@thi.ng/base-n/62"; import { assert } from "@thi.ng/errors/assert"; import { randomBytes, randomBytesFrom } from "@thi.ng/random/random-bytes"; import { padLeft } from "@thi.ng/strings/pad-left"; class AKSUID { constructor(epochSize, opts) { this.epochSize = epochSize; this.base = opts.base || BASE62; this.rnd = opts.rnd; this.epoch = opts.epoch; this.size = this.epochSize + opts.bytes; this.encodedSize = this.base.size(2 ** (this.size * 8) - 1); this.pad = padLeft(this.encodedSize, this.base.base[0]); this.tmp = new Uint8Array(this.size); } size; encodedSize; base; epoch; tmp; rnd; pad; next() { return this.format(this.nextBinary(this.tmp)); } nextBinary(buf) { return this.fromEpochBinary(void 0, buf); } timeOnly(epoch) { return this.format( this.timeOnlyBinary(epoch, this.tmp.fill(0, this.epochSize)) ); } fromEpoch(epoch) { return this.format(this.fromEpochBinary(epoch, this.tmp)); } fromEpochBinary(epoch, buf) { buf = this.timeOnlyBinary(epoch, buf); return this.rnd ? randomBytesFrom(this.rnd, buf, this.epochSize) : randomBytes(buf, this.epochSize); } format(buf) { this.ensureSize(buf); return this.pad(this.base.encodeBytes(buf)); } ensureSize(buf) { assert( buf.length == this.size, `illegal KSUID size, expected ${this.size} bytes` ); return buf; } ensureTime(t, max) { assert(t >= 0, "configured base epoch must be in the past"); max && assert(t <= max, `given epoch is out of range ([0...${max}])`); return t; } u32(buf, i = 0) { return (buf[i] << 24 | buf[i + 1] << 16 | buf[i + 2] << 8 | buf[i + 3]) >>> 0; } } export { AKSUID };