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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@jsonql/validator

v0.11.2

Published

@jsonql/validator general purpose validation library that test type and value with a plugin system

Downloads

38

Readme

@jsonql/validator

This was call jsonql-param-validator now move into the @jsonql scope with many additional features

We are now making it a general purpose validation library to use with Javascript / Typescript project

Usage

// breaking change 0.7.0 change to Validator
import { Validator } from '@jsonql/validator'
// @TODO how to get the ast
const validator = new Validator(ast: Array<JsonqlPropertyParamMap>)

validator.validate(values)
        .then(result => {
          // The same that comes in - if there is default value then nit will be apply
        })
        .catch(error => {
          console.log(error.details)
          // contains position where your validation failed
        })

Type JsonqlPropertyParamMap

This is what the ast looks like, we use our other plugin @jsonql/ast to generate this automatically, but you can write your own

// @TODO should we have two different types map 1 for external use?
declare type JsonqlPropertyParamMap = {
  name: string // the argument name
  required: boolean
  type: string | Array<string> // Array<string> is union type
  tstype?: string // generated by swc
  types?: any // this is internal generated
  validate?: (value: unknown) => boolean
  validateAsync?: (value: unknown) => Promise<boolean>
  // rules get create the moment we init the object
  rules?: Array<JsonqlValidateCbFn>
}

Here is an example of a normal function using our customized AST extractor

export default function baselineFn(value1: string, value2: number, value3 = false): string {

  return `${value1} with number ${value2} ${value3 ? ' fine' : ' or not'}`
}

please note our extractor only support default export - not export this is to do with how we organize code within a jsonql project but fear not if this doesn't work for you. The way we build our @jsonql/ast is like many small parts, and you can easily mix and match to build one for your need

Then it will transform into this for the validator:

{
  "baselineFn": [
    {
      "name": "value1",
      "required": true,
      "type": "string"
    },
    {
      "name": "value2",
      "required": true,
      "type": "number"
    },
    {
      "name": "value3",
      "required": true,
      "type": "boolean",
      "defaultvalue": false
    }
  ]
}

To use with the @jsonql/validator

const validator = new Validator(ast.baselineFn)

Note that we ONLY use the ast.baselineFn part which is an array, because the validator only cares about the argument parameter and nothing else

When use with Javascript, you can create your own AST map like the above example, and it will able to understand your parameter types.

Register your plugin

import { Validator } from '@jsonql/validator'
// See above example how to get the ast
const validator = new Validator(ast)

validator.registerPlugin('myStandardPlugin', {
  main: (value: any): boolean => {
  // must return boolean
  }
})

validator.registerPlugin('myPluginRequireArg', {
  main: (arg1: number, arg2: number, value: any): boolean => {
    // do things with it
  },
  params: ['arg1', 'arg2']
})

There are required fields when you build a plugin with extra argument:

  • name: string - the name of the plugin, and we will check if its collide with one of our own (see the complete list below)
  • main: Function: boolean - the actual function to perform the validation. It expect a boolean return for validator to know if the validation is success or not.
  • params: Array<string> - the parameters name used in your main function, see the value which is the value that gets validate MUST be the last of the argument. The reason is during run time, we curry your function first and pass into a queue system to run the value. See example below

Curry main function example:

// your function
function main(arg1: number, arg2: number, value: any) {
  return value >= arg2 && value < arg2
}
const queueFn = curry(main)(arg1, arg2) // see how we get that value from example below
queueFn(value): boolean

Then when you need to execute your validator


validator.addValidationRules([
  {plugin: 'myPlugin', arg1: 100, arg2: 200}
])

validator.validate([101])
          .then((result: any) => {
            // result will be an object
            // {arg: 101}
          })
          .catch((err: ValidationError) => {
            // look for err.detail

            // if this fail you will get an array contain two number
            // [1,0]
            // what that means is it failed the second rule (zero based index) on the first position
            // the built in type checking rule always comes first
          })

At first it might sound weird why the Error return [argNameIndex, validationRuleIndex]. The reason behind this is because we need to have a super tiny format that travel between server side / client side. Instead of adding loads of code just to deal with an Error message. We just tell you the position which rule failed. And you can map it out yourself. And the up side is - you can map it in multiple languages, in different code, your imagination is the limitation :)

The built-in rule supported types

The build in rules only support primitive types: string, number, boolean, array checking (without value check, just Array.isArray) and object (typeof) check. If you need complex array or object key/value check, please DIY that's what the plugin is for!

Built-in plugins

All the built-in plugins provide by another package, please see @jsonql/validator-core for more info.

Writing plugin vs writing function

Apart from creating a plugin, you could just pass a function or Async function directly.

// continue from previous example
// using name parameter
validator.addValidationRules({
  value2: {
    name: 'myExtraRule1',
    validate: function(value: number) {
      return value > 1000
    }
  }
})

The above method will automatically insert into the validation sequence right after the built-in rule (in our example, which is checking if your input value is a number)

If this fail then you will get [1,1] as an error result.

You could also add async method (all rules convert to async internally):

// let say we want to validate an email address against the database
validator.addValidatorRules({
  email: {
    name: 'MyServerSideCheck',
    server: true, // for our contract system this will only get call on the server side @TODO
    validateAsync: async function(value: string) {
      return db.checkIfExists(value)
        .then(result => {
          return true
        })
        .catch(error => {
          return false
        })
    }
  }
})

All the function(s) expect a true / false boolean return result and nothing else.

Important unlike using registerPlugin, you can not pass extra parameters (of course you can just put the required value inside the function itself). Also this validate method will always be one side only (server:true if you use the jsonql / velocejs) system because we can not pass your inline function over the wire.

If you need share this rule between client / server. You MUST create it in an external file, and use the loadPlugin method.

Server side only validation rule

@TODO will be available in next release

By default, all your inline plugin, validate, validateAsync function will treat as server: true what that means is, when using our jsonql / velocejs system, the contract will not contain (@TODO in the next release) those validation info.

The reason is, very often on a SPA system, you validate user input on your UI once then the same rule will run again on the server side. Which is a lot of duplication; and in our example about validate an email address, you might want to do some server side only check (i.e. check against the database). We provide this mapping to separate rules for front end / back end.

More to come.

Standalone ValidatorBase

You can use a standalone (without plugin system) Jsonql Validator by import it like this:

import { ValidatorBase } from '@jsonql/validator/base'

const vb = new ValidatorBase(astMap)

  vb.validate(values)
    .then(result=> {
        // the result object is different
    })

This module will only validate against the type you define plus the validate or validateAsync method you add via the addValidationRules method. If you try to use the plugin then it will throw in that particular rule you add with ValidationError object, and the ValidationError.message will be NO_PLUGIN_DUMMY_FUNCTION.

We add this mainly for us to create a more modular system, in our @jsonql/validators (@NOTE with an s) it will create one plugin system and share among all the API interfaces.


Joel Chu

NEWBRAN LTD / TO1SOURCE CN (c) 2022