Skip to content

Add Emulator Overlay #8977

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 33 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
02c52c6
Added test project to fdc
maneesht Apr 11, 2025
37d0f18
Fixed formatting
maneesht Apr 11, 2025
a4d7813
Fix linting
maneesht Apr 11, 2025
170b15a
Excluded example integration from tsconfig
maneesht Apr 11, 2025
ec405ae
Fixed example-integration package.json
maneesht Apr 11, 2025
248f654
Added ssl check for firestore
maneesht Apr 23, 2025
14ef1bc
Added ssl checks for RTDB
maneesht Apr 23, 2025
a8fe7de
Merge remote-tracking branch 'origin/main' into mtewani/add-ssl-check…
maneesht Apr 23, 2025
df4a115
Removed unnecessary files
maneesht Apr 23, 2025
f867d3b
Removed unnecessary data connect changes
maneesht Apr 23, 2025
4f23f33
Create gentle-laws-kneel.md
maneesht Apr 23, 2025
55b1c0a
Added code to pass on credentials if using a cloud workstation
maneesht Apr 23, 2025
e0a59c5
Create nine-pugs-crash.md
maneesht Apr 23, 2025
8d93780
Addressed comments
maneesht Apr 24, 2025
78142b9
Merge branch 'mtewani/fix-auth-redirects-studio' of https://github.co…
maneesht Apr 24, 2025
5e76a0a
Removed unused import
maneesht Apr 24, 2025
b8b485d
include storage changes
maneesht Apr 24, 2025
21ecf95
Updated api review
maneesht Apr 24, 2025
24c1575
Removed unnecessary import
maneesht Apr 25, 2025
7c7f3ae
Fix formatting
maneesht Apr 25, 2025
d62409a
Fixed data connect test
maneesht Apr 25, 2025
82faa08
Included extra url in externs
maneesht Apr 25, 2025
32c461a
Fixed fdc tests
maneesht Apr 25, 2025
da5b7bf
Passed in emulator information
maneesht Apr 25, 2025
b424e58
Fixed formattign
maneesht Apr 25, 2025
7d2313f
WIP
maneesht Apr 25, 2025
6e0761d
Added emulator status
maneesht Apr 25, 2025
e7b292d
Removed firebaseapp import
maneesht Apr 25, 2025
beff97f
Merge remote-tracking branch 'origin/main' into mtewani/add-emulator-…
maneesht May 1, 2025
6ca5c24
Removed app check change
maneesht May 1, 2025
6d0af9d
Removed eslintrc change
maneesht May 1, 2025
445c490
Removed unnecessary test
maneesht May 1, 2025
d97337f
Fixed failing tests
maneesht May 1, 2025
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/gentle-laws-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@firebase/database-compat": patch
"@firebase/database": patch
"@firebase/firestore": patch
---

Add SSL checks to `connectDatabaseEmulator` and `connectFirestoreEmulator`
15 changes: 15 additions & 0 deletions common/api-review/util.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ export type EmulatorMockTokenOptions = ({
sub: string;
}) & Partial<FirebaseIdToken>;

// Warning: (ae-missing-release-tag) "EmulatorStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface EmulatorStatus {
// (undocumented)
isRunningEmulator: boolean;
// (undocumented)
name: string;
}

// Warning: (ae-missing-release-tag) "ErrorData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
Expand Down Expand Up @@ -482,6 +492,11 @@ export interface Subscribe<T> {
// @public (undocumented)
export type Unsubscribe = () => void;

// Warning: (ae-missing-release-tag) "updateStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function updateStatus(emulatorStatus: EmulatorStatus, isCloudWorkstation: boolean): void;

// Warning: (ae-missing-release-tag) "validateArgCount" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
Expand Down
14 changes: 12 additions & 2 deletions packages/auth/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
FirebaseError,
isCloudflareWorker,
isCloudWorkstation,
querystring
querystring,
updateStatus
} from '@firebase/util';

