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

@tnid/core

v0.1.0

Published

Type-safe, named, unique identifiers (TNIDs) - UUID-compatible IDs with embedded type names

Readme

@tnid/core

Type-safe, named, unique identifiers (TNIDs) for TypeScript.

TNIDs are UUID-compatible identifiers with embedded type names, providing compile-time type safety and human-readable prefixes.

Installation

# npm
npm install @tnid/core

# pnpm
pnpm add @tnid/core

# bun
bun add @tnid/core

# deno
deno add npm:@tnid/core

Platform Support

Requires globalThis.crypto (Web Crypto API):

  • Node.js 20+
  • Deno 1.0+
  • Bun 1.0+
  • Modern browsers (ES2020+)

Quick Start

import { DynamicTnid, Tnid, TnidType, UuidLike } from "@tnid/core";

// Create a typed NamedTnid (compile-time name validation)
const UserId = Tnid("user");
type UserId = TnidType<typeof UserId>;

// Generate IDs
const id: UserId = UserId.new_v0(); // time-ordered
const id2: UserId = UserId.new_v1(); // high-entropy random

// IDs look like: "user.Br2flcNDfF6LYICnT"
console.log(id);

// Parse existing IDs
const parsed: UserId = UserId.parse("user.Br2flcNDfF6LYICnT");

// Convert to/from UUID format
const uuid: string = UserId.toUuidString(id); // "d6157329-4640-8e30-..."
const fromUuid: UserId = UserId.parseUuidString(uuid);

// Works great with Zod
import { z } from "zod";
const UserSchema = z.object({
  id: z.string().transform(UserId.parse),
  name: z.string(),
});
interface User extends z.infer<typeof UserSchema> {} // { id: UserId; name: string }

const user: User = UserSchema.parse({
  id: "user.Br2flcNDfF6LYICnT",
  name: "Alice",
});

Features

  • Compile-time type safety: Different TNID types are incompatible at the type level
  • Human-readable prefixes: IDs like user.Br2flcNDfF6LYICnT are self-documenting
  • UUID compatible: Store in any UUID column, use with existing UUID tooling
  • Time-ordered (V0): Sortable by creation time, like UUIDv7
  • High-entropy (V1): Maximum randomness, like UUIDv4
  • String-backed: At runtime, TNIDs are their string representation
const id = UserId.new_v0();

// Use as Map key
const cache = new Map<UserId, User>();
cache.set(id, user);

// JSON serialization works naturally
JSON.stringify({ userId: id }); // {"userId":"user.Br2flcNDfF6LYICnT"}

// String comparison
id === otherUserId; // true/false

API Reference

Exports

import {
  Case, // "lower" | "upper"
  DynamicTnid, // Runtime TNID operations (type + namespace)
  NamedTnid, // NamedTnid interface
  Tnid, // NamedTnid creator function
  TnidType, // Type helper to extract ID type
  // Types only:
  TnidValue, // Branded string type
  TnidVariant, // "v0" | "v1" | "v2" | "v3"
  UuidLike, // UUID string operations (type + namespace)
  ValidateName, // Compile-time name validation (for library authors)
} from "@tnid/core";

Tnid(name)

Creates a NamedTnid for a specific name. The name is validated at compile time.

const UserId = Tnid("user");
const PostId = Tnid("post");
const ItemId = Tnid("item");

Name Rules

  • 1-4 characters
  • Only 0-4 and a-z (31 characters total)
  • Lowercase only
// Valid names
Tnid("user"); // OK
Tnid("a"); // OK
Tnid("1234"); // OK
Tnid("a1b2"); // OK

// Compile errors
Tnid("users"); // too long (max 4)
Tnid("User"); // uppercase not allowed
Tnid("a-b"); // hyphen not allowed
Tnid("5"); // only digits 0-4
Tnid(""); // empty not allowed

NamedTnid<Name> (returned by Tnid())

Generation

const UserId = Tnid("user");
type UserId = TnidType<typeof UserId>;

UserId.new_v0(); // time-ordered ID
UserId.new_v1(); // high-entropy random ID
UserId.v0_from_parts(1234567890n, 0n); // V0 with explicit timestamp/random
UserId.v1_from_parts(0n); // V1 with explicit random bits

Parsing

UserId.parse("user.Br2flcNDfF6LYICnT"); // parse TNID string
UserId.parseUuidString("d6157329-4640-8e30-..."); // parse UUID hex string

