@firebase/remote-config 0.4.9 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.esm2017.js +2 -2
- package/dist/esm/index.esm2017.js.map +1 -1
- package/dist/index.cjs.js +475 -640
- package/dist/index.cjs.js.map +1 -1
- package/package.json +6 -8
- package/dist/esm/index.esm.js +0 -1398
- package/dist/esm/index.esm.js.map +0 -1
package/dist/index.cjs.js
CHANGED
|
@@ -6,11 +6,10 @@ var app = require('@firebase/app');
|
|
|
6
6
|
var util = require('@firebase/util');
|
|
7
7
|
var component = require('@firebase/component');
|
|
8
8
|
var logger = require('@firebase/logger');
|
|
9
|
-
var tslib = require('tslib');
|
|
10
9
|
require('@firebase/installations');
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const name = "@firebase/remote-config";
|
|
12
|
+
const version = "0.4.10";
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* @license
|
|
@@ -36,18 +35,17 @@ var version = "0.4.9";
|
|
|
36
35
|
* polyfill recommendation, like we do with the Fetch API, but this minimal shim can easily be
|
|
37
36
|
* swapped out if/when we do.
|
|
38
37
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
class RemoteConfigAbortSignal {
|
|
39
|
+
constructor() {
|
|
41
40
|
this.listeners = [];
|
|
42
41
|
}
|
|
43
|
-
|
|
42
|
+
addEventListener(listener) {
|
|
44
43
|
this.listeners.push(listener);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
this.listeners.forEach(
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
}());
|
|
44
|
+
}
|
|
45
|
+
abort() {
|
|
46
|
+
this.listeners.forEach(listener => listener());
|
|
47
|
+
}
|
|
48
|
+
}
|
|
51
49
|
|
|
52
50
|
/**
|
|
53
51
|
* @license
|
|
@@ -65,7 +63,7 @@ var RemoteConfigAbortSignal = /** @class */ (function () {
|
|
|
65
63
|
* See the License for the specific language governing permissions and
|
|
66
64
|
* limitations under the License.
|
|
67
65
|
*/
|
|
68
|
-
|
|
66
|
+
const RC_COMPONENT_NAME = 'remote-config';
|
|
69
67
|
|
|
70
68
|
/**
|
|
71
69
|
* @license
|
|
@@ -83,29 +81,28 @@ var RC_COMPONENT_NAME = 'remote-config';
|
|
|
83
81
|
* See the License for the specific language governing permissions and
|
|
84
82
|
* limitations under the License.
|
|
85
83
|
*/
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
_a["fetch-client-network" /* ErrorCode.FETCH_NETWORK */] = 'Fetch client failed to connect to a network. Check Internet connection.' +
|
|
84
|
+
const ERROR_DESCRIPTION_MAP = {
|
|
85
|
+
["registration-window" /* ErrorCode.REGISTRATION_WINDOW */]: 'Undefined window object. This SDK only supports usage in a browser environment.',
|
|
86
|
+
["registration-project-id" /* ErrorCode.REGISTRATION_PROJECT_ID */]: 'Undefined project identifier. Check Firebase app initialization.',
|
|
87
|
+
["registration-api-key" /* ErrorCode.REGISTRATION_API_KEY */]: 'Undefined API key. Check Firebase app initialization.',
|
|
88
|
+
["registration-app-id" /* ErrorCode.REGISTRATION_APP_ID */]: 'Undefined app identifier. Check Firebase app initialization.',
|
|
89
|
+
["storage-open" /* ErrorCode.STORAGE_OPEN */]: 'Error thrown when opening storage. Original error: {$originalErrorMessage}.',
|
|
90
|
+
["storage-get" /* ErrorCode.STORAGE_GET */]: 'Error thrown when reading from storage. Original error: {$originalErrorMessage}.',
|
|
91
|
+
["storage-set" /* ErrorCode.STORAGE_SET */]: 'Error thrown when writing to storage. Original error: {$originalErrorMessage}.',
|
|
92
|
+
["storage-delete" /* ErrorCode.STORAGE_DELETE */]: 'Error thrown when deleting from storage. Original error: {$originalErrorMessage}.',
|
|
93
|
+
["fetch-client-network" /* ErrorCode.FETCH_NETWORK */]: 'Fetch client failed to connect to a network. Check Internet connection.' +
|
|
97
94
|
' Original error: {$originalErrorMessage}.',
|
|
98
|
-
|
|
95
|
+
["fetch-timeout" /* ErrorCode.FETCH_TIMEOUT */]: 'The config fetch request timed out. ' +
|
|
99
96
|
' Configure timeout using "fetchTimeoutMillis" SDK setting.',
|
|
100
|
-
|
|
97
|
+
["fetch-throttle" /* ErrorCode.FETCH_THROTTLE */]: 'The config fetch request timed out while in an exponential backoff state.' +
|
|
101
98
|
' Configure timeout using "fetchTimeoutMillis" SDK setting.' +
|
|
102
99
|
' Unix timestamp in milliseconds when fetch request throttling ends: {$throttleEndTimeMillis}.',
|
|
103
|
-
|
|
100
|
+
["fetch-client-parse" /* ErrorCode.FETCH_PARSE */]: 'Fetch client could not parse response.' +
|
|
104
101
|
' Original error: {$originalErrorMessage}.',
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
["fetch-status" /* ErrorCode.FETCH_STATUS */]: 'Fetch server returned an HTTP error status. HTTP status: {$httpStatus}.',
|
|
103
|
+
["indexed-db-unavailable" /* ErrorCode.INDEXED_DB_UNAVAILABLE */]: 'Indexed DB is not supported by current browser'
|
|
104
|
+
};
|
|
105
|
+
const ERROR_FACTORY = new util.ErrorFactory('remoteconfig' /* service */, 'Remote Config' /* service name */, ERROR_DESCRIPTION_MAP);
|
|
109
106
|
// Note how this is like typeof/instanceof, but for ErrorCode.
|
|
110
107
|
function hasErrorCode(e, errorCode) {
|
|
111
108
|
return e instanceof util.FirebaseError && e.code.indexOf(errorCode) !== -1;
|
|
@@ -127,40 +124,38 @@ function hasErrorCode(e, errorCode) {
|
|
|
127
124
|
* See the License for the specific language governing permissions and
|
|
128
125
|
* limitations under the License.
|
|
129
126
|
*/
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (_value === void 0) { _value = DEFAULT_VALUE_FOR_STRING; }
|
|
127
|
+
const DEFAULT_VALUE_FOR_BOOLEAN = false;
|
|
128
|
+
const DEFAULT_VALUE_FOR_STRING = '';
|
|
129
|
+
const DEFAULT_VALUE_FOR_NUMBER = 0;
|
|
130
|
+
const BOOLEAN_TRUTHY_VALUES = ['1', 'true', 't', 'yes', 'y', 'on'];
|
|
131
|
+
class Value {
|
|
132
|
+
constructor(_source, _value = DEFAULT_VALUE_FOR_STRING) {
|
|
137
133
|
this._source = _source;
|
|
138
134
|
this._value = _value;
|
|
139
135
|
}
|
|
140
|
-
|
|
136
|
+
asString() {
|
|
141
137
|
return this._value;
|
|
142
|
-
}
|
|
143
|
-
|
|
138
|
+
}
|
|
139
|
+
asBoolean() {
|
|
144
140
|
if (this._source === 'static') {
|
|
145
141
|
return DEFAULT_VALUE_FOR_BOOLEAN;
|
|
146
142
|
}
|
|
147
143
|
return BOOLEAN_TRUTHY_VALUES.indexOf(this._value.toLowerCase()) >= 0;
|
|
148
|
-
}
|
|
149
|
-
|
|
144
|
+
}
|
|
145
|
+
asNumber() {
|
|
150
146
|
if (this._source === 'static') {
|
|
151
147
|
return DEFAULT_VALUE_FOR_NUMBER;
|
|
152
148
|
}
|
|
153
|
-
|
|
149
|
+
let num = Number(this._value);
|
|
154
150
|
if (isNaN(num)) {
|
|
155
151
|
num = DEFAULT_VALUE_FOR_NUMBER;
|
|
156
152
|
}
|
|
157
153
|
return num;
|
|
158
|
-
}
|
|
159
|
-
|
|
154
|
+
}
|
|
155
|
+
getSource() {
|
|
160
156
|
return this._source;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
}());
|
|
157
|
+
}
|
|
158
|
+
}
|
|
164
159
|
|
|
165
160
|
/**
|
|
166
161
|
* @license
|
|
@@ -185,10 +180,9 @@ var Value = /** @class */ (function () {
|
|
|
185
180
|
*
|
|
186
181
|
* @public
|
|
187
182
|
*/
|
|
188
|
-
function getRemoteConfig(app$1) {
|
|
189
|
-
if (app$1 === void 0) { app$1 = app.getApp(); }
|
|
183
|
+
function getRemoteConfig(app$1 = app.getApp()) {
|
|
190
184
|
app$1 = util.getModularInstance(app$1);
|
|
191
|
-
|
|
185
|
+
const rcProvider = app._getProvider(app$1, RC_COMPONENT_NAME);
|
|
192
186
|
return rcProvider.getImmediate();
|
|
193
187
|
}
|
|
194
188
|
/**
|
|
@@ -199,37 +193,25 @@ function getRemoteConfig(app$1) {
|
|
|
199
193
|
*
|
|
200
194
|
* @public
|
|
201
195
|
*/
|
|
202
|
-
function activate(remoteConfig) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
});
|
|
196
|
+
async function activate(remoteConfig) {
|
|
197
|
+
const rc = util.getModularInstance(remoteConfig);
|
|
198
|
+
const [lastSuccessfulFetchResponse, activeConfigEtag] = await Promise.all([
|
|
199
|
+
rc._storage.getLastSuccessfulFetchResponse(),
|
|
200
|
+
rc._storage.getActiveConfigEtag()
|
|
201
|
+
]);
|
|
202
|
+
if (!lastSuccessfulFetchResponse ||
|
|
203
|
+
!lastSuccessfulFetchResponse.config ||
|
|
204
|
+
!lastSuccessfulFetchResponse.eTag ||
|
|
205
|
+
lastSuccessfulFetchResponse.eTag === activeConfigEtag) {
|
|
206
|
+
// Either there is no successful fetched config, or is the same as current active
|
|
207
|
+
// config.
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
await Promise.all([
|
|
211
|
+
rc._storageCache.setActiveConfig(lastSuccessfulFetchResponse.config),
|
|
212
|
+
rc._storage.setActiveConfigEtag(lastSuccessfulFetchResponse.eTag)
|
|
213
|
+
]);
|
|
214
|
+
return true;
|
|
233
215
|
}
|
|
234
216
|
/**
|
|
235
217
|
* Ensures the last activated config are available to the getters.
|
|
@@ -239,9 +221,9 @@ function activate(remoteConfig) {
|
|
|
239
221
|
* @public
|
|
240
222
|
*/
|
|
241
223
|
function ensureInitialized(remoteConfig) {
|
|
242
|
-
|
|
224
|
+
const rc = util.getModularInstance(remoteConfig);
|
|
243
225
|
if (!rc._initializePromise) {
|
|
244
|
-
rc._initializePromise = rc._storageCache.loadFromStorage().then(
|
|
226
|
+
rc._initializePromise = rc._storageCache.loadFromStorage().then(() => {
|
|
245
227
|
rc._isInitializationComplete = true;
|
|
246
228
|
});
|
|
247
229
|
}
|
|
@@ -252,48 +234,38 @@ function ensureInitialized(remoteConfig) {
|
|
|
252
234
|
* @param remoteConfig - The {@link RemoteConfig} instance.
|
|
253
235
|
* @public
|
|
254
236
|
*/
|
|
255
|
-
function fetchConfig(remoteConfig) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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" /* ErrorCode.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
|
-
}
|
|
237
|
+
async function fetchConfig(remoteConfig) {
|
|
238
|
+
const rc = util.getModularInstance(remoteConfig);
|
|
239
|
+
// Aborts the request after the given timeout, causing the fetch call to
|
|
240
|
+
// reject with an `AbortError`.
|
|
241
|
+
//
|
|
242
|
+
// <p>Aborting after the request completes is a no-op, so we don't need a
|
|
243
|
+
// corresponding `clearTimeout`.
|
|
244
|
+
//
|
|
245
|
+
// Locating abort logic here because:
|
|
246
|
+
// * it uses a developer setting (timeout)
|
|
247
|
+
// * it applies to all retries (like curl's max-time arg)
|
|
248
|
+
// * it is consistent with the Fetch API's signal input
|
|
249
|
+
const abortSignal = new RemoteConfigAbortSignal();
|
|
250
|
+
setTimeout(async () => {
|
|
251
|
+
// Note a very low delay, eg < 10ms, can elapse before listeners are initialized.
|
|
252
|
+
abortSignal.abort();
|
|
253
|
+
}, rc.settings.fetchTimeoutMillis);
|
|
254
|
+
// Catches *all* errors thrown by client so status can be set consistently.
|
|
255
|
+
try {
|
|
256
|
+
await rc._client.fetch({
|
|
257
|
+
cacheMaxAgeMillis: rc.settings.minimumFetchIntervalMillis,
|
|
258
|
+
signal: abortSignal
|
|
295
259
|
});
|
|
296
|
-
|
|
260
|
+
await rc._storageCache.setLastFetchStatus('success');
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
const lastFetchStatus = hasErrorCode(e, "fetch-throttle" /* ErrorCode.FETCH_THROTTLE */)
|
|
264
|
+
? 'throttle'
|
|
265
|
+
: 'failure';
|
|
266
|
+
await rc._storageCache.setLastFetchStatus(lastFetchStatus);
|
|
267
|
+
throw e;
|
|
268
|
+
}
|
|
297
269
|
}
|
|
298
270
|
/**
|
|
299
271
|
* Gets all config.
|
|
@@ -304,8 +276,8 @@ function fetchConfig(remoteConfig) {
|
|
|
304
276
|
* @public
|
|
305
277
|
*/
|
|
306
278
|
function getAll(remoteConfig) {
|
|
307
|
-
|
|
308
|
-
return getAllKeys(rc._storageCache.getActiveConfig(), rc.defaultConfig).reduce(
|
|
279
|
+
const rc = util.getModularInstance(remoteConfig);
|
|
280
|
+
return getAllKeys(rc._storageCache.getActiveConfig(), rc.defaultConfig).reduce((allConfigs, key) => {
|
|
309
281
|
allConfigs[key] = getValue(remoteConfig, key);
|
|
310
282
|
return allConfigs;
|
|
311
283
|
}, {});
|
|
@@ -364,19 +336,19 @@ function getString(remoteConfig, key) {
|
|
|
364
336
|
* @public
|
|
365
337
|
*/
|
|
366
338
|
function getValue(remoteConfig, key) {
|
|
367
|
-
|
|
339
|
+
const rc = util.getModularInstance(remoteConfig);
|
|
368
340
|
if (!rc._isInitializationComplete) {
|
|
369
|
-
rc._logger.debug(
|
|
341
|
+
rc._logger.debug(`A value was requested for key "${key}" before SDK initialization completed.` +
|
|
370
342
|
' Await on ensureInitialized if the intent was to get a previously activated value.');
|
|
371
343
|
}
|
|
372
|
-
|
|
344
|
+
const activeConfig = rc._storageCache.getActiveConfig();
|
|
373
345
|
if (activeConfig && activeConfig[key] !== undefined) {
|
|
374
346
|
return new Value('remote', activeConfig[key]);
|
|
375
347
|
}
|
|
376
348
|
else if (rc.defaultConfig && rc.defaultConfig[key] !== undefined) {
|
|
377
349
|
return new Value('default', String(rc.defaultConfig[key]));
|
|
378
350
|
}
|
|
379
|
-
rc._logger.debug(
|
|
351
|
+
rc._logger.debug(`Returning static value for key "${key}".` +
|
|
380
352
|
' Define a default or remote value if this is unintentional.');
|
|
381
353
|
return new Value('static');
|
|
382
354
|
}
|
|
@@ -389,7 +361,7 @@ function getValue(remoteConfig, key) {
|
|
|
389
361
|
* @public
|
|
390
362
|
*/
|
|
391
363
|
function setLogLevel(remoteConfig, logLevel) {
|
|
392
|
-
|
|
364
|
+
const rc = util.getModularInstance(remoteConfig);
|
|
393
365
|
switch (logLevel) {
|
|
394
366
|
case 'debug':
|
|
395
367
|
rc._logger.logLevel = logger.LogLevel.DEBUG;
|
|
@@ -404,10 +376,8 @@ function setLogLevel(remoteConfig, logLevel) {
|
|
|
404
376
|
/**
|
|
405
377
|
* Dedupes and returns an array of all the keys of the received objects.
|
|
406
378
|
*/
|
|
407
|
-
function getAllKeys(obj1, obj2) {
|
|
408
|
-
|
|
409
|
-
if (obj2 === void 0) { obj2 = {}; }
|
|
410
|
-
return Object.keys(tslib.__assign(tslib.__assign({}, obj1), obj2));
|
|
379
|
+
function getAllKeys(obj1 = {}, obj2 = {}) {
|
|
380
|
+
return Object.keys(Object.assign(Object.assign({}, obj1), obj2));
|
|
411
381
|
}
|
|
412
382
|
|
|
413
383
|
/**
|
|
@@ -433,8 +403,8 @@ function getAllKeys(obj1, obj2) {
|
|
|
433
403
|
* Worker, which requires HTTPS, which would significantly complicate SDK installation. Also, the
|
|
434
404
|
* Cache API doesn't support matching entries by time.
|
|
435
405
|
*/
|
|
436
|
-
|
|
437
|
-
|
|
406
|
+
class CachingClient {
|
|
407
|
+
constructor(client, storage, storageCache, logger) {
|
|
438
408
|
this.client = client;
|
|
439
409
|
this.storage = storage;
|
|
440
410
|
this.storageCache = storageCache;
|
|
@@ -449,62 +419,51 @@ var CachingClient = /** @class */ (function () {
|
|
|
449
419
|
*
|
|
450
420
|
* <p>Visible for testing.
|
|
451
421
|
*/
|
|
452
|
-
|
|
422
|
+
isCachedDataFresh(cacheMaxAgeMillis, lastSuccessfulFetchTimestampMillis) {
|
|
453
423
|
// Cache can only be fresh if it's populated.
|
|
454
424
|
if (!lastSuccessfulFetchTimestampMillis) {
|
|
455
425
|
this.logger.debug('Config fetch cache check. Cache unpopulated.');
|
|
456
426
|
return false;
|
|
457
427
|
}
|
|
458
428
|
// Calculates age of cache entry.
|
|
459
|
-
|
|
460
|
-
|
|
429
|
+
const cacheAgeMillis = Date.now() - lastSuccessfulFetchTimestampMillis;
|
|
430
|
+
const isCachedDataFresh = cacheAgeMillis <= cacheMaxAgeMillis;
|
|
461
431
|
this.logger.debug('Config fetch cache check.' +
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
432
|
+
` Cache age millis: ${cacheAgeMillis}.` +
|
|
433
|
+
` Cache max age millis (minimumFetchIntervalMillis setting): ${cacheMaxAgeMillis}.` +
|
|
434
|
+
` Is cache hit: ${isCachedDataFresh}.`);
|
|
465
435
|
return isCachedDataFresh;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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
|
-
}());
|
|
436
|
+
}
|
|
437
|
+
async fetch(request) {
|
|
438
|
+
// Reads from persisted storage to avoid cache miss if callers don't wait on initialization.
|
|
439
|
+
const [lastSuccessfulFetchTimestampMillis, lastSuccessfulFetchResponse] = await Promise.all([
|
|
440
|
+
this.storage.getLastSuccessfulFetchTimestampMillis(),
|
|
441
|
+
this.storage.getLastSuccessfulFetchResponse()
|
|
442
|
+
]);
|
|
443
|
+
// Exits early on cache hit.
|
|
444
|
+
if (lastSuccessfulFetchResponse &&
|
|
445
|
+
this.isCachedDataFresh(request.cacheMaxAgeMillis, lastSuccessfulFetchTimestampMillis)) {
|
|
446
|
+
return lastSuccessfulFetchResponse;
|
|
447
|
+
}
|
|
448
|
+
// Deviates from pure decorator by not honoring a passed ETag since we don't have a public API
|
|
449
|
+
// that allows the caller to pass an ETag.
|
|
450
|
+
request.eTag =
|
|
451
|
+
lastSuccessfulFetchResponse && lastSuccessfulFetchResponse.eTag;
|
|
452
|
+
// Falls back to service on cache miss.
|
|
453
|
+
const response = await this.client.fetch(request);
|
|
454
|
+
// Fetch throws for non-success responses, so success is guaranteed here.
|
|
455
|
+
const storageOperations = [
|
|
456
|
+
// Uses write-through cache for consistency with synchronous public API.
|
|
457
|
+
this.storageCache.setLastSuccessfulFetchTimestampMillis(Date.now())
|
|
458
|
+
];
|
|
459
|
+
if (response.status === 200) {
|
|
460
|
+
// Caches response only if it has changed, ie non-304 responses.
|
|
461
|
+
storageOperations.push(this.storage.setLastSuccessfulFetchResponse(response));
|
|
462
|
+
}
|
|
463
|
+
await Promise.all(storageOperations);
|
|
464
|
+
return response;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
508
467
|
|
|
509
468
|
/**
|
|
510
469
|
* @license
|
|
@@ -531,8 +490,7 @@ var CachingClient = /** @class */ (function () {
|
|
|
531
490
|
*
|
|
532
491
|
* @param navigatorLanguage Enables tests to override read-only {@link NavigatorLanguage}.
|
|
533
492
|
*/
|
|
534
|
-
function getUserLanguage(navigatorLanguage) {
|
|
535
|
-
if (navigatorLanguage === void 0) { navigatorLanguage = navigator; }
|
|
493
|
+
function getUserLanguage(navigatorLanguage = navigator) {
|
|
536
494
|
return (
|
|
537
495
|
// Most reliable, but only supported in Chrome/Firefox.
|
|
538
496
|
(navigatorLanguage.languages && navigatorLanguage.languages[0]) ||
|
|
@@ -562,8 +520,8 @@ function getUserLanguage(navigatorLanguage) {
|
|
|
562
520
|
/**
|
|
563
521
|
* Implements the Client abstraction for the Remote Config REST API.
|
|
564
522
|
*/
|
|
565
|
-
|
|
566
|
-
|
|
523
|
+
class RestClient {
|
|
524
|
+
constructor(firebaseInstallations, sdkVersion, namespace, projectId, apiKey, appId) {
|
|
567
525
|
this.firebaseInstallations = firebaseInstallations;
|
|
568
526
|
this.sdkVersion = sdkVersion;
|
|
569
527
|
this.namespace = namespace;
|
|
@@ -580,119 +538,103 @@ var RestClient = /** @class */ (function () {
|
|
|
580
538
|
* fetch response.
|
|
581
539
|
* @throws a {@link ErrorCode.FETCH_STATUS} error if the service returns an HTTP error status.
|
|
582
540
|
*/
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
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" /* ErrorCode.FETCH_NETWORK */;
|
|
641
|
-
if ((originalError_1 === null || originalError_1 === void 0 ? void 0 : originalError_1.name) === 'AbortError') {
|
|
642
|
-
errorCode = "fetch-timeout" /* ErrorCode.FETCH_TIMEOUT */;
|
|
643
|
-
}
|
|
644
|
-
throw ERROR_FACTORY.create(errorCode, {
|
|
645
|
-
originalErrorMessage: originalError_1 === null || originalError_1 === void 0 ? void 0 : 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" /* ErrorCode.FETCH_PARSE */, {
|
|
662
|
-
originalErrorMessage: originalError_2 === null || originalError_2 === void 0 ? void 0 : 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" /* ErrorCode.FETCH_STATUS */, {
|
|
686
|
-
httpStatus: status
|
|
687
|
-
});
|
|
688
|
-
}
|
|
689
|
-
return [2 /*return*/, { status: status, eTag: responseEtag, config: config }];
|
|
690
|
-
}
|
|
541
|
+
async fetch(request) {
|
|
542
|
+
const [installationId, installationToken] = await Promise.all([
|
|
543
|
+
this.firebaseInstallations.getId(),
|
|
544
|
+
this.firebaseInstallations.getToken()
|
|
545
|
+
]);
|
|
546
|
+
const urlBase = window.FIREBASE_REMOTE_CONFIG_URL_BASE ||
|
|
547
|
+
'https://firebaseremoteconfig.googleapis.com';
|
|
548
|
+
const url = `${urlBase}/v1/projects/${this.projectId}/namespaces/${this.namespace}:fetch?key=${this.apiKey}`;
|
|
549
|
+
const headers = {
|
|
550
|
+
'Content-Type': 'application/json',
|
|
551
|
+
'Content-Encoding': 'gzip',
|
|
552
|
+
// Deviates from pure decorator by not passing max-age header since we don't currently have
|
|
553
|
+
// service behavior using that header.
|
|
554
|
+
'If-None-Match': request.eTag || '*'
|
|
555
|
+
};
|
|
556
|
+
const requestBody = {
|
|
557
|
+
/* eslint-disable camelcase */
|
|
558
|
+
sdk_version: this.sdkVersion,
|
|
559
|
+
app_instance_id: installationId,
|
|
560
|
+
app_instance_id_token: installationToken,
|
|
561
|
+
app_id: this.appId,
|
|
562
|
+
language_code: getUserLanguage()
|
|
563
|
+
/* eslint-enable camelcase */
|
|
564
|
+
};
|
|
565
|
+
const options = {
|
|
566
|
+
method: 'POST',
|
|
567
|
+
headers,
|
|
568
|
+
body: JSON.stringify(requestBody)
|
|
569
|
+
};
|
|
570
|
+
// This logic isn't REST-specific, but shimming abort logic isn't worth another decorator.
|
|
571
|
+
const fetchPromise = fetch(url, options);
|
|
572
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
573
|
+
// Maps async event listener to Promise API.
|
|
574
|
+
request.signal.addEventListener(() => {
|
|
575
|
+
// Emulates https://heycam.github.io/webidl/#aborterror
|
|
576
|
+
const error = new Error('The operation was aborted.');
|
|
577
|
+
error.name = 'AbortError';
|
|
578
|
+
reject(error);
|
|
691
579
|
});
|
|
692
580
|
});
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
581
|
+
let response;
|
|
582
|
+
try {
|
|
583
|
+
await Promise.race([fetchPromise, timeoutPromise]);
|
|
584
|
+
response = await fetchPromise;
|
|
585
|
+
}
|
|
586
|
+
catch (originalError) {
|
|
587
|
+
let errorCode = "fetch-client-network" /* ErrorCode.FETCH_NETWORK */;
|
|
588
|
+
if ((originalError === null || originalError === void 0 ? void 0 : originalError.name) === 'AbortError') {
|
|
589
|
+
errorCode = "fetch-timeout" /* ErrorCode.FETCH_TIMEOUT */;
|
|
590
|
+
}
|
|
591
|
+
throw ERROR_FACTORY.create(errorCode, {
|
|
592
|
+
originalErrorMessage: originalError === null || originalError === void 0 ? void 0 : originalError.message
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
let status = response.status;
|
|
596
|
+
// Normalizes nullable header to optional.
|
|
597
|
+
const responseEtag = response.headers.get('ETag') || undefined;
|
|
598
|
+
let config;
|
|
599
|
+
let state;
|
|
600
|
+
// JSON parsing throws SyntaxError if the response body isn't a JSON string.
|
|
601
|
+
// Requesting application/json and checking for a 200 ensures there's JSON data.
|
|
602
|
+
if (response.status === 200) {
|
|
603
|
+
let responseBody;
|
|
604
|
+
try {
|
|
605
|
+
responseBody = await response.json();
|
|
606
|
+
}
|
|
607
|
+
catch (originalError) {
|
|
608
|
+
throw ERROR_FACTORY.create("fetch-client-parse" /* ErrorCode.FETCH_PARSE */, {
|
|
609
|
+
originalErrorMessage: originalError === null || originalError === void 0 ? void 0 : originalError.message
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
config = responseBody['entries'];
|
|
613
|
+
state = responseBody['state'];
|
|
614
|
+
}
|
|
615
|
+
// Normalizes based on legacy state.
|
|
616
|
+
if (state === 'INSTANCE_STATE_UNSPECIFIED') {
|
|
617
|
+
status = 500;
|
|
618
|
+
}
|
|
619
|
+
else if (state === 'NO_CHANGE') {
|
|
620
|
+
status = 304;
|
|
621
|
+
}
|
|
622
|
+
else if (state === 'NO_TEMPLATE' || state === 'EMPTY_CONFIG') {
|
|
623
|
+
// These cases can be fixed remotely, so normalize to safe value.
|
|
624
|
+
config = {};
|
|
625
|
+
}
|
|
626
|
+
// Normalize to exception-based control flow for non-success cases.
|
|
627
|
+
// Encapsulates HTTP specifics in this class as much as possible. Status is still the best for
|
|
628
|
+
// differentiating success states (200 from 304; the state body param is undefined in a
|
|
629
|
+
// standard 304).
|
|
630
|
+
if (status !== 304 && status !== 200) {
|
|
631
|
+
throw ERROR_FACTORY.create("fetch-status" /* ErrorCode.FETCH_STATUS */, {
|
|
632
|
+
httpStatus: status
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
return { status, eTag: responseEtag, config };
|
|
636
|
+
}
|
|
637
|
+
}
|
|
696
638
|
|
|
697
639
|
/**
|
|
698
640
|
* @license
|
|
@@ -723,16 +665,16 @@ var RestClient = /** @class */ (function () {
|
|
|
723
665
|
* <p>Visible for testing.
|
|
724
666
|
*/
|
|
725
667
|
function setAbortableTimeout(signal, throttleEndTimeMillis) {
|
|
726
|
-
return new Promise(
|
|
668
|
+
return new Promise((resolve, reject) => {
|
|
727
669
|
// Derives backoff from given end time, normalizing negative numbers to zero.
|
|
728
|
-
|
|
729
|
-
|
|
670
|
+
const backoffMillis = Math.max(throttleEndTimeMillis - Date.now(), 0);
|
|
671
|
+
const timeout = setTimeout(resolve, backoffMillis);
|
|
730
672
|
// Adds listener, rather than sets onabort, because signal is a shared object.
|
|
731
|
-
signal.addEventListener(
|
|
673
|
+
signal.addEventListener(() => {
|
|
732
674
|
clearTimeout(timeout);
|
|
733
675
|
// If the request completes before this timeout, the rejection has no effect.
|
|
734
676
|
reject(ERROR_FACTORY.create("fetch-throttle" /* ErrorCode.FETCH_THROTTLE */, {
|
|
735
|
-
throttleEndTimeMillis
|
|
677
|
+
throttleEndTimeMillis
|
|
736
678
|
}));
|
|
737
679
|
});
|
|
738
680
|
});
|
|
@@ -745,7 +687,7 @@ function isRetriableError(e) {
|
|
|
745
687
|
return false;
|
|
746
688
|
}
|
|
747
689
|
// Uses string index defined by ErrorData, which FirebaseError implements.
|
|
748
|
-
|
|
690
|
+
const httpStatus = Number(e.customData['httpStatus']);
|
|
749
691
|
return (httpStatus === 429 ||
|
|
750
692
|
httpStatus === 500 ||
|
|
751
693
|
httpStatus === 503 ||
|
|
@@ -757,82 +699,49 @@ function isRetriableError(e) {
|
|
|
757
699
|
* <p>Comparable to CachingClient, but uses backoff logic instead of cache max age and doesn't cache
|
|
758
700
|
* responses (because the SDK has no use for error responses).
|
|
759
701
|
*/
|
|
760
|
-
|
|
761
|
-
|
|
702
|
+
class RetryingClient {
|
|
703
|
+
constructor(client, storage) {
|
|
762
704
|
this.client = client;
|
|
763
705
|
this.storage = storage;
|
|
764
706
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
throttleMetadata = (_a.sent()) || {
|
|
773
|
-
backoffCount: 0,
|
|
774
|
-
throttleEndTimeMillis: Date.now()
|
|
775
|
-
};
|
|
776
|
-
return [2 /*return*/, this.attemptFetch(request, throttleMetadata)];
|
|
777
|
-
}
|
|
778
|
-
});
|
|
779
|
-
});
|
|
780
|
-
};
|
|
707
|
+
async fetch(request) {
|
|
708
|
+
const throttleMetadata = (await this.storage.getThrottleMetadata()) || {
|
|
709
|
+
backoffCount: 0,
|
|
710
|
+
throttleEndTimeMillis: Date.now()
|
|
711
|
+
};
|
|
712
|
+
return this.attemptFetch(request, throttleMetadata);
|
|
713
|
+
}
|
|
781
714
|
/**
|
|
782
715
|
* A recursive helper for attempting a fetch request repeatedly.
|
|
783
716
|
*
|
|
784
717
|
* @throws any non-retriable errors.
|
|
785
718
|
*/
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
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
|
-
}());
|
|
719
|
+
async attemptFetch(request, { throttleEndTimeMillis, backoffCount }) {
|
|
720
|
+
// Starts with a (potentially zero) timeout to support resumption from stored state.
|
|
721
|
+
// Ensures the throttle end time is honored if the last attempt timed out.
|
|
722
|
+
// Note the SDK will never make a request if the fetch timeout expires at this point.
|
|
723
|
+
await setAbortableTimeout(request.signal, throttleEndTimeMillis);
|
|
724
|
+
try {
|
|
725
|
+
const response = await this.client.fetch(request);
|
|
726
|
+
// Note the SDK only clears throttle state if response is success or non-retriable.
|
|
727
|
+
await this.storage.deleteThrottleMetadata();
|
|
728
|
+
return response;
|
|
729
|
+
}
|
|
730
|
+
catch (e) {
|
|
731
|
+
if (!isRetriableError(e)) {
|
|
732
|
+
throw e;
|
|
733
|
+
}
|
|
734
|
+
// Increments backoff state.
|
|
735
|
+
const throttleMetadata = {
|
|
736
|
+
throttleEndTimeMillis: Date.now() + util.calculateBackoffMillis(backoffCount),
|
|
737
|
+
backoffCount: backoffCount + 1
|
|
738
|
+
};
|
|
739
|
+
// Persists state.
|
|
740
|
+
await this.storage.setThrottleMetadata(throttleMetadata);
|
|
741
|
+
return this.attemptFetch(request, throttleMetadata);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
836
745
|
|
|
837
746
|
/**
|
|
838
747
|
* @license
|
|
@@ -850,15 +759,15 @@ var RetryingClient = /** @class */ (function () {
|
|
|
850
759
|
* See the License for the specific language governing permissions and
|
|
851
760
|
* limitations under the License.
|
|
852
761
|
*/
|
|
853
|
-
|
|
854
|
-
|
|
762
|
+
const DEFAULT_FETCH_TIMEOUT_MILLIS = 60 * 1000; // One minute
|
|
763
|
+
const DEFAULT_CACHE_MAX_AGE_MILLIS = 12 * 60 * 60 * 1000; // Twelve hours.
|
|
855
764
|
/**
|
|
856
765
|
* Encapsulates business logic mapping network and storage dependencies to the public SDK API.
|
|
857
766
|
*
|
|
858
767
|
* See {@link https://github.com/firebase/firebase-js-sdk/blob/main/packages/firebase/index.d.ts|interface documentation} for method descriptions.
|
|
859
768
|
*/
|
|
860
|
-
|
|
861
|
-
|
|
769
|
+
class RemoteConfig {
|
|
770
|
+
constructor(
|
|
862
771
|
// Required by FirebaseServiceFactory interface.
|
|
863
772
|
app,
|
|
864
773
|
// JS doesn't support private yet
|
|
@@ -896,22 +805,13 @@ var RemoteConfig = /** @class */ (function () {
|
|
|
896
805
|
};
|
|
897
806
|
this.defaultConfig = {};
|
|
898
807
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
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
|
-
}());
|
|
808
|
+
get fetchTimeMillis() {
|
|
809
|
+
return this._storageCache.getLastSuccessfulFetchTimestampMillis() || -1;
|
|
810
|
+
}
|
|
811
|
+
get lastFetchStatus() {
|
|
812
|
+
return this._storageCache.getLastFetchStatus() || 'no-fetch-yet';
|
|
813
|
+
}
|
|
814
|
+
}
|
|
915
815
|
|
|
916
816
|
/**
|
|
917
817
|
* @license
|
|
@@ -933,7 +833,7 @@ var RemoteConfig = /** @class */ (function () {
|
|
|
933
833
|
* Converts an error event associated with a {@link IDBRequest} to a {@link FirebaseError}.
|
|
934
834
|
*/
|
|
935
835
|
function toFirebaseError(event, errorCode) {
|
|
936
|
-
|
|
836
|
+
const originalError = event.target.error || undefined;
|
|
937
837
|
return ERROR_FACTORY.create(errorCode, {
|
|
938
838
|
originalErrorMessage: originalError && (originalError === null || originalError === void 0 ? void 0 : originalError.message)
|
|
939
839
|
});
|
|
@@ -948,22 +848,22 @@ function toFirebaseError(event, errorCode) {
|
|
|
948
848
|
*
|
|
949
849
|
* <p>Visible for testing.
|
|
950
850
|
*/
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
851
|
+
const APP_NAMESPACE_STORE = 'app_namespace_store';
|
|
852
|
+
const DB_NAME = 'firebase_remote_config';
|
|
853
|
+
const DB_VERSION = 1;
|
|
954
854
|
// Visible for testing.
|
|
955
855
|
function openDatabase() {
|
|
956
|
-
return new Promise(
|
|
856
|
+
return new Promise((resolve, reject) => {
|
|
957
857
|
try {
|
|
958
|
-
|
|
959
|
-
request.onerror =
|
|
858
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
859
|
+
request.onerror = event => {
|
|
960
860
|
reject(toFirebaseError(event, "storage-open" /* ErrorCode.STORAGE_OPEN */));
|
|
961
861
|
};
|
|
962
|
-
request.onsuccess =
|
|
862
|
+
request.onsuccess = event => {
|
|
963
863
|
resolve(event.target.result);
|
|
964
864
|
};
|
|
965
|
-
request.onupgradeneeded =
|
|
966
|
-
|
|
865
|
+
request.onupgradeneeded = event => {
|
|
866
|
+
const db = event.target.result;
|
|
967
867
|
// We don't use 'break' in this switch statement, the fall-through
|
|
968
868
|
// behavior is what we want, because if there are multiple versions between
|
|
969
869
|
// the old version and the current version, we want ALL the migrations
|
|
@@ -987,171 +887,139 @@ function openDatabase() {
|
|
|
987
887
|
/**
|
|
988
888
|
* Abstracts data persistence.
|
|
989
889
|
*/
|
|
990
|
-
|
|
890
|
+
class Storage {
|
|
991
891
|
/**
|
|
992
892
|
* @param appId enables storage segmentation by app (ID + name).
|
|
993
893
|
* @param appName enables storage segmentation by app (ID + name).
|
|
994
894
|
* @param namespace enables storage segmentation by namespace.
|
|
995
895
|
*/
|
|
996
|
-
|
|
997
|
-
if (openDbPromise === void 0) { openDbPromise = openDatabase(); }
|
|
896
|
+
constructor(appId, appName, namespace, openDbPromise = openDatabase()) {
|
|
998
897
|
this.appId = appId;
|
|
999
898
|
this.appName = appName;
|
|
1000
899
|
this.namespace = namespace;
|
|
1001
900
|
this.openDbPromise = openDbPromise;
|
|
1002
901
|
}
|
|
1003
|
-
|
|
902
|
+
getLastFetchStatus() {
|
|
1004
903
|
return this.get('last_fetch_status');
|
|
1005
|
-
}
|
|
1006
|
-
|
|
904
|
+
}
|
|
905
|
+
setLastFetchStatus(status) {
|
|
1007
906
|
return this.set('last_fetch_status', status);
|
|
1008
|
-
}
|
|
907
|
+
}
|
|
1009
908
|
// This is comparable to a cache entry timestamp. If we need to expire other data, we could
|
|
1010
909
|
// consider adding timestamp to all storage records and an optional max age arg to getters.
|
|
1011
|
-
|
|
910
|
+
getLastSuccessfulFetchTimestampMillis() {
|
|
1012
911
|
return this.get('last_successful_fetch_timestamp_millis');
|
|
1013
|
-
}
|
|
1014
|
-
|
|
912
|
+
}
|
|
913
|
+
setLastSuccessfulFetchTimestampMillis(timestamp) {
|
|
1015
914
|
return this.set('last_successful_fetch_timestamp_millis', timestamp);
|
|
1016
|
-
}
|
|
1017
|
-
|
|
915
|
+
}
|
|
916
|
+
getLastSuccessfulFetchResponse() {
|
|
1018
917
|
return this.get('last_successful_fetch_response');
|
|
1019
|
-
}
|
|
1020
|
-
|
|
918
|
+
}
|
|
919
|
+
setLastSuccessfulFetchResponse(response) {
|
|
1021
920
|
return this.set('last_successful_fetch_response', response);
|
|
1022
|
-
}
|
|
1023
|
-
|
|
921
|
+
}
|
|
922
|
+
getActiveConfig() {
|
|
1024
923
|
return this.get('active_config');
|
|
1025
|
-
}
|
|
1026
|
-
|
|
924
|
+
}
|
|
925
|
+
setActiveConfig(config) {
|
|
1027
926
|
return this.set('active_config', config);
|
|
1028
|
-
}
|
|
1029
|
-
|
|
927
|
+
}
|
|
928
|
+
getActiveConfigEtag() {
|
|
1030
929
|
return this.get('active_config_etag');
|
|
1031
|
-
}
|
|
1032
|
-
|
|
930
|
+
}
|
|
931
|
+
setActiveConfigEtag(etag) {
|
|
1033
932
|
return this.set('active_config_etag', etag);
|
|
1034
|
-
}
|
|
1035
|
-
|
|
933
|
+
}
|
|
934
|
+
getThrottleMetadata() {
|
|
1036
935
|
return this.get('throttle_metadata');
|
|
1037
|
-
}
|
|
1038
|
-
|
|
936
|
+
}
|
|
937
|
+
setThrottleMetadata(metadata) {
|
|
1039
938
|
return this.set('throttle_metadata', metadata);
|
|
1040
|
-
}
|
|
1041
|
-
|
|
939
|
+
}
|
|
940
|
+
deleteThrottleMetadata() {
|
|
1042
941
|
return this.delete('throttle_metadata');
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
};
|
|
1071
|
-
}
|
|
1072
|
-
catch (e) {
|
|
1073
|
-
reject(ERROR_FACTORY.create("storage-get" /* ErrorCode.STORAGE_GET */, {
|
|
1074
|
-
originalErrorMessage: e === null || e === void 0 ? void 0 : e.message
|
|
1075
|
-
}));
|
|
1076
|
-
}
|
|
1077
|
-
})];
|
|
1078
|
-
}
|
|
1079
|
-
});
|
|
942
|
+
}
|
|
943
|
+
async get(key) {
|
|
944
|
+
const db = await this.openDbPromise;
|
|
945
|
+
return new Promise((resolve, reject) => {
|
|
946
|
+
const transaction = db.transaction([APP_NAMESPACE_STORE], 'readonly');
|
|
947
|
+
const objectStore = transaction.objectStore(APP_NAMESPACE_STORE);
|
|
948
|
+
const compositeKey = this.createCompositeKey(key);
|
|
949
|
+
try {
|
|
950
|
+
const request = objectStore.get(compositeKey);
|
|
951
|
+
request.onerror = event => {
|
|
952
|
+
reject(toFirebaseError(event, "storage-get" /* ErrorCode.STORAGE_GET */));
|
|
953
|
+
};
|
|
954
|
+
request.onsuccess = event => {
|
|
955
|
+
const result = event.target.result;
|
|
956
|
+
if (result) {
|
|
957
|
+
resolve(result.value);
|
|
958
|
+
}
|
|
959
|
+
else {
|
|
960
|
+
resolve(undefined);
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
catch (e) {
|
|
965
|
+
reject(ERROR_FACTORY.create("storage-get" /* ErrorCode.STORAGE_GET */, {
|
|
966
|
+
originalErrorMessage: e === null || e === void 0 ? void 0 : e.message
|
|
967
|
+
}));
|
|
968
|
+
}
|
|
1080
969
|
});
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
};
|
|
1106
|
-
}
|
|
1107
|
-
catch (e) {
|
|
1108
|
-
reject(ERROR_FACTORY.create("storage-set" /* ErrorCode.STORAGE_SET */, {
|
|
1109
|
-
originalErrorMessage: e === null || e === void 0 ? void 0 : e.message
|
|
1110
|
-
}));
|
|
1111
|
-
}
|
|
1112
|
-
})];
|
|
1113
|
-
}
|
|
1114
|
-
});
|
|
970
|
+
}
|
|
971
|
+
async set(key, value) {
|
|
972
|
+
const db = await this.openDbPromise;
|
|
973
|
+
return new Promise((resolve, reject) => {
|
|
974
|
+
const transaction = db.transaction([APP_NAMESPACE_STORE], 'readwrite');
|
|
975
|
+
const objectStore = transaction.objectStore(APP_NAMESPACE_STORE);
|
|
976
|
+
const compositeKey = this.createCompositeKey(key);
|
|
977
|
+
try {
|
|
978
|
+
const request = objectStore.put({
|
|
979
|
+
compositeKey,
|
|
980
|
+
value
|
|
981
|
+
});
|
|
982
|
+
request.onerror = (event) => {
|
|
983
|
+
reject(toFirebaseError(event, "storage-set" /* ErrorCode.STORAGE_SET */));
|
|
984
|
+
};
|
|
985
|
+
request.onsuccess = () => {
|
|
986
|
+
resolve();
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
catch (e) {
|
|
990
|
+
reject(ERROR_FACTORY.create("storage-set" /* ErrorCode.STORAGE_SET */, {
|
|
991
|
+
originalErrorMessage: e === null || e === void 0 ? void 0 : e.message
|
|
992
|
+
}));
|
|
993
|
+
}
|
|
1115
994
|
});
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
};
|
|
1138
|
-
}
|
|
1139
|
-
catch (e) {
|
|
1140
|
-
reject(ERROR_FACTORY.create("storage-delete" /* ErrorCode.STORAGE_DELETE */, {
|
|
1141
|
-
originalErrorMessage: e === null || e === void 0 ? void 0 : e.message
|
|
1142
|
-
}));
|
|
1143
|
-
}
|
|
1144
|
-
})];
|
|
1145
|
-
}
|
|
1146
|
-
});
|
|
995
|
+
}
|
|
996
|
+
async delete(key) {
|
|
997
|
+
const db = await this.openDbPromise;
|
|
998
|
+
return new Promise((resolve, reject) => {
|
|
999
|
+
const transaction = db.transaction([APP_NAMESPACE_STORE], 'readwrite');
|
|
1000
|
+
const objectStore = transaction.objectStore(APP_NAMESPACE_STORE);
|
|
1001
|
+
const compositeKey = this.createCompositeKey(key);
|
|
1002
|
+
try {
|
|
1003
|
+
const request = objectStore.delete(compositeKey);
|
|
1004
|
+
request.onerror = (event) => {
|
|
1005
|
+
reject(toFirebaseError(event, "storage-delete" /* ErrorCode.STORAGE_DELETE */));
|
|
1006
|
+
};
|
|
1007
|
+
request.onsuccess = () => {
|
|
1008
|
+
resolve();
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
catch (e) {
|
|
1012
|
+
reject(ERROR_FACTORY.create("storage-delete" /* ErrorCode.STORAGE_DELETE */, {
|
|
1013
|
+
originalErrorMessage: e === null || e === void 0 ? void 0 : e.message
|
|
1014
|
+
}));
|
|
1015
|
+
}
|
|
1147
1016
|
});
|
|
1148
|
-
}
|
|
1017
|
+
}
|
|
1149
1018
|
// Facilitates composite key functionality (which is unsupported in IE).
|
|
1150
|
-
|
|
1019
|
+
createCompositeKey(key) {
|
|
1151
1020
|
return [this.appId, this.appName, this.namespace, key].join();
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
}());
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1155
1023
|
|
|
1156
1024
|
/**
|
|
1157
1025
|
* @license
|
|
@@ -1172,75 +1040,64 @@ var Storage = /** @class */ (function () {
|
|
|
1172
1040
|
/**
|
|
1173
1041
|
* A memory cache layer over storage to support the SDK's synchronous read requirements.
|
|
1174
1042
|
*/
|
|
1175
|
-
|
|
1176
|
-
|
|
1043
|
+
class StorageCache {
|
|
1044
|
+
constructor(storage) {
|
|
1177
1045
|
this.storage = storage;
|
|
1178
1046
|
}
|
|
1179
1047
|
/**
|
|
1180
1048
|
* Memory-only getters
|
|
1181
1049
|
*/
|
|
1182
|
-
|
|
1050
|
+
getLastFetchStatus() {
|
|
1183
1051
|
return this.lastFetchStatus;
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1052
|
+
}
|
|
1053
|
+
getLastSuccessfulFetchTimestampMillis() {
|
|
1186
1054
|
return this.lastSuccessfulFetchTimestampMillis;
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1055
|
+
}
|
|
1056
|
+
getActiveConfig() {
|
|
1189
1057
|
return this.activeConfig;
|
|
1190
|
-
}
|
|
1058
|
+
}
|
|
1191
1059
|
/**
|
|
1192
1060
|
* Read-ahead getter
|
|
1193
1061
|
*/
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
case 3:
|
|
1218
|
-
activeConfig = _a.sent();
|
|
1219
|
-
if (activeConfig) {
|
|
1220
|
-
this.activeConfig = activeConfig;
|
|
1221
|
-
}
|
|
1222
|
-
return [2 /*return*/];
|
|
1223
|
-
}
|
|
1224
|
-
});
|
|
1225
|
-
});
|
|
1226
|
-
};
|
|
1062
|
+
async loadFromStorage() {
|
|
1063
|
+
const lastFetchStatusPromise = this.storage.getLastFetchStatus();
|
|
1064
|
+
const lastSuccessfulFetchTimestampMillisPromise = this.storage.getLastSuccessfulFetchTimestampMillis();
|
|
1065
|
+
const activeConfigPromise = this.storage.getActiveConfig();
|
|
1066
|
+
// Note:
|
|
1067
|
+
// 1. we consistently check for undefined to avoid clobbering defined values
|
|
1068
|
+
// in memory
|
|
1069
|
+
// 2. we defer awaiting to improve readability, as opposed to destructuring
|
|
1070
|
+
// a Promise.all result, for example
|
|
1071
|
+
const lastFetchStatus = await lastFetchStatusPromise;
|
|
1072
|
+
if (lastFetchStatus) {
|
|
1073
|
+
this.lastFetchStatus = lastFetchStatus;
|
|
1074
|
+
}
|
|
1075
|
+
const lastSuccessfulFetchTimestampMillis = await lastSuccessfulFetchTimestampMillisPromise;
|
|
1076
|
+
if (lastSuccessfulFetchTimestampMillis) {
|
|
1077
|
+
this.lastSuccessfulFetchTimestampMillis =
|
|
1078
|
+
lastSuccessfulFetchTimestampMillis;
|
|
1079
|
+
}
|
|
1080
|
+
const activeConfig = await activeConfigPromise;
|
|
1081
|
+
if (activeConfig) {
|
|
1082
|
+
this.activeConfig = activeConfig;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1227
1085
|
/**
|
|
1228
1086
|
* Write-through setters
|
|
1229
1087
|
*/
|
|
1230
|
-
|
|
1088
|
+
setLastFetchStatus(status) {
|
|
1231
1089
|
this.lastFetchStatus = status;
|
|
1232
1090
|
return this.storage.setLastFetchStatus(status);
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1091
|
+
}
|
|
1092
|
+
setLastSuccessfulFetchTimestampMillis(timestampMillis) {
|
|
1235
1093
|
this.lastSuccessfulFetchTimestampMillis = timestampMillis;
|
|
1236
1094
|
return this.storage.setLastSuccessfulFetchTimestampMillis(timestampMillis);
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1095
|
+
}
|
|
1096
|
+
setActiveConfig(activeConfig) {
|
|
1239
1097
|
this.activeConfig = activeConfig;
|
|
1240
1098
|
return this.storage.setActiveConfig(activeConfig);
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
}());
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1244
1101
|
|
|
1245
1102
|
/**
|
|
1246
1103
|
* @license
|
|
@@ -1261,15 +1118,14 @@ var StorageCache = /** @class */ (function () {
|
|
|
1261
1118
|
function registerRemoteConfig() {
|
|
1262
1119
|
app._registerComponent(new component.Component(RC_COMPONENT_NAME, remoteConfigFactory, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
|
|
1263
1120
|
app.registerVersion(name, version);
|
|
1264
|
-
// BUILD_TARGET will be replaced by values like
|
|
1265
|
-
app.registerVersion(name, version, '
|
|
1266
|
-
function remoteConfigFactory(container,
|
|
1267
|
-
var namespace = _a.instanceIdentifier;
|
|
1121
|
+
// BUILD_TARGET will be replaced by values like esm2017, cjs2017, etc during the compilation
|
|
1122
|
+
app.registerVersion(name, version, 'cjs2017');
|
|
1123
|
+
function remoteConfigFactory(container, { instanceIdentifier: namespace }) {
|
|
1268
1124
|
/* Dependencies */
|
|
1269
1125
|
// getImmediate for FirebaseApp will always succeed
|
|
1270
|
-
|
|
1126
|
+
const app$1 = container.getProvider('app').getImmediate();
|
|
1271
1127
|
// The following call will always succeed because rc has `import '@firebase/installations'`
|
|
1272
|
-
|
|
1128
|
+
const installations = container
|
|
1273
1129
|
.getProvider('installations-internal')
|
|
1274
1130
|
.getImmediate();
|
|
1275
1131
|
// Guards against the SDK being used in non-browser environments.
|
|
@@ -1281,7 +1137,7 @@ function registerRemoteConfig() {
|
|
|
1281
1137
|
throw ERROR_FACTORY.create("indexed-db-unavailable" /* ErrorCode.INDEXED_DB_UNAVAILABLE */);
|
|
1282
1138
|
}
|
|
1283
1139
|
// Normalizes optional inputs.
|
|
1284
|
-
|
|
1140
|
+
const { projectId, apiKey, appId } = app$1.options;
|
|
1285
1141
|
if (!projectId) {
|
|
1286
1142
|
throw ERROR_FACTORY.create("registration-project-id" /* ErrorCode.REGISTRATION_PROJECT_ID */);
|
|
1287
1143
|
}
|
|
@@ -1292,18 +1148,18 @@ function registerRemoteConfig() {
|
|
|
1292
1148
|
throw ERROR_FACTORY.create("registration-app-id" /* ErrorCode.REGISTRATION_APP_ID */);
|
|
1293
1149
|
}
|
|
1294
1150
|
namespace = namespace || 'firebase';
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1151
|
+
const storage = new Storage(appId, app$1.name, namespace);
|
|
1152
|
+
const storageCache = new StorageCache(storage);
|
|
1153
|
+
const logger$1 = new logger.Logger(name);
|
|
1298
1154
|
// Sets ERROR as the default log level.
|
|
1299
1155
|
// See RemoteConfig#setLogLevel for corresponding normalization to ERROR log level.
|
|
1300
1156
|
logger$1.logLevel = logger.LogLevel.ERROR;
|
|
1301
|
-
|
|
1157
|
+
const restClient = new RestClient(installations,
|
|
1302
1158
|
// Uses the JS SDK version, by which the RC package version can be deduced, if necessary.
|
|
1303
1159
|
app.SDK_VERSION, namespace, projectId, apiKey, appId);
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1160
|
+
const retryingClient = new RetryingClient(restClient, storage);
|
|
1161
|
+
const cachingClient = new CachingClient(retryingClient, storage, storageCache, logger$1);
|
|
1162
|
+
const remoteConfigInstance = new RemoteConfig(app$1, cachingClient, storageCache, storage, logger$1);
|
|
1307
1163
|
// Starts warming cache.
|
|
1308
1164
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1309
1165
|
ensureInitialized(remoteConfigInstance);
|
|
@@ -1340,19 +1196,10 @@ function registerRemoteConfig() {
|
|
|
1340
1196
|
*
|
|
1341
1197
|
* @public
|
|
1342
1198
|
*/
|
|
1343
|
-
function fetchAndActivate(remoteConfig) {
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
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
|
-
});
|
|
1199
|
+
async function fetchAndActivate(remoteConfig) {
|
|
1200
|
+
remoteConfig = util.getModularInstance(remoteConfig);
|
|
1201
|
+
await fetchConfig(remoteConfig);
|
|
1202
|
+
return activate(remoteConfig);
|
|
1356
1203
|
}
|
|
1357
1204
|
/**
|
|
1358
1205
|
* This method provides two different checks:
|
|
@@ -1364,29 +1211,17 @@ function fetchAndActivate(remoteConfig) {
|
|
|
1364
1211
|
* can be initialized in this environment, or false if it cannot.
|
|
1365
1212
|
* @public
|
|
1366
1213
|
*/
|
|
1367
|
-
function isSupported() {
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
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
|
-
});
|
|
1214
|
+
async function isSupported() {
|
|
1215
|
+
if (!util.isIndexedDBAvailable()) {
|
|
1216
|
+
return false;
|
|
1217
|
+
}
|
|
1218
|
+
try {
|
|
1219
|
+
const isDBOpenable = await util.validateIndexedDBOpenable();
|
|
1220
|
+
return isDBOpenable;
|
|
1221
|
+
}
|
|
1222
|
+
catch (error) {
|
|
1223
|
+
return false;
|
|
1224
|
+
}
|
|
1390
1225
|
}
|
|
1391
1226
|
|
|
1392
1227
|
/**
|