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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@warlock.js/seal

v3.0.33

Published

Comprehensive Validation library

Readme

🔮 Warlock Seal

Cast validation seals on your schemas to protect your data

A powerful, type-safe validation library for TypeScript with an intuitive API and framework-agnostic design.

📦 Installation

npm install @warlock.js/seal
yarn add @warlock.js/seal
pnpm add @warlock.js/seal

🚀 Quick Start

import { v, type Infer } from "@warlock.js/seal";

// Define your validation schema (cast a seal)
const userSeal = v.object({
  name: v.string().required().min(3),
  email: v.string().email().required(),
  age: v.int().min(18),
  status: v.string().in(["active", "inactive"]),
});

// Extract TypeScript type automatically
type User = Infer<typeof userSeal>;
// Result: { name: string; email: string; age: number; status: string }

// Validate data
const result = await v.validate(userSeal, userData);

if (result.isValid) {
  console.log("Data is sealed! ✅", result.data);
} else {
  console.log("Seal broken! ❌", result.errors);
  // [
  //   { error: "The name must be at least 3 characters", path: "name" },
  //   { error: "The email must be a valid email address", path: "email" }
  // ]
}

🎯 Core Concepts

Three-Layer Validation Pipeline

Seal uses a unique three-layer architecture that separates concerns:

Input Data
    ↓
🔧 MUTATORS (Prep for Validation)
    - Normalize data before validation
    - Examples: trim(), lowercase(), toStartOfDay()
    - Run BEFORE validation rules
    ↓
✅ VALIDATORS (Check Constraints)
    - Validate against rules
    - Examples: email(), min(), after()
    - Return errors if validation fails
    ↓
🎨 TRANSFORMERS (Format Output)
    - Format validated data for output
    - Examples: toISOString(), toFormat()
    - Run AFTER validation passes
    ↓
Output Data

Example: Date Validation

const schema = v
  .date()
  .toStartOfDay() // 🔧 Mutator: normalize to 00:00:00
  .after("2024-01-01") // ✅ Validator: check Date object
  .toISOString(); // 🎨 Transformer: output as ISO string

const result = await v.validate(schema, "2024-06-15 14:30:00");
// result.data = "2024-06-15T00:00:00.000Z"

Why this matters:

  • Mutators prepare data for validation (no more string comparison issues!)
  • Validators check constraints on normalized data
  • Transformers format output without affecting validation

🌟 Key Features

✅ Type Inference

Automatically extract TypeScript types from your schemas:

const schema = v.object({
  name: v.string().required(),
  age: v.int(),
  tags: v.array(v.string()),
});

type User = Infer<typeof schema>;
// { name: string; age: number; tags: string[] }

✅ Intuitive API

Readable, chainable methods:

v.string().required().email().min(5).max(100);

v.int().min(0).max(100).positive();

v.array(v.string()).minLength(1).unique();

✅ Conditional Validation

Apply different validators based on other field values:

const schema = v.object({
  type: v.string().required().in(["admin", "user"]),
  role: v.string().when("type", {
    is: {
      admin: v.string().required().in(["super", "moderator"]),
      user: v.string().required().in(["member", "guest"]),
    },
  }),
});

// If type is "admin", role must be "super" or "moderator"
// If type is "user", role must be "member" or "guest"

✅ Field Comparison

Compare fields against each other (global or sibling scope):

const schema = v.object({
  password: v.string().required().min(8),
  confirmPassword: v.string().required().sameAs("password"), // Compare with password field

  startDate: v.date().required(),
  endDate: v.date().required().after("startDate"), // Compare with startDate field
});

✅ Custom Validation

Add your own validation logic:

v.string().refine(async value => {
  const exists = await checkUsername(value);
  if (exists) return "Username already taken";
});

✅ Mutators & Transformers

Normalize input and format output:

