Skip to content

Commit 443ea13

Browse files
committed
added indexedDB check for remote-config
1 parent 29327b2 commit 443ea13

File tree

4 files changed

+76
-29
lines changed

4 files changed

+76
-29
lines changed

packages/firebase/index.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,6 +1772,15 @@ declare namespace firebase.remoteConfig {
17721772
* Defines levels of Remote Config logging.
17731773
*/
17741774
export type LogLevel = 'debug' | 'error' | 'silent';
1775+
/**
1776+
* An async function that returns true if current browser context supports initialization of remote config module
1777+
* (`firebase.remoteConfig()`).
1778+
*
1779+
* Returns false otherwise.
1780+
*
1781+
*
1782+
*/
1783+
function isSupported(): Promise<boolean>;
17751784
}
17761785

17771786
declare namespace firebase.functions {

packages/remote-config/index.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ import { ERROR_FACTORY, ErrorCode } from './src/errors';
2828
import { RetryingClient } from './src/client/retrying_client';
2929
import { Logger, LogLevel as FirebaseLogLevel } from '@firebase/logger';
3030
import { name as packageName, version } from './package.json';
31+
import {
32+
isIndexedDBAvailable,
33+
validateIndexedDBOpenable
34+
} from '@firebase/util';
3135
import {
3236
Component,
3337
ComponentType,
@@ -48,11 +52,11 @@ export function registerRemoteConfig(
4852
firebaseInstance: _FirebaseNamespace
4953
): void {
5054
firebaseInstance.INTERNAL.registerComponent(
51-
new Component(
52-
'remoteConfig',
53-
remoteConfigFactory,
54-
ComponentType.PUBLIC
55-
).setMultipleInstances(true)
55+
new Component('remoteConfig', remoteConfigFactory, ComponentType.PUBLIC)
56+
.setMultipleInstances(true)
57+
.setServiceProps({
58+
isSupported
59+
})
5660
);
5761

5862
firebaseInstance.registerVersion(packageName, version);
@@ -71,7 +75,10 @@ export function registerRemoteConfig(
7175
if (typeof window === 'undefined') {
7276
throw ERROR_FACTORY.create(ErrorCode.REGISTRATION_WINDOW);
7377
}
74-
78+
// Guards against the SDK being used when indexedDB is not available.
79+
if (!isIndexedDBAvailable()) {
80+
throw ERROR_FACTORY.create(ErrorCode.INDEXED_DB_UNSUPPORTED);
81+
}
7582
// Normalizes optional inputs.
7683
const { projectId, apiKey, appId } = app.options;
7784
if (!projectId) {
@@ -139,3 +146,22 @@ declare module '@firebase/app-types' {
139146
remoteConfig(): RemoteConfigType;
140147
}
141148
}
149+
/**
150+
* this is a public static method provided to users that wraps two different checks:
151+
*
152+
* 1. check if IndexedDB is supported by the browser environment.
153+
* 2. check if the current browser context is valid for using IndexedDB.
154+
*
155+
*/
156+
async function isSupported(): Promise<boolean> {
157+
if (!isIndexedDBAvailable()) {
158+
return false;
159+
}
160+
161+
try {
162+
const isDBOpenable: boolean = await validateIndexedDBOpenable();
163+
return isDBOpenable;
164+
} catch (error) {
165+
return false;
166+
}
167+
}

packages/remote-config/src/errors.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ export const enum ErrorCode {
3030
FETCH_TIMEOUT = 'fetch-timeout',
3131
FETCH_THROTTLE = 'fetch-throttle',
3232
FETCH_PARSE = 'fetch-client-parse',
33-
FETCH_STATUS = 'fetch-status'
33+
FETCH_STATUS = 'fetch-status',
34+
INDEXED_DB_UNSUPPORTED = 'indexed-db-unsupported'
3435
}
3536

3637
const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = {
@@ -64,7 +65,9 @@ const ERROR_DESCRIPTION_MAP: { readonly [key in ErrorCode]: string } = {
6465
'Fetch client could not parse response.' +
6566
' Original error: {$originalErrorMessage}.',
6667
[ErrorCode.FETCH_STATUS]:
67-
'Fetch server returned an HTTP error status. HTTP status: {$httpStatus}.'
68+
'Fetch server returned an HTTP error status. HTTP status: {$httpStatus}.',
69+
[ErrorCode.INDEXED_DB_UNSUPPORTED]:
70+
'Indexed DB is not supported by current browser'
6871
};
6972

7073
// Note this is effectively a type system binding a code to params. This approach overlaps with the

packages/remote-config/src/storage/storage.ts

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
} from '../client/remote_config_fetch_client';
2323
import { ERROR_FACTORY, ErrorCode } from '../errors';
2424
import { FirebaseError } from '@firebase/util';
25+
import { ErrorFactory } from '@firebase/util';
2526

2627
/**
2728
* Converts an error event associated with a {@link IDBRequest} to a {@link FirebaseError}.
@@ -75,28 +76,36 @@ type ProjectNamespaceKeyFieldValue =
7576
// Visible for testing.
7677
export function openDatabase(): Promise<IDBDatabase> {
7778
return new Promise((resolve, reject) => {
78-
const request = indexedDB.open(DB_NAME, DB_VERSION);
79-
request.onerror = event => {
80-
reject(toFirebaseError(event, ErrorCode.STORAGE_OPEN));
81-
};
82-
request.onsuccess = event => {
83-
resolve((event.target as IDBOpenDBRequest).result);
84-
};
85-
request.onupgradeneeded = event => {
86-
const db = (event.target as IDBOpenDBRequest).result;
79+
try {
80+
const request = indexedDB.open(DB_NAME, DB_VERSION);
81+
request.onerror = event => {
82+
reject(toFirebaseError(event, ErrorCode.STORAGE_OPEN));
83+
};
84+
request.onsuccess = event => {
85+
resolve((event.target as IDBOpenDBRequest).result);
86+
};
87+
request.onupgradeneeded = event => {
88+
const db = (event.target as IDBOpenDBRequest).result;
8789

88-
// We don't use 'break' in this switch statement, the fall-through
89-
// behavior is what we want, because if there are multiple versions between
90-
// the old version and the current version, we want ALL the migrations
91-
// that correspond to those versions to run, not only the last one.
92-
// eslint-disable-next-line default-case
93-
switch (event.oldVersion) {
94-
case 0:
95-
db.createObjectStore(APP_NAMESPACE_STORE, {
96-
keyPath: 'compositeKey'
97-
});
98-
}
99-
};
90+
// We don't use 'break' in this switch statement, the fall-through
91+
// behavior is what we want, because if there are multiple versions between
92+
// the old version and the current version, we want ALL the migrations
93+
// that correspond to those versions to run, not only the last one.
94+
// eslint-disable-next-line default-case
95+
switch (event.oldVersion) {
96+
case 0:
97+
db.createObjectStore(APP_NAMESPACE_STORE, {
98+
keyPath: 'compositeKey'
99+
});
100+
}
101+
};
102+
} catch (error) {
103+
reject(
104+
ERROR_FACTORY.create(ErrorCode.STORAGE_OPEN, {
105+
originalErrorMessage: error
106+
})
107+
);
108+
}
100109
});
101110
}
102111

0 commit comments

Comments
 (0)