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

zerot

v1.0.2

Published

Design by Contract framework for TypeScript with zero-trust architecture principles and AI-friendly development

Readme

Zerot: TypeScript contracts that actually work

Node.js TypeScript License

Stop worrying about your backend breaking. Zerot uses simple contracts to make your data layer bulletproof, even when you're coding at 3am or letting AI write half your code.

@contract({
  requires: [auth("user"), validates(UserSchema)],
  ensures: [auditLog("user_created")],
})
async createUser(input: UserInput) {
  // This method is now protected by contracts
  return await db.user.create(input);
}

No matter how tired you are or how rushed the deployment is, your contracts have your back.

Why Zerot?

Your data layer should be unbreakable

Database operations are the heart of your app. One bad update, one missing validation, one forgotten auth check - and you're debugging production at midnight. Zerot puts guardrails around everything that touches your data.

Start small, grow gradually

You don't need to rewrite your entire app. Drop a @contract on your most critical methods first. Add more as you go. Each contract makes your code a little more bulletproof.

AI-friendly patterns

When you're using Copilot or ChatGPT to generate code, you want consistent patterns. Zerot's templates mean every generated method follows the same security and validation patterns - no surprises.

Core Concepts

What are contracts?

Think of contracts like TypeScript, but for runtime behavior. While TypeScript catches type errors at compile time, contracts catch logic errors at runtime:

// TypeScript ensures the parameter is a string
function updateUser(name: string) { }

// Contracts ensure the business rules are followed
@contract({
  requires: [
    auth("user"),           // Must be logged in
    validates(UserSchema),  // Input must be valid
    owns("userId"),         // Must own this resource
  ],
  ensures: [
    auditLog("user_updated") // Log what happened
  ]
})
function updateUser(input: { userId: string, name: string }) { }

The three types of conditions

Pre-conditions (requires): What must be true before your method runs

  • Authentication checks
  • Input validation
  • Business rule verification
  • Rate limiting

Post-conditions (ensures): What must be true after your method succeeds

  • Output validation
  • Audit logging
  • Side effect verification

Invariants: What must always be true (both before and after)

  • System health checks
  • Data consistency rules
@contract({
  requires: [auth("user")],                    // Pre: User must be logged in
  ensures: [auditLog("action_performed")],     // Post: Action must be logged
  invariants: [systemHealthCheck],             // Always: System must be healthy
})

Fail fast philosophy

Instead of discovering problems deep in your application or in production, contracts catch issues immediately:

// Without contracts: Error happens deep in database layer
async function createUser(input) {
  const user = await userService.create(input);  // Works
  const profile = await profileService.create({  // Fails here
    userId: user.id,
    email: input.emal  // Typo! Database constraint error
  });
}

// With contracts: Error caught immediately at the boundary
@contract({
  requires: [validates(UserSchema)], // Catches typos instantly
})
async function createUser(input) {
  // This never runs if input is invalid
}

Layer-based protection

Different layers in your application have different concerns:

// API Layer: "Is this request valid?"
@contract({
  requires: [validates(schema), rateLimit("api", 100)],
  layer: "presentation"
})

// Service Layer: "Is this user allowed to do this?"
@contract({
  requires: [auth("user"), businessRule("rule", check)],
  layer: "service"
})

// Data Layer: "Is this data safe to store?"
@contract({
  requires: [validates(schema)],
  retryAttempts: 3,
  layer: "data"
})

Each layer acts as a checkpoint, catching different types of problems.

Quick start

Install

npm install zerot zod

5-minute setup

// lib/zerot.ts - One-time setup
import { configureZerot } from "zerot";

await configureZerot({
  sessionProvider: async () => {
    // Your existing auth logic
    const session = await getSession();
    return { user: session?.user };
  },
});

Your first protected method

import { contract, auth, validates } from "zerot";
import { z } from "zod";

const UserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
});

class UserService {
  @contract({
    requires: [validates(UserSchema)],
    layer: "service",
  })
  async createUser(input: z.infer<typeof UserSchema>) {
    // Input is guaranteed to be valid
    return await this.userRepo.create(input);
  }
}

That's it. Your method now validates input automatically and throws clear errors when something's wrong.

Core philosophy

Contracts as guardrails, not obstacles

Think of contracts like TypeScript for runtime behavior. They catch problems before they become disasters, but they don't get in your way when you're building features.

Layer-based protection

