{{pkg.description}}
Idea based on segmentio/ksuid, though the added flexibility in terms of configuration & implementation also enables the creation of ULIDs:
Feature | KSUID default | ULID default |
---|---|---|
Configurable bit size | 160 bits | 128 bits |
Base-N encoding scheme | base62(1) | base32 (Crockford) |
Timestamp resolution | seconds (32 bits) | milliseconds (48 bits) |
milliseconds (64 bits) | ||
Epoch start time offset | approx. 2020-09-13(2) | none |
Time-only base ID generation | ✅ | ✅ |
ID parsing / decomposition | ✅ | ✅ |
Configurable RNG source(3) | ✅ | ✅ |
- (1) See @thi.ng/base-n for alternatives
- (2) With the default offset, the max. supported date for
KSUID32
is 2156-10-20T18:54:55Z - (3) Default:
window.crypto
,Math.random
as fallback
IDs generated w/ this package are composed of a 32, 48 or 64 bit Unix epochs and
N additional bits of a random payload (from a configurable source). By default
all timestamps are shifted to free up bits for the future. IDs can be generated
as byte arrays or base-N encoded strings. For the latter, the JS runtime MUST
support
BigInt
.
Since v3.1.0 a small CLI for ad-hoc KSUID generation is included. Currently only KSUID32 is supported and IDs are always based on the current time:
npx @thi.ng/ksuid
# 0dwncLZE8byaQdccncWDmsNmlYt
# optionally provide number of random bytes to be used (default: 16)
npx @thi.ng/ksuid 8
# 01ogp9KDpWlQ0pXCY
{{meta.status}}
Since v3.0.0 all
epoch
time-shift config values are to be given in milliseconds. This change is
unifying this behavior and is only a breaking change if using KSUID32
and
specifying custom epoch
offsets (using defaults is not impacted).
Previously, KSUID32
used an offset given in seconds, whereas the other
implementations already used milliseconds.
{{repo.supportPackages}}
{{repo.relatedPackages}}
{{meta.blogPosts}}
{{pkg.install}}
{{pkg.size}}
{{pkg.deps}}
{{repo.examples}}
{{pkg.docs}}
import { defKSUID32, defKSUID64, defULID } from "@thi.ng/ksuid";
// init 32bit epoch (resolution: seconds) w/ defaults
const id = defKSUID32();
// init 64bit epoch (resolution: milliseconds), same API
const id = defKSUID64();
// init 48bit epoch (resolution: milliseconds), same API
const id = defULID();
id.next();
// '05XCWbXa3akRqLDBUw4ogCVKGkd'
const a = id.nextBinary()
// Uint8Array(20) [
// 0, 160, 48, 77, 101, 251,
// 244, 17, 155, 97, 24, 101,
// 70, 71, 207, 23, 32, 21,
// 244, 116
// ]
// format a binary KSUID
id.format(a);
// '05XCZ32AaDZfZt0SWE2C22o6cqK'
id.parse("05XCZ32AaDZfZt0SWE2C22o6cqK")
// {
// epoch: 1610498125000,
// id: Uint8Array(16) [
// 101, 251, 244, 17, 155, 97,
// 24, 101, 70, 71, 207, 23,
// 32, 21, 244, 116
// ]
// }
new Date(1610498125000).toISOString()
// '2021-01-13T00:35:25.000Z'
Creating custom IDs:
import { BASE36 } from "@thi.ng/base-n";
// using base36, no time shift, 64bit random part
const id36 = defKSUID32({ base: BASE36, epoch: 0, bytes: 8 });
id36.next();
// '2VOUKH4K59AG0RXR4XH'
Benchmarks can be run via yarn bench
. All timings in milliseconds (test
config: Node v20.4.0, MBA M1 2021, 16GB). The benchmark collects N KSUIDs w/
different configs in an array, with each case being run 100 times.
Title | Iter | Size | Total | Mean | Median | Min | Max | Q1 | Q3 | SD% |
---|---|---|---|---|---|---|---|---|---|---|
b62, 128bit, n=10000 | 100 | 1 | 2158.68 | 21.59 | 21.57 | 19.91 | 25.91 | 20.42 | 21.87 | 6.26 |
b62, 64bit, n=10000 | 100 | 1 | 1200.40 | 12.00 | 11.95 | 11.27 | 14.66 | 11.82 | 12.10 | 3.99 |