import * as Debug from 'debug' ; import * as fs from 'fs' ; import { IncomingMessage , ServerResponse } from 'http' ; import { nohm as instantiatedNohm , NohmClass } from '.' ; import { NohmModel } from './model' ; import { universalValidatorPath } from './validators' ; export interface IExclusionsOption { [ key ] : Array < number | boolean > | boolean ; } export type TRequestHandler = ( req : IncomingMessage , res : ServerResponse , next ? : any ) => void ; export interface IMiddlewareOptions { url ? : string ; namespace ? : string ; maxAge ? : number ; exclusions ? : { [ key ] : IExclusionsOption | boolean ; } ; extraFiles ? : string | Array < string > ; uglify ? : any ; } const debug = Debug ( 'nohm:middleware' ) ; const MAX_DEPTH = 5 ; function customToString ( obj , depth = 0 ) { if ( depth > MAX_DEPTH ) { console . warn ( new Error ( 'nohm: middleware customToString() maxdepth exceeded' ) . stack ) ; return '' ; } switch ( typeof obj ) { case 'string' : return '"' + obj + '"' ; case 'number' : return obj . toString ( ) ; case 'boolean' : return obj ? 'true' : 'false' ; case 'function' : if ( obj instanceof RegExp ) { return obj . toString ( ) ; } break ; case 'object' : if ( Array . isArray ( obj ) ) { const arr = [ ] ; obj . forEach ( ( val ) => { arr . push ( customToString ( val , depth + 1 ) ) ; } ) ; return '[' + arr . join ( ',' ) + ']' ; } else if ( obj instanceof RegExp ) { return obj . toString ( ) ; } else { const arr = [ ] ; Object . keys ( obj ) . forEach ( ( val ) => { arr . push ( '"' + val + '":' + customToString ( obj [ val ] , depth + 1 ) ) ; } ) ; return '{' + arr . join ( ',' ) + '}' ; } default : return '' ; } return '' ; } function validationsFlatten ( model , exclusions = { } ) { const instance = new model ( ) ; const definitions = instance . getDefinitions ( ) ; let str = instance . modelName + ': {' ; const exclusionsStrings = [ ] ; const exclusionsObject = { } ; Object . keys ( exclusions ) . forEach ( ( key ) => { const value = exclusions [ key ] ; if ( Array . isArray ( value ) ) { exclusionsObject [ key ] = value . map ( ( x ) => ! ! x ) ; } exclusionsStrings . push ( key ) ; } ) ; Object . keys ( definitions ) . forEach ( ( key ) => { const isExcepted = exclusionsStrings . indexOf ( key ) !== - 1 && ! exclusionsObject . hasOwnProperty ( key ) ; if ( ! isExcepted ) { const vals = definitions [ key ] . validations ; if ( Array . isArray ( vals ) && vals . length > 0 ) { str += `${ key }: [` ; const strVals = [ ] ; vals . forEach ( ( val , index ) => { if ( ! exclusionsObject [ key ] || exclusionsObject [ key ] [ index ] ) { strVals . push ( customToString ( val ) ) ; } } ) ; str += strVals . join ( ',' ) + '],           ' ; } } } ) ; return str + '}' ; } let extraFilesIndex = 0 ; function wrapFile ( fileStr , namespace ) { let str = `${ namespace }.extraValidations[${ extraFilesIndex }]={};(function (exports) {` ; str += fileStr ; str += `})(${ namespace }.extraValidations[${ extraFilesIndex }]);` ; extraFilesIndex ++ ; return str ; } function wrapExtraFiles ( files , namespace ) { let str = '' ; files . forEach ( ( path ) => { const fileStr = fs . readFileSync ( path , 'utf-8' ) ; str += wrapFile ( fileStr , namespace ) ; } ) ; return str ; } see https param options param { string } options . url param { object . < string , object | boolean > } options . exclusions param { string } options . namespace param { string } options . extraFiles param { number } options . maxAge param { boolean } options . uglify return { Middleware instance memberof export function middleware ( options , nohm = instantiatedNohm ) { options = options || { } ; const url = options . url || '/nohmValidations.js' ; const namespace = options . namespace || 'nohmValidations' ; const maxAge = options . maxAge || 3600 ; const exclusions = options . exclusions || { } ; let extraFiles = options . extraFiles || [ ] ; let uglify = options . uglify || false ; if ( ! Array . isArray ( extraFiles ) ) { extraFiles = [ extraFiles ] ; } const collectedModels = [ ] ; const models = nohm . getModels ( ) ; Object . keys ( models ) . forEach ( ( name ) => { const model = models [ name ] ; let exclusion = exclusions [ name ] ; if ( exclusion === true ) { return ; } else { if ( exclusion === true || exclusion === false ) { exclusion = { } ; } collectedModels . push ( validationsFlatten ( model , exclusion ) ) ; } } ) ; let str = `var nohmValidationsNamespaceName = "${ namespace }";
  var ${ namespace }={"extraValidations": [], "models":{${ collectedModels . join ( ',' ) }}};
  // extrafiles
  ${ wrapExtraFiles ( extraFiles , namespace ) }
  // extravalidations
  ${ wrapExtraFiles ( nohm . getExtraValidatorFileNames ( ) , namespace ) }
  // validators.js
  ${ fs . readFileSync ( universalValidatorPath , 'utf-8' ) }` ; if ( uglify ) { try { uglify = require ( 'uglify-js' ) ; } catch ( e ) { console . warn ( 'You tried to use the uglify option in Nohm.connect but uglify-js is not requirable.' , 'Continuing without uglify.' , e ) ; } if ( uglify . parser && uglify . uglify ) { const jsp = uglify . parser ; const pro = uglify . uglify ; const ast = jsp . parse ( str ) ; const squeezed = pro . ast_squeeze ( ast ) ; str = pro . gen_code ( squeezed ) ; } } debug ( `Setting up middleware to be served on '%s' with namespace '%s' and collected these models: %o` , url , namespace , collectedModels ) ; /**
     * This function is what is returned by  NohmClass .
     *
     *  callback Middleware @callback Middleware~callback
     *  name function param { Object } req param { Object } res param { function } next memberof return ( req , res , next ? ) => { if ( req . url === url ) { res . statusCode = 200 ; res . setHeader ( 'Content-Type' , 'text/javascript' ) ; res . setHeader ( 'Content-Length' , str . length . toString ( ) ) ; res . setHeader ( 'Cache-Control' , 'public, max-age=' + maxAge ) ; res . end ( str ) ; } else if ( next && typeof next === 'function' ) { next ( ) ; } } ; }