xid-ts
v1.3.1
Published
xid is a globally unique id generator thought for the web. A Typescript port of https://github.com/rs/xid.
Readme
xid-ts
Globally unique sortable id generator. A Typescript port of https://github.com/rs/xid.
The binary representation is compatible with the Mongo DB 12-byte ObjectId. The value consists of:
- a 4-byte timestamp value in seconds since the Unix epoch
- a 3-byte machine identifier
- a 2-byte value based on the process id
- a 3-byte incrementing counter, initialized to a random value
The string representation is 20 bytes, using a base32 hex variant with characters [0-9a-v]
to retain the sortable property of the id.
See the original xid project for more details.
Differences from the Go implementation
The wire format (bytes and string) is fully compatible with Go's xid, but a few
behaviors differ:
- Machine identifier: Go derives it from the platform machine id or hostname, so all processes on one machine share the same value. This port fills it with random bytes per state instead: JavaScript runtimes often host many isolates, worker threads or bundled copies of this module within one machine and even one process, and a per-state random value keeps ids from those instances from colliding. The trade-off is that ids cannot be grouped by machine.
- Process id: taken from
process.pidwhen available (without Go's containercpusetmixing), otherwise 2 random bytes. - JSON null: like Go, a zero (nil) Xid serializes to JSON
null(toJSON()returnsnull), andXid.fromValue(null)returns a zero Xid.
Requirements
A runtime with the Web Crypto API (crypto.getRandomValues): Node.js >= 20, Deno, Bun,
Cloudflare Workers, and all modern browsers.
Usage
npm i xid-ts --saveExamples
Create and Parse Xid
import { Xid } from 'xid-ts'
const defaultXid = Xid.default()
assert.equal(defaultXid.isZero(), true)
const now = Math.floor(Date.now() / 1000)
const newXid = new Xid()
assert.equal(newXid.isZero(), false)
assert.isTrue(newXid.timestamp() >= now)
const xid = Xid.parse('9m4e2mr0ui3e8a215n4g')
assert.equal(xid.isZero(), false)
console.log(xid.toBytes()) // Uint8Array(12) [77, 136, 225, 91, 96, 244, 134, 228, 40, 65, 45, 201]
assert.equal(xid.toString(), '9m4e2mr0ui3e8a215n4g')
assert.equal(xid.timestamp(), 1300816219)
assert.equal(xid.counter(), 4271561)
assert.equal(xid.equals(newXid), false)
assert.equal(xid.equals(Xid.fromValue('9m4e2mr0ui3e8a215n4g')), true)
assert.equal(xid.equals(Xid.fromValue([77, 136, 225, 91, 96, 244, 134, 228, 40, 65, 45, 201])), true)
assert.equal(xid.equals(Xid.fromValue(Buffer.from([77, 136, 225, 91, 96, 244, 134, 228, 40, 65, 45, 201]))), true)
assert.equal(xid.equals(Xid.fromValue(new Uint8Array([77, 136, 225, 91, 96, 244, 134, 228, 40, 65, 45, 201]))), true)
// generate an id with a given time in seconds, not milliseconds (the equivalent of Go's NewWithTime)
const oldXid = Xid.newWithTime(1300816219)
assert.equal(oldXid.timestamp(), 1300816219)
// compare ids byte by byte (sorting by this order == sorting by string)
const ids = [new Xid(), oldXid, xid]
ids.sort((a, b) => a.compare(b))Encode & Decode With JSON and CBOR
https://github.com/yiwen-ai/xid-ts/blob/main/src/index.test.ts
import { decode, encode } from 'cborg'
import { Xid } from 'xid-ts'
const xid = Xid.fromValue('9m4e2mr0ui3e8a215n4g')
const obj = {
id: xid,
name: 'yiwen'
}
const json = JSON.stringify(obj)
assert.equal(json, '{"id":"9m4e2mr0ui3e8a215n4g","name":"yiwen"}')
const obj1 = JSON.parse(json)
assert.isTrue(xid.equals(Xid.fromValue(obj1.id)))
const data = encode(obj)
assert.equal(Buffer.from(data).toString('hex'), 'a26269644c4d88e15b60f486e428412dc9646e616d6565796977656e')
// https://cbor.me/
// {"id": h'4D88E15B60F486E428412DC9', "name": "yiwen"}
const obj2 = decode(data)
assert.isTrue(xid.equals(Xid.fromValue(obj2.id)))Cloudflare Workers
new Xid() works in Workers as is. The default state is created lazily on the first call
(Workers forbid crypto.getRandomValues during module initialization), and its random
machine identifier keeps ids from different isolates from colliding.
import { Xid } from 'xid-ts'
export default {
async fetch(request, env, ctx): Promise<Response> {
return Response.json({ id: new Xid() })
// {"id":"d4amdocsodcmnao0bddg"}
},
} satisfies ExportedHandler<Env>An Xid passed through RPC (Durable Objects, service bindings) arrives as a plain
Uint8Array; wrap it with new Xid(bytes) or Xid.fromValue(bytes).
License
Copyright © 2023-present Yiwen AI.
yiwen-ai/xid-ts is licensed under the MIT License. See LICENSE for the full license text.
