From 85e8dc4983382225bda73a5d5a6592dec4d4334d Mon Sep 17 00:00:00 2001 From: dwyfrequency Date: Mon, 19 Dec 2022 14:09:41 -0500 Subject: [PATCH 1/8] Add forceEnvironment and check value in isNode --- packages/util/src/defaults.ts | 3 ++- packages/util/src/environment.ts | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/util/src/defaults.ts b/packages/util/src/defaults.ts index 0bf32b1968a..d9a553996f7 100644 --- a/packages/util/src/defaults.ts +++ b/packages/util/src/defaults.ts @@ -39,6 +39,7 @@ export interface FirebaseDefaults { emulatorHosts?: Record; _authTokenSyncURL?: string; _authIdTokenMaxAge?: number; + forceEnvironment?: 'browser' | 'node'; [key: string]: unknown; } @@ -88,7 +89,7 @@ const getDefaultsFromCookie = (): FirebaseDefaults | undefined => { * (2) if such an object was provided on a shell environment variable * (3) if such an object exists in a cookie */ -const getDefaults = (): FirebaseDefaults | undefined => { +export const getDefaults = (): FirebaseDefaults | undefined => { try { return ( getDefaultsFromGlobal() || diff --git a/packages/util/src/environment.ts b/packages/util/src/environment.ts index 5c9be0d26c0..d3601e5d6f6 100644 --- a/packages/util/src/environment.ts +++ b/packages/util/src/environment.ts @@ -16,6 +16,7 @@ */ import { CONSTANTS } from './constants'; +import { getDefaults } from './defaults'; /** * Returns navigator.userAgent string or '' if it's not defined. @@ -49,13 +50,19 @@ export function isMobileCordova(): boolean { ); } +// TODO(jackdwyer): update this to use getDefaults() check if there is the force +// environment field. If there, return true or false depending on results +// else proceed with previous implementation of function. /** * Detect Node.js. * - * @return true if Node.js environment is detected. + * @return true if Node.js environment is detected or specified. */ // Node detection logic from: https://github.com/iliakan/detect-node/ export function isNode(): boolean { + if (getDefaults()?.forceEnvironment === 'node') { + return true; + } try { return ( Object.prototype.toString.call(global.process) === '[object process]' From 22cafa10c30a7e50c35b9d7a06da24e40744a446 Mon Sep 17 00:00:00 2001 From: dwyfrequency Date: Mon, 19 Dec 2022 19:40:26 +0000 Subject: [PATCH 2/8] Update API reports --- common/api-review/util.api.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index eacd4ef3aa8..db2d3e00eb4 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -193,6 +193,8 @@ export interface FirebaseDefaults { config?: Record; // (undocumented) emulatorHosts?: Record; + // (undocumented) + forceEnvironment?: 'browser' | 'node'; } // Warning: (ae-missing-release-tag) "FirebaseError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -221,6 +223,11 @@ export const getDefaultEmulatorHost: (productName: string) => string | undefined // @public export const getDefaultEmulatorHostnameAndPort: (productName: string) => [hostname: string, port: number] | undefined; +// Warning: (ae-missing-release-tag) "getDefaults" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export const getDefaults: () => FirebaseDefaults | undefined; + // @public export const getExperimentalSetting: (name: T) => FirebaseDefaults[`_${T}`]; From 6f8c557017c56319a216f4899d89d301acdca8f4 Mon Sep 17 00:00:00 2001 From: dwyfrequency Date: Mon, 19 Dec 2022 20:40:30 -0500 Subject: [PATCH 3/8] Add tests for environment package --- packages/util/src/environment.ts | 10 +++-- packages/util/test/environments.test.ts | 50 +++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 packages/util/test/environments.test.ts diff --git a/packages/util/src/environment.ts b/packages/util/src/environment.ts index d3601e5d6f6..ed818aca43e 100644 --- a/packages/util/src/environment.ts +++ b/packages/util/src/environment.ts @@ -50,9 +50,6 @@ export function isMobileCordova(): boolean { ); } -// TODO(jackdwyer): update this to use getDefaults() check if there is the force -// environment field. If there, return true or false depending on results -// else proceed with previous implementation of function. /** * Detect Node.js. * @@ -60,9 +57,14 @@ export function isMobileCordova(): boolean { */ // Node detection logic from: https://github.com/iliakan/detect-node/ export function isNode(): boolean { - if (getDefaults()?.forceEnvironment === 'node') { + console.log({ getDefualts: getDefaults() }); + const forceEnvironment = getDefaults()?.forceEnvironment; + if (forceEnvironment === 'node') { return true; + } else if (forceEnvironment === 'browser') { + return false; } + try { return ( Object.prototype.toString.call(global.process) === '[object process]' diff --git a/packages/util/test/environments.test.ts b/packages/util/test/environments.test.ts new file mode 100644 index 00000000000..61a335c0118 --- /dev/null +++ b/packages/util/test/environments.test.ts @@ -0,0 +1,50 @@ +/** + * @license + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { isNode } from '../src/environment'; +import { SinonStub, stub } from 'sinon'; +import * as defaults from '../src/defaults'; + +const firebaseDefaults: defaults.FirebaseDefaults = { + _authTokenSyncURL: 'string', + _authIdTokenMaxAge: 200, + forceEnvironment: 'node' +}; + +describe('isNode()', () => { + const getDefaultsFromGlobalStub: SinonStub = stub(defaults, 'getDefaults'); + + afterEach(async () => { + getDefaultsFromGlobalStub.reset(); + }); + + it('returns true if forceEnvironment lists `node`', () => { + getDefaultsFromGlobalStub.returns(firebaseDefaults); + + expect(isNode()).to.be.true; + }); + + it('returns false if forceEnvironment lists `browser`', () => { + getDefaultsFromGlobalStub.returns({ + ...firebaseDefaults, + forceEnvironment: 'browser' + }); + + expect(isNode()).to.be.false; + }); +}); From e825176649acb252e43415c8fb893cf231b80991 Mon Sep 17 00:00:00 2001 From: dwyfrequency Date: Tue, 20 Dec 2022 13:11:37 -0500 Subject: [PATCH 4/8] Update tests and remove console.log --- packages/util/src/environment.ts | 1 - packages/util/test/environments.test.ts | 10 +++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/util/src/environment.ts b/packages/util/src/environment.ts index ed818aca43e..d5beeabb0a7 100644 --- a/packages/util/src/environment.ts +++ b/packages/util/src/environment.ts @@ -57,7 +57,6 @@ export function isMobileCordova(): boolean { */ // Node detection logic from: https://github.com/iliakan/detect-node/ export function isNode(): boolean { - console.log({ getDefualts: getDefaults() }); const forceEnvironment = getDefaults()?.forceEnvironment; if (forceEnvironment === 'node') { return true; diff --git a/packages/util/test/environments.test.ts b/packages/util/test/environments.test.ts index 61a335c0118..0abc12ea633 100644 --- a/packages/util/test/environments.test.ts +++ b/packages/util/test/environments.test.ts @@ -17,7 +17,7 @@ import { expect } from 'chai'; import { isNode } from '../src/environment'; -import { SinonStub, stub } from 'sinon'; +import { SinonStub, stub, restore } from 'sinon'; import * as defaults from '../src/defaults'; const firebaseDefaults: defaults.FirebaseDefaults = { @@ -27,10 +27,14 @@ const firebaseDefaults: defaults.FirebaseDefaults = { }; describe('isNode()', () => { - const getDefaultsFromGlobalStub: SinonStub = stub(defaults, 'getDefaults'); + let getDefaultsFromGlobalStub: SinonStub; + + beforeEach(async () => { + getDefaultsFromGlobalStub = stub(defaults, 'getDefaults'); + }); afterEach(async () => { - getDefaultsFromGlobalStub.reset(); + restore(); }); it('returns true if forceEnvironment lists `node`', () => { From 6ea2769117406684e46ac9cbc5dee305b1977bed Mon Sep 17 00:00:00 2001 From: dwyfrequency Date: Tue, 20 Dec 2022 14:34:49 -0500 Subject: [PATCH 5/8] Add changeset --- .changeset/silly-suns-drop.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/silly-suns-drop.md diff --git a/.changeset/silly-suns-drop.md b/.changeset/silly-suns-drop.md new file mode 100644 index 00000000000..2472b476bb4 --- /dev/null +++ b/.changeset/silly-suns-drop.md @@ -0,0 +1,5 @@ +--- +'@firebase/util': minor +--- + +allow users to specify their environment as Node or Browser to override Firebase's runtime environment detection and force the SDK to act as if it were in the respective environment From 5172acaf57a22f05c68895529508b89bc6dd8f27 Mon Sep 17 00:00:00 2001 From: dwyfrequency Date: Wed, 21 Dec 2022 18:29:56 -0500 Subject: [PATCH 6/8] Remove old changeset and update it with a new one --- .changeset/silly-suns-drop.md | 5 ----- .changeset/young-hornets-rescue.md | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) delete mode 100644 .changeset/silly-suns-drop.md create mode 100644 .changeset/young-hornets-rescue.md diff --git a/.changeset/silly-suns-drop.md b/.changeset/silly-suns-drop.md deleted file mode 100644 index 2472b476bb4..00000000000 --- a/.changeset/silly-suns-drop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@firebase/util': minor ---- - -allow users to specify their environment as Node or Browser to override Firebase's runtime environment detection and force the SDK to act as if it were in the respective environment diff --git a/.changeset/young-hornets-rescue.md b/.changeset/young-hornets-rescue.md new file mode 100644 index 00000000000..b6aba80702c --- /dev/null +++ b/.changeset/young-hornets-rescue.md @@ -0,0 +1,6 @@ +--- +'@firebase/util': minor +'firebase': minor +--- + +Allow users to specify their environment as `node` or `browser` to override Firebase's runtime environment detection and force the SDK to act as if it were in the respective environment. From 9e4dfaf22d525ba97a47fc087fc4bb028231d0ac Mon Sep 17 00:00:00 2001 From: dwyfrequency Date: Thu, 22 Dec 2022 11:22:05 -0500 Subject: [PATCH 7/8] Update jsdoc --- common/api-review/util.api.md | 3 --- packages/util/src/defaults.ts | 5 +++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index db2d3e00eb4..3ee306a0cf5 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -193,7 +193,6 @@ export interface FirebaseDefaults { config?: Record; // (undocumented) emulatorHosts?: Record; - // (undocumented) forceEnvironment?: 'browser' | 'node'; } @@ -223,8 +222,6 @@ export const getDefaultEmulatorHost: (productName: string) => string | undefined // @public export const getDefaultEmulatorHostnameAndPort: (productName: string) => [hostname: string, port: number] | undefined; -// Warning: (ae-missing-release-tag) "getDefaults" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export const getDefaults: () => FirebaseDefaults | undefined; diff --git a/packages/util/src/defaults.ts b/packages/util/src/defaults.ts index d9a553996f7..06cb6683050 100644 --- a/packages/util/src/defaults.ts +++ b/packages/util/src/defaults.ts @@ -39,6 +39,10 @@ export interface FirebaseDefaults { emulatorHosts?: Record; _authTokenSyncURL?: string; _authIdTokenMaxAge?: number; + /** + * Override Firebase's runtime environment detection and + * force the SDK to act as if it were in the specified environment. + */ forceEnvironment?: 'browser' | 'node'; [key: string]: unknown; } @@ -88,6 +92,7 @@ const getDefaultsFromCookie = (): FirebaseDefaults | undefined => { * (1) if such an object exists as a property of `globalThis` * (2) if such an object was provided on a shell environment variable * (3) if such an object exists in a cookie + * @public */ export const getDefaults = (): FirebaseDefaults | undefined => { try { From 409f9c561bc99f2c1882f3dc80648d99bdc22495 Mon Sep 17 00:00:00 2001 From: dwyfrequency Date: Thu, 22 Dec 2022 12:57:14 -0500 Subject: [PATCH 8/8] Move getGlobal into separate file to remove circular dep --- common/api-review/util.api.md | 2 -- packages/util/index.node.ts | 1 + packages/util/index.ts | 1 + packages/util/src/defaults.ts | 2 +- packages/util/src/environment.ts | 17 --------------- packages/util/src/global.ts | 34 +++++++++++++++++++++++++++++ packages/util/test/defaults.test.ts | 20 ++++++++--------- 7 files changed, 47 insertions(+), 30 deletions(-) create mode 100644 packages/util/src/global.ts diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index 3ee306a0cf5..be8703adf3f 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -228,8 +228,6 @@ export const getDefaults: () => FirebaseDefaults | undefined; // @public export const getExperimentalSetting: (name: T) => FirebaseDefaults[`_${T}`]; -// Warning: (ae-missing-release-tag) "getGlobal" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export function getGlobal(): typeof globalThis; diff --git a/packages/util/index.node.ts b/packages/util/index.node.ts index d63584284ac..9c3b54b1c86 100644 --- a/packages/util/index.node.ts +++ b/packages/util/index.node.ts @@ -42,3 +42,4 @@ export * from './src/uuid'; export * from './src/exponential_backoff'; export * from './src/formatters'; export * from './src/compat'; +export * from './src/global'; diff --git a/packages/util/index.ts b/packages/util/index.ts index c529580b24e..38b944cd9b5 100644 --- a/packages/util/index.ts +++ b/packages/util/index.ts @@ -37,3 +37,4 @@ export * from './src/uuid'; export * from './src/exponential_backoff'; export * from './src/formatters'; export * from './src/compat'; +export * from './src/global'; diff --git a/packages/util/src/defaults.ts b/packages/util/src/defaults.ts index 06cb6683050..0adaccc4f9d 100644 --- a/packages/util/src/defaults.ts +++ b/packages/util/src/defaults.ts @@ -16,7 +16,7 @@ */ import { base64Decode } from './crypt'; -import { getGlobal } from './environment'; +import { getGlobal } from './global'; /** * Keys for experimental properties on the `FirebaseDefaults` object. diff --git a/packages/util/src/environment.ts b/packages/util/src/environment.ts index d5beeabb0a7..04bbd92165d 100644 --- a/packages/util/src/environment.ts +++ b/packages/util/src/environment.ts @@ -201,20 +201,3 @@ export function areCookiesEnabled(): boolean { } return true; } - -/** - * Polyfill for `globalThis` object. - * @returns the `globalThis` object for the given environment. - */ -export function getGlobal(): typeof globalThis { - if (typeof self !== 'undefined') { - return self; - } - if (typeof window !== 'undefined') { - return window; - } - if (typeof global !== 'undefined') { - return global; - } - throw new Error('Unable to locate global object.'); -} diff --git a/packages/util/src/global.ts b/packages/util/src/global.ts new file mode 100644 index 00000000000..88a31efbd19 --- /dev/null +++ b/packages/util/src/global.ts @@ -0,0 +1,34 @@ +/** + * @license + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Polyfill for `globalThis` object. + * @returns the `globalThis` object for the given environment. + * @public + */ +export function getGlobal(): typeof globalThis { + if (typeof self !== 'undefined') { + return self; + } + if (typeof window !== 'undefined') { + return window; + } + if (typeof global !== 'undefined') { + return global; + } + throw new Error('Unable to locate global object.'); +} diff --git a/packages/util/test/defaults.test.ts b/packages/util/test/defaults.test.ts index 05fcf883482..a5780096b30 100644 --- a/packages/util/test/defaults.test.ts +++ b/packages/util/test/defaults.test.ts @@ -21,13 +21,13 @@ import { getDefaultEmulatorHost, getDefaultEmulatorHostnameAndPort } from '../src/defaults'; -import * as environment from '../src/environment'; +import * as global from '../src/global'; use(sinonChai); describe('getDefaultEmulatorHost', () => { after(() => { - delete environment.getGlobal().__FIREBASE_DEFAULTS__; + delete global.getGlobal().__FIREBASE_DEFAULTS__; }); context('with no config', () => { @@ -68,7 +68,7 @@ describe('getDefaultEmulatorHost', () => { context('with no config and something unexpected throws', () => { let consoleInfoStub: SinonStub; before(() => { - stub(environment, 'getGlobal').throws(new Error('getGlobal threw!')); + stub(global, 'getGlobal').throws(new Error('getGlobal threw!')); consoleInfoStub = stub(console, 'info'); }); after(() => { @@ -83,7 +83,7 @@ describe('getDefaultEmulatorHost', () => { context('with global config not listing the emulator', () => { before(() => { - environment.getGlobal().__FIREBASE_DEFAULTS__ = { + global.getGlobal().__FIREBASE_DEFAULTS__ = { emulatorHosts: { /* no firestore */ database: '127.0.0.1:8080' @@ -98,7 +98,7 @@ describe('getDefaultEmulatorHost', () => { context('with IPv4 hostname in global config', () => { before(() => { - environment.getGlobal().__FIREBASE_DEFAULTS__ = { + global.getGlobal().__FIREBASE_DEFAULTS__ = { emulatorHosts: { firestore: '127.0.0.1:8080' } @@ -112,7 +112,7 @@ describe('getDefaultEmulatorHost', () => { context('with quoted IPv6 hostname in global config', () => { before(() => { - environment.getGlobal().__FIREBASE_DEFAULTS__ = { + global.getGlobal().__FIREBASE_DEFAULTS__ = { emulatorHosts: { firestore: '[::1]:8080' } @@ -127,7 +127,7 @@ describe('getDefaultEmulatorHost', () => { describe('getDefaultEmulatorHostnameAndPort', () => { after(() => { - delete environment.getGlobal().__FIREBASE_DEFAULTS__; + delete global.getGlobal().__FIREBASE_DEFAULTS__; }); context('with no config', () => { @@ -138,7 +138,7 @@ describe('getDefaultEmulatorHostnameAndPort', () => { context('with global config not listing the emulator', () => { before(() => { - environment.getGlobal().__FIREBASE_DEFAULTS__ = { + global.getGlobal().__FIREBASE_DEFAULTS__ = { emulatorHosts: { /* no firestore */ database: '127.0.0.1:8080' @@ -153,7 +153,7 @@ describe('getDefaultEmulatorHostnameAndPort', () => { context('with IPv4 hostname in global config', () => { before(() => { - environment.getGlobal().__FIREBASE_DEFAULTS__ = { + global.getGlobal().__FIREBASE_DEFAULTS__ = { emulatorHosts: { firestore: '127.0.0.1:8080' } @@ -170,7 +170,7 @@ describe('getDefaultEmulatorHostnameAndPort', () => { context('with quoted IPv6 hostname in global config', () => { before(() => { - environment.getGlobal().__FIREBASE_DEFAULTS__ = { + global.getGlobal().__FIREBASE_DEFAULTS__ = { emulatorHosts: { firestore: '[::1]:8080' }