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 🙏

© 2026 – Pkg Stats / Ryan Hefner

id758

v1.1.0

Published

Prefixed, sortable public ids: a UUIDv7 at rest, a fixed-width base58 skin on the wire. Zero dependencies, zero imports, runs anywhere Web Crypto does.

Downloads

474

Readme

id758

Prefixed, sortable public ids. A UUIDv7 in the database, a fixed-width base58 string with a type prefix everywhere else. The two forms encode the same 16 bytes and convert in either direction without loss.

0192f8a1-7c3e-7b9a-8c1d-4e5f6a7b8c9d   ⟷   org_1CGtMb233ezidDvSwDLNBn

No dependencies. Runs wherever crypto.getRandomValues exists: Node 20+, Bun, Deno, Cloudflare Workers, browsers.

npm install id758

Usage

import { createIdRegistry } from "id758";

export const ids = createIdRegistry({
	org: "org",
	invoice: "inv",
	user: "usr",
});

const uuid = ids.invoice.decode("inv_1CGtMb233ezidDvSwDLNBn"); // → "0192f8a1-…"
const id = ids.invoice.encode(uuid); // → "inv_1CGtMb233ezidDvSwDLNBn"
ids.invoice.mint(); // → "inv_…" with a fresh UUIDv7 inside

ids.invoice.is("org_…"); // false
ids.invoice.decodeOrNull("org_…"); // null
ids.invoice.decode("org_…"); // throws "not a inv_ id"

Each helper's ids are typed Id<"invoice">, a branded string, so TypeScript refuses to pass an invoice id where a user id is expected. Decoded values are typed Uuid, a different brand, so a still-encoded id can't reach a uuid slot either. Both are plain strings at runtime.

Without a registry

import { uuidv7, encodeId, decodeId, mintId } from "id758";

uuidv7(); // "0192f8a1-7c3e-7b9a-8c1d-4e5f6a7b8c9d"
encodeId(uuid, "org"); // "org_1CGtMb233ezidDvSwDLNBn"
decodeId("org_1CGtMb233ezidDvSwDLNBn"); // "0192f8a1-…"  (any prefix accepted)
mintId("org"); // encodeId(uuidv7(), "org")

Ids of unknown entity

A prefixed id names its own entity, so it can be resolved without context, for example a polymorphic foreign key or an event payload:

import { entityOf, decodeAnyId } from "id758";

entityOf(ids, "inv_1CGtMb233ezidDvSwDLNBn"); // "invoice"
entityOf(ids, "0192f8a1-…"); // null — a bare uuid names nothing

decodeAnyId(ids, "inv_…"); // the uuid
decodeAnyId(ids, "0192f8a1-…"); // passed through unchanged

decodeAnyId passes unrecognised values through unchanged, so it can sit in front of a query that may receive either form. When you know the entity, use that helper's decode, which rejects the wrong prefix.

Store the uuid, encode at the boundary

Keep the column a native uuid. Database defaults and ::uuid casts keep working, and UUIDv7's timestamp prefix keeps inserts clustered. Encode when sending an id to a client; decode when one comes in, at the request boundary. Everything below that works with raw uuids.

Storing the prefix in every row would duplicate what the table already says.

In Postgres

The codec is also shipped as plain plpgsql — id758.sql in the package, or import { ID758_SQL } from "id758/sql" — so the database speaks both forms and nothing has to convert in application code before touching it directly:

select * from invoices where id = id758_decode('inv_1CGtMb233ezidDvSwDLNBn');
select id758_encode('inv', id) as id, total from invoices;

| Function | Description | | --- | --- | | id758_encode(prefix text, id uuid) → text | encodeId. | | id758_decode(id text) → uuid | decodeId: validates the shape, any prefix. | | id758_decode(id text, prefix text) → uuid | Also requires that prefix, like a registry helper's decode. | | id758_prefix(id text) → text | prefixOf: the prefix, or NULL. |

