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

jnv

v0.8.0

Published

JSON Validator

Downloads

175

Readme

🎲 jnv | JSON Validator

Быстрый и расширяемый JSON-Валидатор.

  • Простая декларативная схема.
  • Смешанная декларация моделей JsonLike + *Model<JsonLike>.
  • Регистрация ошибок.
  • Композиция валидаторов через .pipe().
  • Расширяемый API.
  • Поддержка TypeScript.
  • Без зависимостей(кроме js-base-error).

Установка

npm i jnv

jnv использует "peerDependencies": { "js-base-error": "0.7.0" }. Использование peerDependency гарантирует, что все части вашего приложения (включая jnv и другие библиотеки) будут использовать один и тот же экземпляр базового класса ошибок BaseError. Если ваш проект не использует js-base-error, библиотека установится автоматически, иначе проверьте совместимость версии [email protected] в вашем package.json.

🔥 Пример использования

import { Factory } from 'jnv'

const v = new Factory()

const userModel = v.obj({
  id: v.positive(), // эквивалентно int().min(1)
  name: v.str().min(3),
  email: /^[0-9a-z]+@[0-9a-z]+\.[a-z]+$/i, // эквивалентно v.re(...)
  gender: v.enum('male', 'female').optional(),
  // Оборачиваем объект в тип, для возможности применения .stopError() - игнорировать
  // ошибку и установить значение по умолчанию или `null`(если его нет).
  address: v.obj({
    city: v.str().nonempty(), // эквивалентно .str().min(1)
    street: 'any string',     // эквивалентно .str()
    zipCode: ''
  }).stopError()
}).rename('User') // полезно для регистрации ошибки

const sampleUser = {
  id: 1,
  name: 'John',
  email: '[email protected]',
  // gender: 'male' as ('male' | 'female'),
  address: {
    city: 'Springfield',
    street: '742 Evergreen Terrace',
    zipCode: '90210'
  }
}
expect(userModel.validate(sampleUser)).toStrictEqual({ ok: true, value: sampleUser })

API(Кратко)

Базовые типы - методы Factory

  • of() - Автоматический парсер.
  • raw() - Необработанный JsonLike, объект не проверяется и возвращается как есть.
  • bool() - boolean.
  • num()/int()/nonnegative()/positive()/range() - number.
  • str()/nonempty()/re() - string
  • literal()/enum() - Литералы(примитивы null|bollean|number|string).
  • union() - Один из вариантов JsonLike.
  • obj() - PlainObject(не массив).
  • arr() - Массивы JsonLike[].
  • tuple() - Эмуляция Tuple.
  • custom() - Пользовательская функция.
  • pipe() - Цепочка Model<JsonLike>.

Модификаторы типа значения - методы Model

  • min()/max()/range() - Для чисел, строк и массивов.
  • nonempty() - Непустая строка или массив.

Опции - методы Model

  • def() - Установить значение по умолчанию.
  • optional() - Аналог необязательного свойства TS { prop?: type }.
  • stopError() - Подавить ошибку и заменить на значение по умолчанию для недопустимого типа.
  • removeFaulty() - Удалить недопустимый элемент массива.
  • rename() - Установить имя модели.
  • freeze() - Запрещает дальнейшие расширения типа.

Результат - методы Model

validate() - Валидация с гарантированным подавлением ошибки и результатом {ok,value,error,warning} validateOrThrow() - Вернуть тип или поднять ошибку.

{
  ok: boolean,
  value?: T,         // только если ok: true
  error?: JnvError,  // только если ok: false
  warning?: JnvError // только если нет error и ok:true
}

Копирование или перезапись выходного объекта зависит от параметра конфигурации {createMode:'all'|'obj'|'arr'|'none'}.

Больше примеров

Необязательно определять каждому вложенному типу собственный класс модели, если он не использует модификаторы. Любой Json-тип будет выведен из его JS-типа:

// Это ...
v.arr([v.obj({...})])
// ... эквивалентно
v.arr([{...}])

Регулярное выражение RegExp приводится к типу string с проверкой по регулярному выражению: {foo: /re/} => {foo: v.re(/re/, ...)}. Любая функция приводится к CustomModel.

Единственные типы которые невозможно вывести, это литералы. Литералы допускают только Json-примитивы:

const abbr = v.literal('ABBR')
const enabled = v.enum(false, true, 0, 1, 'off', 'on')

Как заменить недопустимый тип массива:

// Параметры конфигурации доступны через JSDoc.
const v = new Factory({
  createMode: 'obj' // создадим только объекты, массивы перезапишем
})

const arrModel = v.arr([
  v.obj({ enabled: v.enum('on', 'off') })
    .def({ AhHaHa: '😋' }) // значение по умолчанию
    .stopError()           // подавить ошибку
]).freeze()

expect(arrModel.validate([
  { enabled: 'on' },
  { enabled: 'oh no' },
  { enabled: 'off' }
]).value).toStrictEqual([
  { enabled: 'on' },
  { AhHaHa: '😋' },
  { enabled: 'off' }
])

Как пропустить невалидный элемент:

const arrCleared = v.arr([
  v.obj({ enabled: v.enum('on', 'off') })
]).removeFaulty() // удалить невалидное значение

expect(arrCleared.validate([
  { enabled: 'on' },
  { enabled: 'oh no' },
  { enabled: 'off' }
]).value).toStrictEqual([
  { enabled: 'on' },
  { enabled: 'off' }
])

Расширение классов

Более подробное описание расширения пользовательских типов можно увидеть на этой странице dev.md

Класс валидатора обязан реализовать единственный абстрактный метод _validate(ctx, value)

import { 
  type Context,
  type Re,
  type TRes,
  StrMetadata,
  Model, 
  Factory, 
  isString 
} from 'jnv'

class PhoneNumberModel extends Model<TJsonLike, any> {
  // Подскажем TS более конкретный тип Metadata
  declare protected readonly _meta: StrMetadata & { re: readonly Re[] }

  protected _validate (ctx: Context, value: any): TRes<string> {
    if (!isString(value)) {
      return ctx.faultyValueMessage(this.kind, 'Expected a string.', value)
    }
    // Получим ссылку на Metadata и проверим варианты RegExp
    for (const re of this._meta.re) {
      if (re.test(value)) {
        return { ok: true, value }
      }
    }
    return ctx.faultyValueMessage(this.kind, 'Invalid phone number format.', value)
  }
}

Расширяем фабрику валидаторов:

class MyFactory extends Factory {
  // Добавим к фабрике новый тип
  phoneNumber (): PhoneNumberModel {
    // Используем кеш regExp
    const re = this._regExpCache.getOf(/^\d{3}-\d{3}-\d{4}$/)
    const meta = new StrMetadata(null, null, false, [re])
    // Последний параметр false - это ключ isFrozen и здесь он не нужен.
    // Model будет автоматически привязана к свойству объекта.
    return new PhoneNumberModel(this._config, this._defaultOptions, meta, false)
  }
}

Используем наш валидатор:

const v = new MyFactory()

const phoneModel = v.phoneNumber()

expect(phoneModel.validate('123-456-7890'))
  .toStrictEqual({ ok: true, value: '123-456-7890' })

expect(phoneModel.validate('123-456-789').error!.message)
  .toContain('Invalid phone number format.')