JSPM

  • Created
  • Published
  • Downloads 6
  • Score
    100M100P100Q52242F
  • License MIT

JavaScript filter, validation, and transformation language

Package Exports

  • jsxl

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (jsxl) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

jsxl is a utility module providing powerful features for filtering, validating, and transforming JSON objects in Node.js applications.

Use cases of jsxl include:

  • inbound application REST APIs, when providing services
    • vaildating and transforming request parameters, arguments, headers, and bodies
    • transforming response bodies
  • outbound application REST APIs, when consuming services
    • transforming request parameters, arguments, headers, and bodies
    • validating and transforming response bodies
  • application plugins, when extending applications
    • filtering, validating, and transforming plugin interfaces

This is illustrated below.

Image of use cases

At jungle.io, makers of jsxl and of the jungle application development framework, we use jsxl extensively for all of the above.

Getting Started

To install jsxl from your command line, run

npm install jsxl

To start using jsxl in code, write

const jsxl = require('jsxl');

Filtering, validating, and transforming happens through application of jsxl-objects to JSON-objects. jsxl-objects, also known as filters, are themselves JSON-objects which upon application are validated by an internal filter-filter. This internal filter-filter is listed near the end of this README.

Example 1 illustrates successful validation of a JSON-object, an Array of Objects of Numbers,

// example 1, validating
jsxl(
    {
        input: [ { number: 7 }, { number: 8 }, { number: 9 } ]
    },
    [ { number: Number } ], // filter
    (err, output) => {
        console.log(output);
        // yields: [ { number: 7 }, { number: 8 }, { number: 9 } ]
    }
);

whereas example 2 illustrates unsuccessful validation of similar.

// example 2, invalidating
jsxl(
    {
        input: [ { number: 'a' }, { number: 'b' }, { number: 'c' } ]
    },
    [ { number: Number } ], // filter
    (err, output) => {
        console.log(err);
        // yields: 'input[0].number must be of type Number (not String)'
    }
);

Example 3 extends example 1 by filtering out even-numbered objects and multiplying numbers by 3 in remaining objects.

// example 3, filtering and transforming
jsxl(
    {
        input: [ { number: 7 }, { number: 8 }, { number: 9 } ]
    },
    [ {
        $filter: (object, context, next) => {
            // reject even-numbered objects
            // note that explicit validation is necessary in $filter
            next(null, !(object && typeof object.number == 'number' && 
                         object.number % 2 == 0)); 
        },
        $type: {
            number: {
                $type: Number,
                $transform: (number, context, next) => {
                    next(null, number * 3);
                }
            }
        },
    } ], // filter
    (err, output) => {
        console.log(output);
        // yields: [ { number: 21 }, { number: 27 } ]
    }
);

Filters may be compiled and executed combined for single use, as illustrated in above examples, or may be compiled and executed separately for repeated use.

Example 4 extends example 1, compiling and executing the filter separately.

// example 4, compiling and executing filter separately
jsxl.compile(
    [ { number: Number } ], // filter
    (err, filter) => {
        if (err) return console.log(err);
        jsxl.execute(
            {
                input: [ { number: 7 }, { number: 8 }, { number: 9 } ]
            },
            filter, // compiled filter
            (err, output) => {
                console.log(output);
                // yields: [ { number: 7 }, { number: 8 }, { number: 9 } ]
            }
        );
    }
);

Understanding Context

In example 3, embedded functions $filter and $transform receive contexts.

Contexts provide filters and embedded functions understanding of filtering path and surrounding state. The deeper into the object the filter processes the deeper the filtering path. Surrounding state, of course, changes accordingly.

Context holds the following entries

