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

@jqgl/humanid

v0.0.1

Published

Downloads

165

Readme

HUMANID

the central source of truth for your identifiers

Why?

Typescript is a structural type system. In the end you have strings everywhere.

With branded ids:

  • Mistakes are visible, no more id inversion in parameters
// before 😢
function unsubscribe(userId: string, subscriptionId: string): Promise<void>;

// after 🥳
function unsubscribe(userId: Id<"UserId">, subscriptionId: Id<"SubscriptionId">): Promise<void>;
  • Collections can finally express your intent
// before 😢
function findUsersSubscriptions(userIds: Set<string>): Map<string, { id: string }[]>;

// after 🥳
function findUsersSubscriptions(
  userIds: Set<Id<"UserId">>,
): Map<Id<"UserId">, Id<"SubscriptionId">[]>;

With a central id registry:

  • It becomes impossible to create duplicated Ids by design
// typescript will prevent this by default
const ids = defineIds((h) => ({ user: h.uuid(), user: h.uuid() }));

// Brands are built by convention
// `UserId` + `Emails/UserId` + `Auth/UserId`
const ids = defineIds((h) => ({
  user: h.uuid(),
  emails: { user: h.uuid() },
  auth: { user: h.uuid() },
}));

How to use

// = ids.ts =
import { defineIds } from "humanid";

export const ids = defineIds((h) => ({
  // Each entry will be unique by construction,
  // and have a type alias: `UserId`, `EmailId`, `FileId`
  user: h.uuid(),
  email: h.uuid(),
  file: h.uuid(),
}));

declare module "humanid" {
  interface Registry {
    /**
     * The declaration merging typing
     * `Id<...>` in a safe way.
     * Otherwise, it accepts any string
     */
    ids: typeof ids;
  }
}

// = user.repository.ts =
import type { Id } from "humanid";

import { db } from "@/db.js";

type User = { id: Id<"UserId">; name: string };

// the Id type will only allow the registered Id
export async function findById(id: Id<"UserId">): Promise<User> {
  const user = await db.query.findFirst({
    where: { id }, // id is still a string
    columns: { id: true, name: true },
  });
  if (!user) throw new Error();

  return { id: ids.user(user.id), name: user.name };
}

Namespacing

It's possible to organize your ids in namespaces (⚠️ no more than 10 level deep):

const ids = defineIds((h) => ({
  users: { user: h.uuid(), subscription: h.uuid(), session: h.uuid() },
  emails: { email: h.uuid(), attachment: h.uuid(), participant: h.uuid() },
}));

Id formats

UUID

uses crypto.randomUUID() under the hood:

const ids = defineIds((h) => ({ user: h.uuid() }));
const userId = ids.user();
//    ^ bf54ca8e-a3bf-4cc0-b4af-2f8e89faa1e6

Uint8Array Buffer

uses crypto.getRandomValues with a Uint8Array under the hood.

const ids = defineIds((h) => ({ user: h.buffer() }));
const userId = ids.user();
//    ^ 5h4o411hu2osn5g6w

you can configure the size of the buffer, and the radix for int-to-string conversion

const ids = defineIds((h) => ({ user: h.buffer({ size: 24, radix: 16 }) }));
const userId = ids.user();
//    ^ 91d91f767e3ea0efd596d5682cfc4bfd92dcf9fda8efa

Prefixed

you can prefix your id with a string, to build an ID à la Stripe.

const ids = defineIds((h) => ({ user: h.prefixed({ prefix: "usr", suffix: h.buffer() }) }));
const userId = ids.user();
//    ^ usr_g4332685v5p423u3119

Custom

you can use any synchronous function that returns a string

let i = 10_000n;
const ids = defineIds((h) => ({ user: h.custom(() => `usr_${++i}`) }));
const userId = ids.user();
//    ^ usr_10001

Branding conventions

We follow a convention to build each Id Branding, with some assumptions:

  1. Key should not use the id suffix, we add it automatically. If your key uses ...Id the brand will repeat it ...IdId.
const ids = defineIds((h) => ({ userId: h.uuid() }));
//    ^ Ids<'UserIdId'>
  1. Keys should be valid JS class names. Each generated Brand will look like a class name by design.
const ids = defineIds((h) => ({ user: h.uuid() }));
//    ^ Id<'UserId'> and not Id<'user'> or Id<'userId'>
  1. Namespaces are separated by /
const ids = defineIds((h) => ({ users: { subscription: h.uuid() } }));
//    ^ Id<'Users/SubscriptionId'>