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

@nexus-state/form-schema-dsl

v0.2.0

Published

Custom DSL schema validator for Nexus State forms

Readme

@nexus-state/form-schema-dsl

Custom DSL schema validator for Nexus State forms

npm version TypeScript License


🎯 When to Use DSL

✅ Choose DSL if you need:

| Use Case | Why DSL | |----------|---------| | Simple syntax | Easy to read and write | | Composable rules | Combine validators easily | | Async support | Debouncing, retry, cache built-in | | Lightweight | Minimal bundle size |

❌ Use alternatives if:

| Use Case | Better Alternative | |----------|-------------------| | Rich ecosystem | @nexus-state/form-schema-zod | | Mature library | @nexus-state/form-schema-yup | | JSON Schema standard | @nexus-state/form-schema-ajv |


🎯 Overview

DSL plugin provides a custom Domain-Specific Language for form validation in Nexus State. It offers a simple, declarative way to define validation rules without the complexity of full schema libraries.

Why DSL?

  • ✅ Simple syntax — easy to read and write
  • ✅ Composable — combine rules easily
  • ✅ Async support — debouncing, retry, cache built-in
  • ✅ Lightweight — minimal bundle size
  • ✅ Extensible — create custom validators

📦 Installation

npm install @nexus-state/form-schema-dsl

Peer dependencies:

  • @nexus-state/form — core form package

🚀 Quick Start

Basic Form with DSL

import { createForm } from '@nexus-state/form';
import { createStore } from '@nexus-state/core';
import { dslPlugin, required, minLength, email } from '@nexus-state/form-schema-dsl';

const store = createStore();

// Define schema
const loginSchema = {
  email: [required, email],
  password: [required, minLength(8)],
};

// Create form with DSL plugin
const loginForm = createForm(store, {
  schemaPlugin: dslPlugin,
  schemaConfig: loginSchema,
  initialValues: {
    email: '',
    password: '',
  },
});

// Validate form
const isValid = await loginForm.validate();
if (isValid) {
  console.log('Form is valid!');
} else {
  console.log('Errors:', loginForm.errors);
}

📖 API Reference

DSL Schema

interface DSLSchema<TValues = Record<string, unknown>> {
  [fieldName: string]: DSLRule | DSLRule[];
}

interface DSLRule<TValue = any> {
  validate: (value: TValue, allValues: Record<string, unknown>) => string | null | Promise<string | null>;
  code?: string;
  message?: string;
  options?: {
    debounce?: number;
    retry?: number;
    timeout?: number;
  };
}

Schema Configuration

interface DSLFormOptions {
  schemaType: 'dsl';
  schemaConfig: DSLSchema;
  initialValues: Record<string, unknown>;
  defaultValidationMode?: 'onChange' | 'onBlur' | 'onSubmit';
  showErrorsOnTouched?: boolean;
}

📝 Examples

1. Registration Form

import {
  required,
  minLength,
  maxLength,
  email,
  pattern,
} from '@nexus-state/form-schema-dsl';

const registrationSchema = {
  username: [required, minLength(3), maxLength(20), pattern(/^[a-zA-Z0-9_]+$/)],
  email: [required, email],
  password: [required, minLength(8)],
  age: [required, minValue(18), maxValue(120)],
  terms: [required, equalTo(true)],
};

2. Async Validation (Uniqueness Check)

import { required, minLength, email, unique } from '@nexus-state/form-schema-dsl';

const registrationSchema = {
  username: [
    required,
    minLength(3),
    unique('users', 'username', {
      debounce: 500,  // Wait 500ms after typing
      retry: 2,       // Retry on network error
      timeout: 5000,  // 5 second timeout
    }),
  ],
  email: [
    required,
    email,
    unique('users', 'email', {
      debounce: 500,
    }),
  ],
};

3. Custom Validators

import { custom } from '@nexus-state/form-schema-dsl';