// the filter context
{
    context: Object,                 // the parent context
    parameters: Object,              // named parameters provided in initial context
    path: String,                    // the path to current context
    key: { $any: [ String, Number ], // the key or index of current entry
    source: null,                    // any type, current input context
    target: null,                    // any type, current output context
    aux: null,                       // any type, auxillary data possibly set through the $aux-modifier
}

TODO: study _context more carefully and describe other entries...

context.target[context.key] is likely undefined, but when traversing parent contexts and from there diving into previous sibling contexts, targets may well be set. This allows embedded functions to act conditionally on previosly validated and transformed object entries. Note that such retrospective conditioning is ONLY trustworthy when objects and arrays are processed in serial (NOT parallel) manner. Reference $parallel in a later section for further information on parallel processing.

Target in current context may, however, already be set if initial context provides a target to filter into.

Initiating Context

In previous examples, filtered object is passed in as

// implicit context
{
    input: [ { number: 7 }, { number: 8 }, { number: 9 } ]
}

This represents an initial implict context. Implicit contexts must be objects with single entries, the keys of which represent names of filtered objects.

The corresponding initial explicit context would read

// explicit context
{ 
    key: 'input',
    source: { 
        input: [ { number: 7 }, { number: 8 }, { number: 9 } ] 
    } 
}

Note that initial context never holds a parent context nor a path.

Initial explicit contexts must minimally hold a key, and a source, which in turn holds an entry that corresponds with the key. Initial explict contexts need only be used when additional context (e.g. parameters and/or target) must be provided.

Example 5 provides parameters and target in initail context to demonstrate use hereof.

// example 5, using explicit initial context
const target = {
    input: [ { number: 1 }, { number: 2 }, { number: 3 }, { number: 4 } ],
    other: 'this is other stuff...'
};
const parameters = {
    modulus: 2	
};
jsxl(
    {	
        target,
        parameters,
        key: 'input',
        source: {
            input: [ { number: 7 }, { number: 8 }, { number: 9 } ]
        }
    }, // explicit context
    [ { 
        $filter: (object, context, next) => {
            // rejects parameter-numbered objects
            // note that explicit validation is necessary in $filter
            next(null, !(object && typeof object.number == 'number' && 
                         object.number % context.parameters.modulus == 0)); 
        },
        $type: { number: Number }
    } ], // filter
    (err, output) => {
        console.log(target);
        // yields: { 
        //    input: [ { number: 7 }, { number: 2 }, { number: 9 }, { number: 4 } ],
        //    other: 'this is other stuff...' 
        // }
    }
);

Note that example 5 logs target, not output. Output is, however, nicely filtered into target in place of input.

Also note that some numbers (1 and 3) are replaced with new numbers (7 and 9) while others (2 and 4) remain unchanged in target. This is due to providing target in intial context, the filtered object is "filtrated" into target.

Managing Types

In previous examples, $type is used in conjunction with other $-modifiers.

The convention used is one of not mixing modified and unmodified filteres (ref. modifiers in a later section). So when modifying filters, we must capture type information in a $type entry.

The following filter uses a $gt-modifier to ensure a minimum value and must thus specify $type.

// a number greater than 7
{ 
    $type: Number,
    $gt: 7 // this is a modifier
}

Filter types include any of the following JavaScript types (partial filter-filter of types).

// patial filter-filter for types
$any: [ RegExp, String, Boolean, Number, Date, Function, [ null ], { $: null } ]

Note that Arrays hold any type while Objects hold any number of entrires of any type.

Filter types also include null, basically meaning any type.

Filter types finally include string-literals representing names of precompiled utility filters. jsxl provides the following precompiled utility filters:

  • "Boolean", requires any of Boolean or String which may translate into a Boolean
  • "Number", requires any of Number or String which may translate into a Number (conventional punctuation)
  • ** "IntNumber"**, requires any of Number or String which may translate into a Number (international punctuation)
  • "Date", requires any of Date or String which may translate into a Date
  • "RegExp", requires any of RegExp or String which may translate into a RegExp
  • "Filter", requires any of Function by name 'script' (filter) or anything (null) which may compile into a filter (Function by name 'script')

Users of jsxl may also extend these precompiled utility filters by calling the jsxl useFilters() method.

// example 6, adding and using a utility filter
jsxl.useFilters({
    EvenNumbers: [ { 
        $type: Number,
        $transform: (number, context, next) => {
            if (number %2 != 0) return next('%s must be an even number');
            next();
        }
    } ]
});
jsxl(
    {
        input: [ 2, 4, 6 ]
    },
    "EvenNumbers",
    (err, output) => {
        console.log(output);
        // yields: [ 2, 4, 6 ]
    }
);

Types may also be specified using the $merge- and $any-modifiers.

Merging Types

Type merging is used when wanting to build types in a structured manner, e.g. when having multiple different filters sharing commonalities.

The filter-filter for merging is the following:

// filter-filter for merging types
[ { $: null } ]

Note that $ representes any number of entries (ref. later section on $) and null represents any type (ref. later section on null). In other words, merging requires an array of objects with any number of entries of any type. In case an empty array is merged, null is substituted in place. In case an array of one entry is merged, that entry itself is used.

Also note that only Object-typed filters may be merged.

const ... // TODO

Using any Type

Any (of multiple) types are used when wanting to provide flexibility, e.g. allowing a variety of types to be used, likely types convertible between one another. In case an empty array is specified, null is substituted in place. In case an array of one entry is specified, that entry itself is used.

The precompiled utility filter "Date" offers a good example hereof, it is defined as follows:

// use of $any to provide flexibility in the Date utility filter
{ 
    $any: [ 
        Date, 
        { 
            $type: String, 
            $transform: (string, context, next) => {
                string = new Date(string);
                if (string == 'Invalid Date') return next('%s is an invalid Date string');
                next(null, string);
            }
        }
    ]
},

Absence of Type

When using other $-modifiers, type is assumed null (any type) unless explicitly specified by one of $type, $merge, or $any. In other words, absence of type means any type,

Note that $type, $merge, and $any cannot be specified simultanuously, but they may be nested within eachother.

Nesting Types

Types may be nested virtually deep. This comes in handy when merging types.

The following filter defines an object with entry speed, a non-negative Number less than 3 or a corresponding text.

// use of nested types
{
    speed: { 
        $any: [
            {
                $type: {
                    $type: Number
                    $gte: 0
                },
                $lt: 3
            },
            {
                $type: String,
                $in: [ 'slow', 'normal', 'fast' ]
            }
        ] 
    }
}

Using Modifiers

jsxl provides a variety of $-modifiers.

The modifier filter-filter is listed below and described in further detail in following sub sections.

{  
    $type:                       null, 
    $merge:                      [ { $: null } ], 
    $any:                        [ null ], 
    $remove:                     Boolean, 
    $insert:                     null,
    $default:                    null,
    $optional:                   Boolean,
    $map:                        { $any: [ Array, Object, Function ] },
    $parallel:                   Boolean,
    '$lt|$lte|$eq|$ne|$gte|$gt': null,
    '$in|$nin':                  { $any: [ [ null ], { $: null }, Function ] },
    '$inc|$ninc':                { $any: [ Boolean, Number, String, RegExp, Date, Function ] },
    $match:                      { $any: [ String, RegExp, Function ] },
    '$name|$constructor':        { $any: [ String, Function ] },
    '$length|$minlen|$maxlen':   { $any: [ Number, Function ] },
    $message:                    String,
    $rename:                     { $type: String, 
                                     $match: /^[_a-z][_a-z0-9\-]*$/i,
                                     $message: '%s is an invalid name', 
                                     $transform: (value, context, next) => {
                                         if (value === undefined) return next();            
                                         if (context.context === undefined || context.context.key.constructor !== String)
                                             return next(cannotRename);
                                         next();
                                    }
                                },
    '$toArray|$toObject':       String,
    '$filter|$transform':       Function,
    $aux:                       null,
};

Modifier $type

Use the $type-modifier only when other $-modifiers are needed. Otherwise the type may be specified directly.

// numbers with and without the $type-modifier
{ 
    a: {
        $type: Number,
        $gt: 7 // this is a modifier
    },
    b: Number, // $type-modifier is not needed here
}

Modifier filter is

$type: null

Modifier $merge

Use the $merge-modifier when constructing Object-type filters from multiple Object-type filters.

// decorating (merging) the orders-filter with paging
const orders = {
    customer: String,
    type: String
};
const page = {
    offset: { $type: Number, $gte: 0 },
    length: { $type: Number, $gt: 0 }
};
const pagedOrders = {
    $merge: [
        orders, 
        page    
    ]
}

The $merge-modifier must be of type Array of Object, each entry representing a filter component. In case an empty array is specified, null (any type) is substituted in place. In case an array of one type is specified, that type itself is used in place.

Modifier $any

Use the $any-modifier when specifying entries of alternative types.

// alternative Numbers (Number or convertable String)
{
    number: {  
        $any: [ 
            Number, 
            { 
                $type: String, 
                $transform: (string, context, next) => {
                    string = Number(string);
                    if (isNaN(string)) return next('%s is an invalid Number string');
                    next(null, string);
                }
            }
        ]
    }
}

The $any-modifier must be of type Array of null (any), each entry representing an alternative type. In case an empty array is specified, null (any type) is substituted in place. In case an array of one type is specified, that type itself is used in place.

Modifer $remove

Use the $remove-modifier when wanting to remove input entries from output.

// removing secrets
{
    secrets: { $remove: true }
}

The $remove-modifier must be of type Boolean (true or false).

Note the absence of the $type-modifier. Hence, secrets are of any type, which really doesnt matter since the secrets-entry is removed regardless.

Modifer $insert

Use the $insert-modifier when wanting to override entries or insert upon undefined entries.

// seeking orders per customer, open orders only
{
    state: { $insert: 'open' },
    id: String,
    $: { $remove: true }
}

The $insert-modifier may be of any type, including a Function. When of type Function, the $insert-modifier is called with arguments (entry, context, next).

// seeking orders per customer, open orders only
{
    state: { $insert: (state, context, next) => next(null, 'open') },
    id: String,
    $: { $remove: true }
}

Either of the above filters may be applied to a full customer object and yield the following output query:

{
    state: 'open',
    id: <customer-id>,
}

Note that state need not have a $type-modifier, it will be overridden/inserted regardless with the specified value. However, if the $insert-function cannot guarantee a String type, then specifing a String type may be advisable.

Modifer $default

Use the $default-modifier when wanting to insert only upon undefined entries.

// validating order
{
    delivery: { 
        $type: String,
        $in: [ 'express', 'priority', 'batch' ], 
        $default: 'priority'
    },
    // other order detail...
}

The $default-modifier may be of any type, including a Function. When of type Function, the $default-modifier is called with arguments (entry, context, next).

// validating order
{
    delivery: { 
        $type: String,
        $in: [ 'express', 'priority', 'batch' ],
        $default: (delivery, context, next) => next(null, 'priority') 
    },
    // other order detail...
}

In either case, when orders are submitted without delivery specifications, they are defaulted to priority-delivery.

Modifer $optional

Use the $optional-modifier when wanting to allow absence of certain entries.

// validating customer address
{
    street: String,
    number: Number,
    extension: { $type: String, $optinal: true },
    floor:     { $type: String, $optinal: true },
    side:      { $type: String, $optinal: true }
}

The $optional-modifier must be of type Boolean (true or false).

Modifer $map

Use the $map-modifier when wanting to map entries from String to object entry or from Number to array entry.

// validating and mapping order on request
{
    delivery: { 
        $type: Number,
        $map: { express: 0, priority: 1, batch: 2 }, 
        $default: 'priority'
    },
    // other order detail...
}

// mapping order on response
{
    delivery: { 
        $type: String,
        $map: [ 'express', 'priority', 'batch' ] 
    },
    // other order detail...
}

Note that order delivery speeds are mapped from String to Number upon request and from Number to String upon response.

Also note that the $type-modifier specifies type of the mapped entry, not the entry itself. The entry itself is controlled by the $map-modifier, String if map is of type Object or Number if map is of type Array.

The $map-modifier may be of type Object, Array, or Function. When of type Function, the $map-modifier is called with arguments (entry, context, next).

// validating and mapping order on request
{
    delivery: { 
        $type: Number,
        $map: (delivery, context, next) => next(null, { express: 0, priority: 1, batch: 2 }), 
        $default: 'priority'
    },
    // other order detail...
}

// mapping order on response
{
    delivery: { 
        $type: String,
        $map: (delivery, context, next) => next (null, [ 'express', 'priority', 'batch' ])
    },
    // other order detail...
}

Modifer $parallel

Use the $parallel-modifier when wanting to optimize filtering objects or arrays which in turn have entries that filter by calling lengthy asynchronous services, e.g. disk i/o or web services.

// example 7, processing an array in parallel
const request = require('request');
jsxl(
    {
        input: [ 'EUR', 'CHF', 'GBP' ]
    },
    { 
        $type : [ { 
            $type: String, 
            $transform: (currency, context, next) => {
                request(`http://api.myexchange.io/USD/${currency}`, (err, res, body) => {
                    if (err) 
                        return next(`%s causes: ${err.message}`);
                    if (res.statusCode != 200 ) 
                        return next(`%s causes: http response ${res.statusCode}`);
                    next(null, body[currency]);
                });
            }
        } ],
        $parallel: true
    },
    (err, output) => {
        console.log(output);
        // yields: [  0.85, 0.91, 0.77 ]
    }
);

Modifers $lt, $lte, $eq, $ne, $gte, and $gt

Use any of the $lt-, $lte-, $eq-, $ne-, $gte-, or $gt-modifiers when wanting to compare entries to value boundaries.

// validating various types
{
    number:  { $type: Number,  $gte: 0          }, // a non-negative number
    string:  { $type: String,  $ne: 'reserved'  }, // any string not reading 'reserved'
    date:    { $type: Date,    $lt:  new Date() }, // a past date
    boolean: { $type: Boolean, $eq:  true       }  // must be true
}

Comparisson modifiers may be of any type, including a Function. When of type Function, comparisson modifiers are called with arguments (entry, context, next).

// validating various types
{
    number:  { $type: Number,  $gte: (number, context, next) => next(null, 0)          }, // a non-negative number
    string:  { $type: String,  $ne:  (number, context, next) => next(null, 'reserved') }, // any string not reading 'reserved'
    date:    { $type: Date,    $lt:  (number, context, next) => next(null, new Date()) }, // a past date
    boolean: { $type: Boolean, $eq:  (number, context, next) => next(null, true)       }  // must be true
}

Modifers $in and $nin

Use any of the $in- or $nin-modifiers when wanting to validate entries against array or object entries.

// validating order, ensuring proper delivery and avoiding blacklisted customers
{
    delivery: { 
        $type: String,
        $in: [ 'express', 'priority', 'batch' ], 
    },
    customerId: {
        $type: String,
        $nin: (customerId, context, next) => Customer.findBlacklistedIds(next)
    }
    // other order detail...
}

$in- and $nin-modifiers may be of types Array, Object, or Function. When of type Function, $in- and $nin-modifiers are called with arguments (entry, context, next).

Modifers $inc and $ninc

Use any of the $inc- or $ninc-modifiers when wanting to validate array or object entries holding particular entries. Array entries are compared by value while objet enries are compared by key

// validating with arrays and objects
{
    a: { 
        $type: [ String ], 
        $inc: 'A'
    },
    b: {
        $type: { $: Boolean },
        $inc: 'B'
    },
    notc: { 
        $type: [ String ], 
        $ninc: 'C'
    },
    notd: {
        $type: { $: Date },
        $ninc: 'D'
    }
}

The $inc- and $ninc-modifiers may be of types Boolean, Number, String, RegExp, Date, or Function. When of type Function, $in- and $nin-modifiers are called with arguments (entry, context, next).

The $inc- and $ninc-modifiers correspond to the $in- and $nin-modifiers repsectively, but with reverse inclusion detection. A $in B is similar to B $inc A whereas A $nin B is similar to B $ninc A.

Modifer $match

Use the $match-modifier when wanting to String-match entries.

// matching id againts valid Id-format
{
    id: { 
        $type: String, 
        $match: /^Id[0-9]{8}/
    }
}

The $match-modifier may be of types String, RegExp, or Function. When of type Function, the $match-modifier is called with arguments (entry, context, next).

//  matching id againts valid Id-format
{
    id: { 
        $type: String, 
        $match: (id, context, next) => next(null, /^Id[0-9]{8}/)
    }
}

Modifers $name and $constructor

Use any of the $name- or $constructor-modifiers when wanting to validate entry name or entry constructor name.

// matching plugin and plugin constructor names
{
    plugin: { 
        $name: 'BankOfAmerica',
        $constructor: 'BankPlugin'
    }
}

The $name- and $constructor-modifiers may be of types String or Function. When of type Function, the $name- and $constructor-modifiers are called with arguments (entry, context, next).

// matching plugin and plugin constructor names
{
    plugin: { 
        $name: (plugin, context, next) => next(null, 'BankOfAmerica'),
        $constructor: (plugin, context, next) => next(null, 'BankPlugin'),
    }
}

Note that plugin need not have a $type-modifier, plugin type will be tested by means of the constructor name as opposed to the constructor function itself.

Modifers $length, $minlen, and $maxlen

Use any of the $length-, $minlen, or $maxlen-modifiers when wanting to validate entry lengths.

// matching lengths of arrays
{
    short: { 
        $type: [ Number ],
        $maxlen: 6
    },
    exact: { 
        $type: [ Number ],
        $length: 7
    },
    long: { 
        $type: [ Number ],
        $minlen: 8
    }
}

The $length-, $minlen-, and $maxlen-modifiers may be of types Number or Function. When of type Function, the $length-, $minlen-, and $maxlen-modifiers are called with arguments (entry, context, next).

// matching lengths of arrays
{
    short: { 
        $type: [ Number ],
        $maxlen: (short, context, next) => next(null, 6)
    },
    exact: { 
        $type: [ Number ],
        $length: (exact, context, next) => next(null, 7)
    },
    long: { 
        $type: [ Number ],
        $minlen: (long, context, next) => next(null, 8)
    }
}

Modifer $message

Use the $message-modifier when wanting to override any the standard validation messages.

// vaildating IP-address format
{
    ip: { 
        $type: String, 
        $match: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
        $message: '%s must be a well-formed IP-address (0-255.0-255.0-255.0-255)'
    }
}

The $message-modifier must be of type String and must include a '%s' placeholder (preferable at the front) for the entry path.

Modifer $rename

Use the $rename-modifier when wanting to rename input entries upon output.

// renaming entry
{
    connect: { 
        $type: String, 
        $rename: 'base'
        $transform: (connect, context, next) => {
            MongoClient.connect(connect, next);
        }
    }
}

The $rename-modifier must be of type String.

Modifer $toArray

Use the $toArray-modifier when wanting to convert an object-entry to an array-entry.

// eaxmple 8, converting object to array
jsxl(
{
    input: {
        nyc: { city: 'New York City' },
        atl: { city: 'Atlanta' },
        chi: { city: 'Chicago' }
    }
},
{
    $type: {
        $: { 
            city: String 
        }
    },
    $toArray: 'initials'
},
(err, output) => {
    console.log(output);
    // yields: [ { city: 'New York City', initials: 'nyc' },
    //           { city: 'Atlanta', initials: 'atl' },
    //           { city: 'Chicago', initials: 'chi' } 
    //         ]
});

The $toArray-modifier must be of type String and must represent output sub entry keys consuming input entry keys.

Modifer $toObject

Use the $toObject-modifier when wanting to convert an array-entry to an object-entry.

// eaxmple 9, converting array to object
jsxl(
{
    input: [ { city: 'New York City', initials: 'nyc' },
             { city: 'Atlanta', initials: 'atl' },
             { city: 'Chicago', initials: 'chi' } 
           ]
},
{
    $type: [
        { 
            city: String,
            initials: String
        }
    ],
    $toObject: 'initials'
},
(err, output) => {
    console.log(output);
    // yields: { nyc: { city: 'New York City' },
    //           atl: { city: 'Atlanta' },
    //           chi: { city: 'Chicago' } 
    //         }
});

The $toObject-modifier must be of type String and must represent input sub entry keys producing output entry keys .

Modifer $filter

Use the $filter-modifier when wanting to filter out any entries.

// filtering out disabled plugins
{
    plugins: [ {
        $type: { 
            enabled: Boolean,
            name: String,
            handler: Function
        },
        $filter: (plugin, context, next) => {
            next(null, plugin.enabled);
        }
    } ]
}

The $filter-modifier must be of type Function and is called with arguments (entry, context, next). The $filter-modifier must return a boolean value, undefined and true means keep whereas false means discard.

Modifer $transform

Use the $transform-modifier when wanting to transform any entries.

// transforming entry from connection string to database connection
{
    redis: { 
        $type: String, 
        $transform: (connect, context, next) => {
            RedisClient.connect(connect, next);
        }
    }
}

The $transform-modifier must be of type Function and is called with arguments (entry, context, next). The $transform-modifier must return a the transformed value, undefined is interpreted as unchanged.

Modifer $aux

Use the $aux-modifier when wanting to capture additional values in context to be referenced from child or successive sibling contexts.

// transforming entry from connection string to database connection
{
    date: { 
        $type: String, 
        $transform: (date, context, next) => {
            next(null, new Date(date));
        },
        $aux: (date, context, next) => next(null, date) // keep original format
    }
}

The $aux-modifier may be of any type, including a Function. When of type Function, the $aux-modifier is called with arguments (entry, context, next).

Method overview

jsxl provides the following methods described in following sub sections.

  • inline
  • compile
  • compileMultiple
  • execute
  • useFilters
  • merge

Method inline

Use the inline method when wanting to compile and execute filters once only. The inline method has no explicit method but is called directly on the module.

// example 9, compiling and executing filter once
jsxl(
    {
        input: 7
    },
    Number, // filter
    (err, output) => {
        console.log(output);
        // yields: 7
    }
);

Method syntax is

jsxl(input, filter [, options ], function next(err [, output ] ))

options are yet to be described...

Method compile

Use the compile method when wanting to compile filters once but execute multiple times.

// example 10, compiling filter once
jsxl.compile(
    Number, // filter
    (err, filter) => {
        console.log(filter);
        // yields: [Function: Filter]
    }
);

Method syntax is

jsxl.compile(filter [, options ], function next(err [, filter ] ))

options are yet to be described...

Method compileMultiple

Use the compileMultiple method when wanting to compile multiple filters once but execute every filter multiple times.

// example 11, compiling filter once
jsxl.compileMultiple(
    {
        number: Number, 
        string: String,
        date: Date
    }, // filters
    (err, filters) => {
        console.log(filters);
        // yields: {
        //             number: [Function: Filter]
        //             string: [Function: Filter]
        //             date: [Function: Filter]
        //         }
    }
);

Method syntax is

jsxl.compileMultiple(filters [, options ], function next(err [, filters ] ))

options are yet to be described...

Method execute

Use the execute method when wanting to compile filters once but execute multiple times.

// example 12, compiling filter once
jsxl.execute(
    {
        input: 7
    },
    filter, // previously calculated in example 10
    (err, output) => {
        console.log(output);
        // yields: 7
    }
);

Method syntax is

jsxl.execute(context, filter, function next(err [, output ] ))

Method useFilters

Use the useFilters method when wanting to provide often used filtering patterns.

// example 13, providing often used filtering patterns
jsxl.useFilters({
    IP: { 
        $type: String, 
        $match: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
        $message: '%s must be a well-formed IP-address (0-255.0-255.0-255.0-255)'
    }
});
jsxl(
    {
        input: '127.1.1.1'
    },
    "IP",
    (err, output) => {
    console.log(output);
    // yields: '127.1.1.1'
});

Note that filter reference is a String litteral of the filter name, in this example "IP".

Method syntax is

jsxl.useFilter(filters)

Method merge

Method merge performs a deep merge of two filters, a merge which understands jsxl $-modifiers and merges accordingly. Method merge is used internally when resolving $merge-modified filters.

Use the merge method when wanting to merge two filters as an alternative to embedding the filters into a merged filter.

// example 14, merging two filters
merged = jsxl.merge(filter1, filter2); 
// provides the same filtering effect as
merged = { 
    $merge: [
        filter1,
        filter2
    ]
};

Method merge may aslo be used to simply deep merge two regular objects, they dont have to be filters.

Method syntax is

jsxl.merge(filter1, filter2)

Known Pitfalls

To be detailed...

  • the compile/iterate scenario