license import { Platform } from './platforms/platform' ; import { isPromise } from './util_base' ; const TENSORFLOWJS_FLAGS_PREFIX = 'tfjsflags' ; type FlagValue = number | boolean ; type FlagEvaluationFn = ( ( ) => FlagValue ) | ( ( ) => Promise < FlagValue > ) ; export type Flags = { [ featureName ] : FlagValue ; } ; export type FlagRegistryEntry = { evaluationFn : FlagEvaluationFn ; setHook ? : ( value : FlagValue ) => void ; } ; doc export class Environment { private flags : Flags = { } ; private flagRegistry : { [ flagName ] : FlagRegistryEntry ; } = { } ; private urlFlags : Flags = { } ; platformName : string ; platform : Platform ; getQueryParams = getQueryParams ; constructor ( public global ) { this . populateURLFlags ( ) ; } setPlatform ( platformName , platform ) { if ( this . platform != null ) { if ( ! ( env ( ) . getBool ( 'IS_TEST' ) || env ( ) . getBool ( 'PROD' ) ) ) { console . warn ( `Platform ${ this . platformName } has already been set. ` + `Overwriting the platform with ${ platform }.` ) ; } } this . platformName = platformName ; this . platform = platform ; } registerFlag ( flagName , evaluationFn , setHook ? ) { this . flagRegistry [ flagName ] = { evaluationFn , setHook } ; if ( this . urlFlags [ flagName ] != null ) { const flagValue = this . urlFlags [ flagName ] ; if ( ! ( env ( ) . getBool ( 'IS_TEST' ) || env ( ) . getBool ( 'PROD' ) ) ) { console . warn ( `Setting feature override from URL ${ flagName }: ${ flagValue }.` ) ; } this . set ( flagName , flagValue ) ; } } async getAsync ( flagName ) { if ( flagName in this . flags ) { return this . flags [ flagName ] ; } this . flags [ flagName ] = await this . evaluateFlag ( flagName ) ; return this . flags [ flagName ] ; } get ( flagName ) { if ( flagName in this . flags ) { return this . flags [ flagName ] ; } const flagValue = this . evaluateFlag ( flagName ) ; if ( isPromise ( flagValue ) ) { throw new Error ( `Flag ${ flagName } cannot be synchronously evaluated. ` + `Please use getAsync() instead.` ) ; } this . flags [ flagName ] = flagValue ; return this . flags [ flagName ] ; } getNumber ( flagName ) { return this . get ( flagName ) as number ; } getBool ( flagName ) { return this . get ( flagName ) as boolean ; } getFlags ( ) { return this . flags ; } get features ( ) : Flags { return this . flags ; } set ( flagName , value ) { if ( this . flagRegistry [ flagName ] == null ) { throw new Error ( `Cannot set flag ${ flagName } as it has not been registered.` ) ; } this . flags [ flagName ] = value ; if ( this . flagRegistry [ flagName ] . setHook != null ) { this . flagRegistry [ flagName ] . setHook ( value ) ; } } private evaluateFlag ( flagName ) { if ( this . flagRegistry [ flagName ] == null ) { throw new Error ( `Cannot evaluate flag '${ flagName }': no evaluation function found.` ) ; } return this . flagRegistry [ flagName ] . evaluationFn ( ) ; } setFlags ( flags ) { this . flags = Object . assign ( { } , flags ) ; } reset ( ) { this . flags = { } ; this . urlFlags = { } ; this . populateURLFlags ( ) ; } private populateURLFlags ( ) { if ( typeof this . global === 'undefined' || typeof this . global . location === 'undefined' || typeof this . global . location . search === 'undefined' ) { return ; } const urlParams = this . getQueryParams ( this . global . location . search ) ; if ( TENSORFLOWJS_FLAGS_PREFIX in urlParams ) { const keyValues = urlParams [ TENSORFLOWJS_FLAGS_PREFIX ] . split ( ',' ) ; keyValues . forEach ( keyValue => { const [ key , value ] = keyValue . split ( ':' ) as [ string , string ] ; this . urlFlags [ key ] = parseValue ( key , value ) ; } ) ; } } } export function getQueryParams ( queryString ) { const params = { } ; queryString . replace ( /[?&]([^=?&]+)(?:=([^&]*))?/g , ( s , ... t ) => { decodeParam ( params , t [ 0 ] , t [ 1 ] ) ; return t . join ( '=' ) ; } ) ; return params ; } function decodeParam ( params , name , value ? ) { params [ decodeURIComponent ( name ) ] = decodeURIComponent ( value || '' ) ; } function parseValue ( flagName , value ) { value = value . toLowerCase ( ) ; if ( value === 'true' || value === 'false' ) { return value === 'true' ; } else if ( `${ + value }` === value ) { return + value ; } throw new Error ( `Could not parse value flag value ${ value } for flag ${ flagName }.` ) ; } doc export function env ( ) { return ENV ; } export let ENV = null ; export function setEnvironmentGlobal ( environment ) { ENV = environment ; }