import { AuthErrorCode, NamedErrorParams } from '../core/errors';
Expand Down Expand Up @@ -198,7 +199,16 @@ export async function _performFetchWithErrorHandling<V>(
customErrorMap: Partial<ServerErrorMap<ServerError>>,
fetchFn: () => Promise<Response>
): Promise<V> {
(auth as AuthInternal)._canInitEmulator = false;
const authInternal = auth as AuthInternal;
updateStatus(
{
name: 'Auth',
isRunningEmulator: authInternal.emulatorConfig !== undefined
},
authInternal.emulatorConfig!! &&
isCloudWorkstation(authInternal.emulatorConfig.host)
);
authInternal._canInitEmulator = false;
const errorMap = { ...SERVER_ERROR_MAP, ...customErrorMap };
try {
const networkTimeout = new NetworkTimeout<Response>(auth);
Expand Down
44 changes: 15 additions & 29 deletions packages/auth/src/core/auth/emulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ import { Auth } from '../../model/public_types';
import { AuthErrorCode } from '../errors';
import { _assert } from '../util/assert';
import { _castAuth } from './auth_impl';
import { deepEqual, isCloudWorkstation, pingServer } from '@firebase/util';
import {
deepEqual,
isCloudWorkstation,
pingServer,
updateStatus
} from '@firebase/util';

/**
* Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production
Expand Down Expand Up @@ -98,7 +103,7 @@ export function connectAuthEmulator(
authInternal.settings.appVerificationDisabledForTesting = true;

if (!disableWarnings) {
emitEmulatorWarning();
emitEmulatorWarning(isCloudWorkstation(emulatorConfig.host));
}

// Workaround to get cookies in Firebase Studio
Expand Down Expand Up @@ -143,38 +148,19 @@ function parsePort(portStr: string): number | null {
return port;
}

function emitEmulatorWarning(): void {
function attachBanner(): void {
const el = document.createElement('p');
const sty = el.style;
el.innerText =
'Running in emulator mode. Do not use with production credentials.';
sty.position = 'fixed';
sty.width = '100%';
sty.backgroundColor = '#ffffff';
sty.border = '.1em solid #000000';
sty.color = '#b50000';
sty.bottom = '0px';
sty.left = '0px';
sty.margin = '0px';
sty.zIndex = '10000';
sty.textAlign = 'center';
el.classList.add('firebase-emulator-warning');
document.body.appendChild(el);
}

function emitEmulatorWarning(isCloudWorkstation: boolean): void {
updateStatus(
{
name: 'Auth',
isRunningEmulator: true
},
isCloudWorkstation
);
if (typeof console !== 'undefined' && typeof console.info === 'function') {
console.info(
'WARNING: You are using the Auth Emulator,' +
' which is intended for local testing only. Do not use with' +
' production credentials.'
);
}
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
if (document.readyState === 'loading') {
window.addEventListener('DOMContentLoaded', attachBanner);
} else {
attachBanner();
}
}
}
3 changes: 2 additions & 1 deletion packages/firestore/src/lite-api/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { Datastore, newDatastore } from '../remote/datastore';
import { Code, FirestoreError } from '../util/error';
import { logDebug } from '../util/log';

import { FirestoreSettingsImpl } from './settings';
import { FirestoreSettingsImpl, PrivateSettings } from './settings';

export const LOG_TAG = 'ComponentProvider';

Expand Down Expand Up @@ -110,6 +110,7 @@ export function makeDatabaseInfo(
persistenceKey: string,
settings: FirestoreSettingsImpl
): DatabaseInfo {
const privateSettings = settings as PrivateSettings;
return new DatabaseInfo(
databaseId,
appId,
Expand Down
8 changes: 8 additions & 0 deletions packages/firestore/src/lite-api/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
EmulatorMockTokenOptions,
getDefaultEmulatorHostnameAndPort,
isCloudWorkstation,
updateStatus,
pingServer
} from '@firebase/util';

Expand Down Expand Up @@ -142,6 +143,13 @@ export class Firestore implements FirestoreService {

_freezeSettings(): FirestoreSettingsImpl {
this._settingsFrozen = true;
updateStatus(
{
name: 'Firestore',
isRunningEmulator: (this._settings as PrivateSettings).emulator!!
},
isCloudWorkstation(this._settings.host)
);
return this._settings;
}

Expand Down
1 change: 1 addition & 0 deletions packages/firestore/src/lite-api/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export interface PrivateSettings extends FirestoreSettings {
experimentalLongPollingOptions?: ExperimentalLongPollingOptions;
useFetchStreams?: boolean;
emulatorOptions?: { mockUserToken?: EmulatorMockTokenOptions | string };
emulator?: boolean;

localCache?: FirestoreLocalCache;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
* limitations under the License.
*/

import { isCloudWorkstation } from '@firebase/util';

import { Token } from '../../api/credentials';
import { Stream } from '../../remote/connection';
import { RestConnection } from '../../remote/rest_connection';
Expand Down
3 changes: 2 additions & 1 deletion packages/storage/src/implementation/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export interface Connection<T extends ConnectionType> {
method: string,
isUsingEmulator: boolean,
body?: ArrayBufferView | Blob | string | null,
headers?: Headers
headers?: Headers,
isUsingEmulator?: boolean
): Promise<void>;

getErrorCode(): ErrorCode;
Expand Down
3 changes: 2 additions & 1 deletion packages/storage/src/platform/browser/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ abstract class XhrConnection<T extends ConnectionType>
method: string,
isUsingEmulator: boolean,
body?: ArrayBufferView | Blob | string,
headers?: Headers
headers?: Headers,
isUsingEmulator?: boolean
): Promise<void> {
if (this.sent_) {
throw internalError('cannot .send() more than once');
Expand Down
62 changes: 62 additions & 0 deletions packages/util/src/emulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,65 @@
signature
].join('.');
}

function getOrCreate(id: string): { created: boolean; element: HTMLElement } {
let parentDiv = document.getElementById(id);
let created = false;
if (!parentDiv) {
parentDiv = document.createElement('div');
parentDiv.setAttribute('id', id);
created = true;
}
return { created, element: parentDiv };
}

export interface EmulatorStatus {
name: string;
isRunningEmulator: boolean;
}
export function updateStatus(

Check failure on line 159 in packages/util/src/emulator.ts

View workflow job for this annotation

GitHub Actions / Lint

Missing return type on function
emulatorStatus: EmulatorStatus,
isCloudWorkstation: boolean
) {
function setupDom() {

Check failure on line 163 in packages/util/src/emulator.ts

View workflow job for this annotation

GitHub Actions / Lint

Missing return type on function
const parentDivId = `__firebase_status`;

let { element: parentDiv, created } = getOrCreate(parentDivId);

Check failure on line 166 in packages/util/src/emulator.ts

View workflow job for this annotation

GitHub Actions / Lint

'created' is never reassigned. Use 'const' instead

Check failure on line 166 in packages/util/src/emulator.ts

View workflow job for this annotation

GitHub Actions / Lint

'parentDiv' is never reassigned. Use 'const' instead

if (created) {
parentDiv.style.position = 'fixed';
parentDiv.style.bottom = '0px';
parentDiv.style.border = 'solid 1px';
parentDiv.style.width = '100%';
parentDiv.style.borderRadius = '10px';
parentDiv.style.padding = '.5em';
parentDiv.style.textAlign = 'center';
parentDiv.classList.add('firebase-emulator-warning');
document.body.appendChild(parentDiv);
}

const { name, isRunningEmulator } = emulatorStatus;
const { element, created: productDivCreated } = getOrCreate(
`${parentDivId}_${name}`
);
// If in prod, and not using a cloud workstation, we should remove the node, as the banner can be distracting.
if (!isRunningEmulator && !isCloudWorkstation) {
element.remove();
return;
}
if (productDivCreated) {
parentDiv.appendChild(element);
}
element.style.color = isRunningEmulator ? 'green' : 'red';
element.innerHTML = `${name} is running in ${
isRunningEmulator ? 'emulator' : 'prod'
} mode`;
}
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
if (document.readyState === 'loading') {
window.addEventListener('DOMContentLoaded', setupDom);
} else {
setupDom();
}
}
}
Loading