import Ajv from 'ajv' ; import * as fs from 'fs' ; import yaml from 'js-yaml' ; import schema from './selectors-schema.json' ; const objectSelector = require ( 'easy-object-selector' ) ; /**
 * This type represents the `value` part of a single Selector.
 */ export type SelectorValueType = string | number | boolean | Array < string > ; /**
 * This type represents the `operator` part of a single Selector.
 */ export type Operator = 'eq' | 'ne' | 'anyof' | 'regex' ; /**
 * The type `Selector` is a list with 3 fields:
 *   - Descriptor - represents the target property
 *   - Operator
 *   - Value
 */ export type Selector = [ string , Operator , SelectorValueType ] ; /**
 * Selectors represetns one or more Selectors combined with `AND`.
 */ export type Selectors = Array < Selector > ; /**
 * Loads `Selectors` from a yaml file.
 */ export function loadSelectors ( fileName ) { const selectorsYaml = fs . readFileSync ( fileName ) ; const candidate = yaml . load ( selectorsYaml . toString ( ) ) ; const ajv = new Ajv ( ) ; const validateSchema = ajv . compile ( schema ) ; const result = validateSchema ( candidate ) ; if ( ! result ) { throw new Error ( JSON . stringify ( validateSchema . errors , null , 4 ) ) ; } return candidate as Selectors ; } /**
 * A convenient shortcut for Repository selection.
 */ export function RepoDescriptorConvertor ( input ) { if ( input === 'org' || input === 'organization' ) { return 'owner.login' ; } return input ; } template T export class ObjectSelector < T > { private listOfSelectors : Array < Selectors > ; private descriptorConvertor : ( input : string ) => string ; constructor ( listOfSelectors , descriptorConvertor = ( input ) : string => { return input ; } ) { this . listOfSelectors = listOfSelectors ; this . descriptorConvertor = descriptorConvertor ; } template T param { Iterable < T > } targets returns { Array < T > } public select ( targets ) { const result = [ ] ; for ( const target of targets ) { if ( this . match ( target ) ) { result . push ( target ) ; } } return result ; } template T param { T } target returns { boolean } public match ( target ) { for ( const selectors of this . listOfSelectors ) { if ( this . _filter ( selectors , target ) ) { return true ; } } return false ; } private _filter ( selectors , candidate ) { if ( selectors . length === 0 ) { return false ; } for ( const selector of selectors ) { if ( ! this . _select ( selector , candidate ) ) { return false ; } } return true ; } private _select ( selector , candidate ) { let descriptor = selector [ 0 ] ; const operator = selector [ 1 ] ; const value = selector [ 2 ] ; descriptor = this . descriptorConvertor ( descriptor ) ; const select = objectSelector . select ; const actualValue = select ( candidate , descriptor ) ; if ( operator === 'eq' ) { return actualValue === value ; } else if ( operator === 'ne' ) { return actualValue !== value ; } else if ( operator === 'anyof' ) { if ( ! Array . isArray ( value ) ) { return false ; } return value . includes ( actualValue ) ; } else if ( operator === 'regex' ) { if ( typeof value !== 'string' ) { return false ; } try { const re = new RegExp ( value ) ; return actualValue . match ( re ) ; } catch ( err ) { return false ; } } return false ; } }