import 'reflect-metadata' export const ensureKey = Symbol('ensure') export const ensureClassKey = Symbol('ensureClass') export type Validate = (context: Context) => boolean | Promise export function ensure( validate: Validate, message: string = 'Validation failed', ) { return function ensureImpl( target: any, key?: string, descriptor?: PropertyDescriptor) { switch (arguments.length) { case 1: return ensureClass(validate, message).apply(null, arguments as any) case 3: return ensureMethod(validate, message).apply(null, arguments as any) default: throw new Error('Unexpected number of arguments for @ensure. ' + 'It can only be used as as class or method decorator') } } } function ensureClass( validate: Validate, message: string = 'Validation failed', ) { // tslint:disable-next-line return (target: any) => { const validators: Array> = getValidatorsForClass(target) validators.push(validate) Reflect.defineMetadata(ensureKey, validators, target) } } function ensureMethod( validate: Validate, message: string = 'Validation failed', ) { return ( target: any, propertyKey: string, // tslint:disable-next-line descriptor: PropertyDescriptor, ) => { const validators: Array> = getValidatorsForMethod(target, propertyKey) validators.push(validate) Reflect.defineMetadata(ensureKey, validators, target, propertyKey) } } export function getValidatorsForInstance( target: any, ): Array> { return getValidatorsForClass(target.__proto__.constructor) } export function getValidatorsForClass( target: any, ): Array> { return Reflect.getMetadata(ensureKey, target) || [] } export function getValidatorsForMethod( target: any, method: string, ): Array> { return Reflect.getMetadata(ensureKey, target, method) || [] }