1 | ;
|
2 |
|
3 | Object.defineProperty(exports, '__esModule', { value: true });
|
4 |
|
5 | var app = require('@firebase/app');
|
6 | var util = require('@firebase/util');
|
7 | var component = require('@firebase/component');
|
8 | var logger = require('@firebase/logger');
|
9 | var tslib = require('tslib');
|
10 | require('@firebase/installations');
|
11 |
|
12 | var name = "@firebase/remote-config";
|
13 | var version = "0.3.8";
|
14 |
|
15 | /**
|
16 | * @license
|
17 | * Copyright 2019 Google LLC
|
18 | *
|
19 | * Licensed under the Apache License, Version 2.0 (the "License");
|
20 | * you may not use this file except in compliance with the License.
|
21 | * You may obtain a copy of the License at
|
22 | *
|
23 | * http://www.apache.org/licenses/LICENSE-2.0
|
24 | *
|
25 | * Unless required by applicable law or agreed to in writing, software
|
26 | * distributed under the License is distributed on an "AS IS" BASIS,
|
27 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
28 | * See the License for the specific language governing permissions and
|
29 | * limitations under the License.
|
30 | */
|
31 | /**
|
32 | * Shims a minimal AbortSignal.
|
33 | *
|
34 | * <p>AbortController's AbortSignal conveniently decouples fetch timeout logic from other aspects
|
35 | * of networking, such as retries. Firebase doesn't use AbortController enough to justify a
|
36 | * polyfill recommendation, like we do with the Fetch API, but this minimal shim can easily be
|
37 | * swapped out if/when we do.
|
38 | */
|
39 | var RemoteConfigAbortSignal = /** @class */ (function () {
|
40 | function RemoteConfigAbortSignal() {
|
41 | this.listeners = [];
|
42 | }
|
43 | RemoteConfigAbortSignal.prototype.addEventListener = function (listener) {
|
44 | this.listeners.push(listener);
|
45 | };
|
46 | RemoteConfigAbortSignal.prototype.abort = function () {
|
47 | this.listeners.forEach(function (listener) { return listener(); });
|
48 | };
|
49 | return RemoteConfigAbortSignal;
|
50 | }());
|
51 |
|
52 | /**
|
53 | * @license
|
54 | * Copyright 2020 Google LLC
|
55 | *
|
56 | * Licensed under the Apache License, Version 2.0 (the "License");
|
57 | * you may not use this file except in compliance with the License.
|
58 | * You may obtain a copy of the License at
|
59 | *
|
60 | * http://www.apache.org/licenses/LICENSE-2.0
|
61 | *
|
62 | * Unless required by applicable law or agreed to in writing, software
|
63 | * distributed under the License is distributed on an "AS IS" BASIS,
|
64 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
65 | * See the License for the specific language governing permissions and
|
66 | * limitations under the License.
|
67 | */
|
68 | var RC_COMPONENT_NAME = 'remote-config';
|
69 |
|
70 | /**
|
71 | * @license
|
72 | * Copyright 2019 Google LLC
|
73 | *
|
74 | * Licensed under the Apache License, Version 2.0 (the "License");
|
75 | * you may not use this file except in compliance with the License.
|
76 | * You may obtain a copy of the License at
|
77 | *
|
78 | * http://www.apache.org/licenses/LICENSE-2.0
|
79 | *
|
80 | * Unless required by applicable law or agreed to in writing, software
|
81 | * distributed under the License is distributed on an "AS IS" BASIS,
|
82 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
83 | * See the License for the specific language governing permissions and
|
84 | * limitations under the License.
|
85 | */
|
86 | var _a;
|
87 | var ERROR_DESCRIPTION_MAP = (_a = {},
|
88 | _a["registration-window" /* REGISTRATION_WINDOW */] = 'Undefined window object. This SDK only supports usage in a browser environment.',
|
89 | _a["registration-project-id" /* REGISTRATION_PROJECT_ID */] = 'Undefined project identifier. Check Firebase app initialization.',
|
90 | _a["registration-api-key" /* REGISTRATION_API_KEY */] = 'Undefined API key. Check Firebase app initialization.',
|
91 | _a["registration-app-id" /* REGISTRATION_APP_ID */] = 'Undefined app identifier. Check Firebase app initialization.',
|
92 | _a["storage-open" /* STORAGE_OPEN */] = 'Error thrown when opening storage. Original error: {$originalErrorMessage}.',
|
93 | _a["storage-get" /* STORAGE_GET */] = 'Error thrown when reading from storage. Original error: {$originalErrorMessage}.',
|
94 | _a["storage-set" /* STORAGE_SET */] = 'Error thrown when writing to storage. Original error: {$originalErrorMessage}.',
|
95 | _a["storage-delete" /* STORAGE_DELETE */] = 'Error thrown when deleting from storage. Original error: {$originalErrorMessage}.',
|
96 | _a["fetch-client-network" /* FETCH_NETWORK */] = 'Fetch client failed to connect to a network. Check Internet connection.' +
|
97 | ' Original error: {$originalErrorMessage}.',
|
98 | _a["fetch-timeout" /* FETCH_TIMEOUT */] = 'The config fetch request timed out. ' +
|
99 | ' Configure timeout using "fetchTimeoutMillis" SDK setting.',
|
100 | _a["fetch-throttle" /* FETCH_THROTTLE */] = 'The config fetch request timed out while in an exponential backoff state.' +
|
101 | ' Configure timeout using "fetchTimeoutMillis" SDK setting.' +
|
102 | ' Unix timestamp in milliseconds when fetch request throttling ends: {$throttleEndTimeMillis}.',
|
103 | _a["fetch-client-parse" /* FETCH_PARSE */] = 'Fetch client could not parse response.' +
|
104 | ' Original error: {$originalErrorMessage}.',
|
105 | _a["fetch-status" /* FETCH_STATUS */] = 'Fetch server returned an HTTP error status. HTTP status: {$httpStatus}.',
|
106 | _a["indexed-db-unavailable" /* INDEXED_DB_UNAVAILABLE */] = 'Indexed DB is not supported by current browser',
|
107 | _a);
|
108 | var ERROR_FACTORY = new util.ErrorFactory('remoteconfig' /* service */, 'Remote Config' /* service name */, ERROR_DESCRIPTION_MAP);
|
109 | // Note how this is like typeof/instanceof, but for ErrorCode.
|
110 | function hasErrorCode(e, errorCode) {
|
111 | return e instanceof util.FirebaseError && e.code.indexOf(errorCode) !== -1;
|
112 | }
|
113 |
|
114 | /**
|
115 | * @license
|
116 | * Copyright 2019 Google LLC
|
117 | *
|
118 | * Licensed under the Apache License, Version 2.0 (the "License");
|
119 | * you may not use this file except in compliance with the License.
|
120 | * You may obtain a copy of the License at
|
121 | *
|
122 | * http://www.apache.org/licenses/LICENSE-2.0
|
123 | *
|
124 | * Unless required by applicable law or agreed to in writing, software
|
125 | * distributed under the License is distributed on an "AS IS" BASIS,
|
126 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
127 | * See the License for the specific language governing permissions and
|
128 | * limitations under the License.
|
129 | */
|
130 | var DEFAULT_VALUE_FOR_BOOLEAN = false;
|
131 | var DEFAULT_VALUE_FOR_STRING = '';
|
132 | var DEFAULT_VALUE_FOR_NUMBER = 0;
|
133 | var BOOLEAN_TRUTHY_VALUES = ['1', 'true', 't', 'yes', 'y', 'on'];
|
134 | var Value = /** @class */ (function () {
|
135 | function Value(_source, _value) {
|
136 | if (_value === void 0) { _value = DEFAULT_VALUE_FOR_STRING; }
|
137 | this._source = _source;
|
138 | this._value = _value;
|
139 | }
|
140 | Value.prototype.asString = function () {
|
141 | return this._value;
|
142 | };
|
143 | Value.prototype.asBoolean = function () {
|
144 | if (this._source === 'static') {
|
145 | return DEFAULT_VALUE_FOR_BOOLEAN;
|
146 | }
|
147 | return BOOLEAN_TRUTHY_VALUES.indexOf(this._value.toLowerCase()) >= 0;
|
148 | };
|
149 | Value.prototype.asNumber = function () {
|
150 | if (this._source === 'static') {
|
151 | return DEFAULT_VALUE_FOR_NUMBER;
|
152 | }
|
153 | var num = Number(this._value);
|
154 | if (isNaN(num)) {
|
155 | num = DEFAULT_VALUE_FOR_NUMBER;
|
156 | }
|
157 | return num;
|
158 | };
|
159 | Value.prototype.getSource = function () {
|
160 | return this._source;
|
161 | };
|
162 | return Value;
|
163 | }());
|
164 |
|
165 | /**
|
166 | * @license
|
167 | * Copyright 2020 Google LLC
|
168 | *
|
169 | * Licensed under the Apache License, Version 2.0 (the "License");
|
170 | * you may not use this file except in compliance with the License.
|
171 | * You may obtain a copy of the License at
|
172 | *
|
173 | * http://www.apache.org/licenses/LICENSE-2.0
|
174 | *
|
175 | * Unless required by applicable law or agreed to in writing, software
|
176 | * distributed under the License is distributed on an "AS IS" BASIS,
|
177 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
178 | * See the License for the specific language governing permissions and
|
179 | * limitations under the License.
|
180 | */
|
181 | /**
|
182 | *
|
183 | * @param app - The {@link @firebase/app#FirebaseApp} instance.
|
184 | * @returns A {@link RemoteConfig} instance.
|
185 | *
|
186 | * @public
|
187 | */
|
188 | function getRemoteConfig(app$1) {
|
189 | if (app$1 === void 0) { app$1 = app.getApp(); }
|
190 | app$1 = util.getModularInstance(app$1);
|
191 | var rcProvider = app._getProvider(app$1, RC_COMPONENT_NAME);
|
192 | return rcProvider.getImmediate();
|
193 | }
|
194 | /**
|
195 | * Makes the last fetched config available to the getters.
|
196 | * @param remoteConfig - The {@link RemoteConfig} instance.
|
197 | * @returns A `Promise` which resolves to true if the current call activated the fetched configs.
|
198 | * If the fetched configs were already activated, the `Promise` will resolve to false.
|
199 | *
|
200 | * @public
|
201 | */
|
202 | function activate(remoteConfig) {
|
203 | return tslib.__awaiter(this, void 0, void 0, function () {
|
204 | var rc, _a, lastSuccessfulFetchResponse, activeConfigEtag;
|
205 | return tslib.__generator(this, function (_b) {
|
206 | switch (_b.label) {
|
207 | case 0:
|
208 | rc = util.getModularInstance(remoteConfig);
|
209 | return [4 /*yield*/, Promise.all([
|
210 | rc._storage.getLastSuccessfulFetchResponse(),
|
211 | rc._storage.getActiveConfigEtag()
|
212 | ])];
|
213 | case 1:
|
214 | _a = _b.sent(), lastSuccessfulFetchResponse = _a[0], activeConfigEtag = _a[1];
|
215 | if (!lastSuccessfulFetchResponse ||
|
216 | !lastSuccessfulFetchResponse.config ||
|
217 | !lastSuccessfulFetchResponse.eTag ||
|
218 | lastSuccessfulFetchResponse.eTag === activeConfigEtag) {
|
219 | // Either there is no successful fetched config, or is the same as current active
|
220 | // config.
|
221 | return [2 /*return*/, false];
|
222 | }
|
223 | return [4 /*yield*/, Promise.all([
|
224 | rc._storageCache.setActiveConfig(lastSuccessfulFetchResponse.config),
|
225 | rc._storage.setActiveConfigEtag(lastSuccessfulFetchResponse.eTag)
|
226 | ])];
|
227 | case 2:
|
228 | _b.sent();
|
229 | return [2 /*return*/, true];
|
230 | }
|
231 | });
|
232 | });
|
233 | }
|
234 | /**
|
235 | * Ensures the last activated config are available to the getters.
|
236 | * @param remoteConfig - The {@link RemoteConfig} instance.
|
237 | *
|
238 | * @returns A `Promise` that resolves when the last activated config is available to the getters.
|
239 | * @public
|
240 | */
|
241 | function ensureInitialized(remoteConfig) {
|
242 | var rc = util.getModularInstance(remoteConfig);
|
243 | if (!rc._initializePromise) {
|
244 | rc._initializePromise = rc._storageCache.loadFromStorage().then(function () {
|
245 | rc._isInitializationComplete = true;
|
246 | });
|
247 | }
|
248 | return rc._initializePromise;
|
249 | }
|
250 | /**
|
251 | * Fetches and caches configuration from the Remote Config service.
|
252 | * @param remoteConfig - The {@link RemoteConfig} instance.
|
253 | * @public
|
254 | */
|
255 | function fetchConfig(remoteConfig) {
|
256 | return tslib.__awaiter(this, void 0, void 0, function () {
|
257 | var rc, abortSignal, e_1, lastFetchStatus;
|
258 | var _this = this;
|
259 | return tslib.__generator(this, function (_a) {
|
260 | switch (_a.label) {
|
261 | case 0:
|
262 | rc = util.getModularInstance(remoteConfig);
|
263 | abortSignal = new RemoteConfigAbortSignal();
|
264 | setTimeout(function () { return tslib.__awaiter(_this, void 0, void 0, function () {
|
265 | return tslib.__generator(this, function (_a) {
|
266 | // Note a very low delay, eg < 10ms, can elapse before listeners are initialized.
|
267 | abortSignal.abort();
|
268 | return [2 /*return*/];
|
269 | });
|
270 | }); }, rc.settings.fetchTimeoutMillis);
|
271 | _a.label = 1;
|
272 | case 1:
|
273 | _a.trys.push([1, 4, , 6]);
|
274 | return [4 /*yield*/, rc._client.fetch({
|
275 | cacheMaxAgeMillis: rc.settings.minimumFetchIntervalMillis,
|
276 | signal: abortSignal
|
277 | })];
|
278 | case 2:
|
279 | _a.sent();
|
280 | return [4 /*yield*/, rc._storageCache.setLastFetchStatus('success')];
|
281 | case 3:
|
282 | _a.sent();
|
283 | return [3 /*break*/, 6];
|
284 | case 4:
|
285 | e_1 = _a.sent();
|
286 | lastFetchStatus = hasErrorCode(e_1, "fetch-throttle" /* FETCH_THROTTLE */)
|
287 | ? 'throttle'
|
288 | : 'failure';
|
289 | return [4 /*yield*/, rc._storageCache.setLastFetchStatus(lastFetchStatus)];
|
290 | case 5:
|
291 | _a.sent();
|
292 | throw e_1;
|
293 | case 6: return [2 /*return*/];
|
294 | }
|
295 | });
|
296 | });
|
297 | }
|
298 | /**
|
299 | * Gets all config.
|
300 | *
|
301 | * @param remoteConfig - The {@link RemoteConfig} instance.
|
302 | * @returns All config.
|
303 | *
|
304 | * @public
|
305 | */
|
306 | function getAll(remoteConfig) {
|
307 | var rc = util.getModularInstance(remoteConfig);
|
308 | return getAllKeys(rc._storageCache.getActiveConfig(), rc.defaultConfig).reduce(function (allConfigs, key) {
|
309 | allConfigs[key] = getValue(remoteConfig, key);
|
310 | return allConfigs;
|
311 | }, {});
|
312 | }
|
313 | /**
|
314 | * Gets the value for the given key as a boolean.
|
315 | *
|
316 | * Convenience method for calling <code>remoteConfig.getValue(key).asBoolean()</code>.
|
317 | *
|
318 | * @param remoteConfig - The {@link RemoteConfig} instance.
|
319 | * @param key - The name of the parameter.
|
320 | *
|
321 | * @returns The value for the given key as a boolean.
|
322 | * @public
|
323 | */
|
324 | function getBoolean(remoteConfig, key) {
|
325 | return getValue(util.getModularInstance(remoteConfig), key).asBoolean();
|
326 | }
|
327 | /**
|
328 | * Gets the value for the given key as a number.
|
329 | *
|
330 | * Convenience method for calling <code>remoteConfig.getValue(key).asNumber()</code>.
|
331 | *
|
332 | * @param remoteConfig - The {@link RemoteConfig} instance.
|
333 | * @param key - The name of the parameter.
|
334 | *
|
335 | * @returns The value for the given key as a number.
|
336 | *
|
337 | * @public
|
338 | */
|
339 | function getNumber(remoteConfig, key) {
|
340 | return getValue(util.getModularInstance(remoteConfig), key).asNumber();
|
341 | }
|
342 | /**
|
343 | * Gets the value for the given key as a string.
|
344 | * Convenience method for calling <code>remoteConfig.getValue(key).asString()</code>.
|
345 | *
|
346 | * @param remoteConfig - The {@link RemoteConfig} instance.
|
347 | * @param key - The name of the parameter.
|
348 | *
|
349 | * @returns The value for the given key as a string.
|
350 | *
|
351 | * @public
|
352 | */
|
353 | function getString(remoteConfig, key) {
|
354 | return getValue(util.getModularInstance(remoteConfig), key).asString();
|
355 | }
|
356 | /**
|
357 | * Gets the {@link Value} for the given key.
|
358 | *
|
359 | * @param remoteConfig - The {@link RemoteConfig} instance.
|
360 | * @param key - The name of the parameter.
|
361 | *
|
362 | * @returns The value for the given key.
|
363 | *
|
364 | * @public
|
365 | */
|
366 | function getValue(remoteConfig, key) {
|
367 | var rc = util.getModularInstance(remoteConfig);
|
368 | if (!rc._isInitializationComplete) {
|
369 | rc._logger.debug("A value was requested for key \"" + key + "\" before SDK initialization completed." +
|
370 | ' Await on ensureInitialized if the intent was to get a previously activated value.');
|
371 | }
|
372 | var activeConfig = rc._storageCache.getActiveConfig();
|
373 | if (activeConfig && activeConfig[key] !== undefined) {
|
374 | return new Value('remote', activeConfig[key]);
|
375 | }
|
376 | else if (rc.defaultConfig && rc.defaultConfig[key] !== undefined) {
|
377 | return new Value('default', String(rc.defaultConfig[key]));
|
378 | }
|
379 | rc._logger.debug("Returning static value for key \"" + key + "\"." +
|
380 | ' Define a default or remote value if this is unintentional.');
|
381 | return new Value('static');
|
382 | }
|
383 | /**
|
384 | * Defines the log level to use.
|
385 | *
|
386 | * @param remoteConfig - The {@link RemoteConfig} instance.
|
387 | * @param logLevel - The log level to set.
|
388 | *
|
389 | * @public
|
390 | */
|
391 | function setLogLevel(remoteConfig, logLevel) {
|
392 | var rc = util.getModularInstance(remoteConfig);
|
393 | switch (logLevel) {
|
394 | case 'debug':
|
395 | rc._logger.logLevel = logger.LogLevel.DEBUG;
|
396 | break;
|
397 | case 'silent':
|
398 | rc._logger.logLevel = logger.LogLevel.SILENT;
|
399 | break;
|
400 | default:
|
401 | rc._logger.logLevel = logger.LogLevel.ERROR;
|
402 | }
|
403 | }
|
404 | /**
|
405 | * Dedupes and returns an array of all the keys of the received objects.
|
406 | */
|
407 | function getAllKeys(obj1, obj2) {
|
408 | if (obj1 === void 0) { obj1 = {}; }
|
409 | if (obj2 === void 0) { obj2 = {}; }
|
410 | return Object.keys(tslib.__assign(tslib.__assign({}, obj1), obj2));
|
411 | }
|
412 |
|
413 | /**
|
414 | * @license
|
415 | * Copyright 2019 Google LLC
|
416 | *
|
417 | * Licensed under the Apache License, Version 2.0 (the "License");
|
418 | * you may not use this file except in compliance with the License.
|
419 | * You may obtain a copy of the License at
|
420 | *
|
421 | * http://www.apache.org/licenses/LICENSE-2.0
|
422 | *
|
423 | * Unless required by applicable law or agreed to in writing, software
|
424 | * distributed under the License is distributed on an "AS IS" BASIS,
|
425 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
426 | * See the License for the specific language governing permissions and
|
427 | * limitations under the License.
|
428 | */
|
429 | /**
|
430 | * Implements the {@link RemoteConfigClient} abstraction with success response caching.
|
431 | *
|
432 | * <p>Comparable to the browser's Cache API for responses, but the Cache API requires a Service
|
433 | * Worker, which requires HTTPS, which would significantly complicate SDK installation. Also, the
|
434 | * Cache API doesn't support matching entries by time.
|
435 | */
|
436 | var CachingClient = /** @class */ (function () {
|
437 | function CachingClient(client, storage, storageCache, logger) {
|
438 | this.client = client;
|
439 | this.storage = storage;
|
440 | this.storageCache = storageCache;
|
441 | this.logger = logger;
|
442 | }
|
443 | /**
|
444 | * Returns true if the age of the cached fetched configs is less than or equal to
|
445 | * {@link Settings#minimumFetchIntervalInSeconds}.
|
446 | *
|
447 | * <p>This is comparable to passing `headers = { 'Cache-Control': max-age <maxAge> }` to the
|
448 | * native Fetch API.
|
449 | *
|
450 | * <p>Visible for testing.
|
451 | */
|
452 | CachingClient.prototype.isCachedDataFresh = function (cacheMaxAgeMillis, lastSuccessfulFetchTimestampMillis) {
|
453 | // Cache can only be fresh if it's populated.
|
454 | if (!lastSuccessfulFetchTimestampMillis) {
|
455 | this.logger.debug('Config fetch cache check. Cache unpopulated.');
|
456 | return false;
|
457 | }
|
458 | // Calculates age of cache entry.
|
459 | var cacheAgeMillis = Date.now() - lastSuccessfulFetchTimestampMillis;
|
460 | var isCachedDataFresh = cacheAgeMillis <= cacheMaxAgeMillis;
|
461 | this.logger.debug('Config fetch cache check.' +
|
462 | (" Cache age millis: " + cacheAgeMillis + ".") +
|
463 | (" Cache max age millis (minimumFetchIntervalMillis setting): " + cacheMaxAgeMillis + ".") +
|
464 | (" Is cache hit: " + isCachedDataFresh + "."));
|
465 | return isCachedDataFresh;
|
466 | };
|
467 | CachingClient.prototype.fetch = function (request) {
|
468 | return tslib.__awaiter(this, void 0, void 0, function () {
|
469 | var _a, lastSuccessfulFetchTimestampMillis, lastSuccessfulFetchResponse, response, storageOperations;
|
470 | return tslib.__generator(this, function (_b) {
|
471 | switch (_b.label) {
|
472 | case 0: return [4 /*yield*/, Promise.all([
|
473 | this.storage.getLastSuccessfulFetchTimestampMillis(),
|
474 | this.storage.getLastSuccessfulFetchResponse()
|
475 | ])];
|
476 | case 1:
|
477 | _a = _b.sent(), lastSuccessfulFetchTimestampMillis = _a[0], lastSuccessfulFetchResponse = _a[1];
|
478 | // Exits early on cache hit.
|
479 | if (lastSuccessfulFetchResponse &&
|
480 | this.isCachedDataFresh(request.cacheMaxAgeMillis, lastSuccessfulFetchTimestampMillis)) {
|
481 | return [2 /*return*/, lastSuccessfulFetchResponse];
|
482 | }
|
483 | // Deviates from pure decorator by not honoring a passed ETag since we don't have a public API
|
484 | // that allows the caller to pass an ETag.
|
485 | request.eTag =
|
486 | lastSuccessfulFetchResponse && lastSuccessfulFetchResponse.eTag;
|
487 | return [4 /*yield*/, this.client.fetch(request)];
|
488 | case 2:
|
489 | response = _b.sent();
|
490 | storageOperations = [
|
491 | // Uses write-through cache for consistency with synchronous public API.
|
492 | this.storageCache.setLastSuccessfulFetchTimestampMillis(Date.now())
|
493 | ];
|
494 | if (response.status === 200) {
|
495 | // Caches response only if it has changed, ie non-304 responses.
|
496 | storageOperations.push(this.storage.setLastSuccessfulFetchResponse(response));
|
497 | }
|
498 | return [4 /*yield*/, Promise.all(storageOperations)];
|
499 | case 3:
|
500 | _b.sent();
|
501 | return [2 /*return*/, response];
|
502 | }
|
503 | });
|
504 | });
|
505 | };
|
506 | return CachingClient;
|
507 | }());
|
508 |
|
509 | /**
|
510 | * @license
|
511 | * Copyright 2019 Google LLC
|
512 | *
|
513 | * Licensed under the Apache License, Version 2.0 (the "License");
|
514 | * you may not use this file except in compliance with the License.
|
515 | * You may obtain a copy of the License at
|
516 | *
|
517 | * http://www.apache.org/licenses/LICENSE-2.0
|
518 | *
|
519 | * Unless required by applicable law or agreed to in writing, software
|
520 | * distributed under the License is distributed on an "AS IS" BASIS,
|
521 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
522 | * See the License for the specific language governing permissions and
|
523 | * limitations under the License.
|
524 | */
|
525 | /**
|
526 | * Attempts to get the most accurate browser language setting.
|
527 | *
|
528 | * <p>Adapted from getUserLanguage in packages/auth/src/utils.js for TypeScript.
|
529 | *
|
530 | * <p>Defers default language specification to server logic for consistency.
|
531 | *
|
532 | * @param navigatorLanguage Enables tests to override read-only {@link NavigatorLanguage}.
|
533 | */
|
534 | function getUserLanguage(navigatorLanguage) {
|
535 | if (navigatorLanguage === void 0) { navigatorLanguage = navigator; }
|
536 | return (
|
537 | // Most reliable, but only supported in Chrome/Firefox.
|
538 | (navigatorLanguage.languages && navigatorLanguage.languages[0]) ||
|
539 | // Supported in most browsers, but returns the language of the browser
|
540 | // UI, not the language set in browser settings.
|
541 | navigatorLanguage.language
|
542 | // Polyfill otherwise.
|
543 | );
|
544 | }
|
545 |
|
546 | /**
|
547 | * @license
|
548 | * Copyright 2019 Google LLC
|
549 | *
|
550 | * Licensed under the Apache License, Version 2.0 (the "License");
|
551 | * you may not use this file except in compliance with the License.
|
552 | * You may obtain a copy of the License at
|
553 | *
|
554 | * http://www.apache.org/licenses/LICENSE-2.0
|
555 | *
|
556 | * Unless required by applicable law or agreed to in writing, software
|
557 | * distributed under the License is distributed on an "AS IS" BASIS,
|
558 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
559 | * See the License for the specific language governing permissions and
|
560 | * limitations under the License.
|
561 | */
|
562 | /**
|
563 | * Implements the Client abstraction for the Remote Config REST API.
|
564 | */
|
565 | var RestClient = /** @class */ (function () {
|
566 | function RestClient(firebaseInstallations, sdkVersion, namespace, projectId, apiKey, appId) {
|
567 | this.firebaseInstallations = firebaseInstallations;
|
568 | this.sdkVersion = sdkVersion;
|
569 | this.namespace = namespace;
|
570 | this.projectId = projectId;
|
571 | this.apiKey = apiKey;
|
572 | this.appId = appId;
|
573 | }
|
574 | /**
|
575 | * Fetches from the Remote Config REST API.
|
576 | *
|
577 | * @throws a {@link ErrorCode.FETCH_NETWORK} error if {@link GlobalFetch#fetch} can't
|
578 | * connect to the network.
|
579 | * @throws a {@link ErrorCode.FETCH_PARSE} error if {@link Response#json} can't parse the
|
580 | * fetch response.
|
581 | * @throws a {@link ErrorCode.FETCH_STATUS} error if the service returns an HTTP error status.
|
582 | */
|
583 | RestClient.prototype.fetch = function (request) {
|
584 | return tslib.__awaiter(this, void 0, void 0, function () {
|
585 | var _a, installationId, installationToken, urlBase, url, headers, requestBody, options, fetchPromise, timeoutPromise, response, originalError_1, errorCode, status, responseEtag, config, state, responseBody, originalError_2;
|
586 | return tslib.__generator(this, function (_b) {
|
587 | switch (_b.label) {
|
588 | case 0: return [4 /*yield*/, Promise.all([
|
589 | this.firebaseInstallations.getId(),
|
590 | this.firebaseInstallations.getToken()
|
591 | ])];
|
592 | case 1:
|
593 | _a = _b.sent(), installationId = _a[0], installationToken = _a[1];
|
594 | urlBase = window.FIREBASE_REMOTE_CONFIG_URL_BASE ||
|
595 | 'https://firebaseremoteconfig.googleapis.com';
|
596 | url = urlBase + "/v1/projects/" + this.projectId + "/namespaces/" + this.namespace + ":fetch?key=" + this.apiKey;
|
597 | headers = {
|
598 | 'Content-Type': 'application/json',
|
599 | 'Content-Encoding': 'gzip',
|
600 | // Deviates from pure decorator by not passing max-age header since we don't currently have
|
601 | // service behavior using that header.
|
602 | 'If-None-Match': request.eTag || '*'
|
603 | };
|
604 | requestBody = {
|
605 | /* eslint-disable camelcase */
|
606 | sdk_version: this.sdkVersion,
|
607 | app_instance_id: installationId,
|
608 | app_instance_id_token: installationToken,
|
609 | app_id: this.appId,
|
610 | language_code: getUserLanguage()
|
611 | /* eslint-enable camelcase */
|
612 | };
|
613 | options = {
|
614 | method: 'POST',
|
615 | headers: headers,
|
616 | body: JSON.stringify(requestBody)
|
617 | };
|
618 | fetchPromise = fetch(url, options);
|
619 | timeoutPromise = new Promise(function (_resolve, reject) {
|
620 | // Maps async event listener to Promise API.
|
621 | request.signal.addEventListener(function () {
|
622 | // Emulates https://heycam.github.io/webidl/#aborterror
|
623 | var error = new Error('The operation was aborted.');
|
624 | error.name = 'AbortError';
|
625 | reject(error);
|
626 | });
|
627 | });
|
628 | _b.label = 2;
|
629 | case 2:
|
630 | _b.trys.push([2, 5, , 6]);
|
631 | return [4 /*yield*/, Promise.race([fetchPromise, timeoutPromise])];
|
632 | case 3:
|
633 | _b.sent();
|
634 | return [4 /*yield*/, fetchPromise];
|
635 | case 4:
|
636 | response = _b.sent();
|
637 | return [3 /*break*/, 6];
|
638 | case 5:
|
639 | originalError_1 = _b.sent();
|
640 | errorCode = "fetch-client-network" /* FETCH_NETWORK */;
|
641 | if (originalError_1.name === 'AbortError') {
|
642 | errorCode = "fetch-timeout" /* FETCH_TIMEOUT */;
|
643 | }
|
644 | throw ERROR_FACTORY.create(errorCode, {
|
645 | originalErrorMessage: originalError_1.message
|
646 | });
|
647 | case 6:
|
648 | status = response.status;
|
649 | responseEtag = response.headers.get('ETag') || undefined;
|
650 | if (!(response.status === 200)) return [3 /*break*/, 11];
|
651 | responseBody = void 0;
|
652 | _b.label = 7;
|
653 | case 7:
|
654 | _b.trys.push([7, 9, , 10]);
|
655 | return [4 /*yield*/, response.json()];
|
656 | case 8:
|
657 | responseBody = _b.sent();
|
658 | return [3 /*break*/, 10];
|
659 | case 9:
|
660 | originalError_2 = _b.sent();
|
661 | throw ERROR_FACTORY.create("fetch-client-parse" /* FETCH_PARSE */, {
|
662 | originalErrorMessage: originalError_2.message
|
663 | });
|
664 | case 10:
|
665 | config = responseBody['entries'];
|
666 | state = responseBody['state'];
|
667 | _b.label = 11;
|
668 | case 11:
|
669 | // Normalizes based on legacy state.
|
670 | if (state === 'INSTANCE_STATE_UNSPECIFIED') {
|
671 | status = 500;
|
672 | }
|
673 | else if (state === 'NO_CHANGE') {
|
674 | status = 304;
|
675 | }
|
676 | else if (state === 'NO_TEMPLATE' || state === 'EMPTY_CONFIG') {
|
677 | // These cases can be fixed remotely, so normalize to safe value.
|
678 | config = {};
|
679 | }
|
680 | // Normalize to exception-based control flow for non-success cases.
|
681 | // Encapsulates HTTP specifics in this class as much as possible. Status is still the best for
|
682 | // differentiating success states (200 from 304; the state body param is undefined in a
|
683 | // standard 304).
|
684 | if (status !== 304 && status !== 200) {
|
685 | throw ERROR_FACTORY.create("fetch-status" /* FETCH_STATUS */, {
|
686 | httpStatus: status
|
687 | });
|
688 | }
|
689 | return [2 /*return*/, { status: status, eTag: responseEtag, config: config }];
|
690 | }
|
691 | });
|
692 | });
|
693 | };
|
694 | return RestClient;
|
695 | }());
|
696 |
|
697 | /**
|
698 | * @license
|
699 | * Copyright 2019 Google LLC
|
700 | *
|
701 | * Licensed under the Apache License, Version 2.0 (the "License");
|
702 | * you may not use this file except in compliance with the License.
|
703 | * You may obtain a copy of the License at
|
704 | *
|
705 | * http://www.apache.org/licenses/LICENSE-2.0
|
706 | *
|
707 | * Unless required by applicable law or agreed to in writing, software
|
708 | * distributed under the License is distributed on an "AS IS" BASIS,
|
709 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
710 | * See the License for the specific language governing permissions and
|
711 | * limitations under the License.
|
712 | */
|
713 | /**
|
714 | * Supports waiting on a backoff by:
|
715 | *
|
716 | * <ul>
|
717 | * <li>Promisifying setTimeout, so we can set a timeout in our Promise chain</li>
|
718 | * <li>Listening on a signal bus for abort events, just like the Fetch API</li>
|
719 | * <li>Failing in the same way the Fetch API fails, so timing out a live request and a throttled
|
720 | * request appear the same.</li>
|
721 | * </ul>
|
722 | *
|
723 | * <p>Visible for testing.
|
724 | */
|
725 | function setAbortableTimeout(signal, throttleEndTimeMillis) {
|
726 | return new Promise(function (resolve, reject) {
|
727 | // Derives backoff from given end time, normalizing negative numbers to zero.
|
728 | var backoffMillis = Math.max(throttleEndTimeMillis - Date.now(), 0);
|
729 | var timeout = setTimeout(resolve, backoffMillis);
|
730 | // Adds listener, rather than sets onabort, because signal is a shared object.
|
731 | signal.addEventListener(function () {
|
732 | clearTimeout(timeout);
|
733 | // If the request completes before this timeout, the rejection has no effect.
|
734 | reject(ERROR_FACTORY.create("fetch-throttle" /* FETCH_THROTTLE */, {
|
735 | throttleEndTimeMillis: throttleEndTimeMillis
|
736 | }));
|
737 | });
|
738 | });
|
739 | }
|
740 | /**
|
741 | * Returns true if the {@link Error} indicates a fetch request may succeed later.
|
742 | */
|
743 | function isRetriableError(e) {
|
744 | if (!(e instanceof util.FirebaseError) || !e.customData) {
|
745 | return false;
|
746 | }
|
747 | // Uses string index defined by ErrorData, which FirebaseError implements.
|
748 | var httpStatus = Number(e.customData['httpStatus']);
|
749 | return (httpStatus === 429 ||
|
750 | httpStatus === 500 ||
|
751 | httpStatus === 503 ||
|
752 | httpStatus === 504);
|
753 | }
|
754 | /**
|
755 | * Decorates a Client with retry logic.
|
756 | *
|
757 | * <p>Comparable to CachingClient, but uses backoff logic instead of cache max age and doesn't cache
|
758 | * responses (because the SDK has no use for error responses).
|
759 | */
|
760 | var RetryingClient = /** @class */ (function () {
|
761 | function RetryingClient(client, storage) {
|
762 | this.client = client;
|
763 | this.storage = storage;
|
764 | }
|
765 | RetryingClient.prototype.fetch = function (request) {
|
766 | return tslib.__awaiter(this, void 0, void 0, function () {
|
767 | var throttleMetadata;
|
768 | return tslib.__generator(this, function (_a) {
|
769 | switch (_a.label) {
|
770 | case 0: return [4 /*yield*/, this.storage.getThrottleMetadata()];
|
771 | case 1:
|
772 | throttleMetadata = (_a.sent()) || {
|
773 | backoffCount: 0,
|
774 | throttleEndTimeMillis: Date.now()
|
775 | };
|
776 | return [2 /*return*/, this.attemptFetch(request, throttleMetadata)];
|
777 | }
|
778 | });
|
779 | });
|
780 | };
|
781 | /**
|
782 | * A recursive helper for attempting a fetch request repeatedly.
|
783 | *
|
784 | * @throws any non-retriable errors.
|
785 | */
|
786 | RetryingClient.prototype.attemptFetch = function (request, _a) {
|
787 | var throttleEndTimeMillis = _a.throttleEndTimeMillis, backoffCount = _a.backoffCount;
|
788 | return tslib.__awaiter(this, void 0, void 0, function () {
|
789 | var response, e_1, throttleMetadata;
|
790 | return tslib.__generator(this, function (_b) {
|
791 | switch (_b.label) {
|
792 | case 0:
|
793 | // Starts with a (potentially zero) timeout to support resumption from stored state.
|
794 | // Ensures the throttle end time is honored if the last attempt timed out.
|
795 | // Note the SDK will never make a request if the fetch timeout expires at this point.
|
796 | return [4 /*yield*/, setAbortableTimeout(request.signal, throttleEndTimeMillis)];
|
797 | case 1:
|
798 | // Starts with a (potentially zero) timeout to support resumption from stored state.
|
799 | // Ensures the throttle end time is honored if the last attempt timed out.
|
800 | // Note the SDK will never make a request if the fetch timeout expires at this point.
|
801 | _b.sent();
|
802 | _b.label = 2;
|
803 | case 2:
|
804 | _b.trys.push([2, 5, , 7]);
|
805 | return [4 /*yield*/, this.client.fetch(request)];
|
806 | case 3:
|
807 | response = _b.sent();
|
808 | // Note the SDK only clears throttle state if response is success or non-retriable.
|
809 | return [4 /*yield*/, this.storage.deleteThrottleMetadata()];
|
810 | case 4:
|
811 | // Note the SDK only clears throttle state if response is success or non-retriable.
|
812 | _b.sent();
|
813 | return [2 /*return*/, response];
|
814 | case 5:
|
815 | e_1 = _b.sent();
|
816 | if (!isRetriableError(e_1)) {
|
817 | throw e_1;
|
818 | }
|
819 | throttleMetadata = {
|
820 | throttleEndTimeMillis: Date.now() + util.calculateBackoffMillis(backoffCount),
|
821 | backoffCount: backoffCount + 1
|
822 | };
|
823 | // Persists state.
|
824 | return [4 /*yield*/, this.storage.setThrottleMetadata(throttleMetadata)];
|
825 | case 6:
|
826 | // Persists state.
|
827 | _b.sent();
|
828 | return [2 /*return*/, this.attemptFetch(request, throttleMetadata)];
|
829 | case 7: return [2 /*return*/];
|
830 | }
|
831 | });
|
832 | });
|
833 | };
|
834 | return RetryingClient;
|
835 | }());
|
836 |
|
837 | /**
|
838 | * @license
|
839 | * Copyright 2019 Google LLC
|
840 | *
|
841 | * Licensed under the Apache License, Version 2.0 (the "License");
|
842 | * you may not use this file except in compliance with the License.
|
843 | * You may obtain a copy of the License at
|
844 | *
|
845 | * http://www.apache.org/licenses/LICENSE-2.0
|
846 | *
|
847 | * Unless required by applicable law or agreed to in writing, software
|
848 | * distributed under the License is distributed on an "AS IS" BASIS,
|
849 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
850 | * See the License for the specific language governing permissions and
|
851 | * limitations under the License.
|
852 | */
|
853 | var DEFAULT_FETCH_TIMEOUT_MILLIS = 60 * 1000; // One minute
|
854 | var DEFAULT_CACHE_MAX_AGE_MILLIS = 12 * 60 * 60 * 1000; // Twelve hours.
|
855 | /**
|
856 | * Encapsulates business logic mapping network and storage dependencies to the public SDK API.
|
857 | *
|
858 | * See {@link https://github.com/FirebasePrivate/firebase-js-sdk/blob/master/packages/firebase/index.d.ts|interface documentation} for method descriptions.
|
859 | */
|
860 | var RemoteConfig = /** @class */ (function () {
|
861 | function RemoteConfig(
|
862 | // Required by FirebaseServiceFactory interface.
|
863 | app,
|
864 | // JS doesn't support private yet
|
865 | // (https://github.com/tc39/proposal-class-fields#private-fields), so we hint using an
|
866 | // underscore prefix.
|
867 | /**
|
868 | * @internal
|
869 | */
|
870 | _client,
|
871 | /**
|
872 | * @internal
|
873 | */
|
874 | _storageCache,
|
875 | /**
|
876 | * @internal
|
877 | */
|
878 | _storage,
|
879 | /**
|
880 | * @internal
|
881 | */
|
882 | _logger) {
|
883 | this.app = app;
|
884 | this._client = _client;
|
885 | this._storageCache = _storageCache;
|
886 | this._storage = _storage;
|
887 | this._logger = _logger;
|
888 | /**
|
889 | * Tracks completion of initialization promise.
|
890 | * @internal
|
891 | */
|
892 | this._isInitializationComplete = false;
|
893 | this.settings = {
|
894 | fetchTimeoutMillis: DEFAULT_FETCH_TIMEOUT_MILLIS,
|
895 | minimumFetchIntervalMillis: DEFAULT_CACHE_MAX_AGE_MILLIS
|
896 | };
|
897 | this.defaultConfig = {};
|
898 | }
|
899 | Object.defineProperty(RemoteConfig.prototype, "fetchTimeMillis", {
|
900 | get: function () {
|
901 | return this._storageCache.getLastSuccessfulFetchTimestampMillis() || -1;
|
902 | },
|
903 | enumerable: false,
|
904 | configurable: true
|
905 | });
|
906 | Object.defineProperty(RemoteConfig.prototype, "lastFetchStatus", {
|
907 | get: function () {
|
908 | return this._storageCache.getLastFetchStatus() || 'no-fetch-yet';
|
909 | },
|
910 | enumerable: false,
|
911 | configurable: true
|
912 | });
|
913 | return RemoteConfig;
|
914 | }());
|
915 |
|
916 | /**
|
917 | * @license
|
918 | * Copyright 2019 Google LLC
|
919 | *
|
920 | * Licensed under the Apache License, Version 2.0 (the "License");
|
921 | * you may not use this file except in compliance with the License.
|
922 | * You may obtain a copy of the License at
|
923 | *
|
924 | * http://www.apache.org/licenses/LICENSE-2.0
|
925 | *
|
926 | * Unless required by applicable law or agreed to in writing, software
|
927 | * distributed under the License is distributed on an "AS IS" BASIS,
|
928 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
929 | * See the License for the specific language governing permissions and
|
930 | * limitations under the License.
|
931 | */
|
932 | /**
|
933 | * Converts an error event associated with a {@link IDBRequest} to a {@link FirebaseError}.
|
934 | */
|
935 | function toFirebaseError(event, errorCode) {
|
936 | var originalError = event.target.error || undefined;
|
937 | return ERROR_FACTORY.create(errorCode, {
|
938 | originalErrorMessage: originalError && originalError.message
|
939 | });
|
940 | }
|
941 | /**
|
942 | * A general-purpose store keyed by app + namespace + {@link
|
943 | * ProjectNamespaceKeyFieldValue}.
|
944 | *
|
945 | * <p>The Remote Config SDK can be used with multiple app installations, and each app can interact
|
946 | * with multiple namespaces, so this store uses app (ID + name) and namespace as common parent keys
|
947 | * for a set of key-value pairs. See {@link Storage#createCompositeKey}.
|
948 | *
|
949 | * <p>Visible for testing.
|
950 | */
|
951 | var APP_NAMESPACE_STORE = 'app_namespace_store';
|
952 | var DB_NAME = 'firebase_remote_config';
|
953 | var DB_VERSION = 1;
|
954 | // Visible for testing.
|
955 | function openDatabase() {
|
956 | return new Promise(function (resolve, reject) {
|
957 | try {
|
958 | var request = indexedDB.open(DB_NAME, DB_VERSION);
|
959 | request.onerror = function (event) {
|
960 | reject(toFirebaseError(event, "storage-open" /* STORAGE_OPEN */));
|
961 | };
|
962 | request.onsuccess = function (event) {
|
963 | resolve(event.target.result);
|
964 | };
|
965 | request.onupgradeneeded = function (event) {
|
966 | var db = event.target.result;
|
967 | // We don't use 'break' in this switch statement, the fall-through
|
968 | // behavior is what we want, because if there are multiple versions between
|
969 | // the old version and the current version, we want ALL the migrations
|
970 | // that correspond to those versions to run, not only the last one.
|
971 | // eslint-disable-next-line default-case
|
972 | switch (event.oldVersion) {
|
973 | case 0:
|
974 | db.createObjectStore(APP_NAMESPACE_STORE, {
|
975 | keyPath: 'compositeKey'
|
976 | });
|
977 | }
|
978 | };
|
979 | }
|
980 | catch (error) {
|
981 | reject(ERROR_FACTORY.create("storage-open" /* STORAGE_OPEN */, {
|
982 | originalErrorMessage: error
|
983 | }));
|
984 | }
|
985 | });
|
986 | }
|
987 | /**
|
988 | * Abstracts data persistence.
|
989 | */
|
990 | var Storage = /** @class */ (function () {
|
991 | /**
|
992 | * @param appId enables storage segmentation by app (ID + name).
|
993 | * @param appName enables storage segmentation by app (ID + name).
|
994 | * @param namespace enables storage segmentation by namespace.
|
995 | */
|
996 | function Storage(appId, appName, namespace, openDbPromise) {
|
997 | if (openDbPromise === void 0) { openDbPromise = openDatabase(); }
|
998 | this.appId = appId;
|
999 | this.appName = appName;
|
1000 | this.namespace = namespace;
|
1001 | this.openDbPromise = openDbPromise;
|
1002 | }
|
1003 | Storage.prototype.getLastFetchStatus = function () {
|
1004 | return this.get('last_fetch_status');
|
1005 | };
|
1006 | Storage.prototype.setLastFetchStatus = function (status) {
|
1007 | return this.set('last_fetch_status', status);
|
1008 | };
|
1009 | // This is comparable to a cache entry timestamp. If we need to expire other data, we could
|
1010 | // consider adding timestamp to all storage records and an optional max age arg to getters.
|
1011 | Storage.prototype.getLastSuccessfulFetchTimestampMillis = function () {
|
1012 | return this.get('last_successful_fetch_timestamp_millis');
|
1013 | };
|
1014 | Storage.prototype.setLastSuccessfulFetchTimestampMillis = function (timestamp) {
|
1015 | return this.set('last_successful_fetch_timestamp_millis', timestamp);
|
1016 | };
|
1017 | Storage.prototype.getLastSuccessfulFetchResponse = function () {
|
1018 | return this.get('last_successful_fetch_response');
|
1019 | };
|
1020 | Storage.prototype.setLastSuccessfulFetchResponse = function (response) {
|
1021 | return this.set('last_successful_fetch_response', response);
|
1022 | };
|
1023 | Storage.prototype.getActiveConfig = function () {
|
1024 | return this.get('active_config');
|
1025 | };
|
1026 | Storage.prototype.setActiveConfig = function (config) {
|
1027 | return this.set('active_config', config);
|
1028 | };
|
1029 | Storage.prototype.getActiveConfigEtag = function () {
|
1030 | return this.get('active_config_etag');
|
1031 | };
|
1032 | Storage.prototype.setActiveConfigEtag = function (etag) {
|
1033 | return this.set('active_config_etag', etag);
|
1034 | };
|
1035 | Storage.prototype.getThrottleMetadata = function () {
|
1036 | return this.get('throttle_metadata');
|
1037 | };
|
1038 | Storage.prototype.setThrottleMetadata = function (metadata) {
|
1039 | return this.set('throttle_metadata', metadata);
|
1040 | };
|
1041 | Storage.prototype.deleteThrottleMetadata = function () {
|
1042 | return this.delete('throttle_metadata');
|
1043 | };
|
1044 | Storage.prototype.get = function (key) {
|
1045 | return tslib.__awaiter(this, void 0, void 0, function () {
|
1046 | var db;
|
1047 | var _this = this;
|
1048 | return tslib.__generator(this, function (_a) {
|
1049 | switch (_a.label) {
|
1050 | case 0: return [4 /*yield*/, this.openDbPromise];
|
1051 | case 1:
|
1052 | db = _a.sent();
|
1053 | return [2 /*return*/, new Promise(function (resolve, reject) {
|
1054 | var transaction = db.transaction([APP_NAMESPACE_STORE], 'readonly');
|
1055 | var objectStore = transaction.objectStore(APP_NAMESPACE_STORE);
|
1056 | var compositeKey = _this.createCompositeKey(key);
|
1057 | try {
|
1058 | var request = objectStore.get(compositeKey);
|
1059 | request.onerror = function (event) {
|
1060 | reject(toFirebaseError(event, "storage-get" /* STORAGE_GET */));
|
1061 | };
|
1062 | request.onsuccess = function (event) {
|
1063 | var result = event.target.result;
|
1064 | if (result) {
|
1065 | resolve(result.value);
|
1066 | }
|
1067 | else {
|
1068 | resolve(undefined);
|
1069 | }
|
1070 | };
|
1071 | }
|
1072 | catch (e) {
|
1073 | reject(ERROR_FACTORY.create("storage-get" /* STORAGE_GET */, {
|
1074 | originalErrorMessage: e && e.message
|
1075 | }));
|
1076 | }
|
1077 | })];
|
1078 | }
|
1079 | });
|
1080 | });
|
1081 | };
|
1082 | Storage.prototype.set = function (key, value) {
|
1083 | return tslib.__awaiter(this, void 0, void 0, function () {
|
1084 | var db;
|
1085 | var _this = this;
|
1086 | return tslib.__generator(this, function (_a) {
|
1087 | switch (_a.label) {
|
1088 | case 0: return [4 /*yield*/, this.openDbPromise];
|
1089 | case 1:
|
1090 | db = _a.sent();
|
1091 | return [2 /*return*/, new Promise(function (resolve, reject) {
|
1092 | var transaction = db.transaction([APP_NAMESPACE_STORE], 'readwrite');
|
1093 | var objectStore = transaction.objectStore(APP_NAMESPACE_STORE);
|
1094 | var compositeKey = _this.createCompositeKey(key);
|
1095 | try {
|
1096 | var request = objectStore.put({
|
1097 | compositeKey: compositeKey,
|
1098 | value: value
|
1099 | });
|
1100 | request.onerror = function (event) {
|
1101 | reject(toFirebaseError(event, "storage-set" /* STORAGE_SET */));
|
1102 | };
|
1103 | request.onsuccess = function () {
|
1104 | resolve();
|
1105 | };
|
1106 | }
|
1107 | catch (e) {
|
1108 | reject(ERROR_FACTORY.create("storage-set" /* STORAGE_SET */, {
|
1109 | originalErrorMessage: e && e.message
|
1110 | }));
|
1111 | }
|
1112 | })];
|
1113 | }
|
1114 | });
|
1115 | });
|
1116 | };
|
1117 | Storage.prototype.delete = function (key) {
|
1118 | return tslib.__awaiter(this, void 0, void 0, function () {
|
1119 | var db;
|
1120 | var _this = this;
|
1121 | return tslib.__generator(this, function (_a) {
|
1122 | switch (_a.label) {
|
1123 | case 0: return [4 /*yield*/, this.openDbPromise];
|
1124 | case 1:
|
1125 | db = _a.sent();
|
1126 | return [2 /*return*/, new Promise(function (resolve, reject) {
|
1127 | var transaction = db.transaction([APP_NAMESPACE_STORE], 'readwrite');
|
1128 | var objectStore = transaction.objectStore(APP_NAMESPACE_STORE);
|
1129 | var compositeKey = _this.createCompositeKey(key);
|
1130 | try {
|
1131 | var request = objectStore.delete(compositeKey);
|
1132 | request.onerror = function (event) {
|
1133 | reject(toFirebaseError(event, "storage-delete" /* STORAGE_DELETE */));
|
1134 | };
|
1135 | request.onsuccess = function () {
|
1136 | resolve();
|
1137 | };
|
1138 | }
|
1139 | catch (e) {
|
1140 | reject(ERROR_FACTORY.create("storage-delete" /* STORAGE_DELETE */, {
|
1141 | originalErrorMessage: e && e.message
|
1142 | }));
|
1143 | }
|
1144 | })];
|
1145 | }
|
1146 | });
|
1147 | });
|
1148 | };
|
1149 | // Facilitates composite key functionality (which is unsupported in IE).
|
1150 | Storage.prototype.createCompositeKey = function (key) {
|
1151 | return [this.appId, this.appName, this.namespace, key].join();
|
1152 | };
|
1153 | return Storage;
|
1154 | }());
|
1155 |
|
1156 | /**
|
1157 | * @license
|
1158 | * Copyright 2019 Google LLC
|
1159 | *
|
1160 | * Licensed under the Apache License, Version 2.0 (the "License");
|
1161 | * you may not use this file except in compliance with the License.
|
1162 | * You may obtain a copy of the License at
|
1163 | *
|
1164 | * http://www.apache.org/licenses/LICENSE-2.0
|
1165 | *
|
1166 | * Unless required by applicable law or agreed to in writing, software
|
1167 | * distributed under the License is distributed on an "AS IS" BASIS,
|
1168 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
1169 | * See the License for the specific language governing permissions and
|
1170 | * limitations under the License.
|
1171 | */
|
1172 | /**
|
1173 | * A memory cache layer over storage to support the SDK's synchronous read requirements.
|
1174 | */
|
1175 | var StorageCache = /** @class */ (function () {
|
1176 | function StorageCache(storage) {
|
1177 | this.storage = storage;
|
1178 | }
|
1179 | /**
|
1180 | * Memory-only getters
|
1181 | */
|
1182 | StorageCache.prototype.getLastFetchStatus = function () {
|
1183 | return this.lastFetchStatus;
|
1184 | };
|
1185 | StorageCache.prototype.getLastSuccessfulFetchTimestampMillis = function () {
|
1186 | return this.lastSuccessfulFetchTimestampMillis;
|
1187 | };
|
1188 | StorageCache.prototype.getActiveConfig = function () {
|
1189 | return this.activeConfig;
|
1190 | };
|
1191 | /**
|
1192 | * Read-ahead getter
|
1193 | */
|
1194 | StorageCache.prototype.loadFromStorage = function () {
|
1195 | return tslib.__awaiter(this, void 0, void 0, function () {
|
1196 | var lastFetchStatusPromise, lastSuccessfulFetchTimestampMillisPromise, activeConfigPromise, lastFetchStatus, lastSuccessfulFetchTimestampMillis, activeConfig;
|
1197 | return tslib.__generator(this, function (_a) {
|
1198 | switch (_a.label) {
|
1199 | case 0:
|
1200 | lastFetchStatusPromise = this.storage.getLastFetchStatus();
|
1201 | lastSuccessfulFetchTimestampMillisPromise = this.storage.getLastSuccessfulFetchTimestampMillis();
|
1202 | activeConfigPromise = this.storage.getActiveConfig();
|
1203 | return [4 /*yield*/, lastFetchStatusPromise];
|
1204 | case 1:
|
1205 | lastFetchStatus = _a.sent();
|
1206 | if (lastFetchStatus) {
|
1207 | this.lastFetchStatus = lastFetchStatus;
|
1208 | }
|
1209 | return [4 /*yield*/, lastSuccessfulFetchTimestampMillisPromise];
|
1210 | case 2:
|
1211 | lastSuccessfulFetchTimestampMillis = _a.sent();
|
1212 | if (lastSuccessfulFetchTimestampMillis) {
|
1213 | this.lastSuccessfulFetchTimestampMillis =
|
1214 | lastSuccessfulFetchTimestampMillis;
|
1215 | }
|
1216 | return [4 /*yield*/, activeConfigPromise];
|
1217 | case 3:
|
1218 | activeConfig = _a.sent();
|
1219 | if (activeConfig) {
|
1220 | this.activeConfig = activeConfig;
|
1221 | }
|
1222 | return [2 /*return*/];
|
1223 | }
|
1224 | });
|
1225 | });
|
1226 | };
|
1227 | /**
|
1228 | * Write-through setters
|
1229 | */
|
1230 | StorageCache.prototype.setLastFetchStatus = function (status) {
|
1231 | this.lastFetchStatus = status;
|
1232 | return this.storage.setLastFetchStatus(status);
|
1233 | };
|
1234 | StorageCache.prototype.setLastSuccessfulFetchTimestampMillis = function (timestampMillis) {
|
1235 | this.lastSuccessfulFetchTimestampMillis = timestampMillis;
|
1236 | return this.storage.setLastSuccessfulFetchTimestampMillis(timestampMillis);
|
1237 | };
|
1238 | StorageCache.prototype.setActiveConfig = function (activeConfig) {
|
1239 | this.activeConfig = activeConfig;
|
1240 | return this.storage.setActiveConfig(activeConfig);
|
1241 | };
|
1242 | return StorageCache;
|
1243 | }());
|
1244 |
|
1245 | /**
|
1246 | * @license
|
1247 | * Copyright 2020 Google LLC
|
1248 | *
|
1249 | * Licensed under the Apache License, Version 2.0 (the "License");
|
1250 | * you may not use this file except in compliance with the License.
|
1251 | * You may obtain a copy of the License at
|
1252 | *
|
1253 | * http://www.apache.org/licenses/LICENSE-2.0
|
1254 | *
|
1255 | * Unless required by applicable law or agreed to in writing, software
|
1256 | * distributed under the License is distributed on an "AS IS" BASIS,
|
1257 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
1258 | * See the License for the specific language governing permissions and
|
1259 | * limitations under the License.
|
1260 | */
|
1261 | function registerRemoteConfig() {
|
1262 | app._registerComponent(new component.Component(RC_COMPONENT_NAME, remoteConfigFactory, "PUBLIC" /* PUBLIC */).setMultipleInstances(true));
|
1263 | app.registerVersion(name, version);
|
1264 | // BUILD_TARGET will be replaced by values like esm5, esm2017, cjs5, etc during the compilation
|
1265 | app.registerVersion(name, version, 'cjs5');
|
1266 | function remoteConfigFactory(container, _a) {
|
1267 | var namespace = _a.instanceIdentifier;
|
1268 | /* Dependencies */
|
1269 | // getImmediate for FirebaseApp will always succeed
|
1270 | var app$1 = container.getProvider('app').getImmediate();
|
1271 | // The following call will always succeed because rc has `import '@firebase/installations'`
|
1272 | var installations = container
|
1273 | .getProvider('installations-internal')
|
1274 | .getImmediate();
|
1275 | // Guards against the SDK being used in non-browser environments.
|
1276 | if (typeof window === 'undefined') {
|
1277 | throw ERROR_FACTORY.create("registration-window" /* REGISTRATION_WINDOW */);
|
1278 | }
|
1279 | // Guards against the SDK being used when indexedDB is not available.
|
1280 | if (!util.isIndexedDBAvailable()) {
|
1281 | throw ERROR_FACTORY.create("indexed-db-unavailable" /* INDEXED_DB_UNAVAILABLE */);
|
1282 | }
|
1283 | // Normalizes optional inputs.
|
1284 | var _b = app$1.options, projectId = _b.projectId, apiKey = _b.apiKey, appId = _b.appId;
|
1285 | if (!projectId) {
|
1286 | throw ERROR_FACTORY.create("registration-project-id" /* REGISTRATION_PROJECT_ID */);
|
1287 | }
|
1288 | if (!apiKey) {
|
1289 | throw ERROR_FACTORY.create("registration-api-key" /* REGISTRATION_API_KEY */);
|
1290 | }
|
1291 | if (!appId) {
|
1292 | throw ERROR_FACTORY.create("registration-app-id" /* REGISTRATION_APP_ID */);
|
1293 | }
|
1294 | namespace = namespace || 'firebase';
|
1295 | var storage = new Storage(appId, app$1.name, namespace);
|
1296 | var storageCache = new StorageCache(storage);
|
1297 | var logger$1 = new logger.Logger(name);
|
1298 | // Sets ERROR as the default log level.
|
1299 | // See RemoteConfig#setLogLevel for corresponding normalization to ERROR log level.
|
1300 | logger$1.logLevel = logger.LogLevel.ERROR;
|
1301 | var restClient = new RestClient(installations,
|
1302 | // Uses the JS SDK version, by which the RC package version can be deduced, if necessary.
|
1303 | app.SDK_VERSION, namespace, projectId, apiKey, appId);
|
1304 | var retryingClient = new RetryingClient(restClient, storage);
|
1305 | var cachingClient = new CachingClient(retryingClient, storage, storageCache, logger$1);
|
1306 | var remoteConfigInstance = new RemoteConfig(app$1, cachingClient, storageCache, storage, logger$1);
|
1307 | // Starts warming cache.
|
1308 | // eslint-disable-next-line @typescript-eslint/no-floating-promises
|
1309 | ensureInitialized(remoteConfigInstance);
|
1310 | return remoteConfigInstance;
|
1311 | }
|
1312 | }
|
1313 |
|
1314 | /**
|
1315 | * @license
|
1316 | * Copyright 2020 Google LLC
|
1317 | *
|
1318 | * Licensed under the Apache License, Version 2.0 (the "License");
|
1319 | * you may not use this file except in compliance with the License.
|
1320 | * You may obtain a copy of the License at
|
1321 | *
|
1322 | * http://www.apache.org/licenses/LICENSE-2.0
|
1323 | *
|
1324 | * Unless required by applicable law or agreed to in writing, software
|
1325 | * distributed under the License is distributed on an "AS IS" BASIS,
|
1326 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
1327 | * See the License for the specific language governing permissions and
|
1328 | * limitations under the License.
|
1329 | */
|
1330 | // This API is put in a separate file, so we can stub fetchConfig and activate in tests.
|
1331 | // It's not possible to stub standalone functions from the same module.
|
1332 | /**
|
1333 | *
|
1334 | * Performs fetch and activate operations, as a convenience.
|
1335 | *
|
1336 | * @param remoteConfig - The {@link RemoteConfig} instance.
|
1337 | *
|
1338 | * @returns A `Promise` which resolves to true if the current call activated the fetched configs.
|
1339 | * If the fetched configs were already activated, the `Promise` will resolve to false.
|
1340 | *
|
1341 | * @public
|
1342 | */
|
1343 | function fetchAndActivate(remoteConfig) {
|
1344 | return tslib.__awaiter(this, void 0, void 0, function () {
|
1345 | return tslib.__generator(this, function (_a) {
|
1346 | switch (_a.label) {
|
1347 | case 0:
|
1348 | remoteConfig = util.getModularInstance(remoteConfig);
|
1349 | return [4 /*yield*/, fetchConfig(remoteConfig)];
|
1350 | case 1:
|
1351 | _a.sent();
|
1352 | return [2 /*return*/, activate(remoteConfig)];
|
1353 | }
|
1354 | });
|
1355 | });
|
1356 | }
|
1357 | /**
|
1358 | * This method provides two different checks:
|
1359 | *
|
1360 | * 1. Check if IndexedDB exists in the browser environment.
|
1361 | * 2. Check if the current browser context allows IndexedDB `open()` calls.
|
1362 | *
|
1363 | * @returns A `Promise` which resolves to true if a {@link RemoteConfig} instance
|
1364 | * can be initialized in this environment, or false if it cannot.
|
1365 | * @public
|
1366 | */
|
1367 | function isSupported() {
|
1368 | return tslib.__awaiter(this, void 0, void 0, function () {
|
1369 | var isDBOpenable;
|
1370 | return tslib.__generator(this, function (_a) {
|
1371 | switch (_a.label) {
|
1372 | case 0:
|
1373 | if (!util.isIndexedDBAvailable()) {
|
1374 | return [2 /*return*/, false];
|
1375 | }
|
1376 | _a.label = 1;
|
1377 | case 1:
|
1378 | _a.trys.push([1, 3, , 4]);
|
1379 | return [4 /*yield*/, util.validateIndexedDBOpenable()];
|
1380 | case 2:
|
1381 | isDBOpenable = _a.sent();
|
1382 | return [2 /*return*/, isDBOpenable];
|
1383 | case 3:
|
1384 | _a.sent();
|
1385 | return [2 /*return*/, false];
|
1386 | case 4: return [2 /*return*/];
|
1387 | }
|
1388 | });
|
1389 | });
|
1390 | }
|
1391 |
|
1392 | /**
|
1393 | * Firebase Remote Config
|
1394 | *
|
1395 | * @packageDocumentation
|
1396 | */
|
1397 | /** register component and version */
|
1398 | registerRemoteConfig();
|
1399 |
|
1400 | exports.activate = activate;
|
1401 | exports.ensureInitialized = ensureInitialized;
|
1402 | exports.fetchAndActivate = fetchAndActivate;
|
1403 | exports.fetchConfig = fetchConfig;
|
1404 | exports.getAll = getAll;
|
1405 | exports.getBoolean = getBoolean;
|
1406 | exports.getNumber = getNumber;
|
1407 | exports.getRemoteConfig = getRemoteConfig;
|
1408 | exports.getString = getString;
|
1409 | exports.getValue = getValue;
|
1410 | exports.isSupported = isSupported;
|
1411 | exports.setLogLevel = setLogLevel;
|
1412 | //# sourceMappingURL=index.cjs.js.map
|