Skip to content

Commit 815ab15

Browse files
committed
Merge branch 'master' into ddb-firebaseserverappsettings-name
2 parents 2b23355 + ed1c993 commit 815ab15

33 files changed

+105
-434
lines changed

.changeset/chilly-moons-play.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@firebase/util': patch
3+
---
4+
5+
fix: browser detection (detect either window or web worker)

.changeset/clever-dryers-double.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@firebase/app': patch
3+
'firebase': patch
4+
---
5+
6+
Guard the use of `FinalizationRegistry` in `FirebaseServerApp` initialization based on the availability of `FinalizationRegistry` in the runtime.

.changeset/fifty-mayflies-hug.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@firebase/app': patch
3+
---
4+
5+
fix: server app should initialize in web workers

.changeset/flat-scissors-suffer.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+
Fix persistence multi-tab snapshot listener metadata sync issue.

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,5 +98,6 @@ docs/
9898
*.tfstate
9999
*.tfstate.*
100100

101-
# generated test case text data
101+
# vertexai test data
102+
vertexai-sdk-test-data
102103
mocks-lookup.ts

packages/app/src/api.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ import {
4848
LogOptions,
4949
setUserLogHandler
5050
} from '@firebase/logger';
51-
import { deepEqual, getDefaultAppConfig, isBrowser } from '@firebase/util';
51+
import {
52+
deepEqual,
53+
getDefaultAppConfig,
54+
isBrowser,
55+
isWebWorker
56+
} from '@firebase/util';
5257

5358
export { FirebaseError } from '@firebase/util';
5459

@@ -230,7 +235,7 @@ export function initializeServerApp(
230235
_options: FirebaseOptions | FirebaseApp,
231236
_serverAppConfig: FirebaseServerAppSettings
232237
): FirebaseServerApp {
233-
if (isBrowser()) {
238+
if (isBrowser() && !isWebWorker()) {
234239
// FirebaseServerApp isn't designed to be run in browsers.
235240
throw ERROR_FACTORY.create(AppError.INVALID_SERVER_APP_ENVIRONMENT);
236241
}

packages/app/src/firebaseServerApp.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class FirebaseServerAppImpl
3232
implements FirebaseServerApp
3333
{
3434
private readonly _serverConfig: FirebaseServerAppSettings;
35-
private _finalizationRegistry: FinalizationRegistry<object>;
35+
private _finalizationRegistry: FinalizationRegistry<object> | null;
3636
private _refCount: number;
3737

3838
constructor(
@@ -67,9 +67,12 @@ export class FirebaseServerAppImpl
6767
...serverConfig
6868
};
6969

70-
this._finalizationRegistry = new FinalizationRegistry(() => {
71-
this.automaticCleanup();
72-
});
70+
this._finalizationRegistry = null;
71+
if (typeof FinalizationRegistry !== 'undefined') {
72+
this._finalizationRegistry = new FinalizationRegistry(() => {
73+
this.automaticCleanup();
74+
});
75+
}
7376

7477
this._refCount = 0;
7578
this.incRefCount(this._serverConfig.releaseOnDeref);
@@ -97,7 +100,7 @@ export class FirebaseServerAppImpl
97100
return;
98101
}
99102
this._refCount++;
100-
if (obj !== undefined) {
103+
if (obj !== undefined && this._finalizationRegistry !== null) {
101104
this._finalizationRegistry.register(obj, this);
102105
}
103106
}

packages/firestore/src/core/sync_engine_impl.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1095,7 +1095,13 @@ export async function syncEngineEmitNewSnapsAndNotifyLocalStore(
10951095
// secondary clients to update query state.
10961096
if (viewSnapshot || remoteEvent) {
10971097
if (syncEngineImpl.isPrimaryClient) {
1098-
const isCurrent = viewSnapshot && !viewSnapshot.fromCache;
1098+
// Query state is set to `current` if:
1099+
// - There is a view change and it is up-to-date, or,
1100+
// - There is a global snapshot, the Target is current, and no changes to be resolved
1101+
const isCurrent = viewSnapshot
1102+
? !viewSnapshot.fromCache
1103+
: remoteEvent?.targetChanges.get(queryView.targetId)?.current;
1104+
10991105
syncEngineImpl.sharedClientState.updateQueryState(
11001106
queryView.targetId,
11011107
isCurrent ? 'current' : 'not-current'

packages/firestore/test/unit/specs/listen_spec.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,4 +1895,40 @@ describeSpec('Listens:', [], () => {
18951895
.restoreListen(query1, 'resume-token-1000', /* expectedCount= */ 1);
18961896
}
18971897
);
1898+
1899+
specTest(
1900+
'Global snapshots would not alter query state if there is no changes',
1901+
['multi-client'],
1902+
() => {
1903+
const query1 = query('collection');
1904+
const docA = doc('collection/a', 1000, { key: 'a' });
1905+
return (
1906+
client(0)
1907+
.becomeVisible()
1908+
.expectPrimaryState(true)
1909+
// Populate the cache first
1910+
.userListens(query1)
1911+
.watchAcksFull(query1, 1000, docA)
1912+
.expectEvents(query1, { added: [docA] })
1913+
.userUnlistens(query1)
1914+
.watchRemoves(query1)
1915+
// Listen to the query in the primary client
1916+
.userListens(query1, { resumeToken: 'resume-token-1000' })
1917+
.expectEvents(query1, {
1918+
added: [docA],
1919+
fromCache: true
1920+
})
1921+
.watchAcksFull(query1, 2000, docA)
1922+
.expectEvents(query1, { fromCache: false })
1923+
// Reproduces: https://github.com/firebase/firebase-js-sdk/issues/8314
1924+
// Watch could send a global snapshot from time to time. If there are no view changes,
1925+
// the query should not be marked as "not-current" as the Target is up to date.
1926+
.watchSnapshots(3000, [], 'resume-token-3000')
1927+
// Listen to the query in the secondary tab. The snapshot is up to date.
1928+
.client(1)
1929+
.userListens(query1)
1930+
.expectEvents(query1, { added: [docA], fromCache: false })
1931+
);
1932+
}
1933+
);
18981934
});

packages/util/src/environment.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
import { CONSTANTS } from './constants';
1919
import { getDefaults } from './defaults';
2020

21+
/**
22+
* Type placeholder for `WorkerGlobalScope` from `webworker`
23+
*/
24+
declare class WorkerGlobalScope {}
25+
2126
/**
2227
* Returns navigator.userAgent string or '' if it's not defined.
2328
* @return user agent string
@@ -77,7 +82,18 @@ export function isNode(): boolean {
7782
* Detect Browser Environment
7883
*/
7984
export function isBrowser(): boolean {
80-
return typeof self === 'object' && self.self === self;
85+
return typeof window !== 'undefined' || isWebWorker();
86+
}
87+
88+
/**
89+
* Detect Web Worker context
90+
*/
91+
export function isWebWorker(): boolean {
92+
return (
93+
typeof WorkerGlobalScope !== 'undefined' &&
94+
typeof self !== 'undefined' &&
95+
self instanceof WorkerGlobalScope
96+
);
8197
}
8298

8399
/**

0 commit comments

Comments
 (0)