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

joi-extender

v0.2.10

Published

Extends Joi with new top-level validations and chain-ables.

Downloads

24

Readme

joi-extender

Extends hapi joi with new top-level validation tests.

npm version Build Status Dependencies Status DevDependencies Status

NPM NPM

NOTE: This module relies upon and leverages special knowledge of Joi's internal structure which may change in the future and while I freely admit that this is usually a "VBI*", every effort will be made to assure it continues to work as Joi is updated.

Installation

npm install joi-extender

To build jsdoc:

npm run build

See package.json for other script targets.

Description

joi-extender allows you to add your own "top-level" validate-able types to Joi as well as new "chainable" methods specific to the new type.

In other words, it allows you to add your own base validation types, just like joi.string(), as well as further chain-able tests specific to the newly created type, like joi.string().regex().

Example - from /examples/fiddle.js

Here's a full example, which I'll pick apart below:

(Note that while this is a very trivial example that can be easily replicated using existing joi types and validations, if we did that, we wouldn't get to use this module, would we? For a real-world example, see test/extender.js which defines a new joi.dma() type using is_dma.)


var util = require('util'),
    joi = require('joi'),
    extender = require('../lib/extender'); // require('joi-extender');

var MIN_LEN = 1,   // minimum acceptable length
    MAX_LEN = 100; // maximum acceptable length

extender.addValidator('fiddle', {

  requirements: {

    base: function (val) {
      return 'string' === typeof val;
    },

    len: function (val) {
      return val.length >= MIN_LEN && val.length <= MAX_LEN;
    }

  },

  tests: {

    isUpperCase: function (val, args) {
      return val.match(/^[A-Z]+$/) ? null : 'uppercase';
    },

    range: function (val, args) {
      if (!(Array.isArray(args) && args.length === 2)) {
        throw new Error('joi.fiddle().range() requires two numeric arguments');
      }
      if(!('number' === typeof args[0] && 'number' === typeof args[1])) {
        throw new Error('joi.fiddle().range() requires two numeric arguments');
      }

      return val.length >= args[0] && val.length <= args[1] ? null : 'range';
    },

    disallow: function (val, args) {
      if (!(Array.isArray(args) && 'string' === typeof args[0])) {
        throw new Error('joi.fiddle().disallow() requires one string argument');
      }
      return val === args[0] ? 'disallowed' : null;
    }

  },

  errmsgs: {

    'base': 'must be a string',

    'len': 'must be >= ' + MIN_LEN + ' and <= ' + MAX_LEN + ' chars in length',

    'range': '{{key}} "{{value}}" must be between {{args.0}} and {{args.1}} chars in length',

    'uppercase': 'must be uppercase',

    'disallowed': '"{{value}}" is not an allowed value for "{{key}}"'

  }

});

extender.registerType(joi, 'fiddle');

// ======================================

// and test it out...

function printResult(val) {
  var err = val.error ? val.error.toString() : 'no error';
  //console.log(util.inspect(val,{depth:null}));
  console.log(err);
}

var result;

result = joi.fiddle().required().validate();
printResult(result);
// => {error: '"value" is required', value: undefined }


result = joi.fiddle().validate(1);
printResult(result);
// => {error: '"value" must be a string', value: 1 }

result = joi.fiddle().validate('');
printResult(result);
// => {error:'"value" must be >= 1 and <= 100 chars in length', value: '' }

result = joi.fiddle().label('range value').range(10, 20).validate('1');
printResult(result);
// => {error:'range value "1" must be between 10 and 20 chars in length', value: '1' }

result = joi.fiddle().validate('bar');
printResult(result);
// => {error: null, value: 'bar' }

result = joi.fiddle().disallow('bar').label('name').validate('bar');
printResult(result);
// => {error: '"bar" is not an allowed value for "name"', value: 'bar' }

result = joi.fiddle().required().isUpperCase().validate('foo');
printResult(result);
// => {error: '"value" must be uppercase', value: 'foo' }

result = joi.validate('FOO', joi.fiddle().isUpperCase().disallow('BAR').required());
printResult(result);
// => {error: null, value: 'FOO' }

So, how does this work?

First, we need to create a new validator which we will be able to call as joi.fiddle():

// create a new Joi validation "top-level" validation function called "fiddle"
extender.addValidator('fiddle',{

Note that the first argument is the name of the validator type we want to create, and is used to add a new property to the joi object and to report errors correctly.

Next, we can add "requirements" tests that will all be called when our validator is first invoked. Tests defined here should return true if the value passes validation and will report the error defined below under the same key as the test. (See errmsgs below.)

One possible use for these tests would be to assure the value is a native JS type, such as a String.

  requirements:{
  
    // Let's assure we're working with a string:
    base:function(val)   { 
      return 'string'===typeof val; 
    },
    
    // and that it's the right size:
    length:function(val) {
      return val.length >= MIN_LEN && val.length <= MAX_LEN;
    }
    
  },

Next, we can add further optional validation tests that become our new "chainables" and are specific to our new validation type, like .required() or .length(limit). These tests should return null on success or the key of the appropriate errmsg to display on failure as defined below.


  tests: {

    // tests whether the value is composed of only uppercase letters
    //     if the test fails, return 'uppercase' to access the error message defined below
    isUpperCase: function (val, args) {
      return val.match(/^[A-Z]+$/) ? null : 'uppercase';
    },

    // tests whether the value is within an expected range
    //     if the test fails, return 'range'
    range: function (val, args) {
      if (!(Array.isArray(args) && args.length === 2)) {
        throw new Error('joi.fiddle().range() requires two numeric arguments');
      }
      if(!('number' === typeof args[0] && 'number' === typeof args[1])) {
        throw new Error('joi.fiddle().range() requires two numeric arguments');
      }

      return val.length >= args[0] && val.length <= args[1] ? null : 'range';
    },

    // tests whether the value is not allowed
    //     if the test fails, return 'not_allowed' to access the error message defined below
    disallow: function (val, args) {
      if (!(Array.isArray(args) && 'string' === typeof args[0])) {
        throw new Error('joi.fiddle().disallow() requires one string argument');
      }
      return val === args[0] ? 'disallowed' : null;
    }

  },

Finally, we add useful error messages which will be reported on validation failure.

  errmsgs: {

    'base': 'must be a string',

    'len': 'must be >= ' + MIN_LEN + ' and <= ' + MAX_LEN + ' chars in length',

    'range': '{{key}} "{{value}}" must be between {{args.0}} and {{args.1}} chars in length',

    'uppercase': 'must be uppercase',

    'disallowed': '"{{value}}" is not an allowed value for "{{key}}"'

  }
  
});

Ok, now we have a new validator but Joi has no idea how to use it, so we need to register it with Joi.

extender.register(joi,'fiddle');

Now, we can use joi.fiddle() with .isUpperCase(), range(n:number,m:number) and .disallow(target:string) in our validations.


* Very Bad Idea.