@react-native-firebase/remote-config 23.4.1 → 23.5.0

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/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [23.5.0](https://github.com/invertase/react-native-firebase/compare/v23.4.1...v23.5.0) (2025-10-30)
7
+
8
+ ### Features
9
+
10
+ - **remote-config, other:** official onConfigUpdate API, w/Other platform support ([0fcc983](https://github.com/invertase/react-native-firebase/commit/0fcc983f3531bde799020e16a23e1ecb551c0896))
11
+
6
12
  ## [23.4.1](https://github.com/invertase/react-native-firebase/compare/v23.4.0...v23.4.1) (2025-10-14)
7
13
 
8
14
  ### Bug Fixes
package/lib/index.d.ts CHANGED
@@ -291,6 +291,45 @@ export namespace FirebaseRemoteConfigTypes {
291
291
  */
292
292
  type LastFetchStatusType = 'success' | 'failure' | 'no_fetch_yet' | 'throttled';
293
293
 
294
+ /**
295
+ * Contains information about which keys have been updated.
296
+ */
297
+ export interface ConfigUpdate {
298
+ /**
299
+ * Parameter keys whose values have been updated from the currently activated values.
300
+ * Includes keys that are added, deleted, or whose value, value source, or metadata has changed.
301
+ */
302
+ getUpdatedKeys(): Set<string>;
303
+ }
304
+
305
+ /**
306
+ * Observer interface for receiving real-time Remote Config update notifications.
307
+ *
308
+ * NOTE: Although an `complete` callback can be provided, it will
309
+ * never be called because the ConfigUpdate stream is never-ending.
310
+ */
311
+ export interface ConfigUpdateObserver {
312
+ /**
313
+ * Called when a new ConfigUpdate is available.
314
+ */
315
+ next: (configUpdate: ConfigUpdate) => void;
316
+
317
+ /**
318
+ * Called if an error occurs during the stream.
319
+ */
320
+ error: (error: FirebaseError) => void;
321
+
322
+ /**
323
+ * Called when the stream is gracefully terminated.
324
+ */
325
+ complete: () => void;
326
+ }
327
+
328
+ /**
329
+ * A function that unsubscribes from a real-time event stream.
330
+ */
331
+ export type Unsubscribe = () => void;
332
+
294
333
  /**
295
334
  * The Firebase Remote RemoteConfig service interface.
296
335
  *
@@ -376,14 +415,33 @@ export namespace FirebaseRemoteConfigTypes {
376
415
  */
377
416
  setDefaultsFromResource(resourceName: string): Promise<null>;
378
417
 
418
+ /**
419
+ * Starts listening for real-time config updates from the Remote Config backend and automatically
420
+ * fetches updates from the Remote Config backend when they are available.
421
+ *
422
+ * @remarks
423
+ * If a connection to the Remote Config backend is not already open, calling this method will
424
+ * open it. Multiple listeners can be added by calling this method again, but subsequent calls
425
+ * re-use the same connection to the backend.
426
+ *
427
+ * The list of updated keys passed to the callback will include all keys not currently active,
428
+ * and the config update process fetches the new config but does not automatically activate
429
+ * it for you. Typically you will activate the config in your callback to use the new values.
430
+ *
431
+ * @param remoteConfig - The {@link RemoteConfig} instance.
432
+ * @param observer - The {@link ConfigUpdateObserver} to be notified of config updates.
433
+ * @returns An {@link Unsubscribe} function to remove the listener.
434
+ */
435
+ onConfigUpdate(remoteConfig: RemoteConfig, observer: ConfigUpdateObserver): Unsubscribe;
436
+
379
437
  /**
380
438
  * Start listening for real-time config updates from the Remote Config backend and
381
439
  * automatically fetch updates when they’re available. Note that the list of updated keys
382
440
  * passed to the callback will include all keys not currently active, and the config update
383
441
  * process fetches the new config but does not automatically activate for you. Typically
384
442
  * you will want to activate the config in your callback so the new values are in force.
385
- *
386
443
  * @param listener called with either array of updated keys or error arg when config changes
444
+ * @deprecated use official firebase-js-sdk onConfigUpdate now that web supports realtime
387
445
  */
388
446
  onConfigUpdated(listener: CallbackOrObserver<OnConfigUpdatedListenerCallback>): () => void;
389
447
 
@@ -541,8 +599,10 @@ export namespace FirebaseRemoteConfigTypes {
541
599
  reset(): Promise<void>;
542
600
  }
543
601
 
602
+ // deprecated: from pre-Web realtime remote-config support - remove with onConfigUpdated
544
603
  export type CallbackOrObserver<T extends (...args: any[]) => any> = T | { next: T };
545
604
 
605
+ // deprecated: from pre-Web realtime remote-config support - remove with onConfigUpdated
546
606
  export type OnConfigUpdatedListenerCallback = (
547
607
  event?: { updatedKeys: string[] },
548
608
  error?: {
package/lib/index.js CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  isString,
23
23
  isUndefined,
24
24
  isIOS,
25
+ isFunction,
25
26
  parseListenerOrObserver,
26
27
  } from '@react-native-firebase/app/lib/common';
27
28
  import Value from './RemoteConfigValue';
@@ -241,11 +242,69 @@ class FirebaseConfigModule extends FirebaseModule {
241
242
  return this._promiseWithConstants(this.native.setDefaultsFromResource(resourceName));
242
243
  }
243
244
 
245
+ /**
246
+ * Registers an observer to changes in the configuration.
247
+ *
248
+ * @param observer - The {@link ConfigUpdateObserver} to be notified of config updates.
249
+ * @returns An {@link Unsubscribe} function to remove the listener.
250
+ */
251
+ onConfigUpdate(observer) {
252
+ if (!isObject(observer) || !isFunction(observer.next) || !isFunction(observer.error)) {
253
+ throw new Error("'observer' expected an object with 'next' and 'error' functions.");
254
+ }
255
+
256
+ // We maintaine our pre-web-support native interface but bend it to match
257
+ // the official JS SDK API by assuming the callback is an Observer, and sending it a ConfigUpdate
258
+ // compatible parameter that implements the `getUpdatedKeys` method
259
+ let unsubscribed = false;
260
+ const subscription = this.emitter.addListener(
261
+ this.eventNameForApp('on_config_updated'),
262
+ event => {
263
+ const { resultType } = event;
264
+ if (resultType === 'success') {
265
+ observer.next({
266
+ getUpdatedKeys: () => {
267
+ return new Set(event.updatedKeys);
268
+ },
269
+ });
270
+ return;
271
+ }
272
+
273
+ observer.error({
274
+ code: event.code,
275
+ message: event.message,
276
+ nativeErrorMessage: event.nativeErrorMessage,
277
+ });
278
+ },
279
+ );
280
+ if (this._configUpdateListenerCount === 0) {
281
+ this.native.onConfigUpdated();
282
+ }
283
+
284
+ this._configUpdateListenerCount++;
285
+
286
+ return () => {
287
+ if (unsubscribed) {
288
+ // there is no harm in calling this multiple times to unsubscribe,
289
+ // but anything after the first call is a no-op
290
+ return;
291
+ } else {
292
+ unsubscribed = true;
293
+ }
294
+ subscription.remove();
295
+ this._configUpdateListenerCount--;
296
+ if (this._configUpdateListenerCount === 0) {
297
+ this.native.removeConfigUpdateRegistration();
298
+ }
299
+ };
300
+ }
301
+
244
302
  /**
245
303
  * Registers a listener to changes in the configuration.
246
304
  *
247
305
  * @param listenerOrObserver - function called on config change
248
306
  * @returns {function} unsubscribe listener
307
+ * @deprecated use official firebase-js-sdk onConfigUpdate now that web supports realtime
249
308
  */
250
309
  onConfigUpdated(listenerOrObserver) {
251
310
  const listener = parseListenerOrObserver(listenerOrObserver);
@@ -27,7 +27,11 @@ import RemoteConfigLogLevel = FirebaseRemoteConfigTypes.RemoteConfigLogLevel;
27
27
  import FirebaseApp = ReactNativeFirebase.FirebaseApp;
28
28
  import LastFetchStatusInterface = FirebaseRemoteConfigTypes.LastFetchStatus;
29
29
  import ValueSourceInterface = FirebaseRemoteConfigTypes.ValueSource;
30
+ import ConfigUpdateObserver = FirebaseRemoteConfigTypes.ConfigUpdateObserver;
31
+ import Unsubscribe = FirebaseRemoteConfigTypes.Unsubscribe;
32
+ // deprecated: from pre-Web realtime remote-config support - remove with onConfigUpdated
30
33
  import CallbackOrObserver = FirebaseRemoteConfigTypes.CallbackOrObserver;
34
+ // deprecated: from pre-Web realtime remote-config support - remove with onConfigUpdated
31
35
  import OnConfigUpdatedListenerCallback = FirebaseRemoteConfigTypes.OnConfigUpdatedListenerCallback;
32
36
 
33
37
  export const LastFetchStatus: LastFetchStatusInterface;
@@ -203,12 +207,35 @@ export function setDefaultsFromResource(
203
207
  resourceName: string,
204
208
  ): Promise<null>;
205
209
 
210
+ /**
211
+ * Starts listening for real-time config updates from the Remote Config backend and automatically
212
+ * fetches updates from the Remote Config backend when they are available.
213
+ *
214
+ * @remarks
215
+ * If a connection to the Remote Config backend is not already open, calling this method will
216
+ * open it. Multiple listeners can be added by calling this method again, but subsequent calls
217
+ * re-use the same connection to the backend.
218
+ *
219
+ * The list of updated keys passed to the callback will include all keys not currently active,
220
+ * and the config update process fetches the new config but does not automatically activate
221
+ * it for you. Typically you will activate the config in your callback to use the new values.
222
+ *
223
+ * @param remoteConfig - The {@link RemoteConfig} instance.
224
+ * @param observer - The {@link ConfigUpdateObserver} to be notified of config updates.
225
+ * @returns An {@link Unsubscribe} function to remove the listener.
226
+ */
227
+ export function onConfigUpdate(
228
+ remoteConfig: RemoteConfig,
229
+ observer: ConfigUpdateObserver,
230
+ ): Unsubscribe;
231
+
206
232
  /**
207
233
  * Registers a listener to changes in the configuration.
208
234
  *
209
235
  * @param remoteConfig - RemoteConfig instance
210
236
  * @param callback - function called on config change
211
237
  * @returns {function} unsubscribe listener
238
+ * @deprecated use official firebase-js-sdk onConfigUpdate now that web supports realtime
212
239
  */
213
240
  export function onConfigUpdated(
214
241
  remoteConfig: RemoteConfig,
@@ -28,6 +28,8 @@ import { MODULAR_DEPRECATION_ARG } from '@react-native-firebase/app/lib/common';
28
28
  * @typedef {import('..').FirebaseRemoteConfigTypes.ConfigValues} ConfigValues
29
29
  * @typedef {import('..').FirebaseRemoteConfigTypes.LastFetchStatusType} LastFetchStatusType
30
30
  * @typedef {import('..').FirebaseRemoteConfigTypes.RemoteConfigLogLevel} RemoteConfigLogLevel
31
+ * @typedef {import('..').FirebaseRemoteConfigTypes.ConfigUpdateObserver} ConfigUpdateObserver
32
+ * @typedef {import('..').FirebaseRemoteConfigTypes.Unsubscribe} Unsubscribe
31
33
  * @typedef {import('.').CustomSignals} CustomSignals
32
34
  */
33
35
 
@@ -239,12 +241,25 @@ export function setDefaultsFromResource(remoteConfig, resourceName) {
239
241
  );
240
242
  }
241
243
 
244
+ /**
245
+ * Registers a listener to changes in the configuration.
246
+ *
247
+ * @param {RemoteConfig} remoteConfig - RemoteConfig instance
248
+ * @param {ConfigUpdateObserver} observer - to be notified of config updates.
249
+ * @returns {Unsubscribe} function to remove the listener.
250
+ * @deprecated use official firebase-js-sdk onConfigUpdate now that web supports realtime
251
+ */
252
+ export function onConfigUpdate(remoteConfig, observer) {
253
+ return remoteConfig.onConfigUpdate.call(remoteConfig, observer, MODULAR_DEPRECATION_ARG);
254
+ }
255
+
242
256
  /**
243
257
  * Registers a listener to changes in the configuration.
244
258
  *
245
259
  * @param {RemoteConfig} remoteConfig - RemoteConfig instance
246
260
  * @param {CallbackOrObserver<OnConfigUpdatedListenerCallback>} callback - function called on config change
247
261
  * @returns {function} unsubscribe listener
262
+ * @deprecated use official firebase-js-sdk onConfigUpdate now that web supports realtime
248
263
  */
249
264
  export function onConfigUpdated(remoteConfig, callback) {
250
265
  return remoteConfig.onConfigUpdated.call(remoteConfig, callback, MODULAR_DEPRECATION_ARG);
package/lib/polyfills.js CHANGED
@@ -1,8 +1,9 @@
1
- /*
2
- * Copyright (c) 2016-present Invertase Limited & Contributors
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
3
4
  *
4
5
  * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this library except in compliance with the License.
6
+ * you may not use this file except in compliance with the License.
6
7
  * You may obtain a copy of the License at
7
8
  *
8
9
  * http://www.apache.org/licenses/LICENSE-2.0
@@ -12,17 +13,20 @@
12
13
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
14
  * See the License for the specific language governing permissions and
14
15
  * limitations under the License.
15
- *
16
16
  */
17
-
18
17
  import { polyfillGlobal } from 'react-native/Libraries/Utilities/PolyfillFunctions';
18
+ import { ReadableStream } from 'web-streams-polyfill/dist/ponyfill';
19
+ import { fetch, Headers, Request, Response } from 'react-native-fetch-api';
19
20
 
20
- // maybe this could be remote-config local install of text-encoding (similar to ai package)
21
- import { TextEncoder, TextDecoder } from 'text-encoding';
21
+ polyfillGlobal(
22
+ 'fetch',
23
+ () =>
24
+ (...args) =>
25
+ fetch(args[0], { ...args[1], reactNative: { textStreaming: true } }),
26
+ );
27
+ polyfillGlobal('Headers', () => Headers);
28
+ polyfillGlobal('Request', () => Request);
29
+ polyfillGlobal('Response', () => Response);
30
+ polyfillGlobal('ReadableStream', () => ReadableStream);
22
31
 
23
- polyfillGlobal('TextEncoder', () => TextEncoder);
24
- polyfillGlobal('TextDecoder', () => TextDecoder);
25
- // Object.assign(global, {
26
- // TextEncoder: TextEncoder,
27
- // TextDecoder: TextDecoder,
28
- // });
32
+ import 'text-encoding';
package/lib/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- module.exports = '23.4.1';
2
+ module.exports = '23.5.0';
@@ -8,9 +8,10 @@ import {
8
8
  fetchConfig,
9
9
  getAll,
10
10
  makeIDBAvailable,
11
+ onConfigUpdate,
11
12
  setCustomSignals,
12
13
  } from '@react-native-firebase/app/lib/internal/web/firebaseRemoteConfig';
13
- import { guard, getWebError } from '@react-native-firebase/app/lib/internal/web/utils';
14
+ import { guard, getWebError, emitEvent } from '@react-native-firebase/app/lib/internal/web/utils';
14
15
 
15
16
  let configSettingsForInstance = {
16
17
  // [APP_NAME]: RemoteConfigSettings
@@ -24,6 +25,8 @@ function makeGlobalsAvailable() {
24
25
  makeIDBAvailable();
25
26
  }
26
27
 
28
+ const onConfigUpdateListeners = {};
29
+
27
30
  function getRemoteConfigInstanceForApp(appName, overrides /*: RemoteConfigSettings */) {
28
31
  makeGlobalsAvailable();
29
32
  const configSettings = configSettingsForInstance[appName] ?? {
@@ -122,10 +125,37 @@ export default {
122
125
  return resultAndConstants(remoteConfig, null);
123
126
  });
124
127
  },
125
- onConfigUpdated() {
126
- throw getWebError({
127
- code: 'unsupported',
128
- message: 'Not supported by the Firebase Javascript SDK.',
129
- });
128
+ onConfigUpdated(appName) {
129
+ if (onConfigUpdateListeners[appName]) {
130
+ return;
131
+ }
132
+
133
+ const remoteConfig = getRemoteConfigInstanceForApp(appName);
134
+
135
+ const nativeObserver = {
136
+ next: configUpdate => {
137
+ emitEvent('on_config_updated', {
138
+ appName,
139
+ resultType: 'success',
140
+ updatedKeys: Array.from(configUpdate.getUpdatedKeys()),
141
+ });
142
+ },
143
+ error: firebaseError => {
144
+ emitEvent('on_config_updated', {
145
+ appName,
146
+ event: getWebError(firebaseError),
147
+ });
148
+ },
149
+ complete: () => {},
150
+ };
151
+
152
+ onConfigUpdateListeners[appName] = onConfigUpdate(remoteConfig, nativeObserver);
153
+ },
154
+ removeConfigUpdateRegistration(appName) {
155
+ if (!onConfigUpdateListeners[appName]) {
156
+ return;
157
+ }
158
+ onConfigUpdateListeners[appName]();
159
+ delete onConfigUpdateListeners[appName];
130
160
  },
131
161
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native-firebase/remote-config",
3
- "version": "23.4.1",
3
+ "version": "23.5.0",
4
4
  "author": "Invertase <oss@invertase.io> (http://invertase.io)",
5
5
  "description": "React Native Firebase - React Native Firebase provides native integration with Remote Config, allowing you to change the appearance and/or functionality of your app without requiring an app update.",
6
6
  "main": "lib/index.js",
@@ -24,18 +24,20 @@
24
24
  "remote-config"
25
25
  ],
26
26
  "peerDependencies": {
27
- "@react-native-firebase/analytics": "23.4.1",
28
- "@react-native-firebase/app": "23.4.1"
27
+ "@react-native-firebase/analytics": "23.5.0",
28
+ "@react-native-firebase/app": "23.5.0"
29
29
  },
30
30
  "publishConfig": {
31
31
  "access": "public",
32
32
  "provenance": true
33
33
  },
34
34
  "dependencies": {
35
- "text-encoding": "^0.7.0"
35
+ "react-native-fetch-api": "^3.0.0",
36
+ "text-encoding": "^0.7.0",
37
+ "web-streams-polyfill": "^4.2.0"
36
38
  },
37
39
  "devDependencies": {
38
40
  "@types/text-encoding": "^0.0.40"
39
41
  },
40
- "gitHead": "15a0d8ddaedcaeedf9ee322ff31810f8873c400a"
42
+ "gitHead": "5d27948f349d4d6c977c55036e2afd13df8d622f"
41
43
  }