Skip to content

Commit 440fabd

Browse files
authored
Merge branch 'main' into fix-remote-config-window
2 parents 8e34bd6 + c791ecf commit 440fabd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+899
-469
lines changed

.changeset/flat-plums-hope.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@firebase/remote-config': minor
3+
'firebase': minor
4+
---
5+
6+
Adds support for initial state hydration (from SSR contexts)

.changeset/forty-bags-arrive.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@firebase/database-compat': patch
3+
'@firebase/database': patch
4+
'firebase': patch
5+
---
6+
7+
Fixed: invoking `connectDatabaseEmulator` multiple times with the same parameters will no longer
8+
cause an error. Fixes [GitHub Issue #6824](https://github.com/firebase/firebase-js-sdk/issues/6824).

.changeset/lemon-candles-vanish.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@firebase/auth': patch
3+
'firebase': patch
4+
---
5+
6+
Fixed: invoking `connectAuthEmulator` multiple times with the same parameters will no longer cause
7+
an error. Fixes [GitHub Issue #6824](https://github.com/firebase/firebase-js-sdk/issues/6824).
8+

.changeset/slimy-chicken-mix.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@firebase/firestore': patch
3+
'firebase': patch
4+
---
5+
6+
Reverted a change to use UTF-8 encoding in string comparisons which caused a performance issue. See [GitHub issue #8778](https://github.com/firebase/firebase-js-sdk/issues/8778)

common/api-review/firestore-lite.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,4 +494,5 @@ export class WriteBatch {
494494
// @public
495495
export function writeBatch(firestore: Firestore): WriteBatch;
496496

497+
497498
```

common/api-review/remote-config.api.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,22 @@ export function fetchAndActivate(remoteConfig: RemoteConfig): Promise<boolean>;
2424
// @public
2525
export function fetchConfig(remoteConfig: RemoteConfig): Promise<void>;
2626

27+
// @public
28+
export interface FetchResponse {
29+
config?: FirebaseRemoteConfigObject;
30+
eTag?: string;
31+
status: number;
32+
}
33+
2734
// @public
2835
export type FetchStatus = 'no-fetch-yet' | 'success' | 'failure' | 'throttle';
2936

37+
// @public
38+
export interface FirebaseRemoteConfigObject {
39+
// (undocumented)
40+
[key: string]: string;
41+
}
42+
3043
// @public
3144
export function getAll(remoteConfig: RemoteConfig): Record<string, Value>;
3245

@@ -37,7 +50,7 @@ export function getBoolean(remoteConfig: RemoteConfig, key: string): boolean;
3750
export function getNumber(remoteConfig: RemoteConfig, key: string): number;
3851

3952
// @public (undocumented)
40-
export function getRemoteConfig(app?: FirebaseApp): RemoteConfig;
53+
export function getRemoteConfig(app?: FirebaseApp, options?: RemoteConfigOptions): RemoteConfig;
4154

4255
// @public
4356
export function getString(remoteConfig: RemoteConfig, key: string): string;
@@ -62,6 +75,12 @@ export interface RemoteConfig {
6275
settings: RemoteConfigSettings;
6376
}
6477

78+
// @public
79+
export interface RemoteConfigOptions {
80+
initialFetchResponse?: FetchResponse;
81+
templateId?: string;
82+
}
83+
6584
// @public
6685
export interface RemoteConfigSettings {
6786
fetchTimeoutMillis: number;

docs-devsite/_toc.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,8 +430,14 @@ toc:
430430
section:
431431
- title: CustomSignals
432432
path: /docs/reference/js/remote-config.customsignals.md
433+
- title: FetchResponse
434+
path: /docs/reference/js/remote-config.fetchresponse.md
435+
- title: FirebaseRemoteConfigObject
436+
path: /docs/reference/js/remote-config.firebaseremoteconfigobject.md
433437
- title: RemoteConfig
434438
path: /docs/reference/js/remote-config.remoteconfig.md
439+
- title: RemoteConfigOptions
440+
path: /docs/reference/js/remote-config.remoteconfigoptions.md
435441
- title: RemoteConfigSettings
436442
path: /docs/reference/js/remote-config.remoteconfigsettings.md
437443
- title: Value
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
Project: /docs/reference/js/_project.yaml
2+
Book: /docs/reference/_book.yaml
3+
page_type: reference
4+
5+
{% comment %}
6+
DO NOT EDIT THIS FILE!
7+
This is generated by the JS SDK team, and any local changes will be
8+
overwritten. Changes should be made in the source code at
9+
https://github.com/firebase/firebase-js-sdk
10+
{% endcomment %}
11+
12+
# FetchResponse interface
13+
Defines a successful response (200 or 304).
14+
15+
<p>Modeled after the native `Response` interface, but simplified for Remote Config's use case.
16+
17+
<b>Signature:</b>
18+
19+
```typescript
20+
export interface FetchResponse
21+
```
22+
23+
## Properties
24+
25+
| Property | Type | Description |
26+
| --- | --- | --- |
27+
| [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. |
28+
| [eTag](./remote-config.fetchresponse.md#fetchresponseetag) | string | Defines the ETag response header value.<p>Only defined for 200 and 304 responses. |
29+
| [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. |
30+
31+
## FetchResponse.config
32+
33+
Defines the map of parameters returned as "entries" in the fetch response body.
34+
35+
<p>Only defined for 200 responses.
36+
37+
<b>Signature:</b>
38+
39+
```typescript
40+
config?: FirebaseRemoteConfigObject;
41+
```
42+
43+
## FetchResponse.eTag
44+
45+
Defines the ETag response header value.
46+
47+
<p>Only defined for 200 and 304 responses.
48+
49+
<b>Signature:</b>
50+
51+
```typescript
52+
eTag?: string;
53+
```
54+
55+
## FetchResponse.status
56+
57+
The HTTP status, which is useful for differentiating success responses with data from those without.
58+
59+
<p>The Remote Config client is modeled after the native `Fetch` interface, so HTTP status is first-class.
60+
61+
<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.
62+
63+
<b>Signature:</b>
64+
65+
```typescript
66+
status: number;
67+
```
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Project: /docs/reference/js/_project.yaml
2+
Book: /docs/reference/_book.yaml
3+
page_type: reference
4+
5+
{% comment %}
6+
DO NOT EDIT THIS FILE!
7+
This is generated by the JS SDK team, and any local changes will be
8+
overwritten. Changes should be made in the source code at
9+
https://github.com/firebase/firebase-js-sdk
10+
{% endcomment %}
11+
12+
# FirebaseRemoteConfigObject interface
13+
Defines a self-descriptive reference for config key-value pairs.
14+
15+
<b>Signature:</b>
16+
17+
```typescript
18+
export interface FirebaseRemoteConfigObject
19+
```

docs-devsite/remote-config.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ The Firebase Remote Config Web SDK. This SDK does not work in a Node.js environm
1717
| Function | Description |
1818
| --- | --- |
1919
| <b>function(app, ...)</b> |
20-
| [getRemoteConfig(app)](./remote-config.md#getremoteconfig_cf608e1) | |
20+
| [getRemoteConfig(app, options)](./remote-config.md#getremoteconfig_61d368f) | |
2121
| <b>function(remoteConfig, ...)</b> |
2222
| [activate(remoteConfig)](./remote-config.md#activate_722a192) | Makes the last fetched config available to the getters. |
2323
| [ensureInitialized(remoteConfig)](./remote-config.md#ensureinitialized_722a192) | Ensures the last activated config are available to the getters. |
@@ -38,7 +38,10 @@ The Firebase Remote Config Web SDK. This SDK does not work in a Node.js environm
3838
| Interface | Description |
3939
| --- | --- |
4040
| [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> |
41+
| [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. |
42+
| [FirebaseRemoteConfigObject](./remote-config.firebaseremoteconfigobject.md#firebaseremoteconfigobject_interface) | Defines a self-descriptive reference for config key-value pairs. |
4143
| [RemoteConfig](./remote-config.remoteconfig.md#remoteconfig_interface) | The Firebase Remote Config service interface. |
44+
| [RemoteConfigOptions](./remote-config.remoteconfigoptions.md#remoteconfigoptions_interface) | Options for Remote Config initialization. |
4245
| [RemoteConfigSettings](./remote-config.remoteconfigsettings.md#remoteconfigsettings_interface) | Defines configuration options for the Remote Config SDK. |
4346
| [Value](./remote-config.value.md#value_interface) | Wraps a value with metadata and type-safe getters. |
4447

@@ -52,19 +55,20 @@ The Firebase Remote Config Web SDK. This SDK does not work in a Node.js environm
5255

5356
## function(app, ...)
5457

55-
### getRemoteConfig(app) {:#getremoteconfig_cf608e1}
58+
### getRemoteConfig(app, options) {:#getremoteconfig_61d368f}
5659

5760
<b>Signature:</b>
5861

5962
```typescript
60-
export declare function getRemoteConfig(app?: FirebaseApp): RemoteConfig;
63+
export declare function getRemoteConfig(app?: FirebaseApp, options?: RemoteConfigOptions): RemoteConfig;
6164
```
6265

6366
#### Parameters
6467

6568
| Parameter | Type | Description |
6669
| --- | --- | --- |
6770
| app | [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) | The [FirebaseApp](./app.firebaseapp.md#firebaseapp_interface) instance. |
71+
| options | [RemoteConfigOptions](./remote-config.remoteconfigoptions.md#remoteconfigoptions_interface) | Optional. The [RemoteConfigOptions](./remote-config.remoteconfigoptions.md#remoteconfigoptions_interface) with which to instantiate the Remote Config instance. |
6872

6973
<b>Returns:</b>
7074

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
Project: /docs/reference/js/_project.yaml
2+
Book: /docs/reference/_book.yaml
3+
page_type: reference
4+
5+
{% comment %}
6+
DO NOT EDIT THIS FILE!
7+
This is generated by the JS SDK team, and any local changes will be
8+
overwritten. Changes should be made in the source code at
9+
https://github.com/firebase/firebase-js-sdk
10+
{% endcomment %}
11+
12+
# RemoteConfigOptions interface
13+
Options for Remote Config initialization.
14+
15+
<b>Signature:</b>
16+
17+
```typescript
18+
export interface RemoteConfigOptions
19+
```
20+
21+
## Properties
22+
23+
| Property | Type | Description |
24+
| --- | --- | --- |
25+
| [initialFetchResponse](./remote-config.remoteconfigoptions.md#remoteconfigoptionsinitialfetchresponse) | [FetchResponse](./remote-config.fetchresponse.md#fetchresponse_interface) | Hydrates the state with an initial fetch response. |
26+
| [templateId](./remote-config.remoteconfigoptions.md#remoteconfigoptionstemplateid) | string | The ID of the template to use. If not provided, defaults to "firebase". |
27+
28+
## RemoteConfigOptions.initialFetchResponse
29+
30+
Hydrates the state with an initial fetch response.
31+
32+
<b>Signature:</b>
33+
34+
```typescript
35+
initialFetchResponse?: FetchResponse;
36+
```
37+
38+
## RemoteConfigOptions.templateId
39+
40+
The ID of the template to use. If not provided, defaults to "firebase".
41+
42+
<b>Signature:</b>
43+
44+
```typescript
45+
templateId?: string;
46+
```

packages/auth/src/core/auth/emulator.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,41 @@ describe('core/auth/emulator', () => {
7676
);
7777
});
7878

79+
it('passes with same config if a network request has already been made', async () => {
80+
expect(() => connectAuthEmulator(auth, 'http://127.0.0.1:2020')).to.not
81+
.throw;
82+
await user.delete();
83+
expect(() => connectAuthEmulator(auth, 'http://127.0.0.1:2020')).to.not
84+
.throw;
85+
});
86+
87+
it('fails with alternate config if a network request has already been made', async () => {
88+
expect(() => connectAuthEmulator(auth, 'http://127.0.0.1:2020')).to.not
89+
.throw;
90+
await user.delete();
91+
expect(() => connectAuthEmulator(auth, 'http://127.0.0.1:2021')).to.throw(
92+
FirebaseError,
93+
'auth/emulator-config-failed'
94+
);
95+
});
96+
97+
it('subsequent calls update the endpoint appropriately', async () => {
98+
connectAuthEmulator(auth, 'http://127.0.0.1:2021');
99+
expect(auth.emulatorConfig).to.eql({
100+
protocol: 'http',
101+
host: '127.0.0.1',
102+
port: 2021,
103+
options: { disableWarnings: false }
104+
});
105+
connectAuthEmulator(auth, 'http://127.0.0.1:2020');
106+
expect(auth.emulatorConfig).to.eql({
107+
protocol: 'http',
108+
host: '127.0.0.1',
109+
port: 2020,
110+
options: { disableWarnings: false }
111+
});
112+
});
113+
79114
it('updates the endpoint appropriately', async () => {
80115
connectAuthEmulator(auth, 'http://127.0.0.1:2020');
81116
await user.delete();

packages/auth/src/core/auth/emulator.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { Auth } from '../../model/public_types';
1818
import { AuthErrorCode } from '../errors';
1919
import { _assert } from '../util/assert';
2020
import { _castAuth } from './auth_impl';
21+
import { deepEqual } from '@firebase/util';
2122

2223
/**
2324
* Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production
@@ -47,12 +48,6 @@ export function connectAuthEmulator(
4748
options?: { disableWarnings: boolean }
4849
): void {
4950
const authInternal = _castAuth(auth);
50-
_assert(
51-
authInternal._canInitEmulator,
52-
authInternal,
53-
AuthErrorCode.EMULATOR_CONFIG_FAILED
54-
);
55-
5651
_assert(
5752
/^https?:\/\//.test(url),
5853
authInternal,
@@ -66,15 +61,42 @@ export function connectAuthEmulator(
6661
const portStr = port === null ? '' : `:${port}`;
6762

6863
// Always replace path with "/" (even if input url had no path at all, or had a different one).
69-
authInternal.config.emulator = { url: `${protocol}//${host}${portStr}/` };
70-
authInternal.settings.appVerificationDisabledForTesting = true;
71-
authInternal.emulatorConfig = Object.freeze({
64+
const emulator = { url: `${protocol}//${host}${portStr}/` };
65+
const emulatorConfig = Object.freeze({
7266
host,
7367
port,
7468
protocol: protocol.replace(':', ''),
7569
options: Object.freeze({ disableWarnings })
7670
});
7771

72+
// There are a few scenarios to guard against if the Auth instance has already started:
73+
if (!authInternal._canInitEmulator) {
74+
// Applications may not initialize the emulator for the first time if Auth has already started
75+
// to make network requests.
76+
_assert(
77+
authInternal.config.emulator && authInternal.emulatorConfig,
78+
authInternal,
79+
AuthErrorCode.EMULATOR_CONFIG_FAILED
80+
);
81+
82+
// Applications may not alter the configuration of the emulator (aka pass a different config)
83+
// once Auth has started to make network requests.
84+
_assert(
85+
deepEqual(emulator, authInternal.config.emulator) &&
86+
deepEqual(emulatorConfig, authInternal.emulatorConfig),
87+
authInternal,
88+
AuthErrorCode.EMULATOR_CONFIG_FAILED
89+
);
90+
91+
// It's valid, however, to invoke connectAuthEmulator() after Auth has started making
92+
// connections, so long as the config matches the existing config. This results in a no-op.
93+
return;
94+
}
95+
96+
authInternal.config.emulator = emulator;
97+
authInternal.emulatorConfig = emulatorConfig;
98+
authInternal.settings.appVerificationDisabledForTesting = true;
99+
78100
if (!disableWarnings) {
79101
emitEmulatorWarning();
80102
}

packages/database-compat/test/database.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,9 @@ describe('Database Tests', () => {
301301

302302
expect(() => {
303303
db.useEmulator('localhost', 1234);
304-
}).to.throw(/Cannot call useEmulator/);
304+
}).to.throw(
305+
'FIREBASE FATAL ERROR: connectDatabaseEmulator() cannot initialize or alter the emulator configuration after the database instance has started.'
306+
);
305307
});
306308

307309
it('refFromURL returns an emulated ref with useEmulator', () => {

0 commit comments

Comments
 (0)