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

siftjs

v1.0.9

Published

Run your parameters through the Sift for validation;defining rules upfront, simplifying your js implementations

Readme

Sift

Sift(config, [ function | collection of objects ])

Validations and typing on steriods

Run your parameters through the Sift to define rules upfront, simplifying your js implementation.

I use this a lot with grunt. I plan on using it in a few other js projects too. I'd love to hear any feed back on improvements and ways you've used it!

Installation

  npm install siftjs --save

The Sift Config Object

contract

Type: array

List of valid parameter names.

args

Type: argument object, object literal or array
Default: true

List/object of actual parameters.

pairedArgs

Type: Boolean
Default: false

When false, Sift uses your contract property to map variable names to values. Order matters, so given a contract ["foo", "bar"] the first value of an Arguments obj or array will map to "foo" and the second value will map to "bar". When pairedArgs is set to true Sift will expect Argument objects and arrays passed to Sift config object property "args" to be in the form [paramName1, paramName1value, paramName2, paramName2value].

Motivation: I did this because I was running into a lot of collisions working with passing values on the command line to grunt. To get around it I found myself needing to parse something like grunt task:paramName1:paramName1value:paramName1:paramName2value from the command line. In my grunt task this.args would look like [paramName1, paramName1value, paramName2, paramName2value]

failOnError

Type: Boolean Default: false

Set to true to fail task if Sift encounters an error. If set to false, any errors will cause Sift to return false.

rules

Type: Object

Declaratively perform validation on parameters in Sift's contract

| Name | Description | |:-------------------:|:--------------------------------------------------------:| |atLeastOne|At least one argument in contract must have a value| |collections|Map parameters that are a collections to sift configs to evaluate them| |custom|Define custom validations with a callback function| |defaults|Set default values for arguments in this group that aren't present| |exclusive|Each argument in this group is mutually exclusive| |map|transform user input into some other value you may find more useful| |only|List the only allowable values for an argument| |oneForAll|If one argument exists, then all arguments in this group must be present| |required|An array of required arguments| |requires|Define dependants of an argument| |type|lodash based type checking|

Optional Second Argument

Function to be siftified

Type: Function

When present, Sift will return the function so you might assign it to a variable (for example). After which when this function is called, its arguments will be evaluated by Sift. In practice arguments should be key/value object where keys are parameter names and values are values for respective parameter names. However, although less useful, it is also possible to pass an argument object or an array of alternating key, value pairs in the form, ["foo", "hello", "bar", "world"].

Collection of objects to be validated

Type: Array

When present Sift will validate each object in the collection, returning the original collection if all objects are good.

Example usage

Siftify that function

    var cool = function (name, email){
        return name + " can't be reached at " + email;
    };

    var fn = Sift({
        contract: ["name", "email"],
        failOnError: true,
        rules: {
            required: ["name", "email"]
        }
    }, cool);

    //logs "Russell can't be reached at [email protected]"
    console.log(fn("Russell", "[email protected]"));

Use Sift to validate a collection

      var col = [
            {"name":"Russell", "email":"[email protected]"},
            {"name":"David", "email":"[email protected]"},
            {"name":"Paul", "email":"[email protected]"},
            {"name":"Shawn", "email":"[email protected]"},
            {"name":"Fred", "email":"[email protected]"},
            {"name":"Dennis", "email":"[email protected]"},
            {"name":"Andrew", "email":"[email protected]"}
        ];

        var colConfig = {
          contract: ["name", "email"],
          failOnError: true,
          rules: {
              required: ["name", "email"]
          }
        };

        var assetAllObjectsInOriginalCollectionAreReturnedBySift = function () {
            return _.every(Sift(colConfig, col), function (obj) {
                return !_.isEmpty(_.where(col, obj));
            }.bind(this));
        };

        console.log(assetAllObjectsInOriginalCollectionAreReturnedBySift()); // true!!

Use Sift to validate a nested collection