The functions are IMMUTABLE STRICT PARALLEL SAFE: NULL in gives NULL out, a literal argument is folded at plan time so the ordinary primary-key index serves where id = id758_decode($1), and they can back an expression index or a generated column. No extensions; Postgres 14 or later. The script is create or replace and safe to re-run, so apply it once in a migration (ID758_SQL_STATEMENTS has it one statement per entry for runners that want that) or on the fly:

npx id758 sql | psql "$DATABASE_URL"

On the command line

npx id758 mint inv                                   # inv_1CGtMb233ezidDvSwDLNBn
npx id758 decode inv_1CGtMb233ezidDvSwDLNBn         # 0192f8a1-7c3e-7b9a-8c1d-4e5f6a7b8c9d
npx id758 encode inv 0192f8a1-7c3e-7b9a-8c1d-4e5f6a7b8c9d

Why this shape

  • inv_… tells a reader what the id is for (compare Stripe's cus_… / sub_…).
  • The body is left-padded to 22 characters and the alphabet is in ascending order, so lexical order equals numeric order, which for UUIDv7 equals creation order. Ids minted in the same millisecond are in random order relative to each other.
  • Base58 uses the Bitcoin alphabet: no 0/O, no I/l, no punctuation or padding. Ids are URL-safe and double-click selectable.
  • The database only ever sees standard UUIDs. Removing the library means deleting the encode/decode calls at the boundary; no data migration.

API

| Export | Description | | --- | --- | | uuidv7() | Mint an RFC 9562 UUIDv7 (48-bit ms timestamp, random tail). Returns Uuid. | | encodeId(uuid, prefix) | Hyphenated uuid → prefix_<22 base58 chars>. A no-op on a prefix_ id; throws on an invalid prefix. | | decodeId(id) | Inverse of encodeId. Validates the shape, not the prefix. Throws. | | mintId(prefix) | encodeId(uuidv7(), prefix). | | prefixOf(id) | The prefix of a well-formed id, else null. | | createIdRegistry({ entity: prefix, … }) | One IdHelper per entity: mint, encode, decode, decodeOrNull, is, prefix. | | entityOf(registry, value) | Which registry entity a value belongs to, else null. | | decodeAnyId(registry, value) | Decode any registered entity's id; pass unknown values through. | | isUuid(value) / asUuid(value) | Narrow / assert a string to Uuid (any RFC 9562 version, any case). | | encodeBase58(bytes) / decodeBase58(body) | The raw 16-byte ⟷ 22-char codec. | | uuidToBytes(uuid) / bytesToUuid(bytes) | Hyphenated uuid ⟷ 16 bytes. | | ALPHABET | 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz | | Types | Id<E>, Uuid, IdHelper<E>, IdRegistry<K> | | ID758_SQL / ID758_SQL_STATEMENTS (from id758/sql) | The Postgres functions, as one script / one statement per entry. |

Prefixes must match [A-Za-z0-9]+; the underscore is the separator, so it cannot appear in a prefix.

decodeBase58 rejects bodies above 2¹²⁸ − 1 (base58 has a little more room than 128 bits) rather than truncating, so no two distinct strings decode to the same uuid.

Porting to another language

The encoding is: take the 16 bytes of the UUID, interpret them as one big-endian unsigned integer, write it in base 58 with the alphabet above, and left-pad with 1 (the zero digit) to 22 characters. An implementation is correct when it reproduces these vectors:

| Bytes (hex) | Body | | --- | --- | | 00000000000000000000000000000000 | 1111111111111111111111 | | 00000000000000000000000000000001 | 1111111111111111111112 | | 000000000000000000000000000000ff | 111111111111111111115Q | | 0192f8a17c3e7b9a8c1d4e5f6a7b8c9d | 1CGtMb233ezidDvSwDLNBn | | ffffffffffffffffffffffffffffffff | YcVfxkQb6JRzqk5kF2tNLv |

The same table lives in src/index.test.ts. The Postgres port in id758.sql is checked against it in src/sql.test.ts.

License

MIT © Ingram Technologies