// API layer: Validate requests, check rate limits
@contract({
  requires: [validates(schema), rateLimit("api", 100)],
  layer: "presentation"
})

// Service layer: Check auth, enforce business rules
@contract({
  requires: [auth("user"), businessRule("rule", check)],
  layer: "service"
})

// Data layer: Ensure data integrity, retry on failure
@contract({
  requires: [validates(schema)],
  retryAttempts: 3,
  layer: "data"
})

Each layer handles what it's good at. No missed checks, no forgotten validations.

Fail fast, fail clearly

When something goes wrong, you know exactly what and where:

// Clear error messages
ContractViolationError: Email validation failed in UserService.createUser
  at service layer: Email must be unique
  Code: BUSINESS_RULE_VIOLATION

Getting started patterns

Pattern 1: Protect your database operations first

Start with your repositories - these are your last line of defense:

class UserRepository {
  @contract({
    requires: [validates(CreateUserSchema)],
    ensures: [returns(UserSchema)],
    layer: "data",
    retryAttempts: 3,
  })
  async create(userData: CreateUserInput) {
    return await this.db.user.create({ data: userData });
  }
}

Pattern 2: Add auth to your services

class UserService {
  @contract({
    requires: [auth("user")],
    ensures: [auditLog("user_action")],
    layer: "service",
  })
  async updateProfile(userId: string, data: UpdateData) {
    return await this.userRepo.update(userId, data);
  }
}

Pattern 3: Rate limit your APIs

class UserController {
  @contract({
    requires: [
      validates(RequestSchema),
      rateLimit("create_user", 10), // 10 per minute
    ],
    layer: "presentation",
  })
  async createUser(request: CreateUserRequest) {
    return await this.userService.createUser(request);
  }
}

Pattern 4: Use templates for consistency

import { ContractTemplates } from "zerot";

// One-liner for common patterns
@contract(ContractTemplates.userCRUD("user"))
async createUser(input: UserInput) { /* implementation */ }

@contract(ContractTemplates.adminOnly("delete_user"))
async deleteUser(userId: string) { /* implementation */ }

Perfect for AI code generation - just tell it to use ContractTemplates.userCRUD() and you get consistent security.

Next.js made simple

Setup for Next.js projects

// lib/zerot-setup.ts
import { configureZerot, createNextjsAdapter } from "zerot";

const adapter = createNextjsAdapter({
  sessionCookieName: "auth-token",
});

await configureZerot({
  sessionProvider: () => adapter.extractUser(request),
});

Protect your API routes

// app/api/users/route.ts
import { createZerotApiHandler } from "zerot";

const handler = createZerotApiHandler(adapter, async (request) => {
  const userService = new UserService();
  const data = await request.json();

  // Contracts work automatically with Next.js context
  return Response.json(await userService.createUser(data));
});

export { handler as POST };

JWT sessions in 3 lines

import { NextjsSessionUtils } from "zerot";

const sessions = new NextjsSessionUtils(process.env.JWT_SECRET);
const token = await sessions.createJWTSession(user);
// Set token in cookie, you're done

Configuration that makes sense

Development mode

import { ZerotPresets } from "zerot";

await configureZerot({
  ...ZerotPresets.development(), // Debug logs, helpful errors
  sessionProvider: () => getMySession(),
});

Production mode

await configureZerot({
  ...ZerotPresets.production(), // Optimized, secure defaults
  sessionProvider: () => getMySession(),
});

Custom setup

await configureZerot({
  sessionProvider: () => getMySession(),

  // Simple overrides
  enableAuditLogging: true,
  defaultRetryAttempts: 3,
  sensitiveFields: ["password", "token"],

  // Or go full custom
  customLogger: myLogger,
  rateLimitMaxEntries: 100000,
});

Real-world examples

E-commerce order processing

class OrderService {
  @contract({
    requires: [
      auth("user"),
      validates(OrderSchema),
      businessRule("Stock available", async (order) => {
        return await this.checkStock(order.items);
      }),
      businessRule("Payment valid", async (order) => {
        return await this.validatePayment(order.payment);
      }),
    ],
    ensures: [
      auditLog("order_created"),
      businessRule("Inventory updated", async (result) => {
        return await this.verifyInventoryUpdate(result.orderId);
      }),
    ],
    layer: "service",
  })
  async createOrder(orderData: OrderInput) {
    // All validations pass before this runs
    const order = await this.orderRepo.create(orderData);
    await this.updateInventory(order.items);
    await this.sendConfirmationEmail(order);
    return order;
  }
}

