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

@swatchmaker/typed-keys

v0.2.0

Published

Type-safe key builder with compile-time chain enforcement

Readme

@swatchmaker/typed-keys

A type-safe key builder for TypeScript with full autocomplete and runtime validation. Useful for key-value stores, caching layers, and any system that uses structured keys.

Features

  • 🔒 Type Safety: Full TypeScript autocomplete for valid chain sequences
  • 🆔 Level IDs: Support for duplicate level names with distinct IDs
  • 🔄 Immutable: Partial builders can be reused to create multiple keys
  • 📦 Zero Dependencies: Pure TypeScript implementation
  • 🎯 Developer Experience: Autocomplete shows only valid next methods
  • ⚡ Runtime Performance: Minimal overhead with efficient key generation
  • 🛡️ Runtime Validation: Clear error messages for invalid chains

Installation

npm install @swatchmaker/typed-keys
# or
pnpm add @swatchmaker/typed-keys
# or
yarn add @swatchmaker/typed-keys

Quick Start

import { defineBuilder } from '@swatchmaker/typed-keys';

// Define your key hierarchy as a schema
const KeyBuilder = defineBuilder([
  { name: 'org', params: ['string'], parent: null },
  { name: 'courts', params: ['string'], parent: 'org' },
  { name: 'occupancy', params: ['Date', 'Date'], parent: 'courts' }
] as const);

// Create keys with full type safety
const key = KeyBuilder.create()
  .org('org-123')
  .courts('court-456')
  .occupancy(new Date('2024-01-01'), new Date('2024-01-07'))
  .build();

// Result: "org-123:court-456:2024-01-01T00:00:00.000Z-2024-01-07T00:00:00.000Z"

Schema Definition

Each level in the schema has these properties:

| Property | Type | Required | Description | |----------|------|----------|-------------| | name | string | ✅ | Method name for this level | | params | string[] | ✅ | Parameter types: 'string', 'number', 'Date' | | parent | string \| null | ✅ | Parent level ID (or null for root) | | id | string | ❌ | Unique identifier (defaults to name) | | format | function | ❌ | Custom formatter for key segment |

Native Parameter Types

The following types are automatically cast to strings:

  • string: Passed through as-is
  • number: Converted via String()
  • Date: Converted to ISO string via .toISOString()
const KeyBuilder = defineBuilder([
  { name: 'range', params: ['number', 'number'], parent: null },
  { name: 'date', params: ['Date'], parent: null }
] as const);

// Numbers and dates are auto-cast
KeyBuilder.create().range(1, 100).build();  // "1:100"
KeyBuilder.create().date(new Date()).build(); // "2024-01-01T00:00:00.000Z"

Level IDs for Duplicate Names

When you need multiple levels with the same name at different positions in the hierarchy, use the id field to distinguish them:

const KeyBuilder = defineBuilder([
  { name: 'org', params: ['string'], parent: null },
  // Two different 'courts' levels with distinct IDs
  { id: 'court-string', name: 'courts', params: ['string'], parent: 'org' },
  { id: 'court-number', name: 'courts', params: ['number'], parent: 'org' }
] as const);

// Both work because they have different IDs
const key1 = KeyBuilder.create().org('org-123').courts('court-a').build();
const key2 = KeyBuilder.create().org('org-456').courts(789).build();

⚠️ Error on Duplicate IDs:

If you define levels with the same name (and no explicit IDs), you'll get a helpful error:

defineBuilder([
  { name: 'org', params: ['string'], parent: null },
  { name: 'courts', params: ['string'], parent: 'org' },
  { name: 'courts', params: ['number'], parent: 'org' }  // ❌ Throws!
] as const);
// Error: Duplicate level IDs found: courts.
// If you have levels with the same name, you must provide distinct 'id' fields.

Custom Formatters

Provide a custom format function to control how parameters are formatted into the key:

const KeyBuilder = defineBuilder([
  { name: 'org', params: ['string'], parent: null },
  {
    name: 'occupancy',
    params: ['Date', 'Date'],
    parent: 'org',
    format: (start, end) => 
      `occupancy:${formatDate(start)}-${formatDate(end)}`
  }
] as const);

const key = KeyBuilder.create()
  .org('org-123')
  .occupancy(new Date('2024-01-01'), new Date('2024-01-07'))
  .build();

// Result: "org-123:occupancy:01/01/2024-07/01/2024"

Reusable Partial Builders

Build multiple keys efficiently by reusing partial builders:

const base = KeyBuilder.create();
const orgBuilder = base.org('org-123'); // Reusable!

// Same org, different courts
const key1 = orgBuilder.courts('court-a').build();
const key2 = orgBuilder.courts('court-b').occupancy(d1, d2).build();

// Different org
const key3 = base.org('org-456').courts('court-c').build();

Runtime Validation

The builder validates chains at runtime with clear error messages:

const KeyBuilder = defineBuilder([
  { name: 'org', params: ['string'], parent: null },
  { name: 'courts', params: ['string'], parent: 'org' }
] as const);

// ❌ Runtime Error: Method 'courts' is not available.
// Available root levels: org
KeyBuilder.create().courts('invalid');

// ❌ Runtime Error: Method 'invalid' is not available from current chain state [org].
// Available next levels: courts
KeyBuilder.create().org('org-123').invalid('test');

Schema Validation

The defineBuilder function validates your schema at definition time:

  • Max 10 levels - Prevents TypeScript recursion issues
  • Unique IDs - Each level must have a unique ID (explicit or auto-generated from name)
  • Valid parent references - Parent IDs must exist in the schema
  • No circular references - A level cannot be its own parent
// ❌ Error: Schema has 11 levels, but maximum is 10
defineBuilder([...11 levels]);

// ❌ Error: Duplicate level IDs found: courts
defineBuilder([...duplicate IDs]);

// ❌ Error: Level 'courts' references unknown parent 'nonexistent'
defineBuilder([...invalid parent]);

Complex Example

import { defineBuilder } from '@swatchmaker/typed-keys';

const KeyBuilder = defineBuilder([
  { name: 'org', params: ['string'], parent: null },
  { name: 'user', params: ['string'], parent: 'org' },
  { name: 'session', params: ['string'], parent: 'user' },
  { name: 'cache', params: ['string'], parent: 'session' }
] as const);

const key = KeyBuilder.create()
  .org('org-123')
  .user('user-456')
  .session('sess-789')
  .cache('preferences')
  .build();

// Result: "org-123:user-456:sess-789:preferences"

API Reference

defineBuilder(schema)

Creates a builder factory from a schema definition.

Parameters:

  • schema (LevelDef[]): Array of level definitions

Returns: { create(): Builder }

LevelDef

| Property | Type | Description | |----------|------|-------------| | name | string | Method name | | params | ('string' \| 'number' \| 'Date')[] | Parameter types | | parent | string \| null | Parent level ID | | id | string (optional) | Unique identifier | | format | (...args: (string \| number \| Date)[]) => string (optional) | Custom formatter |

Builder Methods

Methods are dynamically generated based on your schema. Only valid next methods are available at each step.

  • Level methods: (...args) → Next builder state
  • .build(): Available when chain has at least one level, returns string

License

MIT © Sebastian Weidmann