import { extend , arrayUnion } from './utils' ; const PRIVATE_PREFIX = '$$' ; const RESERVED_REGEX = /^(\$\$).*/ ; function validateMethodName ( name ) { if ( ! name ) { throw new Error ( `Illegal method name. Empty method name is not allowed` ) ; } else if ( name in this ) { throw new Error ( `A member name '${ name }' already defined.` ) ; } } param subject function getAssignedPropertyNames ( subject ) { return Object . getOwnPropertyNames ( subject ) . filter ( name => RESERVED_REGEX . test ( name ) ) . map ( name => name . substr ( 2 ) ) ; } export function privateKey ( name ) { return PRIVATE_PREFIX + name ; } function objectDefinePropertyValue ( obj , propertyName , value ) { Object . defineProperty ( obj , propertyName , < any > { configurable : false , enumerable : false , writable : false , value } ) ; } param instance param defaultValues function applyDefaultValues ( instance , defaultValues ) { Object . getOwnPropertyNames ( defaultValues ) . forEach ( name => ( < any > instance ) [ privateKey ( name ) ] = ( < any > defaultValues ) [ name ] ) ; } param obj param propertyName param writeOnce export function setAssignMethod < T > ( obj , propertyName , writeOnce = false ) { validateMethodName . call ( obj , propertyName ) ; const key = privateKey ( propertyName ) ; objectDefinePropertyValue ( obj , propertyName , ( value ) => { if ( writeOnce && this . hasOwnProperty ( key ) ) { throw new Error ( `Overriding config property '${ propertyName }' is not allowed.` ) ; } obj [ key ] = value ; return obj ; } ) ; } param obj param propertyName param srcPropertyName param hard export function setAssignAlias < T > ( obj , propertyName , srcPropertyName , hard = false ) { validateMethodName . call ( obj , propertyName ) ; objectDefinePropertyValue ( obj , propertyName , ( value ) => { obj [ srcPropertyName ] ( value ) ; return obj ; } ) ; if ( hard === true ) { const key = privateKey ( propertyName ) , srcKey = privateKey ( srcPropertyName ) ; Object . defineProperty ( obj , key , < any > { configurable : false , enumerable : false , get : ( ) => obj [ srcKey ] } ) ; } } /**
 * Describes a fluent assign method.
 * A function that gets a value and returns the instance it works on.
 */ export interface FluentAssignMethod < T , Z > { ( value ) : Z ; } export interface IFluentAssignFactory < Z > { fluentAssign : Z ; setMethod ( name , defaultValue ? ) : IFluentAssignFactory < Z > ; } /**
 * Represent a fluent API factory wrapper for defining FluentAssign instances.
 */ export class FluentAssignFactory < T > { private _fluentAssign : FluentAssign < T > ; constructor ( fluentAssign ? ) { this . _fluentAssign = fluentAssign instanceof FluentAssign ? fluentAssign : < any > new FluentAssign ( ) ; } param name param defaultValue setMethod ( name , defaultValue = undefined ) { setAssignMethod ( this . _fluentAssign , name ) ; if ( defaultValue !== undefined ) { ( < any > this . _fluentAssign ) [ name ] ( defaultValue ) ; } return this ; } /**
     * The FluentAssign instance.
     */ get fluentAssign ( ) : FluentAssign < T > { return this . _fluentAssign ; } } /**
 * Represent an object where every property is a function representing an assignment function.
 * Calling each function with a value will assign the value to the object and return the object.
 * Calling 'toJSON' returns an object with the same properties but this time representing the
 * assigned values.
 *
 * This allows setting an object in a fluent API manner.
 * Example:
 let fluent = new FluentAssign<any>(undefined, ['some', 'went']);
 fluent.some('thing').went('wrong').toJSON();
 // { some: 'thing', went: 'wrong' }
 */ export class FluentAssign < T > { private __fluent$base__ : new ( ) => T ; param defaultValues param initialSetters static compose < T > ( defaultValues = undefined , initialSetters = undefined ) { return < any > FluentAssign . composeWith < FluentAssign < T > > ( new FluentAssign < T > ( defaultValues , initialSetters ) ) ; } param fluentAssign static composeWith < Z > ( fluentAssign ) { return < any > new FluentAssignFactory < any > ( < any > fluentAssign ) ; } param defaultValues param initialSetters param baseType constructor ( defaultValues = undefined , initialSetters = undefined , baseType = undefined ) { if ( Array . isArray ( defaultValues ) ) { ( defaultValues as Array < any > ) . forEach ( d => applyDefaultValues ( this , d ) ) ; } else if ( defaultValues ) { applyDefaultValues ( this , defaultValues ) ; } if ( Array . isArray ( initialSetters ) ) { initialSetters . forEach ( name => setAssignMethod ( this , name ) ) ; } if ( baseType ) { this . __fluent$base__ = baseType ; } } toJSON ( ) { return getAssignedPropertyNames ( this ) . reduce ( ( obj , name ) => { const key = privateKey ( name ) ; let propDesc = Object . getOwnPropertyDescriptor ( this , key ) ; if ( propDesc ) { Object . defineProperty ( obj , name , propDesc ) ; } else { ( < any > obj ) [ name ] = ( < any > this ) [ key ] ; } return obj ; } , this . __fluent$base__ ? new this . __fluent$base__ ( ) : < any > { } ) ; } }