npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ui7

v0.2.3

Published

Generate sortable, timestamped UUID's, based on the new-uuid-format-04 RFC draft

Downloads

2,966

Readme

ui7

npm deno.land CI includes TypeScript types node-current MIT license

A small UUIDv7 generator, targeting a proposed update to RFC 4122. v7 UUID’s are lexically sortable by their included timestamp.

import uuid, { timestamp } from "ui7";

const id = uuid();
// ==> "01836531-a895-7a2d-a70d-504ea62b40e2"

const when = new Date(timestamp(id));
// ==> 2022-09-22T12:34:56.789Z

Installation

ui7 is available on NPM and deno.land:

npm

npm install --save ui7

Node.js versions 12.0 and up are supported, and ui7 includes an ESM build. It has no runtime dependencies.

deno

import uuid from "https://deno.land/x/[email protected]/mod.ts";

Usage

Default generator

The default UUIDv7 generator is available as both a default export and a named v7 function:

import uuid from "ui7";

console.log(uuid());
// ==> "01836d65-3ec5-789f-b509-9ebd1d5ac2d3"
import { v7 } from "ui7";

console.log(v7());
// ==> "01836d65-4a33-7aeb-aceb-8bfe42901787"

Monotonic generators

Version 7 UUID’s include a millisecond-precision timestamp part and a random part. By default, ui7 will fully populate the random part with random bits. This means that ID’s generated during the same millisecond will sort in an arbitrary order:

import uuid from "ui7";

const ids = [uuid(), uuid(), uuid()];
// [
//   "01836d99-1b31-75c7-bb83-aa52c651b085",
//   "01836d99-1b31-7b6a-8010-98fe59822b40",
//   "01836d99-1b31-71c4-bed3-6d3111c2de7d"
// ]

[...ids].sort().map(id => ids.indexOf(id))
// [ 2, 0, 1 ]

If you want UUID’s generated in the same process to sort in the order they were generated, you can instead create a monotonic generator:

import { generator } from "ui7";

// `generator` uses a monotonic entropy source by default.
const uuid = generator();

const ids = [uuid(), uuid(), uuid()];
// [
//   "01836da2-8a0d-7429-812d-f23f4d3d14c1",
//   "01836da2-8a0d-742a-89a3-0442ac613d11",
//   "01836da2-8a0d-742b-85e0-476f85ee36d9"
// ]

[...ids].sort().map(id => ids.indexOf(id))
// [ 0, 1, 2 ]

This uses the 12-bit rand_a field of UUIDv7 as a counter. For the first timestamp generated in a particular millisecond, the high bit is set to 0, so that there are always at least 2¹¹ (2,048) sequential counter values available before the field overflows (and wraps back to 0).

Retrieving the timestamp

Use the timestamp function to read the timestamp field of a UUIDv7:

import { timestamp } from "ui7";

const when = timestamp("01836db9-3d80-7df2-9c5e-d66442a3cf21");
// 1663993200000

const date = new Date(when);
// 2022-09-24T04:20:00.000Z

If the input to timestamp is not a UUIDv7, it throws a ParseError exception.

Customizing generation

The default uuid function, the generator factory function, and the functions returned by generator all accept some options to customize their behavior.

Setting the time

You can pass a numeric timestamp, Date object, or custom clock function to a UUID generator or factory:

import uuid from "ui7";

const id1 = uuid(0);
// "00000000-0000-7e2f-898b-a1f288a85661"

const id2 = uuid(new Date("2009-08-23T03:58:16.491Z"));
// "01234567-89ab-772e-9998-11f7395091b3"

const id3 = uuid(() => Date.now() * 2);
// "0306db58-42bc-722f-b2a5-4b13763cc462"

// Pass an object with a `time` property to combine with other options:
const id4 = uuid({ time: 1663993200000 });
// "01836db9-3d80-74a3-8b2a-8bf75bae40d5"

Controlling the format

You can omit dashes from the generated UUID’s, or use upper-case hex characters:

import uuid from "ui7";

const id1 = uuid({ dashes: false });
// "01836db3b12d7f4e88c9fdc29019862e"

const id2 = uuid({ upper: true });
// "01836DB3-E2C7-7C84-8EB7-5CBE9205615B"

const id3 = uuid({ dashes: false, upper: true });
// "01836DB44DEF7836B69838F38A0A2A2A"

Using a different entropy source

By default, ui7 uses crypto.getRandomValues to populate the random part of the UUID (or crypto.randomFillSync on Node.js versions that don't support Web-compatible crypto).

To instead set the "random" bits to either all 0 or all 1, pass a literal 0 or 0xff as the entropy option:

import uuid from "ui7";

const id1 = uuid({ entropy: 0 });
// "018abdfe-4693-7000-8000-000000000000"

const id2 = uuid({ entropy: 0xff });
// "18abfe1-984b-7fff-bfff-ffffffffffff"

Full control is available by providing an entropy function, which must return a Uint8Array with the requested byteLength:

import uuid from "ui7";

const id1 = uuid({ entropy: (n) => new Uint8Array(n) });
// "01836db8-4831-7000-8000-000000000000"

const id2 = uuid({ entropy: (n) => new Uint8Array(n).map((_, i) => i) })
// "01836dbb-bd3d-7001-8203-040506070809"

// The entropy function receives the timestamp of the ID being generated:
const id4 = uuid({
  entropy(n, time) {
    const b = new Uint8Array(n);
    new DataView(b.buffer).setBigUint64(0, BigInt(time));
    return b;
  }
});
// "01836dbd-448c-7000-8183-6dbd448c0000"

Note that the default implementation avoids making a getRandomValues call every time entropy() is called; it makes larger, less infrequent reads to improve performance when many UUID's are being generated.

Creating a preset

In addition to providing options to the default generator, you can pass them to the generator factory function, which will return a generator that uses those options as its defaults:

import { generator } from "ui7";

const uuid = generator({
  time: () => Math.trunc(Date.now() / 1000),
  entropy: 0xff,
  upper: true,
});

const id1 = uuid();
// "0000632F-3ACC-7FFF-BFFF-FFFFFFFFFFFF"

// Options can still be set for each individual ID:
const id2 = uuid({ dashes: false, time: 0 }):
// "0000000000007FFFBFFFFFFFFFFFFFFF"

TypeScript

ui7 is written in TypeScript and ships with type definitions.

Should I use this?

UUID’s are currently defined by RFC 4122 (2005), which specifies three UUID versions in common use: 1, 4, and 5. The “UUIDv7” identifiers generated by this package conform to a proposed update to that standard, but as of this writing (September 2023) it has not yet obsoleted the 2005 edition.

UUIDv7 – a sortable UUID containing a UNIX timestamp – was first proposed in the second draft from April 2021. Originally, UUIDv7 held a variable-precision timestamp, but this was simplified to millisecond precision only in the fourth draft from March 2022. This implementation originally targeted a June 2022 draft.

There are advantages to the UUIDv7 format: they encode a timestamp, while still being collision-resistant with up to 74 bits of entropy per UUID. Their natural sortability by putting the timestamp field first is convenient for display and efficient for insertion into the B-tree structures that underlie many databases. And they're still UUID's, and can be used directly in (e.g.) a Postgres uuid column without conversion.

But you might reasonably decide to say “no” or “not yet” to UUIDv7 until it is specified in an accepted RFC, or to use a different unique identifier format like ULID.

Should I use this implementation?

ui7 is a compact, zero-dependency implementation of UUIDv7, using a cryptographic PRNG provided by its host environment. It is one of the simplest, but neither the fastest nor the slowest implementation; a few others can be found on NPM.