UNPKG

doormen

Version:

Validate, sanitize and assert: the silver bullet of data!

1,669 lines (1,528 loc) 145 kB
# TOC - [Assertion utilities](#assertion-utilities) - [Equality checker](#equality-checker) - [Basic types](#basic-types) - [Optional, default and forced data](#optional-default-and-forced-data) - [Built-in types](#built-in-types) - [Mixed types](#mixed-types) - [Top-level filters](#top-level-filters) - [Filters](#filters) - [Children and recursivity](#children-and-recursivity) - [Mask](#mask) - [Numbers meta types](#numbers-meta-types) - [Strings meta types](#strings-meta-types) - [Sanitize](#sanitize) - [Sanitize + Patch reporting](#sanitize-patch-reporting) - [Full report mode](#full-report-mode) - [Patch validation](#patch-validation) - [Patch application](#patch-application) - ['keys' attribute](#keys-attribute) - [Alternatives](#alternatives) - [Complex multiple-children constraints](#complex-multiple-children-constraints) - [Schema validation](#schema-validation) - [Purify](#purify) - [Export mode](#export-mode) - [Expect BDD assertion library](#expect-bdd-assertion-library) - [Sub-schema path](#sub-schema-path) - [Extract constraint-only schema](#extract-constraint-only-schema) - [Forms](#forms) - [MongoDB's ObjectID](#mongodbs-objectid) - [Misc](#misc) <a name=""></a> <a name="assertion-utilities"></a> # Assertion utilities doormen.shouldThrow() should throw if the callback has not throw, and catch if it has throw. ```js var thrown ; thrown = false ; try { doormen.shouldThrow( () => {} ) ; } catch ( error ) { thrown = true ; } if ( ! thrown ) { throw new Error( 'It should throw!' ) ; } thrown = false ; try { doormen.shouldThrow( () => { throw new Error( 'Fatal error' ) ; } ) ; } catch ( error ) { thrown = true ; } if ( thrown ) { throw new Error( 'It should *NOT* throw' ) ; } ``` doormen.not() should throw if the data validate, and catch if it has throw. ```js var thrown ; thrown = false ; try { doormen.not( { type: 'string' } , 'text' ) ; } catch ( error ) { thrown = true ; } if ( ! thrown ) { throw new Error( 'It should throw' ) ; } thrown = false ; try { doormen.not( { type: 'string' } , 1 ) ; } catch ( error ) { thrown = true ; } if ( thrown ) { throw new Error( 'It should *NOT* throw' ) ; } ``` <a name="equality-checker"></a> # Equality checker Equality of simple type. ```js doormen.equals( undefined , undefined ) ; doormen.equals( null , null ) ; doormen.equals( true , true ) ; doormen.equals( false , false ) ; doormen.not.equals( undefined , null ) ; doormen.not.equals( true , false ) ; doormen.not.equals( null , false ) ; doormen.not.equals( undefined , false ) ; doormen.equals( NaN , NaN ) ; doormen.not.equals( NaN , null ) ; doormen.not.equals( NaN , undefined ) ; doormen.equals( Infinity , Infinity ) ; doormen.equals( -Infinity , -Infinity ) ; doormen.not.equals( Infinity , -Infinity ) ; doormen.equals( 0 , 0 ) ; doormen.equals( 123 , 123 ) ; doormen.equals( 0.123 , 0.123 ) ; doormen.equals( "" , "" ) ; doormen.equals( "abc" , "abc" ) ; doormen.equals( " abc" , " abc" ) ; doormen.equals( "abc " , "abc " ) ; doormen.equals( " abc " , " abc " ) ; doormen.not.equals( 0 , "" ) ; doormen.not.equals( false , "" ) ; ``` Equality of objects. ```js var o = {} ; doormen.equals( {} , {} ) ; doormen.equals( o , o ) ; doormen.equals( { a: 2 , b: 5 } , { a: 2 , b: 5 } ) ; doormen.not.equals( { a: 2 , b: 6 } , { a: 2 , b: 5 } ) ; doormen.equals( { b: 5 , a: 2 } , { a: 2 , b: 5 } ) ; doormen.not.equals( { a: 2 , b: 5 , c: null } , { a: 2 , b: 5 } ) ; doormen.not.equals( { a: 2 , b: 5 } , { a: 2 , b: 5 , c: null } ) ; doormen.not.equals( { a: 2 , b: 5 , c: {} } , { a: 2 , b: 5 } ) ; doormen.equals( { a: 2 , b: 5 , c: {} } , { a: 2 , b: 5 , c: {} } ) ; doormen.equals( { a: 2 , b: 5 , c: { d: 'titi' } } , { a: 2 , b: 5 , c: { d: 'titi' } } ) ; doormen.not.equals( { a: 2 , b: 5 , c: { d: 'titi' } } , { a: 2 , b: 5 , c: { d: 'toto' } } ) ; doormen.equals( { a: 2 , b: 5 , c: { d: 'titi' , e: { f: 'f' , g: 7 } } } , { a: 2 , b: 5 , c: { d: 'titi' , e: { f: 'f' , g: 7 } } } ) ; ``` when a property is undefined in the left-side and non-existant in the right-side, they should be equals. ```js doormen.equals( { a: 2 , b: 5 , c: undefined } , { a: 2 , b: 5 } ) ; doormen.equals( { a: 2 , b: 5 } , { a: 2 , b: 5 , c: undefined } ) ; doormen.equals( { a: 2 , b: 5 , c: undefined } , { a: 2 , b: 5 , c: undefined } ) ; doormen.equals( { a: 2 , b: 5 , c: undefined } , { a: 2 , b: 5 , d: undefined } ) ; doormen.equals( { a: 2 , b: 5 , c: { d: 'titi' } } , { a: 2 , b: 5 , c: { d: 'titi' , e: undefined } } ) ; ``` Equality of functions. ```js var o = {} ; var fn = function() {} ; var fn2 = function() {} ; doormen.equals( fn , fn ) ; doormen.not.equals( fn , fn2 ) ; doormen.equals( { a: fn } , { a: fn } ) ; doormen.equals( { b: 2 , a: fn } , { a: fn , b: 2 } ) ; doormen.equals( [ fn ] , [ fn ] ) ; doormen.equals( [ 1 , 2 , fn ] , [ 1 , 2 , fn ] ) ; doormen.not.equals( [ 1 , 2 , fn ] , [ 1 , 2 ] ) ; ``` Equality of arrays. ```js var o = [] ; doormen.equals( [] , [] ) ; doormen.equals( o , o ) ; doormen.equals( [ 1 ] , [ 1 ] ) ; doormen.not.equals( [ 1 , undefined ] , [ 1 ] ) ; doormen.not.equals( [ 1 ] , [ 1 , undefined ] ) ; doormen.not.equals( [ 1 ] , [ 2 ] ) ; doormen.equals( [ 1 , 2 , 3 ] , [ 1 , 2 , 3 ] ) ; doormen.equals( [ 1 , [] , 3 ] , [ 1 , [] , 3 ] ) ; doormen.equals( [ 1 , [ 2 ] , 3 ] , [ 1 , [ 2 ] , 3 ] ) ; doormen.equals( [ 1 , [ 2 , 'a' ] , 3 ] , [ 1 , [ 2 , 'a' ] , 3 ] ) ; doormen.not.equals( [ 1 , [ 2 , 'a' ] , 3 ] , [ 1 , [ 2 , 'b' ] , 3 ] ) ; doormen.equals( [ 1 , [ 2 , [ null ] , 'a' ] , 3 ] , [ 1 , [ 2 , [ null ] , 'a' ] , 3 ] ) ; doormen.not.equals( [ 1 , [ 2 , [ undefined ] , 'a' ] , 3 ] , [ 1 , [ 2 , [ null ] , 'a' ] , 3 ] ) ; doormen.equals( [ [ 'one' ] , [ 'two' ] ] , [ [ 'one' ] , [ 'two' ] ] ) ; doormen.not.equals( [ [ 'one' ] , [ 'two' ] ] , [ [ 'one' ] , [ 'twoa' ] ] ) ; ``` Equality of nested and mixed objects and arrays. ```js doormen.not.equals( {} , [] ) ; doormen.equals( { a: 2 , b: 5 , c: [ 'titi' , { f: 'f' , g: 7 } ] } , { a: 2 , b: 5 , c: [ 'titi' , { f: 'f' , g: 7 } ] } ) ; doormen.equals( [ 'a' , 'b' , { c: 'titi' , d: [ 'f' , 7 ] } ] , [ 'a' , 'b' , { c: 'titi' , d: [ 'f' , 7 ] } ] ) ; ``` Circular references: stop searching when both part have reached circular references. ```js var a , b ; a = { a: 1 , b: 2 } ; a.c = a ; b = { a: 1 , b: 2 } ; b.c = b ; doormen.equals( a , b ) ; a = { a: 1 , b: 2 , c: { a: 1 , b: 2 } } ; a.c.c = a ; b = { a: 1 , b: 2 } ; b.c = b ; doormen.equals( a , b ) ; ``` Date. ```js var date1 = new Date( '2019-06-18' ) , date2 = new Date( '2019-08-21' ) , date3 = new Date( '2019-08-21' ) ; doormen.not.equals( date1 , date2 ) ; doormen.equals( date2 , date3 ) ; ``` Buffers. ```js var buf , buf2 , i ; buf = Buffer.allocUnsafe( 80 ) ; buf2 = Buffer.allocUnsafe( 80 ) ; for ( i = 0 ; i < 80 ; i ++ ) { buf[ i ] = i ; } buf.copy( buf2 ) ; doormen.equals( buf , buf2 ) ; buf2[ 4 ] = 117 ; doormen.not.equals( buf , buf2 ) ; ``` <a name="basic-types"></a> # Basic types should validate undefined accordingly. ```js doormen( { type: 'undefined' } , undefined ) ; doormen.not( { type: 'undefined' } , null ) ; doormen.not( { type: 'undefined' } , false ) ; doormen.not( { type: 'undefined' } , true ) ; doormen.not( { type: 'undefined' } , 0 ) ; doormen.not( { type: 'undefined' } , 1 ) ; doormen.not( { type: 'undefined' } , '' ) ; doormen.not( { type: 'undefined' } , 'text' ) ; doormen.not( { type: 'undefined' } , {} ) ; doormen.not( { type: 'undefined' } , [] ) ; ``` should validate null accordingly. ```js doormen.not( { type: 'null' } , undefined ) ; doormen( { type: 'null' } , null ) ; doormen.not( { type: 'null' } , false ) ; doormen.not( { type: 'null' } , true ) ; doormen.not( { type: 'null' } , 0 ) ; doormen.not( { type: 'null' } , 1 ) ; doormen.not( { type: 'null' } , '' ) ; doormen.not( { type: 'null' } , 'text' ) ; doormen.not( { type: 'null' } , {} ) ; doormen.not( { type: 'null' } , [] ) ; ``` should validate boolean accordingly. ```js doormen.not( { type: 'boolean' } , undefined ) ; doormen.not( { type: 'boolean' } , null ) ; doormen( { type: 'boolean' } , false ) ; doormen( { type: 'boolean' } , true ) ; doormen.not( { type: 'boolean' } , 0 ) ; doormen.not( { type: 'boolean' } , 1 ) ; doormen.not( { type: 'boolean' } , '' ) ; doormen.not( { type: 'boolean' } , 'text' ) ; doormen.not( { type: 'boolean' } , {} ) ; doormen.not( { type: 'boolean' } , [] ) ; ``` should validate number accordingly. ```js doormen.not( { type: 'number' } , undefined ) ; doormen.not( { type: 'number' } , null ) ; doormen.not( { type: 'number' } , false ) ; doormen.not( { type: 'number' } , true ) ; doormen( { type: 'number' } , 0 ) ; doormen( { type: 'number' } , 1 ) ; doormen( { type: 'number' } , Infinity ) ; doormen( { type: 'number' } , NaN ) ; doormen.not( { type: 'number' } , '' ) ; doormen.not( { type: 'number' } , 'text' ) ; doormen.not( { type: 'number' } , {} ) ; doormen.not( { type: 'number' } , [] ) ; ``` should validate string accordingly. ```js doormen.not( { type: 'string' } , undefined ) ; doormen.not( { type: 'string' } , null ) ; doormen.not( { type: 'string' } , false ) ; doormen.not( { type: 'string' } , true ) ; doormen.not( { type: 'string' } , 0 ) ; doormen.not( { type: 'string' } , 1 ) ; doormen( { type: 'string' } , '' ) ; doormen( { type: 'string' } , 'text' ) ; doormen.not( { type: 'string' } , {} ) ; doormen.not( { type: 'string' } , [] ) ; ``` should validate object accordingly. ```js doormen.not( { type: 'object' } , undefined ) ; doormen.not( { type: 'object' } , null ) ; doormen.not( { type: 'object' } , false ) ; doormen.not( { type: 'object' } , true ) ; doormen.not( { type: 'object' } , 0 ) ; doormen.not( { type: 'object' } , 1 ) ; doormen.not( { type: 'object' } , '' ) ; doormen.not( { type: 'object' } , 'text' ) ; doormen( { type: 'object' } , {} ) ; doormen( { type: 'object' } , { a: 1 , b: 2 } ) ; doormen( { type: 'object' } , [] ) ; doormen( { type: 'object' } , [ 1 , 2 , 3 ] ) ; doormen( { type: 'object' } , new Date() ) ; doormen.not( { type: 'object' } , () => {} ) ; ``` should validate function accordingly. ```js doormen.not( { type: 'function' } , undefined ) ; doormen.not( { type: 'function' } , null ) ; doormen.not( { type: 'function' } , false ) ; doormen.not( { type: 'function' } , true ) ; doormen.not( { type: 'function' } , 0 ) ; doormen.not( { type: 'function' } , 1 ) ; doormen.not( { type: 'function' } , '' ) ; doormen.not( { type: 'function' } , 'text' ) ; doormen.not( { type: 'function' } , {} ) ; doormen.not( { type: 'function' } , [] ) ; doormen( { type: 'function' } , () => {} ) ; ``` <a name="optional-default-and-forced-data"></a> # Optional, default and forced data when a data is null, undefined or unexistant, and the optional flag is set the schema, it should validate. ```js doormen.not( { type: 'string' } , null ) ; doormen( { optional: true , type: 'string' } , null ) ; doormen.not( { type: 'string' } , undefined ) ; doormen( { optional: true , type: 'string' } , undefined ) ; doormen( { type: 'string' } , 'text' ) ; doormen( { optional: true , type: 'string' } , 'text' ) ; doormen.not( { type: 'string' } , 1 ) ; doormen.not( { optional: true , type: 'string' } , 1 ) ; doormen.not( { properties: { a: { type: 'string' } } } , {} ) ; doormen( { properties: { a: { optional: true , type: 'string' } } } , {} ) ; doormen( { properties: { a: { optional: true , type: 'string' } } } , { a: null } ) ; doormen( { properties: { a: { optional: true , type: 'string' } } } , { a: undefined } ) ; ``` if 'nullIsValue' and 'optional' flags are set, null values should validate instead being considerate a non-value. ```js doormen.not( { nullIsValue: true , type: 'string' } , null ) ; doormen.not( { nullIsValue: true , optional: true , type: 'string' } , null ) ; doormen.not( { nullIsValue: true , type: 'string' } , undefined ) ; doormen( { nullIsValue: true , optional: true , type: 'string' } , undefined ) ; doormen.not( { properties: { a: { nullIsValue: true , type: 'string' } } } , {} ) ; doormen( { properties: { a: { nullIsValue: true , optional: true , type: 'string' } } } , {} ) ; doormen.not( { properties: { a: { nullIsValue: true , optional: true , type: 'string' } } } , { a: null } ) ; doormen( { properties: { a: { nullIsValue: true , optional: true , type: 'string' } } } , { a: undefined } ) ; ``` if 'nullIsUndefined' is set null values are turned to 'undefined' before applying anything else. ```js doormen.equals( doormen( { nullIsUndefined: false } , null ) , null ) ; doormen.equals( doormen( { nullIsUndefined: true } , null ) , undefined ) ; doormen.not( { nullIsUndefined: true , type: 'string' } , null ) ; doormen( { nullIsUndefined: true , optional: true , type: 'string' } , null ) ; doormen.not( { nullIsUndefined: true , type: 'string' } , undefined ) ; doormen( { nullIsUndefined: true , optional: true , type: 'string' } , undefined ) ; doormen.equals( doormen( { properties: { a: { nullIsUndefined: true , optional: true , type: 'string' } } } , { a: null } ) , { a: undefined } ) ; doormen.equals( doormen( { properties: { a: { nullIsUndefined: true , optional: true , type: 'string' } } } , { a: null } ) , {} ) ; ``` missing optional properties should not be created (i.e. with undefined).. ```js var result ; result = doormen( { properties: { a: { optional: true , type: 'string' } } } , {} ) ; // {a:undefined} is equals to {} for doormen.equals() (this is the correct behaviour), but here we want to know for sure // that a key is not defined, so we have to check it explicitly doormen.equals( 'a' in result , false ) ; result = doormen( { properties: { a: { optional: true , type: 'string' } , b: { optional: true , type: 'string' } , c: { optional: true , properties: { d: { optional: true , type: 'string' } } } } } , {} ) ; doormen.equals( 'a' in result , false ) ; doormen.equals( 'b' in result , false ) ; doormen.equals( 'c' in result , false ) ; result = doormen( { properties: { a: { optional: true , type: 'string' } , b: { optional: true , type: 'string' } , c: { optional: true , properties: { d: { optional: true , type: 'string' } } } } } , { c: undefined } ) ; doormen.equals( 'a' in result , false ) ; doormen.equals( 'b' in result , false ) ; doormen.equals( 'c' in result , true ) ; doormen.equals( result.c , undefined ) ; result = doormen( { properties: { a: { optional: true , type: 'string' } , b: { optional: true , type: 'string' } , c: { optional: true , properties: { d: { optional: true , type: 'string' } } } } } , { c: null } ) ; doormen.equals( 'a' in result , false ) ; doormen.equals( 'b' in result , false ) ; doormen.equals( 'c' in result , true ) ; doormen.equals( result.c , null ) ; result = doormen( { properties: { a: { optional: true , type: 'string' } , b: { optional: true , type: 'string' } , c: { optional: true , properties: { d: { optional: true , type: 'string' } } } } } , { c: {} } ) ; doormen.equals( 'a' in result , false ) ; doormen.equals( 'b' in result , false ) ; doormen.equals( 'c' in result , true ) ; doormen.equals( 'd' in result.c , false ) ; ``` when a data is null, undefined or unexistant, and a default value is specified in the schema, that default value should overwrite the original one. ```js doormen.equals( doormen( { type: 'string' , "default": 'default!' } , null ) , 'default!' ) ; doormen.equals( doormen( { properties: { a: { type: 'string' , "default": 'default!' } } } , { a: null } ) , { a: 'default!' } ) ; doormen.equals( doormen( { properties: { a: { type: 'string' , "default": 'default!' } , b: { type: 'object' , "default": { c: 5 } } } } , { a: null , b: undefined } ) , { a: 'default!' , b: { c: 5 } } ) ; doormen.equals( doormen( { properties: { a: { type: 'string' , "default": 'default!' } , b: { type: 'object' , "default": { c: 5 } } } } , {} ) , { a: 'default!' , b: { c: 5 } } ) ; // verify that default value can be a function (regression of v0.10.9) var fn = () => null ; doormen.equals( doormen( { properties: { a: { type: 'string' , "default": fn } } } , { a: null } ) , { a: fn } ) ; ``` when the 'defaultFn' is specified in the schema and is a string, that builtin default function is executed and its return-value is used as the default. ```js //doormen.equals( doormen( { type: 'date' , defaultFn: 'now' } , null ) , new Date() ) ; doormen.equals( doormen( { properties: { a: { type: 'date' , defaultFn: 'now' } } } , { a: null } ) , { a: new Date() } ) ; ``` when the 'defaultFn' is specified in the schema and is a function, that function is executed and its return-value is used as the default. ```js var date , count = 0 ; doormen.equals( doormen( { type: 'date' , defaultFn: () => date = new Date() } , null ) , date ) ; doormen.equals( doormen( { properties: { a: { type: 'date' , defaultFn: () => date = new Date() } } } , { a: null } ) , { a: date } ) ; doormen.equals( doormen( { type: 'integer' , defaultFn: () => ++ count } , null ) , 1 ) ; doormen.equals( doormen( { properties: { a: { type: 'integer' , defaultFn: () => ++ count } , b: { type: 'integer' , defaultFn: () => ++ count } } } , { a: null } ) , { a: 2 , b: 3 } ) ; ``` if 'nullIsValue' is set and a 'default' value is set, null values are not replaced by the default value. ```js doormen.not( { type: 'string' , nullIsValue: true , "default": 'default!' } , null ) ; doormen.equals( doormen( { nullIsValue: true , "default": 'default!' } , null ) , null ) ; doormen.equals( doormen( { nullIsValue: true , "default": 'default!' } , undefined ) , 'default!' ) ; doormen.not( { properties: { a: { type: 'string' , nullIsValue: true , "default": 'default!' } } } , { a: null } ) , { a: null } doormen.equals( doormen( { properties: { a: { nullIsValue: true , "default": 'default!' } } } , { a: null } ) , { a: null } ) ; doormen.equals( doormen( { properties: { a: { nullIsValue: true , "default": 'default!' } } } , { a: undefined } ) , { a: 'default!' } ) ; doormen.equals( doormen( { properties: { a: { nullIsValue: true , "default": 'default!' } , b: { type: 'object' , nullIsValue: true , "default": { c: 5 } } } } , { a: null , b: undefined } ) , { a: null , b: { c: 5 } } ) ; ``` when the schema has a forced value, it should validate the data and set it to that value. ```js var schema ; schema = { value: 'forced!' } ; doormen.equals( doormen( schema , null ) , 'forced!' ) ; doormen.equals( doormen( schema , undefined ) , 'forced!' ) ; doormen.equals( doormen( schema , 'bob' ) , 'forced!' ) ; doormen.equals( doormen( schema , {} ) , 'forced!' ) ; schema = { properties: { a: { value: 'forced!' } } } ; doormen.equals( doormen( schema , {} ) , { a: 'forced!' } ) ; doormen.equals( doormen( schema , { a: undefined } ) , { a: 'forced!' } ) ; doormen.equals( doormen( schema , { a: null } ) , { a: 'forced!' } ) ; doormen.equals( doormen( schema , { a: 'bob' } ) , { a: 'forced!' } ) ; doormen.equals( doormen( schema , { a: { jack: 'bob' } } ) , { a: 'forced!' } ) ; schema = { properties: { a: { value: 'forced!' } , b: { value: { c: 5 } } } } ; doormen.equals( doormen( schema , { a: null , b: undefined } ) , { a: 'forced!' , b: { c: 5 } } ) ; doormen.equals( doormen( schema , {} ) , { a: 'forced!' , b: { c: 5 } } ) ; ``` <a name="built-in-types"></a> # Built-in types should validate 'unset' accordingly (undefined or null). ```js doormen( { type: 'unset' } , undefined ) ; doormen( { type: 'unset' } , null ) ; doormen.not( { type: 'unset' } , false ) ; doormen.not( { type: 'unset' } , true ) ; doormen.not( { type: 'unset' } , 0 ) ; doormen.not( { type: 'unset' } , 1 ) ; doormen.not( { type: 'unset' } , '' ) ; doormen.not( { type: 'unset' } , 'text' ) ; doormen.not( { type: 'unset' } , {} ) ; doormen.not( { type: 'unset' } , [] ) ; ``` should validate array accordingly. ```js doormen.not( { type: 'array' } , undefined ) ; doormen.not( { type: 'array' } , null ) ; doormen.not( { type: 'array' } , false ) ; doormen.not( { type: 'array' } , true ) ; doormen.not( { type: 'array' } , 0 ) ; doormen.not( { type: 'array' } , 1 ) ; doormen.not( { type: 'array' } , '' ) ; doormen.not( { type: 'array' } , 'text' ) ; doormen.not( { type: 'array' } , {} ) ; doormen.not( { type: 'array' } , { a: 1 , b: 2 } ) ; doormen( { type: 'array' } , [] ) ; doormen( { type: 'array' } , [ 1 , 2 , 3 ] ) ; doormen.not( { type: 'array' } , () => {} ) ; ``` should validate date accordingly. ```js doormen( { type: 'date' } , new Date() ) ; doormen.not( { type: 'date' } , undefined ) ; doormen.not( { type: 'date' } , null ) ; doormen.not( { type: 'date' } , false ) ; doormen.not( { type: 'date' } , true ) ; doormen.not( { type: 'date' } , 0 ) ; doormen.not( { type: 'date' } , 1 ) ; doormen.not( { type: 'date' } , '' ) ; doormen.not( { type: 'date' } , 'text' ) ; doormen.not( { type: 'date' } , {} ) ; doormen.not( { type: 'date' } , { a: 1 , b: 2 } ) ; doormen.not( { type: 'date' } , [] ) ; doormen.not( { type: 'date' } , [ 1 , 2 , 3 ] ) ; doormen.not( { type: 'date' } , () => {} ) ; ``` should validate error accordingly. ```js doormen( { type: 'error' } , new Error() ) ; doormen.not( { type: 'error' } , undefined ) ; doormen.not( { type: 'error' } , null ) ; doormen.not( { type: 'error' } , false ) ; doormen.not( { type: 'error' } , true ) ; doormen.not( { type: 'error' } , 0 ) ; doormen.not( { type: 'error' } , 1 ) ; doormen.not( { type: 'error' } , '' ) ; doormen.not( { type: 'error' } , 'text' ) ; doormen.not( { type: 'error' } , {} ) ; doormen.not( { type: 'error' } , { a: 1 , b: 2 } ) ; doormen.not( { type: 'error' } , [] ) ; doormen.not( { type: 'error' } , [ 1 , 2 , 3 ] ) ; doormen.not( { type: 'error' } , () => {} ) ; ``` should validate arguments accordingly. ```js var fn = function() { doormen( { type: 'arguments' } , arguments ) ; } ; // eslint-disable-line prefer-rest-params fn() ; fn( 1 ) ; fn( 1 , 2 , 3 ) ; doormen.not( { type: 'arguments' } , undefined ) ; doormen.not( { type: 'arguments' } , null ) ; doormen.not( { type: 'arguments' } , false ) ; doormen.not( { type: 'arguments' } , true ) ; doormen.not( { type: 'arguments' } , 0 ) ; doormen.not( { type: 'arguments' } , 1 ) ; doormen.not( { type: 'arguments' } , '' ) ; doormen.not( { type: 'arguments' } , 'text' ) ; doormen.not( { type: 'arguments' } , {} ) ; doormen.not( { type: 'arguments' } , { a: 1 , b: 2 } ) ; doormen.not( { type: 'arguments' } , [] ) ; doormen.not( { type: 'arguments' } , [ 1 , 2 , 3 ] ) ; doormen.not( { type: 'arguments' } , () => {} ) ; ``` <a name="mixed-types"></a> # Mixed types should validate 'strictObject' accordingly, i.e. objects that are *NOT* arrays. ```js doormen.not( { type: 'strictObject' } , undefined ) ; doormen.not( { type: 'strictObject' } , null ) ; doormen.not( { type: 'strictObject' } , false ) ; doormen.not( { type: 'strictObject' } , true ) ; doormen.not( { type: 'strictObject' } , 0 ) ; doormen.not( { type: 'strictObject' } , 1 ) ; doormen.not( { type: 'strictObject' } , '' ) ; doormen.not( { type: 'strictObject' } , 'text' ) ; doormen( { type: 'strictObject' } , {} ) ; doormen( { type: 'strictObject' } , { a: 1 , b: 2 } ) ; doormen.not( { type: 'strictObject' } , [] ) ; doormen.not( { type: 'strictObject' } , [ 1 , 2 , 3 ] ) ; doormen.not( { type: 'strictObject' } , () => {} ) ; ``` should validate 'regexp' accordingly, i.e. RegExp instance or string convertible to RegExp. ```js doormen( { type: 'regexp' } , /Random/ ) ; doormen( { type: 'regexp' } , new RegExp( "Random" ) ) ; doormen( { type: 'regexp' } , "Random" ) ; doormen.not( { type: 'regexp' } , "(Random" ) ; doormen.not( { type: 'regexp' } , undefined ) ; doormen.not( { type: 'regexp' } , null ) ; doormen.not( { type: 'regexp' } , false ) ; doormen.not( { type: 'regexp' } , true ) ; doormen.not( { type: 'regexp' } , 0 ) ; doormen.not( { type: 'regexp' } , 1 ) ; doormen( { type: 'regexp' } , '' ) ; doormen( { type: 'regexp' } , 'text' ) ; doormen.not( { type: 'regexp' } , {} ) ; doormen.not( { type: 'regexp' } , { a: 1 , b: 2 } ) ; doormen.not( { type: 'regexp' } , [] ) ; doormen.not( { type: 'regexp' } , [ 1 , 2 , 3 ] ) ; doormen.not( { type: 'regexp' } , () => {} ) ; ``` should validate 'classId' accordingly, i.e. function (constructor) or non-empty string. ```js doormen.not( { type: 'classId' } , undefined ) ; doormen.not( { type: 'classId' } , null ) ; doormen.not( { type: 'classId' } , false ) ; doormen.not( { type: 'classId' } , true ) ; doormen.not( { type: 'classId' } , 0 ) ; doormen.not( { type: 'classId' } , 1 ) ; doormen.not( { type: 'classId' } , '' ) ; doormen( { type: 'classId' } , 'text' ) ; doormen.not( { type: 'classId' } , {} ) ; doormen.not( { type: 'classId' } , { a: 1 , b: 2 } ) ; doormen.not( { type: 'classId' } , [] ) ; doormen.not( { type: 'classId' } , [ 1 , 2 , 3 ] ) ; doormen( { type: 'classId' } , () => {} ) ; ``` <a name="top-level-filters"></a> # Top-level filters 'instanceOf' should validate object accordingly. ```js function MyClass() {} if ( doormen.isBrowser ) { window[ 'MyClass' ] = MyClass ; } else { global[ 'MyClass' ] = MyClass ; } doormen( { instanceOf: Date } , new Date() ) ; doormen( { instanceOf: Array } , new Array() ) ; doormen( { instanceOf: MyClass } , new MyClass() ) ; doormen( { instanceOf: Object } , new MyClass() ) ; doormen( { instanceOf: 'MyClass' } , new MyClass() ) ; doormen( { instanceOf: 'Object' } , new MyClass() ) ; doormen.not( { instanceOf: Date } , new Array() ) ; doormen.not( { instanceOf: 'Date' } , new Array() ) ; ``` min filter should validate accordingly, non-number should throw. ```js doormen( { min: 3 } , 10 ) ; doormen( { min: 3 } , 3 ) ; doormen.not( { min: 3 } , 1 ) ; doormen.not( { min: 3 } , 0 ) ; doormen.not( { min: 3 } , -10 ) ; doormen( { min: 3 } , Infinity ) ; doormen( { min: Infinity } , Infinity ) ; doormen.not( { min: 3 } , -Infinity ) ; doormen.not( { min: 3 } , NaN ) ; doormen.not( { min: 3 } , true ) ; doormen.not( { min: 3 } , false ) ; doormen.not( { min: 3 } , undefined ) ; doormen.not( { min: 0 } , undefined ) ; doormen.not( { min: -3 } , undefined ) ; doormen.not( { min: 3 } , '10' ) ; ``` max filter should validate accordingly, non-number should throw. ```js doormen.not( { max: 3 } , 10 ) ; doormen( { max: 3 } , 3 ) ; doormen( { max: 3 } , 1 ) ; doormen( { max: 3 } , 0 ) ; doormen( { max: 3 } , -10 ) ; doormen.not( { max: 3 } , Infinity ) ; doormen( { max: 3 } , -Infinity ) ; doormen( { max: -Infinity } , -Infinity ) ; doormen.not( { max: 3 } , NaN ) ; doormen.not( { max: 3 } , true ) ; doormen.not( { max: 3 } , false ) ; doormen.not( { max: 3 } , '1' ) ; ``` min + max filter should validate accordingly, non-number should throw. ```js doormen.not( { min: 3 , max: 10 } , 15 ) ; doormen( { min: 3 , max: 10 } , 10 ) ; doormen( { min: 3 , max: 10 } , 5 ) ; doormen( { min: 3 , max: 10 } , 3 ) ; doormen.not( { min: 3 , max: 10 } , 1 ) ; doormen.not( { min: 3 , max: 10 } , 0 ) ; doormen.not( { min: 3 , max: 10 } , -10 ) ; doormen.not( { min: 3 , max: 10 } , Infinity ) ; doormen.not( { min: 3 , max: 10 } , -Infinity ) ; doormen.not( { min: 3 , max: 10 } , NaN ) ; doormen.not( { min: 3 , max: 10 } , true ) ; doormen.not( { min: 3 , max: 10 } , false ) ; doormen.not( { min: 3 , max: 10 } , '6' ) ; ``` 'length' filter should validate accordingly, data that do not have a length should throw. ```js doormen( { length: 3 } , "abc" ) ; doormen.not( { length: 3 } , "abcde" ) ; doormen.not( { length: 3 } , "ab" ) ; doormen.not( { length: 3 } , "" ) ; doormen.not( { length: 3 } , 1 ) ; doormen.not( { length: 0 } , 1 ) ; doormen.not( { length: 3 } , NaN ) ; doormen.not( { length: 3 } , true ) ; doormen.not( { length: 3 } , false ) ; ``` minLength filter should validate accordingly, data that do not have a length should throw. ```js doormen( { minLength: 3 } , "abc" ) ; doormen( { minLength: 3 } , "abcde" ) ; doormen.not( { minLength: 3 } , "ab" ) ; doormen.not( { minLength: 3 } , "" ) ; doormen( { minLength: 3 } , [ 1,2,3 ] ) ; doormen( { minLength: 3 } , [ 1,2,3,4 ] ) ; doormen.not( { minLength: 3 } , [ 1,2 ] ) ; doormen.not( { minLength: 3 } , 1 ) ; doormen.not( { minLength: 0 } , 1 ) ; doormen.not( { minLength: 3 } , NaN ) ; doormen.not( { minLength: 3 } , true ) ; doormen.not( { minLength: 3 } , false ) ; ``` maxLength filter should validate accordingly, data that do not have a length should throw. ```js doormen( { maxLength: 3 } , "abc" ) ; doormen.not( { maxLength: 3 } , "abcde" ) ; doormen( { maxLength: 3 } , "ab" ) ; doormen( { maxLength: 3 } , "" ) ; doormen( { maxLength: 3 } , [ 1,2,3 ] ) ; doormen.not( { maxLength: 3 } , [ 1,2,3,4 ] ) ; doormen( { maxLength: 3 } , [ 1,2 ] ) ; doormen.not( { maxLength: 3 } , 1 ) ; doormen.not( { maxLength: 0 } , 1 ) ; doormen.not( { maxLength: 3 } , NaN ) ; doormen.not( { maxLength: 3 } , true ) ; doormen.not( { maxLength: 3 } , false ) ; ``` minLength + maxLength filter should validate accordingly, data that do not have a length should throw. ```js doormen( { minLength: 3 , maxLength: 5 } , "abc" ) ; doormen( { minLength: 3 , maxLength: 5 } , "abcd" ) ; doormen( { minLength: 3 , maxLength: 5 } , "abcde" ) ; doormen.not( { minLength: 3 , maxLength: 5 } , "abcdef" ) ; doormen.not( { minLength: 3 , maxLength: 5 } , "ab" ) ; doormen.not( { minLength: 3 , maxLength: 5 } , "" ) ; doormen.not( { minLength: 3 , maxLength: 5 } , 1 ) ; doormen.not( { maxLength: 0 } , 1 ) ; doormen.not( { minLength: 3 , maxLength: 5 } , NaN ) ; doormen.not( { minLength: 3 , maxLength: 5 } , true ) ; doormen.not( { minLength: 3 , maxLength: 5 } , false ) ; ``` 'match' filter should validate accordingly using a RegExp. ```js doormen( { match: "^[a-f]*$" } , "" ) ; doormen.not( { match: "^[a-f]+$" } , "" ) ; doormen( { match: "^[a-f]*$" } , "abc" ) ; doormen( { match: "^[a-f]*$" } , "abcdef" ) ; doormen.not( { match: "^[a-f]*$" } , "ghi" ) ; doormen.not( { match: /^[a-f]*$/ } , "ghi" ) ; doormen.not( { match: "^[a-f]*$" } , 1 ) ; doormen.not( { match: "^[a-f]*$" } , NaN ) ; doormen.not( { match: "^[a-f]*$" } , true ) ; doormen.not( { match: "^[a-f]*$" } , false ) ; ``` 'in' filter should validate if the value is listed. ```js doormen.not( { in: [ 1 , 5 , 7 ] } , 10 ) ; doormen( { in: [ 1 , 5 , 7 ] } , 5 ) ; doormen( { in: [ 1 , 5 , 7 ] } , 1 ) ; doormen.not( { in: [ 1 , 5 , 7 ] } , 0 ) ; doormen.not( { in: [ 1 , 5 , 7 ] } , -10 ) ; doormen.not( { in: [ 1 , 5 , 7 ] } , Infinity ) ; doormen( { in: [ 1 , 5 , Infinity , 7 ] } , Infinity ) ; doormen.not( { in: [ 1 , 5 , 7 ] } , -Infinity ) ; doormen.not( { in: [ 1 , 5 , 7 ] } , NaN ) ; doormen( { in: [ 1 , 5 , NaN , 7 ] } , NaN ) ; doormen( { in: [ 1 , true , 5 , 7 ] } , true ) ; doormen.not( { in: [ 1 , 5 , 7 ] } , true ) ; doormen( { in: [ 1 , false , 5 , 7 ] } , false ) ; doormen.not( { in: [ 1 , 5 , 7 ] } , false ) ; doormen.not( { in: [ 1 , 5 , 7 ] } , "text" ) ; doormen( { in: [ 1 , "text" , 5 , 7 ] } , "text" ) ; doormen( { in: [ "string" , "text" , "bob" ] } , "text" ) ; doormen.not( { in: [ "string" , "text" , "bob" ] } , "bobby" ) ; doormen( { in: [ "string" , "text" , "" ] } , "" ) ; doormen.not( { in: [ "string" , "text" , "bob" ] } , "" ) ; ``` 'notIn' filter should validate if the value is listed. ```js doormen( { notIn: [ 1 , 5 , 7 ] } , 10 ) ; doormen.not( { notIn: [ 1 , 5 , 7 ] } , 5 ) ; doormen.not( { notIn: [ 1 , 5 , 7 ] } , 1 ) ; doormen( { notIn: [ 1 , 5 , 7 ] } , 0 ) ; doormen( { notIn: [ 1 , 5 , 7 ] } , -10 ) ; doormen( { notIn: [ 1 , 5 , 7 ] } , Infinity ) ; doormen.not( { notIn: [ 1 , 5 , Infinity , 7 ] } , Infinity ) ; doormen( { notIn: [ 1 , 5 , 7 ] } , -Infinity ) ; doormen( { notIn: [ 1 , 5 , 7 ] } , NaN ) ; doormen.not( { notIn: [ 1 , 5 , NaN , 7 ] } , NaN ) ; doormen.not( { notIn: [ 1 , true , 5 , 7 ] } , true ) ; doormen( { notIn: [ 1 , 5 , 7 ] } , true ) ; doormen.not( { notIn: [ 1 , false , 5 , 7 ] } , false ) ; doormen( { notIn: [ 1 , 5 , 7 ] } , false ) ; doormen( { notIn: [ 1 , 5 , 7 ] } , "text" ) ; doormen.not( { notIn: [ 1 , "text" , 5 , 7 ] } , "text" ) ; doormen.not( { notIn: [ "string" , "text" , "bob" ] } , "text" ) ; doormen( { notIn: [ "string" , "text" , "bob" ] } , "bobby" ) ; doormen.not( { notIn: [ "string" , "text" , "" ] } , "" ) ; doormen( { notIn: [ "string" , "text" , "bob" ] } , "" ) ; ``` 'in' filter containing object and arrays. ```js doormen( { in: [ 1 , { a: 2 } , 5 , 7 ] } , { a: 2 } ) ; doormen.not( { in: [ 1 , { a: 2 } , 5 , 7 ] } , { a: 2 , b: 5 } ) ; doormen.not( { in: [ 1 , { a: 2 } , { b: 5 } , 7 ] } , { a: 2 , b: 5 } ) ; doormen( { in: [ 1 , { a: 2 } , { a: 2 , b: 5 } , { b: 5 } , 7 ] } , { a: 2 , b: 5 } ) ; doormen( { in: [ 1 , [ 'a' , 2 ] , 5 , 7 ] } , [ 'a' , 2 ] ) ; doormen.not( { in: [ 1 , [ 'a' , 2 , 3 ] , 5 , 7 ] } , [ 'a' , 2 ] ) ; ``` <a name="filters"></a> # Filters 'greaterThan' and aliases ('gt' and '>') filter should validate accordingly, non-number should throw. ```js doormen( { filter: { greaterThan: 3 } } , 10 ) ; doormen( { filter: { greaterThan: 3 } } , 3.00001 ) ; doormen.not( { filter: { greaterThan: 3 } } , 3 ) ; doormen.not( { filter: { greaterThan: 3 } } , 1 ) ; doormen.not( { filter: { greaterThan: 3 } } , 0 ) ; doormen.not( { filter: { greaterThan: 3 } } , -10 ) ; doormen( { filter: { greaterThan: 3 } } , Infinity ) ; doormen.not( { filter: { greaterThan: Infinity } } , Infinity ) ; doormen.not( { filter: { greaterThan: 3 } } , -Infinity ) ; doormen.not( { filter: { greaterThan: 3 } } , NaN ) ; doormen.not( { filter: { greaterThan: 3 } } , true ) ; doormen.not( { filter: { greaterThan: 3 } } , false ) ; doormen.not( { filter: { greaterThan: 3 } } , undefined ) ; doormen.not( { filter: { greaterThan: 0 } } , undefined ) ; doormen.not( { filter: { greaterThan: -3 } } , undefined ) ; doormen.not( { filter: { greaterThan: 3 } } , '10' ) ; doormen( { filter: { gt: 3 } } , 3.00001 ) ; doormen.not( { filter: { gt: 3 } } , 3 ) ; doormen( { filter: { '>': 3 } } , 3.00001 ) ; doormen.not( { filter: { '>': 3 } } , 3 ) ; ``` 'lesserThan' and aliases ('lt' and '<') filter should validate accordingly, non-number should throw. ```js doormen.not( { filter: { lesserThan: 3 } } , 10 ) ; doormen( { filter: { lesserThan: 3 } } , 2.999 ) ; doormen.not( { filter: { lesserThan: 3 } } , 3 ) ; doormen( { filter: { lesserThan: 3 } } , 1 ) ; doormen( { filter: { lesserThan: 3 } } , 0 ) ; doormen( { filter: { lesserThan: 3 } } , -10 ) ; doormen.not( { filter: { lesserThan: 3 } } , Infinity ) ; doormen( { filter: { lesserThan: 3 } } , -Infinity ) ; doormen.not( { filter: { lesserThan: -Infinity } } , -Infinity ) ; doormen.not( { filter: { lesserThan: 3 } } , NaN ) ; doormen.not( { filter: { lesserThan: 3 } } , true ) ; doormen.not( { filter: { lesserThan: 3 } } , false ) ; doormen.not( { filter: { lesserThan: 3 } } , '1' ) ; doormen( { filter: { lt: 3 } } , 2.999 ) ; doormen.not( { filter: { lt: 3 } } , 3 ) ; doormen( { filter: { '<': 3 } } , 2.999 ) ; doormen.not( { filter: { '<': 3 } } , 3 ) ; ``` <a name="children-and-recursivity"></a> # Children and recursivity 'of' should perform the check recursively for each children, using the same given schema for all of them.. ```js var schema ; schema = { of: { type: 'string' } } ; // Object doormen( schema , { b: 'text' } ) ; doormen.not( schema , { a: 1 } ) ; doormen.not( schema , { a: 1 , b: 'text' } ) ; doormen.not( schema , { a: 'text' , b: 3 } ) ; doormen( schema , { a: 'text' , b: 'string' } ) ; doormen.not( schema , { A: 'TEXT' , b: 'text' , c: undefined } ) ; // Array doormen( schema , [ 'text' ] ) ; doormen( schema , [] ) ; doormen( schema , [ 'text' , 'string' ] ) ; doormen.not( schema , [ 'text' , 'string' , null ] ) ; doormen.not( schema , [ 1 , 'text' , 'string' ] ) ; doormen.not( schema , [ 'text' , 'string' , null ] ) ; doormen.not( schema , [ true ] ) ; ``` when 'properties' is an array, it should check if the value has all listed properties, no extra properties are allowed. ```js var schema = { properties: [ 'a' , 'b' ] } ; doormen( schema , { a: 1 , b: 'text' } ) ; doormen( schema , { a: 'text' , b: 3 } ) ; doormen.not( schema , { A: 'TEXT' , a: 1 , b: 'text' , c: 5 } ) ; doormen.not( schema , { b: 'text' } ) ; doormen.not( schema , { a: 1 } ) ; ``` when 'properties' is an array and 'extraProperties' is set, it should allow non-listed extra-properties. ```js var schema = { properties: [ 'a' , 'b' ] , extraProperties: true } ; doormen( schema , { a: 1 , b: 'text' } ) ; doormen( schema , { a: 'text' , b: 3 } ) ; doormen( schema , { A: 'TEXT' , a: 1 , b: 'text' , c: 5 } ) ; doormen.not( schema , { b: 'text' } ) ; doormen.not( schema , { a: 1 } ) ; ``` when 'properties' is an object, it should perform the check recursively for each listed child, no extra properties are allowed. ```js var schema = { properties: { a: { type: 'number' } , b: { type: 'string' } } } ; doormen( schema , { a: 1 , b: 'text' } ) ; doormen.not( schema , { a: 'text' , b: 3 } ) ; doormen.not( schema , { A: 'TEXT' , a: 1 , b: 'text' , c: 5 } ) ; doormen.not( schema , { b: 'text' } ) ; doormen.not( schema , { a: 1 } ) ; ``` when 'properties' is an object and 'extraProperties' is set, it should allow extra-properties. ```js var schema = { properties: { a: { type: 'number' } , b: { type: 'string' } } , extraProperties: true } ; doormen( schema , { a: 1 , b: 'text' } ) ; doormen.not( schema , { a: 'text' , b: 3 } ) ; doormen( schema , { A: 'TEXT' , a: 1 , b: 'text' , c: 5 } ) ; doormen.not( schema , { b: 'text' } ) ; doormen.not( schema , { a: 1 } ) ; ``` 'elements' should perform the check recursively for each children elements, using a specific schema for each one, extra-element are not allowed. ```js var schema = { elements: [ { type: 'string' } , { type: 'number' } , { type: 'boolean' } ] } ; doormen( schema , [ 'text' , 3 , false ] ) ; doormen.not( schema , [ 'text' , 3 , false , 'extra' , true ] ) ; doormen.not( schema , [] ) ; doormen.not( schema , [ 'text' , 3 ] ) ; doormen.not( schema , [ true ] ) ; ``` when 'elements' is used in conjunction with 'extraElements', extra-elements are allowed. ```js var schema = { elements: [ { type: 'string' } , { type: 'number' } , { type: 'boolean' } ] , extraElements: true } ; doormen( schema , [ 'text' , 3 , false ] ) ; doormen( schema , [ 'text' , 3 , false , 'extra' , true ] ) ; doormen.not( schema , [] ) ; doormen.not( schema , [ 'text' , 3 ] ) ; doormen.not( schema , [ true ] ) ; ``` <a name="mask"></a> # Mask should mask data using a tier-level. ```js var schema = { properties: { a: { type: 'number' , tier: 1 } , b: { type: 'boolean' , tier: 3 } , c: { type: 'string' , tier: 2 } } } ; var data = { a: 1 , b: true , c: 'blah!' } ; doormen.equals( doormen.tierMask( schema , data , 0 ) , {} ) ; doormen.equals( doormen.tierMask( schema , data , 1 ) , { a: 1 } ) ; doormen.equals( doormen.tierMask( schema , data , 2 ) , { a: 1 , c: 'blah!' } ) ; doormen.equals( doormen.tierMask( schema , data , 3 ) , { a: 1 , b: true , c: 'blah!' } ) ; doormen.equals( doormen.tierMask( schema , data , 4 ) , { a: 1 , b: true , c: 'blah!' } ) ; ``` should mask nested data using a tier-level. ```js var schema = { properties: { a: { type: 'number' , tier: 1 } , b: { type: 'boolean' , tier: 3 } , c: { type: 'string' , tier: 2 } , d: { type: 'strictObject' , properties: { e: { type: 'number' , tier: 1 } , f: { type: 'boolean' , tier: 3 } , g: { type: 'string' , tier: 2 } } } , d2: { type: 'strictObject' , tier: 2 , extraProperties: true , properties: { e: { type: 'number' , tier: 1 } , f: { type: 'boolean' , tier: 3 } , g: { type: 'string' , tier: 2 } } } } } ; var data = { a: 1 , b: true , c: 'blah!' , d: { e: 7 , f: false , g: 'bob' } , d2: { e: 7 , f: false , g: 'bob' } } ; doormen.equals( doormen.tierMask( schema , data , 1 ) , { a: 1 , d: { e: 7 } } ) ; doormen.equals( doormen.tierMask( schema , data , 2 ) , { a: 1 , c: 'blah!' , d: { e: 7 , g: 'bob' } , d2: { e: 7 , g: 'bob' } } ) ; doormen.equals( doormen.tierMask( schema , data , 3 ) , { a: 1 , b: true , c: 'blah!' , d: { e: 7 , f: false , g: 'bob' } , d2: { e: 7 , f: false , g: 'bob' } } ) ; // Test extra-properties data.d.extra = 'val' ; data.d2.extra = 'val' ; doormen.equals( doormen.tierMask( schema , data , 2 ) , { a: 1 , c: 'blah!' , d: { e: 7 , g: 'bob' } , d2: { e: 7 , g: 'bob' , extra: 'val' } } ) ; // Test submasking schema.properties.d2.noSubmasking = true ; doormen.equals( doormen.tierMask( schema , data , 2 ) , { a: 1 , c: 'blah!' , d: { e: 7 , g: 'bob' } , d2: { e: 7 , f: false , g: 'bob' , extra: 'val' } } ) ; ``` should mask data using tags. ```js var schema = { properties: { _id: { tags: [] } , slug: { tags: [ 'internal' , 'meta' ] } , access: { tags: [ 'internal' ] } , title: { tags: [ 'meta' ] } , post: { tags: [ 'content' ] } } } ; var data = { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , access: 'public' , title: '10 things you should know about nothing' , post: 'blah blah blah blah' } ; doormen.equals( doormen.tagMask( schema , data , [ 'meta' ] ) , { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , title: '10 things you should know about nothing' } ) ; // Test the non-array syntax doormen.equals( doormen.tagMask( schema , data , 'meta' ) , { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , title: '10 things you should know about nothing' } ) ; doormen.equals( doormen.tagMask( schema , data , [ 'internal' ] ) , { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , access: 'public' } ) ; doormen.equals( doormen.tagMask( schema , data , [ 'internal' , 'content' ] ) , { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , access: 'public' , post: 'blah blah blah blah' } ) ; doormen.equals( doormen.tagMask( schema , data , [ 'internal' , 'meta' , 'content' ] ) , { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , access: 'public' , title: '10 things you should know about nothing' , post: 'blah blah blah blah' } ) ; ``` should mask nested data using tags. ```js var schema = { properties: { _id: {} , slug: { tags: [ 'internal' , 'meta' ] } , accesses: { of: { properties: { userId: {} , accessLevel: { tags: [ 'internal' ] } } } } , title: { tags: [ 'meta' ] } , post: { tags: [ 'content' ] } } } ; var data = { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , accesses: [ { userId: 'bob' , accessLevel: 2 } , { userId: 'bill' , accessLevel: 3 } ] , title: '10 things you should know about nothing' , post: 'blah blah blah blah' } ; doormen.equals( doormen.tagMask( schema , data , [ 'meta' ] ) , { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , accesses: [ { userId: 'bob' } , { userId: 'bill' } ] , title: '10 things you should know about nothing' } ) ; doormen.equals( doormen.tagMask( schema , data , [ 'internal' ] ) , { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , accesses: [ { userId: 'bob' , accessLevel: 2 } , { userId: 'bill' , accessLevel: 3 } ] } ) ; doormen.equals( doormen.tagMask( schema , data , [ 'internal' , 'content' ] ) , { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , accesses: [ { userId: 'bob' , accessLevel: 2 } , { userId: 'bill' , accessLevel: 3 } ] , post: 'blah blah blah blah' } ) ; doormen.equals( doormen.tagMask( schema , data , [ 'internal' , 'meta' , 'content' ] ) , { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , accesses: [ { userId: 'bob' , accessLevel: 2 } , { userId: 'bill' , accessLevel: 3 } ] , title: '10 things you should know about nothing' , post: 'blah blah blah blah' } ) ; ``` tag-masking and 'noSubmasking' behavior. ```js var schema = { properties: { _id: {} , slug: { tags: [ 'internal' , 'meta' ] } , accesses: { of: { properties: { userId: {} , accessLevel: { tags: [ 'internal' ] } , details: { tags: [ 'internal' ] , properties: { k1: { type: 'string' , tags: [ 'nope' ] } , k2: { type: 'string' } } } } } } , title: { tags: [ 'meta' ] } , post: { tags: [ 'content' ] } } } ; var data = { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , accesses: [ { userId: 'bob' , accessLevel: 2 , details: { k1: 'one' , k2: 'two' } } , { userId: 'bill' , accessLevel: 3 , details: { k1: 'three' , k2: 'four' } } ] , title: '10 things you should know about nothing' , post: 'blah blah blah blah' } ; doormen.equals( doormen.tagMask( schema , data , [ 'meta' ] ) , { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , accesses: [ { userId: 'bob' } , { userId: 'bill' } ] , title: '10 things you should know about nothing' } ) ; doormen.equals( doormen.tagMask( schema , data , [ 'internal' ] ) , { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , accesses: [ { userId: 'bob' , accessLevel: 2 , details: { k2: 'two' } } , { userId: 'bill' , accessLevel: 3 , details: { k2: 'four' } } ] } ) ; schema.properties.accesses.of.properties.details.noSubmasking = true ; doormen.equals( doormen.tagMask( schema , data , [ 'internal' ] ) , { _id: '1978f09ac3e' , slug: 'ten-things-about-nothing' , accesses: [ { userId: 'bob' , accessLevel: 2 , details: { k1: 'one' , k2: 'two' } } , { userId: 'bill' , accessLevel: 3 , details: { k1: 'three' , k2: 'four' } } ] } ) ; ``` depthLimit with mask and nested data. ```js var schema = { properties: { title: { tags: [ 'meta' ] } , post: { tags: [ 'meta' ] } , accesses: { of: { properties: { userId: { tags: [ 'meta' ] } , accessLevel: { tags: [ 'internal' ] } } } } } } ; var data = { title: '10 things you should know about nothing' , post: 'blah blah blah blah' , accesses: [ { userId: 'bob' , accessLevel: 2 } , { userId: 'bill' , accessLevel: 3 } ] } ; doormen.equals( doormen.tagMask( schema , data , [ 'meta' ] ) , { title: '10 things you should know about nothing' , post: 'blah blah blah blah' , accesses: [ { userId: 'bob' } , { userId: 'bill' } ] } ) ; doormen.equals( doormen.tagMask( schema , data , [ 'meta' ] , 1 ) , { title: '10 things you should know about nothing' , post: 'blah blah blah blah' , accesses: [ { userId: 'bob' , accessLevel: 2 } , { userId: 'bill' , accessLevel: 3 } ] } ) ; doormen.equals( doormen.tagMask( schema , data , [ 'meta' ] , 2 ) , { title: '10 things you should know about nothing' , post: 'blah blah blah blah' , accesses: [ { userId: 'bob' , accessLevel: 2 } , { userId: 'bill' , accessLevel: 3 } ] } ) ; doormen.equals( doormen.tagMask( schema , data , [ 'meta' ] , 3 ) , { title: '10 things you should know about nothing' , post: 'blah blah blah blah' , accesses: [ { userId: 'bob' } , { userId: 'bill' } ] } ) ; ``` .patchTier(). ```js var schema = { type: 'strictObject' , properties: { a: { type: 'number' , tier: 3 } , b: { type: 'string' , tier: 1 } , c: { type: 'string' , tier: 4 } , embedded: { type: 'strictObject' , tier: 3 , extraProperties: true , properties: { d: { type: 'number' , tier: 2 } , e: { type: 'string' , tier: 4 } } } } } ; doormen.equals( doormen.patchTier( schema , {} ) , 1 ) ; doormen.equals( doormen.patchTier( schema , { a: 'some' , b: 'useless' , c: 'values' } ) , 4 ) ; doormen.equals( doormen.patchTier( schema , { a: 'some' } ) , 3 ) ; doormen.equals( doormen.patchTier( schema , { b: 'some' } ) , 1 ) ; doormen.equals( doormen.patchTier( schema , { a: 'some' , c: 'values' } ) , 4 ) ; doormen.equals( doormen.patchTier( schema , { a: 'some' , b: 'useless' } ) , 3 ) ; doormen.equals( doormen.patchTier( schema , { embedded: 'useless' } ) , 3 ) ; doormen.equals( doormen.patchTier( schema , { b: 'some' , 'embedded.e': 'useless' } ) , 4 ) ; doormen.equals( doormen.patchTier( schema , { b: 'some' , 'embedded.unexistant': 'useless' } ) , 3 ) ; doormen.equals( doormen.patchTier( schema , { 'embedded.e': 'useless' } ) , 4 ) ; doormen.equals( doormen.patchTier( schema , { 'embedded.d': 'useless' } ) , 3 ) ; doormen.equals( doormen.patchTier( schema , { 'embedded.unexistant': 'useless' } ) , 3 ) ; ``` .checkPatchByTags(). ```js var schema = { properties: { _id: {} , slug: { tags: [ 'internal' , 'meta' ] } , accesses: { of: { properties: { userId: {} , accessLevel: { tags: [ 'internal' ] } } } } , title: { tags: [ 'meta' ] } , post: { tags: [ 'content' ] } } } ; doormen.checkPatchByTags( schema , { slug: 'bob' } , 'meta' ) ; doormen.checkPatchByTags( schema , { slug: 'bob' } , 'internal' ) ; doormen.checkPatchByTags( schema , { slug: 'bob' } , [ 'meta' , 'content' ] ) ; doormen.shouldThrow( () => doormen.checkPatchByTags( schema , { slug: 'bob' } , 'content' ) ) ; doormen.shouldThrow( () => doormen.checkPatchByTags( schema , { slug: 'bob' } , [ 'content' , 'unknown' ] ) ) ; doormen.checkPatchByTags( schema , { title: 'bob' } , 'meta' ) ; doormen.checkPatchByTags( schema , { title: 'bob' } , [ 'content' , 'meta' ] ) ; doormen.shouldThrow( () => doormen.checkPatchByTags( schema , { title: 'bob' } , 'content' ) ) ; doormen.checkPatchByTags( schema , {