import { Rule , RuleProperty , ValidationDisplayNameAccessor } from './rule' ; import { ValidationMessageParser } from './validation-message-parser' ; import { Rules } from './rules' ; import { validationMessages } from './validation-messages' ; import { PropertyAccessorParser , PropertyAccessor } from '../property-accessor-parser' ; import { isString } from '../util' ; /**
 * Part of the fluent rule API. Enables customizing property rules.
 */ export class FluentRuleCustomizer < TObject , TValue > { private rule : Rule < TObject , TValue > ; constructor ( property , condition , config = { } , private fluentEnsure , private fluentRules , private parsers ) { this . rule = { property , condition , config , when : null , messageKey : 'default' , message : null , sequence : fluentRules . sequence } ; this . fluentEnsure . _addRule ( this . rule ) ; } /**
     * Validate subsequent rules after previously declared rules have
     * been validated successfully. Use to postpone validation of costly
     * rules until less expensive rules pass validation.
     */ public then ( ) { this . fluentRules . sequence ++ ; return this ; } /**
     * Specifies the key to use when looking up the rule's validation message.
     */ public withMessageKey ( key ) { this . rule . messageKey = key ; this . rule . message = null ; return this ; } /**
     * Specifies rule's validation message.
     */ public withMessage ( message ) { this . rule . messageKey = 'custom' ; this . rule . message = this . parsers . message . parse ( message ) ; return this ; } param condition public when ( condition ) { this . rule . when = condition ; return this ; } /**
     * Tags the rule instance, enabling the rule to be found easily
     * using ValidationRules.taggedRules(rules, tag)
     */ public tag ( tag ) { this . rule . tag = tag ; return this ; } param property public ensure < TValue2 > ( subject ) { return this . fluentEnsure . ensure < TValue2 > ( subject ) ; } /**
     * Targets an object with validation rules.
     */ public ensureObject ( ) { return this . fluentEnsure . ensureObject ( ) ; } /**
     * Rules that have been defined using the fluent API.
     */ public get rules ( ) { return this . fluentEnsure . rules ; } param target public on ( target ) { return this . fluentEnsure . on ( target ) ; } param condition public satisfies ( condition , config ? ) { return this . fluentRules . satisfies ( condition , config ) ; } param name param args public satisfiesRule ( name , ... args ) { return this . fluentRules . satisfiesRule ( name , ... args ) ; } /**
     * Applies the "required" rule to the property.
     * The value cannot be null, undefined or whitespace.
     */ public required ( ) { return this . fluentRules . required ( ) ; } /**
     * Applies the "matches" rule to the property.
     * Value must match the specified regular expression.
     * null, undefined and empty-string values are considered valid.
     */ public matches ( regex ) { return this . fluentRules . matches ( regex ) ; } /**
     * Applies the "email" rule to the property.
     * null, undefined and empty-string values are considered valid.
     */ public email ( ) { return this . fluentRules . email ( ) ; } /**
     * Applies the "minLength" STRING validation rule to the property.
     * null, undefined and empty-string values are considered valid.
     */ public minLength ( length ) { return this . fluentRules . minLength ( length ) ; } /**
     * Applies the "maxLength" STRING validation rule to the property.
     * null, undefined and empty-string values are considered valid.
     */ public maxLength ( length ) { return this . fluentRules . maxLength ( length ) ; } /**
     * Applies the "minItems" ARRAY validation rule to the property.
     * null and undefined values are considered valid.
     */ public minItems ( count ) { return this . fluentRules . minItems ( count ) ; } /**
     * Applies the "maxItems" ARRAY validation rule to the property.
     * null and undefined values are considered valid.
     */ public maxItems ( count ) { return this . fluentRules . maxItems ( count ) ; } /**
     * Applies the "min" NUMBER validation rule to the property.
     * Value must be greater than or equal to the specified constraint.
     * null and undefined values are considered valid.
     */ public min ( value ) { return this . fluentRules . min ( value ) ; } /**
     * Applies the "max" NUMBER validation rule to the property.
     * Value must be less than or equal to the specified constraint.
     * null and undefined values are considered valid.
     */ public max ( value ) { return this . fluentRules . max ( value ) ; } /**
     * Applies the "range" NUMBER validation rule to the property.
     * Value must be between or equal to the specified min and max.
     * null and undefined values are considered valid.
     */ public range ( min , max ) { return this . fluentRules . range ( min , max ) ; } /**
     * Applies the "between" NUMBER validation rule to the property.
     * Value must be between but not equal to the specified min and max.
     * null and undefined values are considered valid.
     */ public between ( min , max ) { return this . fluentRules . between ( min , max ) ; } /**
     * Applies the "equals" validation rule to the property.
     * null, undefined and empty-string values are considered valid.
     */ public equals ( expectedValue ) { return this . fluentRules . equals ( expectedValue ) ; } } /**
 * Part of the fluent rule API. Enables applying rules to properties and objects.
 */ export class FluentRules < TObject , TValue > { public static customRules : { [ name ] : { condition : ( value : any , object ? : any , ... fluentArgs : any [ ] ) => boolean | Promise < boolean > ; argsToConfig ? : ( ... args : any [ ] ) => any ; } ; } = { } ; /**
     * Current rule sequence number. Used to postpone evaluation of rules until rules
     * with lower sequence number have successfully validated. The "then" fluent API method
     * manages this property, there's usually no need to set it directly.
     */ public sequence = 0 ; constructor ( private fluentEnsure , private parsers , private property ) { } /**
     * Sets the display name of the ensured property.
     */ public displayName ( name ) { this . property . displayName = name ; return this ; } param condition public satisfies ( condition , config ? ) { return new FluentRuleCustomizer < TObject , TValue > ( this . property , condition , config , this . fluentEnsure , this , this . parsers ) ; } param name param args public satisfiesRule ( name , ... args ) { let rule = FluentRules . customRules [ name ] ; if ( ! rule ) { rule = ( this as any ) [ name ] ; if ( rule instanceof Function ) { return rule . call ( this , ... args ) ; } throw new Error ( `Rule with name "${ name }" does not exist.` ) ; } const config = rule . argsToConfig ? rule . argsToConfig ( ... args ) : undefined ; return this . satisfies ( ( value , obj ) => rule . condition . call ( this , value , obj , ... args ) , config ) . withMessageKey ( name ) ; } /**
     * Applies the "required" rule to the property.
     * The value cannot be null, undefined or whitespace.
     */ public required ( ) { return this . satisfies ( value => value !== null && value !== undefined && ! ( isString ( value ) && ! /\S/ . test ( value as any ) ) ) . withMessageKey ( 'required' ) ; } /**
     * Applies the "matches" rule to the property.
     * Value must match the specified regular expression.
     * null, undefined and empty-string values are considered valid.
     */ public matches ( regex ) { return this . satisfies ( value => value === null || value === undefined || ( value as any ) . length === 0 || regex . test ( value as any ) ) . withMessageKey ( 'matches' ) ; } /**
     * Applies the "email" rule to the property.
     * null, undefined and empty-string values are considered valid.
     */ public email ( ) { return this . matches ( /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ ) . withMessageKey ( 'email' ) ; } /**
     * Applies the "minLength" STRING validation rule to the property.
     * null, undefined and empty-string values are considered valid.
     */ public minLength ( length ) { return this . satisfies ( ( value ) => value === null || value === undefined || value . length === 0 || value . length >= length , { length } ) . withMessageKey ( 'minLength' ) ; } /**
     * Applies the "maxLength" STRING validation rule to the property.
     * null, undefined and empty-string values are considered valid.
     */ public maxLength ( length ) { return this . satisfies ( ( value ) => value === null || value === undefined || value . length === 0 || value . length <= length , { length } ) . withMessageKey ( 'maxLength' ) ; } /**
     * Applies the "minItems" ARRAY validation rule to the property.
     * null and undefined values are considered valid.
     */ public minItems ( count ) { return this . satisfies ( ( value ) => value === null || value === undefined || value . length >= count , { count } ) . withMessageKey ( 'minItems' ) ; } /**
     * Applies the "maxItems" ARRAY validation rule to the property.
     * null and undefined values are considered valid.
     */ public maxItems ( count ) { return this . satisfies ( ( value ) => value === null || value === undefined || value . length <= count , { count } ) . withMessageKey ( 'maxItems' ) ; } /**
     * Applies the "min" NUMBER validation rule to the property.
     * Value must be greater than or equal to the specified constraint.
     * null and undefined values are considered valid.
     */ public min ( constraint ) { return this . satisfies ( ( value ) => value === null || value === undefined || value >= constraint , { constraint } ) . withMessageKey ( 'min' ) ; } /**
     * Applies the "max" NUMBER validation rule to the property.
     * Value must be less than or equal to the specified constraint.
     * null and undefined values are considered valid.
     */ public max ( constraint ) { return this . satisfies ( ( value ) => value === null || value === undefined || value <= constraint , { constraint } ) . withMessageKey ( 'max' ) ; } /**
     * Applies the "range" NUMBER validation rule to the property.
     * Value must be between or equal to the specified min and max.
     * null and undefined values are considered valid.
     */ public range ( min , max ) { return this . satisfies ( ( value ) => value === null || value === undefined || ( value >= min && value <= max ) , { min , max } ) . withMessageKey ( 'range' ) ; } /**
     * Applies the "between" NUMBER validation rule to the property.
     * Value must be between but not equal to the specified min and max.
     * null and undefined values are considered valid.
     */ public between ( min , max ) { return this . satisfies ( ( value ) => value === null || value === undefined || ( value > min && value < max ) , { min , max } ) . withMessageKey ( 'between' ) ; } /**
     * Applies the "equals" validation rule to the property.
     * null and undefined values are considered valid.
     */ public equals ( expectedValue ) { return this . satisfies ( value => value === null || value === undefined || value as any === '' || value === expectedValue , { expectedValue } ) . withMessageKey ( 'equals' ) ; } } /**
 * Part of the fluent rule API. Enables targeting properties and objects with rules.
 */ export class FluentEnsure < TObject > { /**
     * Rules that have been defined using the fluent API.
     */ public rules : Rule < TObject , any > [ ] [ ] = [ ] ; constructor ( private parsers ) { } param property public ensure < TValue > ( property ) { this . assertInitialized ( ) ; const name = this . parsers . property . parse ( property ) ; const fluentRules = new FluentRules < TObject , TValue > ( this , this . parsers , { name , displayName : null } ) ; return this . mergeRules ( fluentRules , name ) ; } /**
     * Targets an object with validation rules.
     */ public ensureObject ( ) { this . assertInitialized ( ) ; const fluentRules = new FluentRules < TObject , TObject > ( this , this . parsers , { name : null , displayName : null } ) ; return this . mergeRules ( fluentRules , null ) ; } param target public on ( target ) { Rules . set ( target , this . rules ) ; return this ; } internal public _addRule ( rule ) { while ( this . rules . length < rule . sequence + 1 ) { this . rules . push ( [ ] ) ; } this . rules [ rule . sequence ] . push ( rule ) ; } private assertInitialized ( ) { if ( this . parsers ) { return ; } throw new Error ( `Did you forget to add ".plugin('aurelia-validation')" to your main.js?` ) ; } private mergeRules ( fluentRules , propertyName ) { const existingRules = this . rules . find ( r => r . length > 0 && r [ 0 ] . property . name == propertyName ) ; if ( existingRules ) { const rule = existingRules [ existingRules . length - 1 ] ; fluentRules . sequence = rule . sequence ; if ( rule . property . displayName !== null ) { fluentRules = fluentRules . displayName ( rule . property . displayName ) ; } } return fluentRules ; } } /**
 * Fluent rule definition API.
 */ export class ValidationRules { private static parsers : Parsers ; public static initialize ( messageParser , propertyParser ) { this . parsers = { message : messageParser , property : propertyParser } ; } param property public static ensure < TObject , TValue > ( property ) { return new FluentEnsure < TObject > ( ValidationRules . parsers ) . ensure ( property ) ; } /**
     * Targets an object with validation rules.
     */ public static ensureObject < TObject > ( ) { return new FluentEnsure < TObject > ( ValidationRules . parsers ) . ensureObject ( ) ; } param name param condition param message param argsToConfig public static customRule ( name , condition , message , argsToConfig ? ) { validationMessages [ name ] = message ; FluentRules . customRules [ name ] = { condition , argsToConfig } ; } param rules param tag public static taggedRules ( rules , tag ) { return rules . map ( x => x . filter ( r => r . tag === tag ) ) ; } param rules public static untaggedRules ( rules ) { return rules . map ( x => x . filter ( r => r . tag === undefined ) ) ; } param target public static off ( target ) { Rules . unset ( target ) ; } } export interface Parsers { message : ValidationMessageParser ; property : PropertyAccessorParser ; }