Inspection and Conversion

UserId.name; // "user" - the TNID name
UserId.variant(id); // "v0" or "v1" - get the variant
UserId.toUuidString(id); // "d6157329-4640-8e30-..." - convert to UUID
UserId.toUuidString(id, "upper"); // "D6157329-4640-8E30-..." - uppercase UUID
UserId.nameHex(); // "d6157" - name as 5-char hex

TnidType<T>

Type helper to extract the TnidValue type from a NamedTnid.

const UserId = Tnid("user");
type UserId = TnidType<typeof UserId>;  // TnidValue<"user">

const PostId = Tnid("post");
type PostId = TnidType<typeof PostId>;  // TnidValue<"post">

// Use in function signatures
function getUser(id: UserId): User { ... }
function getPost(id: PostId): Post { ... }

DynamicTnid

For working with TNIDs when the name isn't known at compile time.

// As a type - accepts any TNID
function logAnyId(id: DynamicTnid) {
  console.log(DynamicTnid.getName(id), id);
}

// Generation with runtime names
DynamicTnid.newV0("user"); // time-ordered (alias: newTimeOrdered)
DynamicTnid.newV1("user"); // high-entropy (alias: newHighEntropy)

// Generation with explicit values (useful for testing/migrations)
DynamicTnid.newV0WithTime("user", new Date("2024-01-15"));
DynamicTnid.newV0WithParts("user", 1705312800000n, 123n);
DynamicTnid.newV1WithRandom("user", 0x123456789abcdef0123456789n);

// Parsing (auto-detects format)
DynamicTnid.parse("post.EUBcUw4T9x3KNOll-"); // TNID string
DynamicTnid.parse("d6157329-4640-..."); // UUID string

// Or use explicit parse methods
DynamicTnid.parseTnidString("post.EUBcUw4T9x3KNOll-");
DynamicTnid.parseUuidString("d6157329-4640-...");

// Inspection
DynamicTnid.getName(id); // "user"
DynamicTnid.getNameHex(id); // "d6157"
DynamicTnid.getVariant(id); // "v0" or "v1"
DynamicTnid.toUuidString(id); // UUID hex string

UuidLike

For working with UUID hex strings that may or may not be valid TNIDs.

UuidLike.fromTnid(id); // convert TNID to UUID string
UuidLike.parse(s); // parse any UUID (validates format only)
UuidLike.toTnid(uuid); // convert UUID to TNID (throws if invalid)
UuidLike.toUpperCase(uuid); // convert to uppercase

Variants

V0 (Time-Ordered)

  • 43 bits: millisecond timestamp
  • 57 bits: random
  • Use case: When you need chronological sorting (logs, events, feeds)

V1 (High-Entropy)

  • 100 bits: random
  • Use case: When you need maximum uniqueness/unpredictability

Type Safety

Different TNID types are completely incompatible at compile time:

const UserId = Tnid("user");
const PostId = Tnid("post");
type UserId = TnidType<typeof UserId>;
type PostId = TnidType<typeof PostId>;

const userId: UserId = UserId.new_v0();
const postId: PostId = PostId.new_v0();

// Compile errors - types don't match
const wrong1: UserId = postId;           // Error
const wrong2: PostId = userId;           // Error
function getUser(id: UserId) { ... }
getUser(postId);                         // Error

// DynamicTnid accepts any TNID
const dynamic: DynamicTnid = userId;     // OK
const dynamic2: DynamicTnid = postId;    // OK

Plain strings cannot be assigned to TNID types:

// Compile errors - plain strings not allowed
const fake: UserId = "user.Br2flcNDfF6LYICnT"; // Error

// Must use parse() or new_*() to get a valid TNID
const valid: UserId = UserId.parse("user.Br2flcNDfF6LYICnT"); // OK

UUID Compatibility

TNIDs are valid UUIDv8 identifiers:

const id = UserId.new_v0();

// To UUID
const uuid = UserId.toUuidString(id);
// "d6157329-4640-8e30-8012-345678901234"

// From UUID
const back = UserId.parseUuidString(uuid);

// Store in database as UUID
await db.query("INSERT INTO users (id) VALUES ($1)", [uuid]);

Related Packages

  • @tnid/encryption - Encrypt V0 TNIDs to V1 to hide timestamp information

License

MIT