Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/lemon-baboons-lick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@firebase/remote-config': minor
'firebase': minor
'@firebase/remote-config-types': minor
---

Added support for Realtime Remote Config for the web. This feature introduces a new `onConfigUpdate` API and allows web applications to receive near-instant configuration updates without requiring periodic polling.
23 changes: 23 additions & 0 deletions common/api-review/remote-config.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,23 @@
```ts

import { FirebaseApp } from '@firebase/app';
import { FirebaseError } from '@firebase/app';

// @public
export function activate(remoteConfig: RemoteConfig): Promise<boolean>;

// @public
export interface ConfigUpdate {
getUpdatedKeys(): Set<string>;
}

// @public
export interface ConfigUpdateObserver {
complete: () => void;
error: (error: FirebaseError) => void;
next: (configUpdate: ConfigUpdate) => void;
}

// @public
export interface CustomSignals {
// (undocumented)
Expand All @@ -29,11 +42,15 @@ export interface FetchResponse {
config?: FirebaseRemoteConfigObject;
eTag?: string;
status: number;
templateVersion?: number;
}

// @public
export type FetchStatus = 'no-fetch-yet' | 'success' | 'failure' | 'throttle';

// @public
export type FetchType = 'BASE' | 'REALTIME';

// @public
export interface FirebaseRemoteConfigObject {
// (undocumented)
Expand Down Expand Up @@ -64,6 +81,9 @@ export function isSupported(): Promise<boolean>;
// @public
export type LogLevel = 'debug' | 'error' | 'silent';

// @public
export function onConfigUpdate(remoteConfig: RemoteConfig, observer: ConfigUpdateObserver): Unsubscribe;

// @public
export interface RemoteConfig {
app: FirebaseApp;
Expand Down Expand Up @@ -93,6 +113,9 @@ export function setCustomSignals(remoteConfig: RemoteConfig, customSignals: Cust
// @public
export function setLogLevel(remoteConfig: RemoteConfig, logLevel: LogLevel): void;

// @public
export type Unsubscribe = () => void;

// @public
export interface Value {
asBoolean(): boolean;
Expand Down
4 changes: 4 additions & 0 deletions docs-devsite/_toc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,10 @@ toc:
- title: remote-config
path: /docs/reference/js/remote-config.md
section:
- title: ConfigUpdate
path: /docs/reference/js/remote-config.configupdate.md
- title: ConfigUpdateObserver
path: /docs/reference/js/remote-config.configupdateobserver.md
- title: CustomSignals
path: /docs/reference/js/remote-config.customsignals.md
- title: FetchResponse
Expand Down
39 changes: 39 additions & 0 deletions docs-devsite/remote-config.configupdate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Project: /docs/reference/js/_project.yaml
Book: /docs/reference/_book.yaml
page_type: reference

{% comment %}
DO NOT EDIT THIS FILE!
This is generated by the JS SDK team, and any local changes will be
overwritten. Changes should be made in the source code at
https://github.com/firebase/firebase-js-sdk
{% endcomment %}

# ConfigUpdate interface
Contains information about which keys have been updated.

<b>Signature:</b>

```typescript
export interface ConfigUpdate
```

## Methods

| Method | Description |
| --- | --- |
| [getUpdatedKeys()](./remote-config.configupdate.md#configupdategetupdatedkeys) | Parameter keys whose values have been updated from the currently activated values. Includes keys that are added, deleted, or whose value, value source, or metadata has changed. |

## ConfigUpdate.getUpdatedKeys()

Parameter keys whose values have been updated from the currently activated values. Includes keys that are added, deleted, or whose value, value source, or metadata has changed.

<b>Signature:</b>

```typescript
getUpdatedKeys(): Set<string>;
```
<b>Returns:</b>

Set&lt;string&gt;

59 changes: 59 additions & 0 deletions docs-devsite/remote-config.configupdateobserver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
Project: /docs/reference/js/_project.yaml
Book: /docs/reference/_book.yaml
page_type: reference

{% comment %}
DO NOT EDIT THIS FILE!
This is generated by the JS SDK team, and any local changes will be
overwritten. Changes should be made in the source code at
https://github.com/firebase/firebase-js-sdk
{% endcomment %}

# ConfigUpdateObserver interface
Observer interface for receiving real-time Remote Config update notifications.

NOTE: Although an `complete` callback can be provided, it will never be called because the ConfigUpdate stream is never-ending.

<b>Signature:</b>

```typescript
export interface ConfigUpdateObserver
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [complete](./remote-config.configupdateobserver.md#configupdateobservercomplete) | () =&gt; void | Called when the stream is gracefully terminated. |
| [error](./remote-config.configupdateobserver.md#configupdateobservererror) | (error: [FirebaseError](./util.firebaseerror.md#firebaseerror_class)<!-- -->) =&gt; void | Called if an error occurs during the stream. |
| [next](./remote-config.configupdateobserver.md#configupdateobservernext) | (configUpdate: [ConfigUpdate](./remote-config.configupdate.md#configupdate_interface)<!-- -->) =&gt; void | Called when a new ConfigUpdate is available. |

## ConfigUpdateObserver.complete

Called when the stream is gracefully terminated.

<b>Signature:</b>

```typescript
complete: () => void;
```

## ConfigUpdateObserver.error

Called if an error occurs during the stream.

<b>Signature:</b>

```typescript
error: (error: FirebaseError) => void;
```

## ConfigUpdateObserver.next

Called when a new ConfigUpdate is available.

<b>Signature:</b>

```typescript
next: (configUpdate: ConfigUpdate) => void;
```
11 changes: 11 additions & 0 deletions docs-devsite/remote-config.fetchresponse.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface FetchResponse
| [config](./remote-config.fetchresponse.md#fetchresponseconfig) | [FirebaseRemoteConfigObject](./remote-config.firebaseremoteconfigobject.md#firebaseremoteconfigobject_interface) | Defines the map of parameters returned as "entries" in the fetch response body.<p>Only defined for 200 responses. |
| [eTag](./remote-config.fetchresponse.md#fetchresponseetag) | string | Defines the ETag response header value.<p>Only defined for 200 and 304 responses. |
| [status](./remote-config.fetchresponse.md#fetchresponsestatus) | number | The HTTP status, which is useful for differentiating success responses with data from those without.<p>The Remote Config client is modeled after the native <code>Fetch</code> interface, so HTTP status is first-class.<p>Disambiguation: the fetch response returns a legacy "state" value that is redundant with the HTTP status code. The former is normalized into the latter. |
| [templateVersion](./remote-config.fetchresponse.md#fetchresponsetemplateversion) | number | The version number of the config template fetched from the server. |

## FetchResponse.config

Expand Down Expand Up @@ -65,3 +66,13 @@ The HTTP status, which is useful for differentiating success responses with data
```typescript
status: number;
```

## FetchResponse.templateVersion

The version number of the config template fetched from the server.

<b>Signature:</b>

```typescript
templateVersion?: number;
```
52 changes: 52 additions & 0 deletions docs-devsite/remote-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ The Firebase Remote Config Web SDK. This SDK does not work in a Node.js environm
| [getNumber(remoteConfig, key)](./remote-config.md#getnumber_476c09f) | Gets the value for the given key as a number.<!-- -->Convenience method for calling <code>remoteConfig.getValue(key).asNumber()</code>. |
| [getString(remoteConfig, key)](./remote-config.md#getstring_476c09f) | Gets the value for the given key as a string. Convenience method for calling <code>remoteConfig.getValue(key).asString()</code>. |
| [getValue(remoteConfig, key)](./remote-config.md#getvalue_476c09f) | Gets the [Value](./remote-config.value.md#value_interface) for the given key. |
| [onConfigUpdate(remoteConfig, observer)](./remote-config.md#onconfigupdate_8b13b26) | Starts listening for real-time config updates from the Remote Config backend and automatically fetches updates from the RC backend when they are available.<p>If a connection to the Remote Config backend is not already open, calling this method will open it. Multiple listeners can be added by calling this method again, but subsequent calls re-use the same connection to the backend. |
| [setCustomSignals(remoteConfig, customSignals)](./remote-config.md#setcustomsignals_aeeb95e) | Sets the custom signals for the app instance. |
| [setLogLevel(remoteConfig, logLevel)](./remote-config.md#setloglevel_039a45b) | Defines the log level to use. |
| <b>function()</b> |
Expand All @@ -37,6 +38,8 @@ The Firebase Remote Config Web SDK. This SDK does not work in a Node.js environm

| Interface | Description |
| --- | --- |
| [ConfigUpdate](./remote-config.configupdate.md#configupdate_interface) | Contains information about which keys have been updated. |
| [ConfigUpdateObserver](./remote-config.configupdateobserver.md#configupdateobserver_interface) | Observer interface for receiving real-time Remote Config update notifications.<!-- -->NOTE: Although an <code>complete</code> callback can be provided, it will never be called because the ConfigUpdate stream is never-ending. |
| [CustomSignals](./remote-config.customsignals.md#customsignals_interface) | Defines the type for representing custom signals and their values.<p>The values in CustomSignals must be one of the following types:<ul> <li><code>string</code> <li><code>number</code> <li><code>null</code> </ul> |
| [FetchResponse](./remote-config.fetchresponse.md#fetchresponse_interface) | Defines a successful response (200 or 304).<p>Modeled after the native <code>Response</code> interface, but simplified for Remote Config's use case. |
| [FirebaseRemoteConfigObject](./remote-config.firebaseremoteconfigobject.md#firebaseremoteconfigobject_interface) | Defines a self-descriptive reference for config key-value pairs. |
Expand All @@ -50,7 +53,9 @@ The Firebase Remote Config Web SDK. This SDK does not work in a Node.js environm
| Type Alias | Description |
| --- | --- |
| [FetchStatus](./remote-config.md#fetchstatus) | Summarizes the outcome of the last attempt to fetch config from the Firebase Remote Config server.<ul> <li>"no-fetch-yet" indicates the [RemoteConfig](./remote-config.remoteconfig.md#remoteconfig_interface) instance has not yet attempted to fetch config, or that SDK initialization is incomplete.</li> <li>"success" indicates the last attempt succeeded.</li> <li>"failure" indicates the last attempt failed.</li> <li>"throttle" indicates the last attempt was rate-limited.</li> </ul> |
| [FetchType](./remote-config.md#fetchtype) | Indicates the type of fetch request.<ul> <li>"BASE" indicates a standard fetch request.</li> <li>"REALTIME" indicates a fetch request triggered by a real-time update.</li> </ul> |
| [LogLevel](./remote-config.md#loglevel) | Defines levels of Remote Config logging. |
| [Unsubscribe](./remote-config.md#unsubscribe) | A function that unsubscribes from a real-time event stream. |
| [ValueSource](./remote-config.md#valuesource) | Indicates the source of a value.<ul> <li>"static" indicates the value was defined by a static constant.</li> <li>"default" indicates the value was defined by default config.</li> <li>"remote" indicates the value was defined by fetched config.</li> </ul> |

## function(app, ...)
Expand Down Expand Up @@ -282,6 +287,31 @@ export declare function getValue(remoteConfig: RemoteConfig, key: string): Value

The value for the given key.

### onConfigUpdate(remoteConfig, observer) {:#onconfigupdate_8b13b26}

Starts listening for real-time config updates from the Remote Config backend and automatically fetches updates from the RC backend when they are available.

<p>If a connection to the Remote Config backend is not already open, calling this method will open it. Multiple listeners can be added by calling this method again, but subsequent calls re-use the same connection to the backend.

<b>Signature:</b>

```typescript
export declare function onConfigUpdate(remoteConfig: RemoteConfig, observer: ConfigUpdateObserver): Unsubscribe;
```

#### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| remoteConfig | [RemoteConfig](./remote-config.remoteconfig.md#remoteconfig_interface) | The [RemoteConfig](./remote-config.remoteconfig.md#remoteconfig_interface) instance. |
| observer | [ConfigUpdateObserver](./remote-config.configupdateobserver.md#configupdateobserver_interface) | The [ConfigUpdateObserver](./remote-config.configupdateobserver.md#configupdateobserver_interface) to be notified of config updates. |

<b>Returns:</b>

[Unsubscribe](./remote-config.md#unsubscribe)

An [Unsubscribe](./remote-config.md#unsubscribe) function to remove the listener.

### setCustomSignals(remoteConfig, customSignals) {:#setcustomsignals_aeeb95e}

Sets the custom signals for the app instance.
Expand Down Expand Up @@ -355,6 +385,18 @@ Summarizes the outcome of the last attempt to fetch config from the Firebase Rem
export type FetchStatus = 'no-fetch-yet' | 'success' | 'failure' | 'throttle';
```

## FetchType

Indicates the type of fetch request.

<ul> <li>"BASE" indicates a standard fetch request.</li> <li>"REALTIME" indicates a fetch request triggered by a real-time update.</li> </ul>

<b>Signature:</b>

```typescript
export type FetchType = 'BASE' | 'REALTIME';
```

## LogLevel

Defines levels of Remote Config logging.
Expand All @@ -365,6 +407,16 @@ Defines levels of Remote Config logging.
export type LogLevel = 'debug' | 'error' | 'silent';
```

## Unsubscribe

A function that unsubscribes from a real-time event stream.

<b>Signature:</b>

```typescript
export type Unsubscribe = () => void;
```

## ValueSource

Indicates the source of a value.
Expand Down
39 changes: 37 additions & 2 deletions packages/remote-config/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import {
LogLevel as RemoteConfigLogLevel,
RemoteConfig,
Value,
RemoteConfigOptions
RemoteConfigOptions,
ConfigUpdateObserver,
Unsubscribe
} from './public_types';
import { RemoteConfigAbortSignal } from './client/remote_config_fetch_client';
import {
Expand Down Expand Up @@ -66,6 +68,9 @@ export function getRemoteConfig(
rc._initializePromise = Promise.all([
rc._storage.setLastSuccessfulFetchResponse(options.initialFetchResponse),
rc._storage.setActiveConfigEtag(options.initialFetchResponse?.eTag || ''),
rc._storage.setActiveConfigTemplateVersion(
options.initialFetchResponse.templateVersion || 0
),
rc._storageCache.setLastSuccessfulFetchTimestampMillis(Date.now()),
rc._storageCache.setLastFetchStatus('success'),
rc._storageCache.setActiveConfig(
Expand Down Expand Up @@ -98,6 +103,7 @@ export async function activate(remoteConfig: RemoteConfig): Promise<boolean> {
!lastSuccessfulFetchResponse ||
!lastSuccessfulFetchResponse.config ||
!lastSuccessfulFetchResponse.eTag ||
!lastSuccessfulFetchResponse.templateVersion ||
lastSuccessfulFetchResponse.eTag === activeConfigEtag
) {
// Either there is no successful fetched config, or is the same as current active
Expand All @@ -106,7 +112,10 @@ export async function activate(remoteConfig: RemoteConfig): Promise<boolean> {
}
await Promise.all([
rc._storageCache.setActiveConfig(lastSuccessfulFetchResponse.config),
rc._storage.setActiveConfigEtag(lastSuccessfulFetchResponse.eTag)
rc._storage.setActiveConfigEtag(lastSuccessfulFetchResponse.eTag),
rc._storage.setActiveConfigTemplateVersion(
lastSuccessfulFetchResponse.templateVersion
)
]);
return true;
}
Expand Down Expand Up @@ -351,3 +360,29 @@ export async function setCustomSignals(
);
}
}

// TODO: Add public document for the Remote Config Realtime API guide on the Web Platform.
/**
* Starts listening for real-time config updates from the Remote Config backend and automatically
* fetches updates from the RC backend when they are available.
*
* <p>If a connection to the Remote Config backend is not already open, calling this method will
* open it. Multiple listeners can be added by calling this method again, but subsequent calls
* re-use the same connection to the backend.
*
* @param remoteConfig - The {@link RemoteConfig} instance.
* @param observer - The {@link ConfigUpdateObserver} to be notified of config updates.
* @returns An {@link Unsubscribe} function to remove the listener.
*
* @public
*/
export function onConfigUpdate(
remoteConfig: RemoteConfig,
observer: ConfigUpdateObserver
): Unsubscribe {
const rc = getModularInstance(remoteConfig) as RemoteConfigImpl;
rc._realtimeHandler.addObserver(observer);
return () => {
rc._realtimeHandler.removeObserver(observer);
};
}
Loading
Loading