UNPKG

5.67 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright 2017 Google LLC. All Rights Reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 * =============================================================================
16 */
17import { isPromise } from './util_base';
18// Expects flags from URL in the format ?tfjsflags=FLAG1:1,FLAG2:true.
19const TENSORFLOWJS_FLAGS_PREFIX = 'tfjsflags';
20/**
21 * The environment contains evaluated flags as well as the registered platform.
22 * This is always used as a global singleton and can be retrieved with
23 * `tf.env()`.
24 *
25 * @doc {heading: 'Environment'}
26 */
27export class Environment {
28 // tslint:disable-next-line: no-any
29 constructor(global) {
30 this.global = global;
31 this.flags = {};
32 this.flagRegistry = {};
33 this.urlFlags = {};
34 // Jasmine spies on this in 'environment_test.ts'
35 this.getQueryParams = getQueryParams;
36 this.populateURLFlags();
37 }
38 setPlatform(platformName, platform) {
39 if (this.platform != null) {
40 console.warn(`Platform ${this.platformName} has already been set. ` +
41 `Overwriting the platform with ${platform}.`);
42 }
43 this.platformName = platformName;
44 this.platform = platform;
45 }
46 registerFlag(flagName, evaluationFn, setHook) {
47 this.flagRegistry[flagName] = { evaluationFn, setHook };
48 // Override the flag value from the URL. This has to happen here because the
49 // environment is initialized before flags get registered.
50 if (this.urlFlags[flagName] != null) {
51 const flagValue = this.urlFlags[flagName];
52 console.warn(`Setting feature override from URL ${flagName}: ${flagValue}.`);
53 this.set(flagName, flagValue);
54 }
55 }
56 async getAsync(flagName) {
57 if (flagName in this.flags) {
58 return this.flags[flagName];
59 }
60 this.flags[flagName] = await this.evaluateFlag(flagName);
61 return this.flags[flagName];
62 }
63 get(flagName) {
64 if (flagName in this.flags) {
65 return this.flags[flagName];
66 }
67 const flagValue = this.evaluateFlag(flagName);
68 if (isPromise(flagValue)) {
69 throw new Error(`Flag ${flagName} cannot be synchronously evaluated. ` +
70 `Please use getAsync() instead.`);
71 }
72 this.flags[flagName] = flagValue;
73 return this.flags[flagName];
74 }
75 getNumber(flagName) {
76 return this.get(flagName);
77 }
78 getBool(flagName) {
79 return this.get(flagName);
80 }
81 getFlags() {
82 return this.flags;
83 }
84 // For backwards compatibility.
85 get features() {
86 return this.flags;
87 }
88 set(flagName, value) {
89 if (this.flagRegistry[flagName] == null) {
90 throw new Error(`Cannot set flag ${flagName} as it has not been registered.`);
91 }
92 this.flags[flagName] = value;
93 if (this.flagRegistry[flagName].setHook != null) {
94 this.flagRegistry[flagName].setHook(value);
95 }
96 }
97 evaluateFlag(flagName) {
98 if (this.flagRegistry[flagName] == null) {
99 throw new Error(`Cannot evaluate flag '${flagName}': no evaluation function found.`);
100 }
101 return this.flagRegistry[flagName].evaluationFn();
102 }
103 setFlags(flags) {
104 this.flags = Object.assign({}, flags);
105 }
106 reset() {
107 this.flags = {};
108 this.urlFlags = {};
109 this.populateURLFlags();
110 }
111 populateURLFlags() {
112 if (typeof this.global === 'undefined' ||
113 typeof this.global.location === 'undefined' ||
114 typeof this.global.location.search === 'undefined') {
115 return;
116 }
117 const urlParams = this.getQueryParams(this.global.location.search);
118 if (TENSORFLOWJS_FLAGS_PREFIX in urlParams) {
119 const keyValues = urlParams[TENSORFLOWJS_FLAGS_PREFIX].split(',');
120 keyValues.forEach(keyValue => {
121 const [key, value] = keyValue.split(':');
122 this.urlFlags[key] = parseValue(key, value);
123 });
124 }
125 }
126}
127export function getQueryParams(queryString) {
128 const params = {};
129 queryString.replace(/[?&]([^=?&]+)(?:=([^&]*))?/g, (s, ...t) => {
130 decodeParam(params, t[0], t[1]);
131 return t.join('=');
132 });
133 return params;
134}
135function decodeParam(params, name, value) {
136 params[decodeURIComponent(name)] = decodeURIComponent(value || '');
137}
138function parseValue(flagName, value) {
139 value = value.toLowerCase();
140 if (value === 'true' || value === 'false') {
141 return value === 'true';
142 }
143 else if (`${+value}` === value) {
144 return +value;
145 }
146 throw new Error(`Could not parse value flag value ${value} for flag ${flagName}.`);
147}
148/**
149 * Returns the current environment (a global singleton).
150 *
151 * The environment object contains the evaluated feature values as well as the
152 * active platform.
153 *
154 * @doc {heading: 'Environment'}
155 */
156export function env() {
157 return ENV;
158}
159export let ENV = null;
160export function setEnvironmentGlobal(environment) {
161 ENV = environment;
162}
163//# sourceMappingURL=environment.js.map
\No newline at end of file