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

@zerocity/define-ensure

v0.2.0

Published

Type-safe runtime assertions with definable error factories and TypeScript narrowing

Readme

define-ensure

Type-safe runtime assertions with definable error factories and TypeScript narrowing.

Inspired by tiny-invariant, but:

  • Returns the value (tiny-invariant returns void)
  • Narrows TypeScript types automatically
  • Custom error factories via defineEnsure()

Installation

import { ensure, defineEnsure } from "jsr:@zerocity/define-ensure";

Why define-ensure?

// tiny-invariant: returns void, need separate variable
invariant(user, "User required");
user.name; // ❌ TypeScript still thinks user might be null

// define-ensure: returns the value, TypeScript narrows
const user = ensure(maybeUser, "User required");
user.name; // ✅ TypeScript knows user is defined

Quick Start

import { ensure, defineEnsure, EnsureError } from "@zerocity/define-ensure";

// Value assertion - returns the value, narrowed
const user = ensure(maybeUser, "User is required");
user.name; // ✅ TypeScript knows user is defined

// Instance check - returns narrowed type
const date = ensure.instance(value, Date, "Expected Date");
date.getTime(); // ✅ TypeScript knows it's a Date

// Condition check
ensure(id > 0, "ID must be positive");

The Killer Feature: defineEnsure()

Create domain-specific validators with custom error classes:

import { defineEnsure } from "@zerocity/define-ensure";

class ValidationError extends Error {
  override name = "ValidationError";
}

const [validate, isValidationError] = defineEnsure({
  error: ValidationError,
  formatMessage: (msg) => `Validation failed: ${msg}`,
});

// Now throws ValidationError instead of EnsureError
const email = validate(formData.email, "Email is required");

// Type-safe error handling
try {
  validate(null, "Required");
} catch (e) {
  if (isValidationError(e)) {
    // e is typed as ValidationError
  }
}

API

ensure(value, message)

Ensures a value is not null, undefined, or false.

// Simple message
const user = ensure(getUser(id), "User not found");

// Lazy message (only computed on failure)
const user = ensure(getUser(id), () => `User ${id} not found`);

// With options
const user = ensure(getUser(id), {
  message: "User not found",
  cause: originalError,
});

Returns: The value, narrowed to exclude null | undefined | false

Throws: EnsureError when value is nullish or false

ensure.instance(value, Class, message)

Ensures a value is an instance of a class.

const date = ensure.instance(value, Date, "Expected Date");
const error = ensure.instance(caught, Error, "Expected Error");

Returns: The value, narrowed to InstanceType<Class>

Throws: EnsureError when instanceof check fails

isEnsureError(value)

Type guard to check if a value is an EnsureError.

try {
  doSomething();
} catch (e) {
  if (isEnsureError(e)) {
    // Handle ensure errors specifically
  }
}

defineEnsure(config)

Create a custom validator with your own error class.

Config options:

| Option | Type | Description | | --------------- | ------------------------- | --------------------------------- | | error | ErrorConstructor | Error class to throw | | formatMessage | (msg: string) => string | Transform message before throwing | | strip | boolean | Strip messages in production | | name | string | Name for stripped messages |

Returns: [ensureFn, isErrorFn] tuple

Types

// Message can be string or lazy function
type Message = string | (() => string);

// Full options object
interface EnsureOptions {
  message: Message;
  error?: ErrorConstructor;
  cause?: unknown;
  strip?: boolean;
}

// Second argument can be message or options
type EnsureArg = Message | EnsureOptions;

Examples

Form Validation

import { defineEnsure } from "@zerocity/define-ensure";

class FormError extends Error {
  constructor(
    message: string,
    public field: string
  ) {
    super(message);
    this.name = "FormError";
  }
}

const [requireField] = defineEnsure({ error: FormError });

function validateForm(data: FormData) {
  const email = requireField(data.get("email"), "Email is required");
  const password = requireField(data.get("password"), "Password is required");

  return { email, password };
}

API Response Handling

import { ensure } from "@zerocity/define-ensure";

async function fetchUser(id: number) {
  const response = await fetch(`/api/users/${id}`);
  const data = await response.json();

  return ensure(data.user, `User ${id} not found`);
}

Error Chaining

import { ensure } from "@zerocity/define-ensure";

try {
  const config = JSON.parse(rawConfig);
} catch (e) {
  ensure(false, {
    message: "Invalid configuration",
    cause: e,
  });
}

Migrating from tiny-invariant

Drop-in replacement with invariant:

// Before (tiny-invariant)
import invariant from "tiny-invariant";
invariant(user, "User required");

// After (define-ensure) - same behavior, but returns the value!
import { invariant } from "@zerocity/define-ensure";
const user = invariant(maybeUser, "User required");

Differences from tiny-invariant:

| Feature | tiny-invariant | define-ensure invariant | |---------|----------------|---------------------------| | Returns value | ❌ void | ✅ Returns narrowed value | | Strips messages in prod | ✅ | ✅ | | Error class | Error | InvariantError | | Type guard | ❌ | ✅ isInvariantError() |

Or use ensure if you don't want message stripping:

import { ensure } from "@zerocity/define-ensure";
const user = ensure(maybeUser, "User required"); // Message always preserved

Credits

Inspired by tiny-invariant by Alex Reardon.

License

MIT