User management with ownership

class ProfileService {
  @contract({
    requires: [
      auth("user"),
      owns("userId"), // User can only edit their own profile
      validates(ProfileUpdateSchema),
    ],
    ensures: [auditLog("profile_updated")],
    layer: "service",
  })
  async updateProfile(input: { userId: string; data: ProfileData }) {
    return await this.userRepo.update(input.userId, input.data);
  }
}

Admin operations

class AdminService {
  @contract({
    requires: [
      auth("admin"),
      rateLimit("admin_action", 20), // Stricter limits for admin
      validates(AdminActionSchema),
    ],
    ensures: [
      auditLog("admin_action", { includeInput: true }), // Full audit trail
    ],
    layer: "service",
  })
  async performAdminAction(action: AdminAction) {
    return await this.executeAdminAction(action);
  }
}

Debugging and monitoring

See what's happening

import { ContractDebugger } from "zerot";

// Get a report of all contract executions
console.log(ContractDebugger.getContractReport());

// Filter by what you care about
const failures = ContractDebugger.getHistoryByStatus("failure");
const serviceLayer = ContractDebugger.getHistoryByLayer("service");

Performance insights

import { ContractPerformanceMonitor } from "zerot";

// See your slowest operations
const report = ContractPerformanceMonitor.getPerformanceReport();
console.log("Bottlenecks:", report.slice(0, 5));

Custom metrics

import { Metrics } from "zerot";

// Track what matters to your app
Metrics.increment("user_signups");
Metrics.record("api_response_time", responseTime);
Metrics.gauge("active_users", userCount);

Error handling you'll actually use

Layer-based error responses

try {
  await userService.createUser(input);
} catch (error) {
  if (error instanceof ContractViolationError) {
    switch (error.layer) {
      case "presentation":
        return NextResponse.json({ error: "Bad request" }, { status: 400 });
      case "service":
        return NextResponse.json({ error: error.message }, { status: 403 });
      case "data":
        return NextResponse.json({ error: "Server error" }, { status: 500 });
    }
  }
}

Testing contracts

import { ZerotPresets } from "zerot";

beforeEach(async () => {
  await configureZerot({
    ...ZerotPresets.testing(), // Predictable test environment
    sessionProvider: () => ({
      user: { id: "test-user", roles: ["user"] },
    }),
  });
});

test("validates input", async () => {
  await expect(userService.createUser(invalidInput)).rejects.toThrow(
    "Validation failed"
  );
});

Quick reference

Common contracts

// Input validation
@contract({ requires: [validates(Schema)] })

// Auth required
@contract({ requires: [auth("user")] })

// Rate limiting
@contract({ requires: [rateLimit("operation", 10)] })

// Resource ownership
@contract({ requires: [owns("resourceId")] })

// Business rules
@contract({
  requires: [businessRule("Description", checkFunction)]
})

// Audit logging
@contract({ ensures: [auditLog("action")] })

// Retry on failure
@contract({ retryAttempts: 3 })

// Layer classification
@contract({ layer: "service" })

Templates for common patterns

import { ContractTemplates, ExtendedContractTemplates } from "zerot";

// Basic patterns
ContractTemplates.userCRUD("user");
ContractTemplates.adminOnly("action");
ContractTemplates.publicAPI("endpoint");

// Advanced patterns
ExtendedContractTemplates.secureCRUD({
  role: "user",
  resourceField: "userId",
  inputSchema: Schema,
  operation: "update",
});

All exports

// Core
export { contract, ContractError, ContractViolationError } from "zerot";

// Conditions
export {
  auth,
  owns,
  validates,
  returns,
  rateLimit,
  auditLog,
  businessRule,
} from "zerot";

// Templates
export {
  ContractTemplates,
  ExtendedContractTemplates,
  smartContract,
} from "zerot";

// Configuration
export { configureZerot, ZerotPresets } from "zerot";

// Next.js
export {
  createNextjsAdapter,
  createZerotApiHandler,
  NextjsSessionUtils,
} from "zerot";

// Development
export { ContractDebugger, ContractPerformanceMonitor, Metrics } from "zerot";

Ready to make your backend unbreakable? Start with one @contract on your most critical method. Add more as you go. Your future self will thank you.

License

MIT - Build something awesome.