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

zodmut

v0.0.3

Published

Mutable Pipeline extension for zod - runtime-adaptive validation

Readme

zodmut

Coverage npm version License: MIT

Mutable Pipeline extension for zod - runtime-adaptive validation.

Supports Zod v3 and Zod v4.

Why zodmut?

zod is great for static validation. But what if your rules need to change at runtime?

  • Multi-tenant apps with tenant-specific rules
  • Form builders where users define fields
  • Feature flags that modify validation
  • Permission-based validation rules
  • Wizard / multi-step forms

zodmut adds mutable operations to zod schemas—add, remove, and update fields dynamically.

For more background on the Mutable Pipeline Pattern, see: Mutable Pipeline Pattern: Revisiting My Thoughts on Dynamic Pipelines

Installation

npm install zodmut zod

Quick Start

import { z } from 'zod';
import { zodmut } from 'zodmut';

const zm = zodmut(z);

// Define schema (same as zod)
const schema = zm.object({
  email: zm.string().email(),
  name: zm.string().min(1),
});

// Make it mutable
const mutable = schema.mutable();

// Add fields dynamically
mutable.addField('company', zm.string().min(1));

// Remove fields
mutable.removeField('name');

// Update existing fields
mutable.updateField('email', zm.string().email().max(255));

// Check current structure
mutable.snapshot(); // ['email', 'company']

// Validate
const result = mutable.safeParse(data);

// Use with zod ecosystem (React Hook Form, tRPC, etc.)
const zodSchema = mutable.toZod();

Features

Mutable Operations

const schema = zm.object({ name: zm.string() });
const mutable = schema.mutable();

mutable.addField('email', zm.string().email());
mutable.removeField('name');
mutable.updateField('email', zm.string().email().max(255));
mutable.snapshot();  // Get current field names
mutable.freeze();    // Convert back to immutable

OpenAPI Support

const UserSchema = zm
  .object({
    id: zm.string(),
    email: zm.string(),
  })
  .openapi('User', { description: 'A user object' })
  .setFieldOpenAPI('email', { format: 'email' });

// Generate OpenAPI schema
const openAPISchema = UserSchema.getOpenAPISchema();

See docs/openapi.md for details.

Protocol Buffers Support

const UserSchema = zm
  .object({
    id: zm.string(),
    email: zm.string(),
    age: zm.number(),
  })
  .setProtobuf({ packageName: 'user', messageName: 'User' })
  .setFieldProtobuf('id', { fieldNumber: 1 })
  .setFieldProtobuf('email', { fieldNumber: 2 })
  .setFieldProtobuf('age', { fieldNumber: 3, type: 'int32' });

// Generate .proto definition
const proto = UserSchema.getProtobufSchema();

See docs/protobuf.md for details.

External Schema Wrapping

Wrap schemas from external libraries like drizzle-zod:

import { createInsertSchema } from 'drizzle-zod';

const drizzleSchema = createInsertSchema(usersTable);
const zmSchema = zm.fromZod(drizzleSchema);

// Now you can use zodmut features
zmSchema
  .mutable()
  .addField('confirmEmail', zm.string())
  .freeze();

Ecosystem Compatibility

zodmut works with the entire zod ecosystem via toZod():

| Library | Usage | |---------|-------| | React Hook Form | zodResolver(schema.toZod()) | | tRPC | .input(schema.toZod()) | | @hono/zod-validator | zValidator('json', schema.toZod()) | | @hono/zod-openapi | schema: schema.toZod() | | drizzle-zod | zm.fromZod(createInsertSchema(table)) | | zod-validation-error | fromZodError(result.error) |

See docs/integrations.md for examples.

API Reference

Initialization

import { z } from 'zod';
import { zodmut } from 'zodmut';

const zm = zodmut(z);

Schema Types

zm.string()
zm.number()
zm.boolean()
zm.date()
zm.array(schema)
zm.object({ ... })
zm.enum(['a', 'b'])
zm.union([...])
zm.literal(value)
zm.tuple([...])
zm.record(keySchema, valueSchema)
zm.any()
zm.unknown()
zm.null()
zm.undefined()

Object Operations

schema.extend({ newField: zm.string() })
schema.merge(otherSchema)
schema.pick(['field1', 'field2'])
schema.omit(['field1'])
schema.partial()
schema.required()
schema.passthrough()
schema.strict()
schema.strip()

Validation

schema.parse(data)
schema.safeParse(data)
schema.parseAsync(data)
schema.safeParseAsync(data)

Refinements

schema.refine(fn, { message: '...' })
schema.superRefine(fn)
schema.transform(fn)

Metadata

// Field metadata
schema.setFieldMeta('field', { label: 'Email' })
schema.getFieldMeta('field')

// Default/catch values
schema.setFieldDefault('field', 'default')
schema.setFieldCatch('field', 'fallback')

Known Limitations

Type inference in updateNested

updateNested returns MutableZodmObject<T> where T is the original type. Fields added/removed inside the callback are not reflected in TypeScript types.

const schema = zm.object({
  user: zm.object({ name: zm.string() })
}).mutable();

// TypeScript doesn't know about 'age'
const updated = schema.updateNested('user', (nested) =>
  nested.addField('age', zm.number())
);

// Runtime works, but no type inference for 'age'
updated.parse({ user: { name: 'John', age: 25 } });

Workaround: Use explicit typing or freeze() and re-wrap if strong typing is needed.

Refinements referencing removed fields

If you use refine() or superRefine() that references a field, then remove that field, you'll get a runtime error:

const schema = zm
  .object({ name: zm.string(), age: zm.number() })
  .refine((data) => data.age >= 0) // references 'age'
  .mutable()
  .removeField('age'); // removes 'age'

schema.parse({ name: 'John' }); // Runtime error: data.age is undefined

Workaround: Only use refinements that don't reference fields you plan to remove, or add refinements after all mutations.

Nested metadata in updateNested

When using updateNested, metadata for the parent field is preserved, but newly added nested fields don't inherit metadata automatically:

const schema = zm
  .object({ user: zm.object({ name: zm.string() }) })
  .setFieldOpenAPI('user', { description: 'User object' })
  .mutable()
  .updateNested('user', (nested) =>
    nested.addField('age', zm.number()) // 'age' has no OpenAPI metadata
  );

Workaround: Set metadata after mutations are complete.

Why zod?

zod is an exceptional validation library. Its TypeScript-first approach, excellent type inference, and elegant API have made it the de facto standard for schema validation in the TypeScript ecosystem.

zodmut doesn't try to replace zod—it builds on top of it. All the heavy lifting (parsing, validation, error handling, type inference) is done by zod. zodmut simply adds a thin layer for runtime mutability.

If you haven't tried zod yet, you should. It's fantastic.

Acknowledgments

Built with respect and gratitude for zod by @colinhacks.

License

MIT