// String mutators
v.string()
  .trim() // Remove whitespace
  .lowercase() // Convert to lowercase
  .email() // Validate email
  .toJSON(); // Transform to JSON string

// Date mutators & transformers
v.date()
  .toStartOfDay() // Normalize to midnight
  .after("2024-01-01")
  .toISOString(); // Output as ISO string

📚 Documentation

For complete documentation, visit: https://warlock.js.org/seal

Available Validators

| Validator | Purpose | Example | | ------------- | ------------------ | -------------------------------- | | v.string() | String validation | v.string().email().min(3) | | v.int() | Integer validation | v.int().min(0).max(100) | | v.float() | Float validation | v.float().positive() | | v.number() | Number validation | v.number().min(0) | | v.boolean() | Boolean validation | v.boolean().accepted() | | v.date() | Date validation | v.date().after(new Date()) | | v.array() | Array validation | v.array(v.string()).min(1) | | v.object() | Object validation | v.object({ name: v.string() }) | | v.scalar() | Scalar values | v.scalar().in([1, "2", true]) |

Common Methods

Available on all validators:

| Method | Purpose | | -------------------------- | -------------------------------- | | .required() | Value must be present | | .optional() | Value is optional | | .forbidden() | Value must not be present | | .equals(value) | Must equal specific value | | .default(value) | Set default value | | .allowsEmpty() | Skip validation if empty | | .when(field, conditions) | Conditional validation | | .omit() | Validate but exclude from output | | .refine(callback) | Custom validation logic |


💡 Examples

User Registration

const registerSchema = v.object({
  email: v.string().required().email(),
  password: v.string().required().min(8).strongPassword(),
  confirmPassword: v.string().required().sameAs("password"),
  age: v.int().required().min(18).max(120),
  terms: v.boolean().required().accepted(),
});

Form with Conditional Fields

const formSchema = v.object({
  accountType: v.string().required().in(["personal", "business"]),

  // Required only if accountType is "business"
  companyName: v.string().requiredIf("accountType", { is: "business" }),

  // Conditional validation
  taxId: v.string().when("accountType", {
    is: {
      business: v
        .string()
        .required()
        .pattern(/^[0-9]{9}$/),
      personal: v.string().forbidden(),
    },
  }),
});

Date Range Validation

const bookingSchema = v.object({
  checkIn: v.date().required().afterToday(),

  checkOut: v
    .date()
    .required()
    .after("checkIn") // Compare with checkIn field
    .withinDays(30), // Max 30 days from checkIn
});

Array Validation

const tagsSchema = v
  .array(v.string())
  .required()
  .minLength(1)
  .maxLength(10)
  .unique();

const usersSchema = v
  .array(
    v.object({
      name: v.string().required(),
      email: v.string().email(),
    })
  )
  .minLength(1);

🔧 Framework Extensions

For Warlock.js projects, framework-specific validators are available:

import { v } from "@warlock.js/core/v";

const schema = v.object({
  email: v.string().email().unique(User), // Database validation
  avatar: v.file().image().maxSize(5000000), // File upload validation
  uploadId: v.string().uploadable(), // Upload hash validation
});

🎨 Why "Seal"?

🔮 Magical Context
Warlocks use seals to protect and verify. Your validation schemas are seals that protect your data integrity.

💻 Programming Context
A "seal of approval" - data that passes validation is sealed and verified.

Developer Experience
Clean, intuitive API that feels natural:

const seal = v.object({ ... });  // Cast a seal
await v.validate(seal, data);     // Verify with the seal

🤝 Philosophy

Seal is designed around three principles:

  1. Type Safety First - TypeScript support is not an afterthought
  2. Framework Agnostic - Works anywhere JavaScript runs
  3. Intuitive API - If it feels right, it probably works

📖 Full Documentation

For complete documentation, visit: https://warlock.js.org/seal

The documentation includes:


📄 License

MIT


🤝 Contributing

See main Warlock.js repository


Cast your seals and protect your data! 🔮✨