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

@detaditya/yamin

v0.1.0

Published

Easy yet powerful functional data type utilities

Readme

yamin

CI codecov npm

Easy yet powerful functional data type utilities.

Motivation

TypeScript has discriminated unions built into its type system, but building them by hand is tedious — you write the type, then the constructors, then the type guards, then the matcher, all separately and all by hand. One renamed variant and you're fixing four different spots.

yamin collapses that boilerplate into a single Union call:

  • Constructors — one per variant, derived automatically from the definition.
  • Type guardsisVariantName(value) predicates generated for every variant.
  • match — exhaustive pattern matching with an optional _ catch-all, enforced at the type level.

Installation

npm install @detaditya/yamin
# or
bun add @detaditya/yamin

Union

Union creates a discriminated union from a variant definition. Each variant is either a plain null (no payload) or data<T>() (carries a typed payload).

Creating a Union

import { Union, type InferUnion } from "@detaditya/yamin";

const DataStatuses = Union(data => ({
  idle: null,
  loading: data<{ progress: number }>(),
  done: data<{ result: string }>(),
  failed: data<{ code: number; message: string }>(),
}))

type DataStatus = InferUnion<typeof DataStatuses>

Constructors

Each variant key becomes a constructor on the union object. Null variants take no arguments; data variants require an object payload.

const idle    = DataStatuses.idle()
const loading = DataStatuses.loading({ progress: 50 })
const done    = DataStatuses.done({ result: "all good" })
const failed  = DataStatuses.failed({ code: 404, message: "not found" })

Type Guards

A isVariantName predicate is generated for every variant and narrows the type when used in a conditional.

const status: DataStatus = DataStatuses.loading({ progress: 50 })

if (DataStatuses.isLoading(status)) {
  console.log(status.payload.progress) // TypeScript knows this is the loading variant
}

DataStatuses.isIdle(status)    // false
DataStatuses.isDone(status)    // false
DataStatuses.isFailed(status)  // false

Pattern Matching

match dispatches to the handler for the active variant. Either cover every variant (exhaustive) or cover a subset and supply a _ catch-all.

// Exhaustive — all variants handled
const message = DataStatuses.match(status, {
  idle:    ()                     => "Waiting...",
  loading: ({ progress })         => `Loading ${progress}%`,
  done:    ({ result })           => `Done: ${result}`,
  failed:  ({ code, message })    => `Error ${code}: ${message}`,
})

// Partial with catch-all
const label = DataStatuses.match(status, {
  loading: ({ progress }) => `${progress}%`,
  _: () => "Not loading",
})

Inferring the Union Type

Use InferUnion to derive the instance type from a union object so you only define the shape once.

const Actions = Union(data => ({
  changeName: data<{ name: string }>(),
  increment:  null,
  decrement:  null,
}))

type Action = InferUnion<typeof Actions>
// { kind: "changeName"; payload: { name: string } }
// | { kind: "increment"; payload: null }
// | { kind: "decrement"; payload: null }

Real-world Example: Reducer

type State = { name: string; count: number }

const Actions = Union(data => ({
  changeName: data<{ name: string }>(),
  increment:  null,
  decrement:  null,
}))
type Action = InferUnion<typeof Actions>

const reducer = (state: State, action: Action): State =>
  Actions.match(action, {
    changeName: ({ name }) => ({ ...state, name }),
    increment:  ()         => ({ ...state, count: state.count + 1 }),
    decrement:  ()         => ({ ...state, count: state.count - 1 }),
  })

Real-world Example: View Rendering

const DataStatuses = Union(data => ({
  loading: null,
  success: data<{ message: string }>(),
  error:   data<{ code: number; message: string }>(),
}))
type DataStatus = InferUnion<typeof DataStatuses>

const render = (status: DataStatus): string =>
  DataStatuses.match(status, {
    loading: ()                  => "<p>Loading...</p>",
    success: ({ message })       => `<p>Success: ${message}</p>`,
    error:   ({ code, message }) => `<p>Error ${code}: ${message}</p>`,
  })

Roadmap

  • [ ] Additional data type utilities

License

MIT