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

structured-id

v0.2.0

Published

Generate and validate structured IDs — numeric or alphanumeric, with optional Luhn or mod36 checksums. Zero dependencies. TypeScript.

Readme

structured-id

Generate and validate structured IDs with ease and flexibility.

  • ✅ Supports numeric (0–9) or alphanumeric (A–Z, 0–9) charsets
  • ✅ Optional checksum (luhn for numeric, mod36 for alphanumeric)
  • ✅ Flexible formatting (grouping, separators)
  • ✅ Configurable RNG (Math.random, Web Crypto via useCrypto, or custom seeded RNG)
  • ✅ Pattern support (E.g. PROMO-###-### with fixed and random parts)
  • ✅ Zero runtime dependencies • TypeScript types included • ESM + CJS

CI npm version License TypeScript ESM + CJS GitHub Repo GitHub issues

What is a Structured ID?

A Structured ID is an identifier generated with a predefined structure. Unlike a raw random string, a structured ID follows rules you define at generation time, such as:

  • Length — total number of characters (e.g. 16, 20, 24).
  • Grouping — split into chunks for readability (e.g. 1234-5678-9012-3456).
  • Charset — restrict characters (numeric only, or alphanumeric uppercase).
  • Checksum (optional) — append a validation digit/character to detect typos.
  • Patterns — define custom templates with # placeholders for random chars and fixed text.

For example, you can generate a 16-character alphanumeric ID with a mod36 checksum, grouped into 4 groups of 4 characters each, making it easy to read and verify:

import { generateId, formatId, validateId } from "structured-id";

// generate a 16-character alphanumeric ID with mod36 checksum
const id = generateId({ charset: "alphanumeric", totalLength: 16, algorithm: "mod36" });
const formatted = formatId(id, { separator: "-", groupSize: 4, charset: "alphanumeric" });

console.log("Raw ID:", id);
// e.g. "AB12CD34EF56GH78"

console.log("Formatted ID:", formatted);
// e.g. "AB12-CD34-EF56-GH78"

console.log("Is valid?", validateId(id, { charset: "alphanumeric", algorithm: "mod36" }));
// true

// generate a promo code using a pattern
const promo = generateId({ pattern: "PROMO-###-###", charset: "alphanumeric" });
console.log("Promo code:", promo);
// e.g. "PROMO-AB1-C3D"

console.log("Promo code valid?", validateId(promo, { pattern: "PROMO-###-###", charset: "alphanumeric" }));
// true

Structured IDs are ideal for cases where codes need to be both human-friendly and machine-validated, such as:

  • Anonymous user IDs
  • Invite codes
  • Voucher / coupon codes
  • Access tokens
  • Simple one-time passwords (OTPs)

The structured-id library provides a clean, dependency-free way to generate and validate these IDs.


Demo

You can try out structured-id in an interactive demo app:

https://structured-id-demo.vercel.app


Install

Install the package via npm:

npm i structured-id

or using yarn:

yarn add structured-id

Quick Start

import { generateId, validateId, formatId, normalizeId } from "structured-id";

// Generate a default ID: 16 random digits (numeric charset, no checksum)
const id = generateId();

console.log("Generated ID:", id);
// e.g. "1234567890123456"

// Validate the generated ID (numeric, no checksum)
const isValid = validateId(id);
console.log("Is valid?", isValid); // true

// Format the ID with spaces every 4 characters
const formatted = formatId(id, { separator: " " });
console.log("Formatted ID:", formatted); // e.g. "1234 5678 9012 3456"

// Normalize an ID by removing spaces and separators
const normalized = normalizeId("1234 5678-9012 3456");
console.log("Normalized ID:", normalized); // "1234567890123456"

// Generate a numeric ID with Luhn checksum
const luhnId = generateId({ algorithm: "luhn" });
console.log("Luhn ID:", luhnId);

console.log("Luhn ID valid?", validateId(luhnId, { algorithm: "luhn" })); // true

// Generate an alphanumeric ID (A–Z, 0–9) without checksum
const alphaNumId = generateId({ charset: "alphanumeric" });
console.log("Alphanumeric ID:", alphaNumId);

// Generate an alphanumeric ID with mod36 checksum
const alphaNumChkId = generateId({ charset: "alphanumeric", algorithm: "mod36" });
console.log("Alphanumeric with checksum:", alphaNumChkId);

console.log("Alphanumeric with checksum valid?", validateId(alphaNumChkId, { charset: "alphanumeric", algorithm: "mod36" })); // true

// Format an alphanumeric ID with dashes every 4 characters
console.log(formatId(alphaNumId, { separator: "-", groupSize: 4, charset: "alphanumeric" })); // e.g. "AB12-CD34-EF56-GH78"

// Generate an ID using Web Crypto RNG
const cryptoId = generateId({ useCrypto: true });
console.log("Crypto RNG ID:", cryptoId);

Patterns

Structured-id supports patterns to define custom ID templates using # placeholders for random characters and literal characters for fixed text.

  • Each # in the pattern is replaced with a random character from the selected charset (numeric or alphanumeric).
  • Literal characters (letters, digits, symbols) in the pattern are included as-is.
  • If a checksum algorithm (luhn or mod36) is specified, the last # in the pattern is reserved for the checksum character.
  • When using a pattern, options like groups, groupSize, separator, and totalLength are ignored.

Examples

Simple numeric pattern:

const id = generateId({ pattern: "###-###" });
console.log(id); // e.g. "123-456"

Alphanumeric promo code pattern:

const promo = generateId({ pattern: "PROMO-###-###", charset: "alphanumeric" });
console.log(promo); // e.g. "PROMO-AB1-C3D"

Numeric pattern with Luhn checksum:

const idWithLuhn = generateId({ pattern: "ID-####", algorithm: "luhn" });
console.log(idWithLuhn); // e.g. "ID-1234" where '4' is the checksum

console.log(validateId(idWithLuhn, { pattern: "ID-####", algorithm: "luhn" }));
// true

Alphanumeric pattern with mod36 checksum:

const code = generateId({ pattern: "CODE-###-#", charset: "alphanumeric", algorithm: "mod36" });
console.log(code); // e.g. "CODE-AB3-Z" where 'Z' is the checksum

console.log(validateId(code, { pattern: "CODE-###-#", charset: "alphanumeric", algorithm: "mod36" }));
// true

Patterns provide a flexible way to mix fixed and random parts in your IDs while ensuring checksum validation when needed.


Options

You can customize ID generation, validation, and formatting using the following options:

| Option | Description | Default | |--------------|----------------------------------------------------------------------------------------------|------------------| | totalLength| Total length of the ID including checksum if used | 16 | | charset | Character set to use: "numeric" (digits 0–9) or "alphanumeric" (A–Z, 0–9) | "numeric" | | algorithm | Checksum algorithm to use: "luhn" (numeric), "mod36" (alphanumeric), or "none" | "none" | | separator | Character to use as group separator in formatting (e.g., " ", "-") | undefined | | groups | Number of groups used for formatting and default length calculation (groups * groupSize) | 4 | | groupSize | Number of characters per group for formatting | 4 | | rng | Custom random number generator function to use (defaults to Math.random) | Math.random | | useCrypto | Use Web Crypto API for RNG instead of Math.random | false | | pattern | A string template where # is replaced with a generated character and literals are preserved. The last # is reserved for checksum if an algorithm is used. When supplied, groups, groupSize, separator, and totalLength are ignored. | undefined |


validateId

Validate an ID string for correctness, optionally verifying checksum and charset.

import { validateId } from "structured-id";

const id = "79927398713"; // Example numeric ID with Luhn checksum

// Validate numeric ID with Luhn checksum
const valid = validateId(id, { algorithm: "luhn" });
console.log(valid); // true or false

Options:

  • charset: "numeric" or "alphanumeric" (default "numeric")
  • algorithm: "luhn", "mod36", or "none"
  • pattern: string template matching the generated pattern (if used)

formatId

Format an ID string by adding separators at regular intervals.

import { formatId } from "structured-id";

const id = "1234567890123456";

// Format with dashes every 4 characters
const formatted = formatId(id, { separator: "-", groupSize: 4 });
console.log(formatted); // "1234-5678-9012-3456"

Options:

  • groups: number of groups (default 4)
  • charset: "numeric" or "alphanumeric" (default "numeric")
  • separator: string to insert between groups (e.g., " ", "-")
  • groupSize: number of characters per group (default 4)

Normalization helpers

Normalize ID strings by removing all whitespace and separators to obtain the raw ID.

import { normalizeId } from "structured-id";

const messyId = "1234 5678-9012 3456";
const normalized = normalizeId(messyId);
console.log(normalized); // "1234567890123456"

Checksum helpers

You can use the included checksum helper functions directly if needed:

  • luhnChecksumDigit(id: string): string — Generates a Luhn checksum digit for a numeric string.
  • luhnValidate(id: string): boolean — Validates a numeric string using the Luhn algorithm.
  • mod36CheckChar(id: string): string — Generates a mod36 checksum character for an alphanumeric string.
  • mod36Validate(id: string): boolean — Validates an alphanumeric string using mod36 checksum.

Example:

import { luhnValidate, luhnChecksumDigit } from "structured-id";

const partialId = "7992739871";
const checksum = luhnChecksumDigit(partialId);
const fullId = partialId + checksum;

console.log(luhnValidate(fullId)); // true

Comparison Guide

| Charset | Algorithm | Description | ID Length | Checksum Length | Notes | |---------------|-----------|-----------------------------------|-----------|-----------------|-------------------------------------| | numeric | luhn | Numeric digits with Luhn checksum | 15 | 1 | Common for credit cards and IDs | | alphanumeric| mod36 | Alphanumeric with mod36 checksum | 15 | 1 | Supports A–Z and digits 0–9 | | numeric | none | Numeric digits only | 16 | 0 | Simple random numeric IDs | | alphanumeric| none | Alphanumeric only | 16 | 0 | Random alphanumeric IDs |


Entropy and Rule of Thumb

| Charset | Bits per Character | Example ID Length | Approximate Entropy (bits) | |---------------|--------------------|-------------------|----------------------------| | Numeric (0–9) | 3.32 | 16 | 53 | | Alphanumeric (A–Z, 0–9) | 5.17 | 16 | 83 |

Rule of Thumb:

  • Use numeric + Luhn for compatibility and simplicity when only digits are allowed.
  • Use alphanumeric + mod36 when you need a larger character space and stronger entropy per character.
  • Adjust totalLength to meet your entropy/security requirements.

RNG Options

By default, structured-id uses Math.random() as the random number generator, but you can enable Web Crypto RNG by setting useCrypto: true:

import { generateId } from "structured-id";

const id = generateId({ useCrypto: true });
console.log(id);

You may also provide seeded or custom RNG functions for deterministic ID generation (useful for testing):

import { generateId } from "structured-id";

// Custom RNG example
const customRng = () => {
  // deterministic or seeded RNG implementation
  return 0.5;
};

const id = generateId({ rng: customRng });
console.log(id);

License

MIT © Steve Foster