const strongPassword = custom(
  (value) => {
    if (!/[A-Z]/.test(value)) return 'Need uppercase letter';
    if (!/[a-z]/.test(value)) return 'Need lowercase letter';
    if (!/[0-9]/.test(value)) return 'Need number';
    if (!/[!@#$%^&*]/.test(value)) return 'Need special character';
    return null;
  },
  'strong_password'
);

const schema = {
  password: [required, minLength(8), strongPassword],
};

4. Cross-field Validation

import { required, minLength, equalTo } from '@nexus-state/form-schema-dsl';

const passwordSchema = {
  password: [required, minLength(8)],
  confirmPassword: [required, equalTo('password')], // Must match 'password' field
};

5. Conditional Validation

import { required, conditional, matchesField } from '@nexus-state/form-schema-dsl';

const schema = {
  needsShipping: [],
  shippingAddress: [
    conditional({
      field: 'needsShipping',
      operator: 'equals',
      value: true,
      then: [required],
    }),
  ],
};

6. Phone Number Validation

import { required, phone } from '@nexus-state/form-schema-dsl';

const schema = {
  phone: [required, phone('US')], // US phone format
};

// Or with custom pattern
import { pattern } from '@nexus-state/form-schema-dsl';

const schema = {
  phone: [required, pattern(/^\+?[1-9]\d{1,14}$/, 'Invalid phone number')],
};

7. URL Validation

import { required, url } from '@nexus-state/form-schema-dsl';

const schema = {
  website: [url], // Optional URL
  portfolio: [required, url('https')], // Required HTTPS URL
};

8. Credit Card Validation

import { required, creditCard } from '@nexus-state/form-schema-dsl';

const schema = {
  cardNumber: [required, creditCard],
};

9. Array Length Validation

import { required, arrayLength } from '@nexus-state/form-schema-dsl';

const schema = {
  skills: [required, arrayLength(1, 10)], // 1-10 skills
};

10. One Of / Not One Of

import { oneOf, notOneOf } from '@nexus-state/form-schema-dsl';

const schema = {
  role: [oneOf(['admin', 'user', 'guest'])],
  status: [notOneOf(['banned', 'deleted'])],
};

🔧 Built-in Validators

String Validators

| Validator | Description | Example | |-----------|-------------|---------| | required | Field is required | [required] | | minLength(n) | Minimum string length | [minLength(3)] | | maxLength(n) | Maximum string length | [maxLength(100)] | | length(n) | Exact string length | [length(5)] | | lengthRange(min, max) | Length range | [lengthRange(3, 20)] | | pattern(regex, message?) | Regex pattern | [pattern(/^[a-z]+$/)] | | email | Email format | [email] | | url(protocol?) | URL format | [url('https')] | | phone(country?) | Phone format | [phone('US')] |

Number Validators

| Validator | Description | Example | |-----------|-------------|---------| | minValue(n) | Minimum value | [minValue(18)] | | maxValue(n) | Maximum value | [maxValue(120)] | | valueRange(min, max) | Value range | [valueRange(1, 10)] | | positive | Must be positive | [positive] | | negative | Must be negative | [negative] | | integer | Must be integer | [integer] |

Boolean Validators

| Validator | Description | Example | |-----------|-------------|---------| | equalTo(value) | Must equal value | [equalTo(true)] | | notEqualTo(value) | Must not equal value | [notEqualTo(false)] |

Array Validators

| Validator | Description | Example | |-----------|-------------|---------| | arrayLength(min, max?) | Array length | [arrayLength(1, 10)] |

Selection Validators

| Validator | Description | Example | |-----------|-------------|---------| | oneOf(values) | Must be one of values | [oneOf(['a', 'b'])] | | notOneOf(values) | Must not be any of values | [notOneOf(['x', 'y'])] |

Cross-field Validators

| Validator | Description | Example | |-----------|-------------|---------| | equalTo(field) | Must equal another field | [equalTo('password')] | | matchesField(field) | Must match another field | [matchesField('password')] | | conditional(config) | Conditional validation | See below |

Async Validators

| Validator | Description | Example | |-----------|-------------|---------| | unique(table, field, options?) | Check uniqueness | [unique('users', 'email')] | | exists(table, field, options?) | Check existence | [exists('users', 'id')] | | asyncCustom(fn, options?) | Custom async validator | See below |


🔧 Advanced Usage

Conditional Validation

import { conditional, required } from '@nexus-state/form-schema-dsl';

const schema = {
  needsShipping: [],
  shippingAddress: [
    conditional({
      field: 'needsShipping',
      operator: 'equals',
      value: true,
      then: [required],
    }),
  ],
};

Custom Async Validator

import { asyncCustom } from '@nexus-state/form-schema-dsl';

const promoCodeValidator = asyncCustom(
  async (value) => {
    const response = await fetch(`/api/check-promo?code=${value}`);
    const data = await response.json();
    return data.valid ? null : 'Invalid promo code';
  },
  {
    debounce: 500,
    retry: 2,
    timeout: 5000,
  }
);

const schema = {
  promoCode: [required, promoCodeValidator],
};

Combining Validators

import { required, minLength, maxLength, pattern } from '@nexus-state/form-schema-dsl';

const schema = {
  username: [
    required,
    minLength(3),
    maxLength(20),
    pattern(/^[a-zA-Z0-9_]+$/, 'Only letters, numbers, and underscores allowed'),
  ],
};

Custom Error Messages

import { minLength } from '@nexus-state/form-schema-dsl';

const schema = {
  password: [
    minLength(8, 'Password must be at least 8 characters long'),
  ],
};

⚠️ Troubleshooting

Issue: "validateField always returns null"

Explanation: DSL validators require all values for cross-field validation. The plugin's validateField method returns null and relies on full validate() for proper validation.

Solution: Use form.validate() instead of field-level validation.

Issue: Async validator not working

Solution: Ensure your validator returns a Promise:

// ✅ Correct
const validator = asyncCustom(async (value) => {
  const result = await checkValue(value);
  return result ? null : 'Error';
});

// ❌ Wrong
const validator = asyncCustom((value) => {
  checkValue(value); // Missing return and async
});

Issue: Conditional validation not triggering

Solution: Ensure the conditional field name matches exactly:

// ✅ Correct
conditional({
  field: 'needsShipping', // Must match field name exactly
  operator: 'equals',
  value: true,
  then: [required],
});

// ❌ Wrong
conditional({
  field: 'needShipping', // Typo - doesn't match
  // ...
});

📚 Resources


🔗 See Also

Full ecosystem: Nexus State Packages


📄 License

MIT © Nexus State Contributors