Configs for nested collections work as usual. The only thing you have to remember is that the args for nested config (in the Rules.collections object) will be ignored. Lets clarify this idea with the following code fragment from our tests:

        var captainCollection = [
            {'captain': 'captain1', 'email': '[email protected]', wars: 3},
            {'captain': 'captain2', 'email': '[email protected]', wars: 3},
            {'captain': 'captain3', 'email': '[email protected]'},
            {'captain': 'captain4', 'email': '[email protected]', wars: 4},
            {'captain': 'captain5', 'email': '[email protected]', wars: 6}
        ];

        var captainCollectionConfig = {
            // process current level in hierarchy
            contract: ['captain', 'wars', 'email'],
            failOnError: true,
            pairedArgs: true,
            rules: {
                required: ['captain', 'email', 'wars'],
                type:{
                    'wars':['number']
                }
            }
        };

        regionalCommanders = [
            {
                'general': 'Russell',
                ships: 6,
                region: 'France',
                'email': '[email protected]',
                captains: captainCollection
            },
            {
                'general': 'David',
                ships: 3,
                region: 'England',
                'email': '[email protected]',
                captains: captainCollection
            },
            {
                'general': 'Paul',
                ships: 1,
                region: 'Spain',
                'email': '[email protected]',
                captains: captainCollection
            },
            {
                'general': 'Shawn',
                ships: 3,
                region: 'Netherlands',
                'email': '[email protected]',
                captains: captainCollection
            },
            {
                'general': 'Ryan',
                ships: 5,
                region: 'Belgium',
                'email': '[email protected]',
                captains: captainCollection
            },
            {
                'general': 'Anthony',
                ships: 9,
                region: 'Ireland',
                'email': '[email protected]',
                captains: captainCollection
            },
            {
                'general': 'Fred',
                ships: 7,
                region: 'Germany',
                'email': '[email protected]',
                captains: captainCollection
            },
            {
                'general': 'Dennis',
                ships: 1,
                region: 'Italy',
                'email': '[email protected]',
                captains: captainCollection
            },
            {
                'general': 'Andrew',
                ships: 7,
                region: 'Switzerland',
                'email': '[email protected]',
                captains: captainCollection
            }
        ];

        regionalCommandersConfig = {
            // process current level in hierarchy
            contract: ['general', 'ships', 'region', 'email', 'captains'],
            failOnError: true,
            pairedArgs: true,
            rules: {
                required: ['captains'],
                // process next level down in hierarchy
                collections: {
                    'captains': captainCollectionConfig
                },
                type:{
                    'ships':['number']
                }
            }
        };

        var mainConfig = {
            contract: ['planet', 'attempt', 'generals'],
            args: [
                'planet',
                'earth',
                'attempt',
                '3',
                'generals',
                regionalCommanders
            ],
            pairedArgs: true,
            rules: {
                collections: {
                    'generals': regionalCommandersConfig
                }
            }
        };

        expect(function(){
            Sift(mainConfig);
        }).toThrow(
            new Error(
                '\nFailing Collection Item:\n'+
                '{"captain":"captain3","email":"[email protected]"}'+
                '\nCollection Failure!!\n'+
                'Sift.rules.required violation: ' +
                '1 or more required argument(s) missing. ' +
                'Required argument(s): [captain,email,wars]'
            )
        );

Contrived grunt worker task

module.exports = function(grunt) {
    grunt.registerTask(
    'worker',
    'worker task to update files on S3 or DynamoDB',
    function() {

        var inputObj = Sift(["update"],
            this.args, {
                only: {
                    "update": ["s3Assets", "dynamoDBAssets"]
                },
                defaults: {
                    "update": "s3Assets"
                },
                map:{
                    "update"{
                        "s3Assets": [
                                      'shell:buildS3AssetsLocally', 
                                      'shell:pushToS3'
                        ],
                        "dynamoDBAssets": [
                                      'shell:buildDynamoAssetsLocally', 
                                      'shell:pushToS3', 
                                      'shell:cleanTempFiles'
                        ]
                    }
                }
            }, true);

        turnOffTaskLogHeader([
            'shell'
        ]);

        grunt.task.run(inputObj["update"]);
    });
};

Contrived total usage

fooBar(){
     var colConfig = {
         contract: ["name", "email"],
         failOnError: true,
         rules: {
             required: ["name", "email"]
         }
     };

     var col = [
        {"name":"Russell", "email":"[email protected]"},
        {"name":"David", "email":"[email protected]"},
        {"name":"Paul", "email":"[email protected]"},
        {"name":"Fred", "email":"[email protected]"},
        {"name":"Dennis", "email":"[email protected]"},
        {"name":"Andrew", "email":"[email protected]"}
     ];

     var inputObj = Sift({
        contract:[
          "url", "named", "clientId", "reconcile", "shell", "config", "year"
        ],
        args: arguments,
        failOnError: true,
        pairedArgs: true,
        rules:{
                exclusive: [
                    ["url", "named"]
                ],
                collection: {
                    "users": colConfig
                },
                requires: {
                    "reconcile": ["clientId"]
                },
                only: {
                    "shell": ["Terminal", "iTerm"],
                    "config": ["yes", "no"]
                },
                defaults: {
                    "shell": "Terminal"
                },
                oneForAll: ["module", "clientId"],
                atleastOne: true,
                required: ["name"],
                map: {
                    "config": {
                        "yes": true,
                        "no": false
                    }
                },
               type:{
                   "url":["String"],
                   "year":["number"],
                   "clientId":["String", "regex"]
               },
               custom:{
                   "shell":function(value){
                      return value.toLowerCase() == "terminal" 
                         || value.toLowerCase() == "iterm" 
                         || value.toLowerCase() == "gitbash";
                    }
               }        
            }
        });
}

See Tests for more examples!!