From 39b2df4ad4e0bf04257d54f860a40461d3fca4bc Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 12:20:54 +0100 Subject: [PATCH 01/17] refactor(perf): convert package sources to TypeScript --- .../perf/lib/{HttpMetric.js => HttpMetric.ts} | 36 +- ...hAttributes.js => MetricWithAttributes.ts} | 24 +- .../lib/{ScreenTrace.js => ScreenTrace.ts} | 14 +- packages/perf/lib/{Trace.js => Trace.ts} | 26 +- packages/perf/lib/index.d.ts | 583 ------------------ packages/perf/lib/index.js | 174 ------ packages/perf/lib/index.ts | 340 ++++++++++ packages/perf/lib/modular/index.d.ts | 89 --- .../perf/lib/modular/{index.js => index.ts} | 86 ++- packages/perf/package.json | 44 +- packages/perf/tsconfig.json | 21 + yarn.lock | 1 + 12 files changed, 540 insertions(+), 898 deletions(-) rename packages/perf/lib/{HttpMetric.js => HttpMetric.ts} (77%) rename packages/perf/lib/{MetricWithAttributes.js => MetricWithAttributes.ts} (73%) rename packages/perf/lib/{ScreenTrace.js => ScreenTrace.ts} (79%) rename packages/perf/lib/{Trace.js => Trace.ts} (82%) delete mode 100644 packages/perf/lib/index.d.ts delete mode 100644 packages/perf/lib/index.js create mode 100644 packages/perf/lib/index.ts delete mode 100644 packages/perf/lib/modular/index.d.ts rename packages/perf/lib/modular/{index.js => index.ts} (51%) create mode 100644 packages/perf/tsconfig.json diff --git a/packages/perf/lib/HttpMetric.js b/packages/perf/lib/HttpMetric.ts similarity index 77% rename from packages/perf/lib/HttpMetric.js rename to packages/perf/lib/HttpMetric.ts index f4e52de31b..51a89c3f89 100644 --- a/packages/perf/lib/HttpMetric.js +++ b/packages/perf/lib/HttpMetric.ts @@ -16,10 +16,30 @@ */ import { isNull, isNumber, isString } from '@react-native-firebase/app/dist/module/common'; + import MetricWithAttributes from './MetricWithAttributes'; +import type { + FirebasePerformanceTypes, + RNFBPerfHttpMetricData, + RNFBPerfNativeModule, +} from './index'; + export default class HttpMetric extends MetricWithAttributes { - constructor(native, url, httpMethod) { + private readonly _url: string; + private readonly _httpMethod: FirebasePerformanceTypes.HttpMethod; + private _httpResponseCode: number | null; + private _requestPayloadSize: number | null; + private _responsePayloadSize: number | null; + private _responseContentType: string | null; + private _started: boolean; + private _stopped: boolean; + + constructor( + native: RNFBPerfNativeModule, + url: string, + httpMethod: FirebasePerformanceTypes.HttpMethod, + ) { super(native); this._url = url; @@ -34,7 +54,7 @@ export default class HttpMetric extends MetricWithAttributes { this._stopped = false; } - setHttpResponseCode(code) { + setHttpResponseCode(code: number | null): void { if (!isNumber(code) && !isNull(code)) { throw new Error( "firebase.perf.HttpMetric.setHttpResponseCode(*) 'code' must be a number or null.", @@ -44,7 +64,7 @@ export default class HttpMetric extends MetricWithAttributes { this._httpResponseCode = code; } - setRequestPayloadSize(bytes) { + setRequestPayloadSize(bytes: number | null): void { if (!isNumber(bytes) && !isNull(bytes)) { throw new Error( "firebase.perf.HttpMetric.setRequestPayloadSize(*) 'bytes' must be a number or null.", @@ -54,7 +74,7 @@ export default class HttpMetric extends MetricWithAttributes { this._requestPayloadSize = bytes; } - setResponsePayloadSize(bytes) { + setResponsePayloadSize(bytes: number | null): void { if (!isNumber(bytes) && !isNull(bytes)) { throw new Error( "firebase.perf.HttpMetric.setResponsePayloadSize(*) 'bytes' must be a number or null.", @@ -64,7 +84,7 @@ export default class HttpMetric extends MetricWithAttributes { this._responsePayloadSize = bytes; } - setResponseContentType(contentType) { + setResponseContentType(contentType: string | null): void { if (!isString(contentType) && !isNull(contentType)) { throw new Error( "firebase.perf.HttpMetric.setResponseContentType(*) 'contentType' must be a string or null.", @@ -74,7 +94,7 @@ export default class HttpMetric extends MetricWithAttributes { this._responseContentType = contentType; } - start() { + start(): Promise { if (this._started) { return Promise.resolve(null); } @@ -83,13 +103,13 @@ export default class HttpMetric extends MetricWithAttributes { return this.native.startHttpMetric(this._id, this._url, this._httpMethod); } - stop() { + stop(): Promise { if (this._stopped) { return Promise.resolve(null); } this._stopped = true; - const metricData = { + const metricData: RNFBPerfHttpMetricData = { attributes: Object.assign({}, this._attributes), }; diff --git a/packages/perf/lib/MetricWithAttributes.js b/packages/perf/lib/MetricWithAttributes.ts similarity index 73% rename from packages/perf/lib/MetricWithAttributes.js rename to packages/perf/lib/MetricWithAttributes.ts index 5eaa768236..ff4f956012 100644 --- a/packages/perf/lib/MetricWithAttributes.js +++ b/packages/perf/lib/MetricWithAttributes.ts @@ -17,23 +17,33 @@ import { hasOwnProperty, isString } from '@react-native-firebase/app/dist/module/common'; +import type { RNFBPerfNativeModule } from './index'; + let id = 0; export default class MetricWithAttributes { - constructor(native) { + protected readonly native: RNFBPerfNativeModule; + protected readonly _id: number; + protected _attributes: Record; + + constructor(native: RNFBPerfNativeModule) { this._id = id++; this.native = native; this._attributes = {}; } - getAttribute(attribute) { + getAttribute(attribute: string): string | null { if (!isString(attribute)) { throw new Error("firebase.perf.*.getAttribute(*) 'attribute' must be a string."); } return this._attributes[attribute] || null; } - putAttribute(attribute, value) { + getAttributes(): Record { + return Object.assign({}, this._attributes); + } + + putAttribute(attribute: string, value: string): void { // TODO(VALIDATION): attribute: no leading or trailing whitespace, no leading underscore '_' if (!isString(attribute) || attribute.length > 40) { throw new Error( @@ -55,4 +65,12 @@ export default class MetricWithAttributes { this._attributes[attribute] = value; } + + removeAttribute(attribute: string): void { + if (!isString(attribute)) { + throw new Error("firebase.perf.*.removeAttribute(*) 'attribute' must be a string."); + } + + delete this._attributes[attribute]; + } } diff --git a/packages/perf/lib/ScreenTrace.js b/packages/perf/lib/ScreenTrace.ts similarity index 79% rename from packages/perf/lib/ScreenTrace.js rename to packages/perf/lib/ScreenTrace.ts index 2016830c72..4805be2118 100644 --- a/packages/perf/lib/ScreenTrace.js +++ b/packages/perf/lib/ScreenTrace.ts @@ -17,10 +17,18 @@ import { isIOS } from '@react-native-firebase/app/dist/module/common'; +import type { RNFBPerfNativeModule } from './index'; + let id = 0; export default class ScreenTrace { - constructor(native, identifier) { + readonly native: RNFBPerfNativeModule; + private readonly _identifier: string; + private readonly _id: number; + private _started: boolean; + private _stopped: boolean; + + constructor(native: RNFBPerfNativeModule, identifier: string) { this.native = native; this._identifier = identifier; this._id = id++; @@ -28,7 +36,7 @@ export default class ScreenTrace { this._stopped = false; } - start() { + start(): Promise { if (isIOS) { return Promise.reject(new Error('Custom screentraces are currently not supported on iOS.')); } @@ -40,7 +48,7 @@ export default class ScreenTrace { return this.native.startScreenTrace(this._id, this._identifier); } - stop() { + stop(): Promise { if (!this._started || this._stopped) { return Promise.resolve(null); } diff --git a/packages/perf/lib/Trace.js b/packages/perf/lib/Trace.ts similarity index 82% rename from packages/perf/lib/Trace.js rename to packages/perf/lib/Trace.ts index 8d37b1a84d..4c14f6e28b 100644 --- a/packages/perf/lib/Trace.js +++ b/packages/perf/lib/Trace.ts @@ -16,10 +16,18 @@ */ import { hasOwnProperty, isNumber, isString } from '@react-native-firebase/app/dist/module/common'; + import MetricWithAttributes from './MetricWithAttributes'; +import type { RNFBPerfNativeModule } from './index'; + export default class Trace extends MetricWithAttributes { - constructor(native, identifier) { + private readonly _identifier: string; + private readonly _metrics: Record; + private _started: boolean; + private _stopped: boolean; + + constructor(native: RNFBPerfNativeModule, identifier: string) { super(native); this._identifier = identifier; @@ -29,19 +37,19 @@ export default class Trace extends MetricWithAttributes { this._stopped = false; } - getMetric(metricName) { + getMetric(metricName: string): number { if (!isString(metricName)) { throw new Error("firebase.perf.Trace.getMetric(*) 'metricName' must be a string."); } - return hasOwnProperty(this._metrics, metricName) ? this._metrics[metricName] : 0; + return hasOwnProperty(this._metrics, metricName) ? this._metrics[metricName]! : 0; } - getMetrics() { + getMetrics(): Record { return Object.assign({}, this._metrics); } - putMetric(metricName, value) { + putMetric(metricName: string, value: number): void { // TODO(VALIDATION): metricName: no leading or trailing whitespace, no leading underscore '_' character, max length is 32 characters // TODO(VALIDATION): value: >= 0 if (!isString(metricName)) { @@ -55,7 +63,7 @@ export default class Trace extends MetricWithAttributes { this._metrics[metricName] = value; } - incrementMetric(metricName, incrementBy) { + incrementMetric(metricName: string, incrementBy: number): void { // TODO(VALIDATION): metricName: no leading or trailing whitespace, no leading underscore '_' character, max length is 32 characters // TODO(VALIDATION): value: >= 0 if (!isString(metricName)) { @@ -69,7 +77,7 @@ export default class Trace extends MetricWithAttributes { this._metrics[metricName] = this.getMetric(metricName) + incrementBy; } - removeMetric(metric) { + removeMetric(metric: string): void { if (!isString(metric)) { throw new Error("firebase.perf.Trace.removeMetric(*) 'metric' must be a string."); } @@ -77,7 +85,7 @@ export default class Trace extends MetricWithAttributes { delete this._metrics[metric]; } - start() { + start(): Promise { if (this._started) { return Promise.resolve(null); } @@ -86,7 +94,7 @@ export default class Trace extends MetricWithAttributes { return this.native.startTrace(this._id, this._identifier); } - stop() { + stop(): Promise { if (this._stopped) { return Promise.resolve(null); } diff --git a/packages/perf/lib/index.d.ts b/packages/perf/lib/index.d.ts deleted file mode 100644 index aa25dede79..0000000000 --- a/packages/perf/lib/index.d.ts +++ /dev/null @@ -1,583 +0,0 @@ -/* - * Copyright (c) 2016-present Invertase Limited & Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this library 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 { ReactNativeFirebase } from '@react-native-firebase/app'; - -/** - * Firebase Performance Monitoring package for React Native. - * - * #### Example 1 - * - * Access the firebase export from the `perf` package: - * - * ```js - * import { firebase } from '@react-native-firebase/perf'; - * - * // firebase.perf().X - * ``` - * - * #### Example 2 - * - * Using the default export from the `perf` package: - * - * ```js - * import perf from '@react-native-firebase/perf'; - * - * // perf().X - * ``` - * - * #### Example 3 - * - * Using the default export from the `app` package: - * - * ```js - * import firebase from '@react-native-firebase/app'; - * import '@react-native-firebase/perf'; - * - * // firebase.perf().X - * ``` - * - * @firebase perf - */ -export namespace FirebasePerformanceTypes { - /** - * Type alias describing the valid HTTP methods accepted when creating a new {@link perf.HttpMetric} instance. - * - * #### Example - * - * ```js - * const metric = perf().newHttpMetric('https://api.com/user', 'PATCH'); - * ``` - */ - import FirebaseModule = ReactNativeFirebase.FirebaseModule; - - /** - * Valid HTTP methods. - */ - export type HttpMethod = - | 'GET' - | 'HEAD' - | 'PUT' - | 'POST' - | 'PATCH' - | 'TRACE' - | 'DELETE' - | 'CONNECT' - | 'OPTIONS'; - - /** - * Trace allows you to time the beginning to end of a certain action in your app with additional metric values and attributes. - */ - export class Trace { - /** - * Returns the value of an attribute. Returns null if it does not exist. - * - * #### Example - * - * ```js - * const attribute = trace.getAttribute('userId'); - * ``` - * - * @param attribute Name of the attribute to fetch the value of. - */ - getAttribute(attribute: string): string | null; - - /** - * Sets a String value for the specified attribute. Updates the value of the attribute if it already exists. - * The maximum number of attributes that can be added is 5. - * - * #### Example - * - * ```js - * trace.putAttribute('userId', '123456789'); - * ``` - * - * @param attribute Name of the attribute. Max length is 40 chars. - * @param value Value of the attribute. Max length is 100 chars. - */ - putAttribute(attribute: string, value: string): void; - - /** - * Gets the value of the metric with the given name in the current trace. If the metric - * doesn't exist, it will not be created and a 0 is returned. - * - * #### Example - * - * ```js - * const metric = trace.getMetric('hits'); - * ``` - * - * @param metricName Name of the metric to get. - */ - getMetric(metricName: string): number; - - /** - * Returns an object of all the currently added metrics and their number values. - * - * #### Example - * - * ```js - * const metrics = trace.getMetrics(); - * - * metrics.forEach(($) => { - * console.log($); - * }); - * ``` - */ - getMetrics(): { [key: string]: number }; - - /** - * Sets the value of the named metric with the provided number. - * - * If a metric with the given name exists it will be overwritten. - * If a metric with the given name doesn't exist, a new one will be created. - * - * #### Example - * - * ```js - * trace.putMetric('hits', 1); - * ``` - * - * @param metricName Name of the metric to set. Must not have a leading or trailing whitespace, no leading underscore '_' character and have a max length of 32 characters. - * @param value The value the metric should be set to. - */ - putMetric(metricName: string, value: number): void; - - /** - * Increments the named metric by the `incrementBy` value. - * - * If a metric with the given name doesn't exist, a new one will be created starting with the value of `incrementBy`. - * - * ```js - * trace.incrementMetric('hits', 1); - * ``` - * - * @param metricName Name of the metric to increment. Must not have a leading or trailing whitespace, no leading underscore '_' character and have a max length of 32 characters. - * @param incrementBy The value the metric should be incremented by. - */ - incrementMetric(metricName: string, incrementBy: number): void; - - /** - * Removes a metric by name if it exists. - * - * #### Example - * - * ```js - * trace.removeMetric('hits'); - * ``` - * - * @param metricName Name of the metric to remove. - */ - removeMetric(metricName: string): void; - - /** - * Marks the start time of the trace. Does nothing if already started. - * - * #### Example - * - * ```js - * const trace = firebase.perf().newTrace('example'); - * await trace.start(); - * ``` - */ - start(): Promise; - - /** - * Marks the end time of the trace and queues the metric on the device for transmission. Does nothing if already stopped. - * - * * #### Example - * - * ```js - * const trace = firebase.perf().newTrace('example'); - * await trace.start(); - * trace.putMetric('hits', 1); - * await trace.stop(); - * ``` - */ - stop(): Promise; - } - - /** - * ScreenTrace allows you to record a custom screen rendering trace of slow and frozen frames. - * Throws on constructor if hardware acceleration is off or if Android is 9.0 or 9.1. - * - * @platform android Android !== 9.0.0 && Adnroid !== 9.1.0 - */ - export class ScreenTrace { - /** - * Starts a new screen trace. Does nothing if already started. - * - * #### Example - * - * ```js - * try { - * const trace = firebase.perf().newScreenTrace('FooScreen'); - * await trace.start(); - * } catch (e) { - * - * } - * ``` - * @platform android Android >= 9.0.0 - */ - start(): Promise; - /** - * Stops and sends the screen trace. - * - * #### Example - * - * ```js - * try { - * const trace = firebase.perf().newScreenTrace('FooScreen'); - * await trace.start(); - * await trace.stop(); - * } catch (e) { - * - * } - * ``` - * @platform android Android >= 9.0.0 - */ - stop(): Promise; - } - - /** - * Metric used to collect data for network requests/responses. A new instance must be used for every request/response. - */ - export class HttpMetric { - /** - * Returns the value of an attribute. Returns null if it does not exist. - * - * #### Example - * - * ```js - * const attribute = metric.getAttribute('user_role'); - * ``` - * - * @param attribute Name of the attribute to fetch the value of - */ - getAttribute(attribute: string): string | null; - - /** - * Returns an object of all the currently added attributes. - * - * #### Example - * - * ```js - * const attributes = metric.getAttributes(); - * - * attributes.forEach(($) => { - * console.log($); - * }); - * ``` - */ - getAttributes(): { [key: string]: string }; - - /** - * Sets a String value for the specified attribute. Updates the value of the attribute if it already exists. - * The maximum number of attributes that can be added is 5. - * - * #### Example - * - * ```js - * metric.putAttribute('user_role', 'admin'); - * ``` - * - * @param attribute Name of the attribute. Max length is 40 chars. - * @param value Value of the attribute. Max length is 100 chars. - */ - putAttribute(attribute: string, value: string): void; - - /** - * Removes an already added attribute. Does nothing if attribute does not exist. - * - * #### Example - * - * ```js - * metric.removeAttribute('user_role'); - * ``` - * - * @param attribute Name of the attribute to be removed. - */ - removeAttribute(attribute: string): void; - - /** - * Sets the httpResponse code of the request. - * - * #### Example - * - * ```js - * const response = await fetch(url); - * metric.setHttpResponseCode(response.status); - * ``` - * > This is required for every request, if you do not provide this your metric will not be captured. - * - * - * @param code Value must be greater than 0. Set to null to remove. Invalid usage will be logged natively. - */ - setHttpResponseCode(code: number | null): void; - - /** - * Sets the size of the request payload. - * - * #### Example - * - * ```js - * const response = await fetch(url); - * metric.setRequestPayloadSize(response.headers.get('Content-Type')); - * ``` - * - * @param bytes Value must be greater than 0. Set to null to remove. Invalid usage will be logged natively. - */ - setRequestPayloadSize(bytes: number | null): void; - - /** - * Sets the size of the response payload. - * - * #### Example - * - * ```js - * const response = await fetch(url); - * metric.setResponsePayloadSize(response.headers.get('Content-Length')); - * ``` - * - * @param bytes Value must be greater than 0. Set to null to remove. Invalid usage will be logged natively. - */ - setResponsePayloadSize(bytes: number | null): void; - - /** - * Content type of the response e.g. `text/html` or `application/json`. - * - * #### Example - * - * ```js - * const response = await fetch(url); - * metric.setResponsePayloadSize(response.headers.get('Content-Type')); - * ``` - * - * @param contentType Valid string of MIME type. Set to null to remove. Invalid usage will be logged natively. - */ - setResponseContentType(contentType: string | null): void; - - /** - * Marks the start time of the request. Does nothing if already started. - * - * #### Example - * - * ```js - * const metric = firebase.perf().newHttpMetric('https://api.com/login', 'POST'); - * await metric.start(); - * ``` - */ - start(): Promise; - - /** - * Marks the end time of the response and queues the network request metric on the device for transmission. Does nothing if already stopped. - * - * #### Example - * - * ```js - * const metric = firebase.perf().newHttpMetric('https://api.com/login', 'POST'); - * await metric.start(); - * metric.putAttribute('user_role', 'admin'); - * await metric.stop(); - * ``` - */ - stop(): Promise; - } - - export interface Statics { - SDK_VERSION: string; - } - - /** - * The Firebase Performance Monitoring service interface. - * - * > This module is available for the default app only. - * - * #### Example - * - * Get the Performance Monitoring service for the default app: - * - * ```js - * const defaultAppPerf = firebase.perf(); - * ``` - */ - export class Module extends FirebaseModule { - /** - * The current `FirebaseApp` instance for this Firebase service. - */ - app: ReactNativeFirebase.FirebaseApp; - /** - * Determines whether performance monitoring is enabled or disabled. - * - * #### Example - * - * ```js - * const isEnabled = firebase.perf().isPerformanceCollectionEnabled; - * console.log('Performance collection enabled: ', isEnabled); - * ``` - */ - isPerformanceCollectionEnabled: boolean; - /** - * Determines whether to collect 'out of the box' (i.e already setup for Firebase Performance) events. - * This can be set for iOS. Android will always return "true" as it has to be set at gradle level. - */ - instrumentationEnabled: boolean; - /** - * Determines whether performance monitoring is enabled or disabled. - * - * #### Example - * - * ```js - * const isEnabled = firebase.perf().dataCollectionEnabled; - * console.log('Performance collection enabled: ', isEnabled); - * ``` - */ - dataCollectionEnabled: boolean; - /** - * Enables or disables performance monitoring. - * - * #### Example - * - * ```js - * // Disable performance monitoring collection - * await firebase.perf().setPerformanceCollectionEnabled(false); - * ``` - * @deprecated prefer setting `dataCollectionEnabled = boolean`. - * @param enabled Should performance monitoring be enabled. For iOS only, this also toggles whether instrumentation - * is enabled. See: https://firebase.google.com/docs/reference/ios/firebaseperformance/api/reference/Classes/FIRPerformance#instrumentationenabled - */ - setPerformanceCollectionEnabled(enabled: boolean): Promise; - - /** - * Creates a Trace instance with the given identifier. - * - * #### Example - * - * ```js - * const trace = firebase.perf().newTrace('user_profile'); - * await trace.start(); - * ``` - * - * @param identifier Name of the trace, no leading or trailing whitespace allowed, no leading underscore '_' character allowed, max length is 100. - */ - newTrace(identifier: string): Trace; - - /** - * Creates a Trace instance with the given identifier and immediately starts it. - * - * #### Example - * - * ```js - * const trace = await firebase.perf().startTrace('user_profile'); - * ``` - * - * @param identifier Name of the trace, no leading or trailing whitespace allowed, no leading underscore '_' character allowed, max length is 100. - */ - startTrace(identifier: string): Promise; - - /** - * Creates a ScreenTrace instance with the given identifier. - * Throws if hardware acceleration is disabled or if Android is 9.0 or 9.1. - * - * #### Example - * - * ```js - * try { - * const trace = firebase.perf().newScreenTrace('FooScreen'); - * await trace.start(); - * } catch (e) { - * - * } - * ``` - * - * @param identifier Name of the trace, no leading or trailing whitespace allowed, no leading underscore '_' character allowed, max length is 100. - */ - newScreenTrace(identifier: string): ScreenTrace; - - /** - * Creates a ScreenTrace instance with the given identifier and immediately starts it. - * Throws if hardware acceleration is disabled or if Android is 9.0 or 9.1. - * - * #### Example - * - * ```js - * try { - * const trace = await firebase.perf().startScreenTrace('FooScreen'); - * await trace.stop(); - * } catch (e) { - * - * } - * ``` - * @platform android Android !== 9.0.0 && Android !== 9.1.0 - * - * @param identifier Name of the screen - */ - startScreenTrace(identifier: string): Promise; - - /** - * Creates a HttpMetric instance for collecting network performance data for a single request/response - * - * #### Example - * - * ```js - * const metric = firebase.perf().newHttpMetric('https://api.com/user/1', 'GET'); - * await metric.start(); - * ``` - * - * @param url A valid url String, cannot be empty - * @param httpMethod One of the values GET, PUT, POST, DELETE, HEAD, PATCH, OPTIONS, TRACE, or CONNECT - */ - newHttpMetric(url: string, httpMethod: HttpMethod): HttpMetric; - } -} - -type PerfNamespace = ReactNativeFirebase.FirebaseModuleWithStatics< - FirebasePerformanceTypes.Module, - FirebasePerformanceTypes.Statics -> & { - firebase: ReactNativeFirebase.Module; - app(name?: string): ReactNativeFirebase.FirebaseApp; -}; - -declare const defaultExport: PerfNamespace; - -export const firebase: ReactNativeFirebase.Module & { - perf: typeof defaultExport; - app(name?: string): ReactNativeFirebase.FirebaseApp & { perf(): FirebasePerformanceTypes.Module }; -}; - -export default defaultExport; - -export * from './modular'; - -/** - * Attach namespace to `firebase.` and `FirebaseApp.`. - */ -declare module '@react-native-firebase/app' { - namespace ReactNativeFirebase { - import FirebaseModuleWithStatics = ReactNativeFirebase.FirebaseModuleWithStatics; - interface Module { - perf: FirebaseModuleWithStatics< - FirebasePerformanceTypes.Module, - FirebasePerformanceTypes.Statics - >; - } - interface FirebaseApp { - perf(): FirebasePerformanceTypes.Module; - } - } -} diff --git a/packages/perf/lib/index.js b/packages/perf/lib/index.js deleted file mode 100644 index 96f9d2e57f..0000000000 --- a/packages/perf/lib/index.js +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2016-present Invertase Limited & Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this library 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 { isBoolean, isOneOf, isString } from '@react-native-firebase/app/dist/module/common'; -import { - createModuleNamespace, - FirebaseModule, - getFirebaseRoot, -} from '@react-native-firebase/app/dist/module/internal'; -import { Platform } from 'react-native'; -import HttpMetric from './HttpMetric'; -import Trace from './Trace'; -import ScreenTrace from './ScreenTrace'; -import version from './version'; - -const statics = {}; - -const namespace = 'perf'; - -const nativeModuleName = 'RNFBPerfModule'; - -const VALID_HTTP_METHODS = [ - 'CONNECT', - 'DELETE', - 'GET', - 'HEAD', - 'OPTIONS', - 'PATCH', - 'POST', - 'PUT', - 'TRACE', -]; - -class FirebasePerfModule extends FirebaseModule { - constructor(...args) { - super(...args); - this._isPerformanceCollectionEnabled = this.native.isPerformanceCollectionEnabled; - this._instrumentationEnabled = this.native.isInstrumentationEnabled; - } - - get isPerformanceCollectionEnabled() { - return this._isPerformanceCollectionEnabled; - } - - get instrumentationEnabled() { - return this._instrumentationEnabled; - } - - set instrumentationEnabled(enabled) { - if (!isBoolean(enabled)) { - throw new Error("firebase.perf().instrumentationEnabled = 'enabled' must be a boolean."); - } - if (Platform.OS == 'ios') { - // We don't change for android as it cannot be set from code, it is set at gradle build time. - this._instrumentationEnabled = enabled; - // No need to await, as it only takes effect on the next app run. - this.native.instrumentationEnabled(enabled); - } - } - - get dataCollectionEnabled() { - return this._isPerformanceCollectionEnabled; - } - - set dataCollectionEnabled(enabled) { - if (!isBoolean(enabled)) { - throw new Error("firebase.perf().dataCollectionEnabled = 'enabled' must be a boolean."); - } - this._isPerformanceCollectionEnabled = enabled; - this.native.setPerformanceCollectionEnabled(enabled); - } - - async setPerformanceCollectionEnabled(enabled) { - if (!isBoolean(enabled)) { - throw new Error( - "firebase.perf().setPerformanceCollectionEnabled(*) 'enabled' must be a boolean.", - ); - } - - if (Platform.OS == 'ios') { - // '_instrumentationEnabled' is updated here as well to maintain backward compatibility. See: - // https://github.com/invertase/react-native-firebase/commit/b705622e64d6ebf4ee026d50841e2404cf692f85 - this._instrumentationEnabled = enabled; - await this.native.instrumentationEnabled(enabled); - } - - this._isPerformanceCollectionEnabled = enabled; - return this.native.setPerformanceCollectionEnabled(enabled); - } - - newTrace(identifier) { - // TODO(VALIDATION): identifier: no leading or trailing whitespace, no leading underscore '_' - if (!isString(identifier) || identifier.length > 100) { - throw new Error( - "firebase.perf().newTrace(*) 'identifier' must be a string with a maximum length of 100 characters.", - ); - } - - return new Trace(this.native, identifier); - } - - startTrace(identifier) { - const trace = this.newTrace(identifier); - return trace.start().then(() => trace); - } - - newScreenTrace(identifier) { - if (!isString(identifier) || identifier.length > 100) { - throw new Error( - "firebase.perf().newScreenTrace(*) 'identifier' must be a string with a maximum length of 100 characters.", - ); - } - - return new ScreenTrace(this.native, identifier); - } - - startScreenTrace(identifier) { - const screenTrace = this.newScreenTrace(identifier); - return screenTrace.start().then(() => screenTrace); - } - - newHttpMetric(url, httpMethod) { - if (!isString(url)) { - throw new Error("firebase.perf().newHttpMetric(*, _) 'url' must be a string."); - } - - if (!isString(url) || !isOneOf(httpMethod, VALID_HTTP_METHODS)) { - throw new Error( - `firebase.perf().newHttpMetric(_, *) 'httpMethod' must be one of ${VALID_HTTP_METHODS.join( - ', ', - )}.`, - ); - } - - return new HttpMetric(this.native, url, httpMethod); - } -} - -// import { SDK_VERSION } from '@react-native-firebase/perf'; -export const SDK_VERSION = version; - -// import perf from '@react-native-firebase/perf'; -// perf().X(...); -export default createModuleNamespace({ - statics, - version, - namespace, - nativeModuleName, - nativeEvents: false, - hasMultiAppSupport: false, - hasCustomUrlOrRegionSupport: false, - ModuleClass: FirebasePerfModule, -}); - -export * from './modular'; - -// import perf, { firebase } from '@react-native-firebase/perf'; -// perf().X(...); -// firebase.perf().X(...); -export const firebase = getFirebaseRoot(); diff --git a/packages/perf/lib/index.ts b/packages/perf/lib/index.ts new file mode 100644 index 0000000000..b48f4cd5b1 --- /dev/null +++ b/packages/perf/lib/index.ts @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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 { isBoolean, isOneOf, isString } from '@react-native-firebase/app/dist/module/common'; +import { + createModuleNamespace, + FirebaseModule, + getFirebaseRoot, +} from '@react-native-firebase/app/dist/module/internal'; +import { Platform } from 'react-native'; + +import HttpMetric from './HttpMetric'; +import ScreenTrace from './ScreenTrace'; +import Trace from './Trace'; +import { version } from './version'; + +import type { ReactNativeFirebase } from '@react-native-firebase/app'; +import type { ModuleConfig } from '@react-native-firebase/app/dist/module/types/internal'; + +export interface RNFBPerfTraceData { + metrics: Record; + attributes: Record; +} + +export interface RNFBPerfHttpMetricData { + attributes: Record; + httpResponseCode?: number; + requestPayloadSize?: number; + responsePayloadSize?: number; + responseContentType?: string; +} + +export interface RNFBPerfNativeModule { + isPerformanceCollectionEnabled: boolean; + isInstrumentationEnabled: boolean; + setPerformanceCollectionEnabled(enabled: boolean): Promise; + instrumentationEnabled(enabled: boolean): Promise; + startTrace(id: number, identifier: string): Promise; + stopTrace(id: number, traceData: RNFBPerfTraceData): Promise; + startHttpMetric( + id: number, + url: string, + httpMethod: FirebasePerformanceTypes.HttpMethod, + ): Promise; + stopHttpMetric(id: number, metricData: RNFBPerfHttpMetricData): Promise; + startScreenTrace(id: number, identifier: string): Promise; + stopScreenTrace(id: number): Promise; +} + +/** + * Firebase Performance Monitoring package for React Native. + * + * @firebase perf + */ +// eslint-disable-next-line @typescript-eslint/no-namespace +export declare namespace FirebasePerformanceTypes { + /** + * Valid HTTP methods. + */ + export type HttpMethod = + | 'GET' + | 'HEAD' + | 'PUT' + | 'POST' + | 'PATCH' + | 'TRACE' + | 'DELETE' + | 'CONNECT' + | 'OPTIONS'; + + /** + * Trace allows you to time the beginning to end of a certain action in your app with additional metric values and attributes. + */ + export class Trace { + getAttribute(attribute: string): string | null; + putAttribute(attribute: string, value: string): void; + getMetric(metricName: string): number; + getMetrics(): { [key: string]: number }; + putMetric(metricName: string, value: number): void; + incrementMetric(metricName: string, incrementBy: number): void; + removeMetric(metricName: string): void; + start(): Promise; + stop(): Promise; + } + + /** + * ScreenTrace allows you to record a custom screen rendering trace of slow and frozen frames. + */ + export class ScreenTrace { + start(): Promise; + stop(): Promise; + } + + /** + * Metric used to collect data for network requests/responses. A new instance must be used for every request/response. + */ + export class HttpMetric { + getAttribute(attribute: string): string | null; + getAttributes(): { [key: string]: string }; + putAttribute(attribute: string, value: string): void; + removeAttribute(attribute: string): void; + setHttpResponseCode(code: number | null): void; + setRequestPayloadSize(bytes: number | null): void; + setResponsePayloadSize(bytes: number | null): void; + setResponseContentType(contentType: string | null): void; + start(): Promise; + stop(): Promise; + } + + export interface Statics { + SDK_VERSION: string; + } + + /** + * The Firebase Performance Monitoring service interface. + * + * > This module is available for the default app only. + */ + export class Module extends ReactNativeFirebase.FirebaseModule { + app: ReactNativeFirebase.FirebaseApp; + isPerformanceCollectionEnabled: boolean; + instrumentationEnabled: boolean; + dataCollectionEnabled: boolean; + /** + * @deprecated prefer setting `dataCollectionEnabled = boolean`. + */ + setPerformanceCollectionEnabled(enabled: boolean): Promise; + newTrace(identifier: string): Trace; + startTrace(identifier: string): Promise; + newScreenTrace(identifier: string): ScreenTrace; + startScreenTrace(identifier: string): Promise; + newHttpMetric(url: string, httpMethod: HttpMethod): HttpMetric; + } +} + +const statics: FirebasePerformanceTypes.Statics = { + SDK_VERSION: version, +}; + +const namespace = 'perf'; + +const nativeModuleName = 'RNFBPerfModule'; + +const VALID_HTTP_METHODS: FirebasePerformanceTypes.HttpMethod[] = [ + 'CONNECT', + 'DELETE', + 'GET', + 'HEAD', + 'OPTIONS', + 'PATCH', + 'POST', + 'PUT', + 'TRACE', +]; + +class FirebasePerfModule extends FirebaseModule { + private _isPerformanceCollectionEnabled: boolean; + private _instrumentationEnabled: boolean; + + constructor( + app: ReactNativeFirebase.FirebaseAppBase, + config: ModuleConfig, + customUrlOrRegion?: string | null, + ) { + super(app, config, customUrlOrRegion); + this._isPerformanceCollectionEnabled = this.perfNative.isPerformanceCollectionEnabled; + this._instrumentationEnabled = this.perfNative.isInstrumentationEnabled; + } + + private get perfNative(): RNFBPerfNativeModule { + return this.native as RNFBPerfNativeModule; + } + + get isPerformanceCollectionEnabled(): boolean { + return this._isPerformanceCollectionEnabled; + } + + get instrumentationEnabled(): boolean { + return this._instrumentationEnabled; + } + + set instrumentationEnabled(enabled: boolean) { + if (!isBoolean(enabled)) { + throw new Error("firebase.perf().instrumentationEnabled = 'enabled' must be a boolean."); + } + if (Platform.OS === 'ios') { + // We don't change for android as it cannot be set from code, it is set at gradle build time. + this._instrumentationEnabled = enabled; + // No need to await, as it only takes effect on the next app run. + this.perfNative.instrumentationEnabled(enabled); + } + } + + get dataCollectionEnabled(): boolean { + return this._isPerformanceCollectionEnabled; + } + + set dataCollectionEnabled(enabled: boolean) { + if (!isBoolean(enabled)) { + throw new Error("firebase.perf().dataCollectionEnabled = 'enabled' must be a boolean."); + } + this._isPerformanceCollectionEnabled = enabled; + // Keep setter behavior fire-and-forget; callers that need completion should use setPerformanceCollectionEnabled(). + void this.perfNative.setPerformanceCollectionEnabled(enabled); + } + + async setPerformanceCollectionEnabled(enabled: boolean): Promise { + if (!isBoolean(enabled)) { + throw new Error( + "firebase.perf().setPerformanceCollectionEnabled(*) 'enabled' must be a boolean.", + ); + } + + if (Platform.OS === 'ios') { + // '_instrumentationEnabled' is updated here as well to maintain backward compatibility. See: + // https://github.com/invertase/react-native-firebase/commit/b705622e64d6ebf4ee026d50841e2404cf692f85 + this._instrumentationEnabled = enabled; + await this.perfNative.instrumentationEnabled(enabled); + } + + this._isPerformanceCollectionEnabled = enabled; + return this.perfNative.setPerformanceCollectionEnabled(enabled); + } + + newTrace(identifier: string): Trace { + // TODO(VALIDATION): identifier: no leading or trailing whitespace, no leading underscore '_' + if (!isString(identifier) || identifier.length > 100) { + throw new Error( + "firebase.perf().newTrace(*) 'identifier' must be a string with a maximum length of 100 characters.", + ); + } + + return new Trace(this.perfNative, identifier); + } + + startTrace(identifier: string): Promise { + const trace = this.newTrace(identifier); + return trace.start().then(() => trace); + } + + newScreenTrace(identifier: string): ScreenTrace { + if (!isString(identifier) || identifier.length > 100) { + throw new Error( + "firebase.perf().newScreenTrace(*) 'identifier' must be a string with a maximum length of 100 characters.", + ); + } + + return new ScreenTrace(this.perfNative, identifier); + } + + startScreenTrace(identifier: string): Promise { + const screenTrace = this.newScreenTrace(identifier); + return screenTrace.start().then(() => screenTrace); + } + + newHttpMetric(url: string, httpMethod: FirebasePerformanceTypes.HttpMethod): HttpMetric { + if (!isString(url)) { + throw new Error("firebase.perf().newHttpMetric(*, _) 'url' must be a string."); + } + + if (!isOneOf(httpMethod, VALID_HTTP_METHODS)) { + throw new Error( + `firebase.perf().newHttpMetric(_, *) 'httpMethod' must be one of ${VALID_HTTP_METHODS.join( + ', ', + )}.`, + ); + } + + return new HttpMetric(this.perfNative, url, httpMethod); + } +} + +// import { SDK_VERSION } from '@react-native-firebase/perf'; +export const SDK_VERSION = version; + +export type PerfNamespace = ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< + FirebasePerformanceTypes.Module, + FirebasePerformanceTypes.Statics +> & { + firebase: ReactNativeFirebase.Module; + app(name?: string): ReactNativeFirebase.FirebaseApp; +}; + +const defaultExport = createModuleNamespace({ + statics, + version, + namespace, + nativeModuleName, + nativeEvents: false, + hasMultiAppSupport: false, + hasCustomUrlOrRegionSupport: false, + ModuleClass: FirebasePerfModule, +}) as unknown as PerfNamespace; + +export default defaultExport; + +export * from './modular'; + +// import perf, { firebase } from '@react-native-firebase/perf'; +// perf().X(...); +// firebase.perf().X(...); +export const firebase = + getFirebaseRoot() as unknown as ReactNativeFirebase.FirebaseNamespacedExport< + 'perf', + FirebasePerformanceTypes.Module, + FirebasePerformanceTypes.Statics + >; + +/** + * Attach namespace to `firebase.` and `FirebaseApp.`. + */ +declare module '@react-native-firebase/app' { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace ReactNativeFirebase { + import FirebaseModuleWithStatics = ReactNativeFirebase.FirebaseModuleWithStatics; + interface Module { + perf: FirebaseModuleWithStatics< + FirebasePerformanceTypes.Module, + FirebasePerformanceTypes.Statics + >; + } + interface FirebaseApp { + perf(): FirebasePerformanceTypes.Module; + } + } +} diff --git a/packages/perf/lib/modular/index.d.ts b/packages/perf/lib/modular/index.d.ts deleted file mode 100644 index 04a5be1ca8..0000000000 --- a/packages/perf/lib/modular/index.d.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2016-present Invertase Limited & Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this library 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 { ReactNativeFirebase } from '@react-native-firebase/app'; -import { FirebasePerformanceTypes } from '..'; - -import Performance = FirebasePerformanceTypes.Module; - -import Trace = FirebasePerformanceTypes.Module.Trace; -import HttpMethod = FirebasePerformanceTypes.HttpMethod; -import HttpMetric = FirebasePerformanceTypes.HttpMetric; -import ScreenTrace = FirebasePerformanceTypes.ScreenTrace; -import FirebaseApp = ReactNativeFirebase.FirebaseApp; - -/** - * Returns a Performance instance for the given app. - * @param app - FirebaseApp. Optional. - * @returns {Performance} - */ -export function getPerformance(app?: FirebaseApp): Performance; - -type PerformanceSettings = { - dataCollectionEnabled: boolean; -}; - -/** - * Returns a Performance instance for the given app. - * @param app - FirebaseApp. Required. - * @param settings - Optional PerformanceSettings. Set "dataCollectionEnabled" which will enable/disable Performance collection. - * @returns {Promise} - */ -export function initializePerformance( - app: FirebaseApp, - settings?: PerformanceSettings, -): Promise; - -/** - * Returns a Trace instance. - * @param perf - Performance instance. - * @param identifier - A String to identify the Trace instance. - * @returns {Trace} - */ -export function trace(perf: Performance, identifier: string): Trace; - -/** - * Returns a HttpMetric instance. - * @param perf - Performance instance. - * @param identifier - A String to identify the HttpMetric instance. - * @param httpMethod - The HTTP method for the HttpMetric instance. - * @returns {HttpMetric} - */ -export function httpMetric( - perf: Performance, - identifier: string, - httpMethod: HttpMethod, -): HttpMetric; - -/** - * Creates a ScreenTrace instance with the given identifier. - * Throws if hardware acceleration is disabled or if Android is 9.0 or 9.1. - * @platform android Android !== 9.0.0 && Android !== 9.1.0 - * @param perf - Performance instance. - * @param identifier - Name of the trace, no leading or trailing whitespace allowed, no leading underscore '_' character allowed, max length is 100. - * @returns {ScreenTrace} - */ -export function newScreenTrace(perf: Performance, identifier: string): ScreenTrace; - -/** - * Creates a ScreenTrace instance with the given identifier and immediately starts it. - * Throws if hardware acceleration is disabled or if Android is 9.0 or 9.1. - * @platform android Android !== 9.0.0 && Android !== 9.1.0 - * @param perf - Performance instance. - * @param identifier - Name of the screen. - * @returns {Promise} - */ -export function startScreenTrace(perf: Performance, identifier: string): Promise; diff --git a/packages/perf/lib/modular/index.js b/packages/perf/lib/modular/index.ts similarity index 51% rename from packages/perf/lib/modular/index.js rename to packages/perf/lib/modular/index.ts index 138b4b4d06..e5f3fe163e 100644 --- a/packages/perf/lib/modular/index.js +++ b/packages/perf/lib/modular/index.ts @@ -15,24 +15,38 @@ * */ -/** - * @typedef {import('@firebase/app').FirebaseApp} FirebaseApp - * @typedef {import('..').FirebasePerformanceTypes.Module} Performance - * @typedef {import('..').FirebasePerformanceTypes.Trace} Trace - * @typedef {import('..').FirebasePerformanceTypes.ScreenTrace} ScreenTrace - * @typedef {import('..').FirebasePerformanceTypes.HttpMetric} HttpMetric - */ - import { getApp } from '@react-native-firebase/app'; - import { MODULAR_DEPRECATION_ARG } from '@react-native-firebase/app/dist/module/common'; +import type { FirebaseApp } from '@react-native-firebase/app'; +import type { FirebasePerformanceTypes } from '..'; + +type Performance = FirebasePerformanceTypes.Module; +type Trace = FirebasePerformanceTypes.Trace; +type HttpMethod = FirebasePerformanceTypes.HttpMethod; +type HttpMetric = FirebasePerformanceTypes.HttpMetric; +type ScreenTrace = FirebasePerformanceTypes.ScreenTrace; + +type PerformanceSettings = { + dataCollectionEnabled: boolean; +}; + +type PerformanceSettingsInternal = Partial< + PerformanceSettings & { + instrumentationEnabled: boolean; + } +>; + +type WithModularDeprecationArg = F extends (...args: infer P) => infer R + ? (...args: [...P, typeof MODULAR_DEPRECATION_ARG]) => R + : never; + /** * Returns a Performance instance for the given app. * @param app - FirebaseApp. Optional. * @returns {Performance} */ -export function getPerformance(app) { +export function getPerformance(app?: FirebaseApp): Performance { if (app) { return getApp(app.name).perf(); } @@ -46,14 +60,18 @@ export function getPerformance(app) { * @param settings - Optional PerformanceSettings. Set "dataCollectionEnabled" which will enable/disable Performance collection. * @returns {Performance} */ -export async function initializePerformance(app, settings) { +export async function initializePerformance( + app: FirebaseApp, + settings?: PerformanceSettings, +): Promise { const perf = getApp(app.name).perf(); + const resolvedSettings = settings as PerformanceSettingsInternal | undefined; - if (settings && settings.dataCollectionEnabled !== undefined) { - perf.dataCollectionEnabled = settings.dataCollectionEnabled; + if (resolvedSettings && resolvedSettings.dataCollectionEnabled !== undefined) { + perf.dataCollectionEnabled = resolvedSettings.dataCollectionEnabled; } - if (settings && settings.instrumentationEnabled !== undefined) { - perf.instrumentationEnabled = settings.instrumentationEnabled; + if (resolvedSettings && resolvedSettings.instrumentationEnabled !== undefined) { + perf.instrumentationEnabled = resolvedSettings.instrumentationEnabled; } return perf; @@ -65,8 +83,12 @@ export async function initializePerformance(app, settings) { * @param identifier - A String to identify the Trace instance * @returns {Trace} */ -export function trace(perf, identifier) { - return perf.newTrace.call(perf, identifier, MODULAR_DEPRECATION_ARG); +export function trace(perf: Performance, identifier: string): Trace { + return (perf.newTrace as WithModularDeprecationArg).call( + perf, + identifier, + MODULAR_DEPRECATION_ARG, + ); } /** @@ -75,8 +97,17 @@ export function trace(perf, identifier) { * @param identifier - A String to identify the HttpMetric instance * @returns {HttpMetric} */ -export function httpMetric(perf, identifier, httpMethod) { - return perf.newHttpMetric.call(perf, identifier, httpMethod, MODULAR_DEPRECATION_ARG); +export function httpMetric( + perf: Performance, + identifier: string, + httpMethod: HttpMethod, +): HttpMetric { + return (perf.newHttpMetric as WithModularDeprecationArg).call( + perf, + identifier, + httpMethod, + MODULAR_DEPRECATION_ARG, + ); } /** @@ -87,9 +118,14 @@ export function httpMetric(perf, identifier, httpMethod) { * @param identifier Name of the trace, no leading or trailing whitespace allowed, no leading underscore '_' character allowed, max length is 100. * @returns {ScreenTrace} */ -export function newScreenTrace(perf, identifier) { - return perf.newScreenTrace.call(perf, identifier, MODULAR_DEPRECATION_ARG); +export function newScreenTrace(perf: Performance, identifier: string): ScreenTrace { + return (perf.newScreenTrace as WithModularDeprecationArg).call( + perf, + identifier, + MODULAR_DEPRECATION_ARG, + ); } + /** * Creates a ScreenTrace instance with the given identifier and immediately starts it. * Throws if hardware acceleration is disabled or if Android is 9.0 or 9.1. @@ -98,6 +134,10 @@ export function newScreenTrace(perf, identifier) { * @param identifier Name of the screen * @returns {Promise} */ -export function startScreenTrace(perf, identifier) { - return perf.startScreenTrace.call(perf, identifier, MODULAR_DEPRECATION_ARG); +export function startScreenTrace(perf: Performance, identifier: string): Promise { + return (perf.startScreenTrace as WithModularDeprecationArg).call( + perf, + identifier, + MODULAR_DEPRECATION_ARG, + ); } diff --git a/packages/perf/package.json b/packages/perf/package.json index 9cefb4fd83..e6d9c1dbdd 100644 --- a/packages/perf/package.json +++ b/packages/perf/package.json @@ -3,14 +3,15 @@ "version": "24.0.0", "author": "Invertase (http://invertase.io)", "description": "React Native Firebase - React Native Firebase provides native integration with Performance Monitoring to gain insight into key performance characteristics within your React Native application.", - "main": "lib/index.js", - "types": "lib/index.d.ts", + "main": "./dist/module/index.js", + "types": "./dist/typescript/lib/index.d.ts", "scripts": { - "build": "genversion --semi lib/version.js", + "build": "genversion --esm --semi lib/version.ts", "build:clean": "rimraf android/build && rimraf ios/build", "build:plugin": "rimraf plugin/build && tsc --build plugin", + "compile": "bob build", "lint:plugin": "eslint plugin/src/*", - "prepare": "yarn run build && yarn run build:plugin" + "prepare": "yarn run build && yarn compile && yarn run build:plugin" }, "repository": { "type": "git", @@ -33,7 +34,8 @@ "expo": ">=47.0.0" }, "devDependencies": { - "expo": "^55.0.5" + "expo": "^55.0.5", + "react-native-builder-bob": "^0.40.13" }, "peerDependenciesMeta": { "expo": { @@ -43,5 +45,35 @@ "publishConfig": { "access": "public", "provenance": true - } + }, + "exports": { + ".": { + "source": "./lib/index.ts", + "types": "./dist/typescript/lib/index.d.ts", + "default": "./dist/module/index.js" + }, + "./package.json": "./package.json" + }, + "react-native-builder-bob": { + "source": "lib", + "output": "dist", + "targets": [ + [ + "module", + { + "esm": true + } + ], + [ + "typescript", + { + "tsc": "../../node_modules/.bin/tsc" + } + ] + ] + }, + "eslintIgnore": [ + "node_modules/", + "dist/" + ] } diff --git a/packages/perf/tsconfig.json b/packages/perf/tsconfig.json new file mode 100644 index 0000000000..3e4e85585b --- /dev/null +++ b/packages/perf/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.packages.base.json", + "compilerOptions": { + "baseUrl": ".", + "rootDir": ".", + "paths": { + "@react-native-firebase/app/dist/module/common/*": ["../app/dist/typescript/lib/common/*"], + "@react-native-firebase/app/dist/module/common": ["../app/dist/typescript/lib/common"], + "@react-native-firebase/app/dist/module/internal/*": [ + "../app/dist/typescript/lib/internal/*" + ], + "@react-native-firebase/app/dist/module/internal": ["../app/dist/typescript/lib/internal"], + "@react-native-firebase/app": ["../app/dist/typescript/lib"], + "@react-native-firebase/app/dist/module/types/internal": [ + "../app/dist/typescript/lib/types/internal" + ] + } + }, + "include": ["lib/**/*"], + "exclude": ["node_modules", "dist", "__tests__", "**/*.test.ts"] +} diff --git a/yarn.lock b/yarn.lock index 47da2ad991..26594cb15b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6408,6 +6408,7 @@ __metadata: resolution: "@react-native-firebase/perf@workspace:packages/perf" dependencies: expo: "npm:^55.0.5" + react-native-builder-bob: "npm:^0.40.13" peerDependencies: "@react-native-firebase/app": 24.0.0 expo: ">=47.0.0" From 26b2bfb2cda6e06bd40be088a9e717a06d78abb5 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 12:26:45 +0100 Subject: [PATCH 02/17] refactor(perf): split modular and namespaced TypeScript surfaces --- packages/perf/lib/HttpMetric.ts | 15 +- packages/perf/lib/MetricWithAttributes.ts | 2 +- packages/perf/lib/ScreenTrace.ts | 2 +- packages/perf/lib/Trace.ts | 2 +- packages/perf/lib/index.ts | 323 +----------------- .../perf/lib/{modular/index.ts => modular.ts} | 8 +- packages/perf/lib/namespaced.ts | 205 +++++++++++ packages/perf/lib/types/internal.ts | 44 +++ packages/perf/lib/types/namespaced.ts | 162 +++++++++ packages/perf/lib/types/perf.ts | 93 +++++ 10 files changed, 516 insertions(+), 340 deletions(-) rename packages/perf/lib/{modular/index.ts => modular.ts} (93%) create mode 100644 packages/perf/lib/namespaced.ts create mode 100644 packages/perf/lib/types/internal.ts create mode 100644 packages/perf/lib/types/namespaced.ts create mode 100644 packages/perf/lib/types/perf.ts diff --git a/packages/perf/lib/HttpMetric.ts b/packages/perf/lib/HttpMetric.ts index 51a89c3f89..19912cfe31 100644 --- a/packages/perf/lib/HttpMetric.ts +++ b/packages/perf/lib/HttpMetric.ts @@ -19,15 +19,12 @@ import { isNull, isNumber, isString } from '@react-native-firebase/app/dist/modu import MetricWithAttributes from './MetricWithAttributes'; -import type { - FirebasePerformanceTypes, - RNFBPerfHttpMetricData, - RNFBPerfNativeModule, -} from './index'; +import type { RNFBPerfHttpMetricData, RNFBPerfNativeModule } from './types/internal'; +import type { HttpMethod } from './types/perf'; export default class HttpMetric extends MetricWithAttributes { private readonly _url: string; - private readonly _httpMethod: FirebasePerformanceTypes.HttpMethod; + private readonly _httpMethod: HttpMethod; private _httpResponseCode: number | null; private _requestPayloadSize: number | null; private _responsePayloadSize: number | null; @@ -35,11 +32,7 @@ export default class HttpMetric extends MetricWithAttributes { private _started: boolean; private _stopped: boolean; - constructor( - native: RNFBPerfNativeModule, - url: string, - httpMethod: FirebasePerformanceTypes.HttpMethod, - ) { + constructor(native: RNFBPerfNativeModule, url: string, httpMethod: HttpMethod) { super(native); this._url = url; diff --git a/packages/perf/lib/MetricWithAttributes.ts b/packages/perf/lib/MetricWithAttributes.ts index ff4f956012..619f963228 100644 --- a/packages/perf/lib/MetricWithAttributes.ts +++ b/packages/perf/lib/MetricWithAttributes.ts @@ -17,7 +17,7 @@ import { hasOwnProperty, isString } from '@react-native-firebase/app/dist/module/common'; -import type { RNFBPerfNativeModule } from './index'; +import type { RNFBPerfNativeModule } from './types/internal'; let id = 0; diff --git a/packages/perf/lib/ScreenTrace.ts b/packages/perf/lib/ScreenTrace.ts index 4805be2118..af46df07ad 100644 --- a/packages/perf/lib/ScreenTrace.ts +++ b/packages/perf/lib/ScreenTrace.ts @@ -17,7 +17,7 @@ import { isIOS } from '@react-native-firebase/app/dist/module/common'; -import type { RNFBPerfNativeModule } from './index'; +import type { RNFBPerfNativeModule } from './types/internal'; let id = 0; diff --git a/packages/perf/lib/Trace.ts b/packages/perf/lib/Trace.ts index 4c14f6e28b..b4bfb47cd4 100644 --- a/packages/perf/lib/Trace.ts +++ b/packages/perf/lib/Trace.ts @@ -19,7 +19,7 @@ import { hasOwnProperty, isNumber, isString } from '@react-native-firebase/app/d import MetricWithAttributes from './MetricWithAttributes'; -import type { RNFBPerfNativeModule } from './index'; +import type { RNFBPerfNativeModule } from './types/internal'; export default class Trace extends MetricWithAttributes { private readonly _identifier: string; diff --git a/packages/perf/lib/index.ts b/packages/perf/lib/index.ts index b48f4cd5b1..b1f7adf0bc 100644 --- a/packages/perf/lib/index.ts +++ b/packages/perf/lib/index.ts @@ -15,326 +15,11 @@ * */ -import { isBoolean, isOneOf, isString } from '@react-native-firebase/app/dist/module/common'; -import { - createModuleNamespace, - FirebaseModule, - getFirebaseRoot, -} from '@react-native-firebase/app/dist/module/internal'; -import { Platform } from 'react-native'; - -import HttpMetric from './HttpMetric'; -import ScreenTrace from './ScreenTrace'; -import Trace from './Trace'; -import { version } from './version'; - -import type { ReactNativeFirebase } from '@react-native-firebase/app'; -import type { ModuleConfig } from '@react-native-firebase/app/dist/module/types/internal'; - -export interface RNFBPerfTraceData { - metrics: Record; - attributes: Record; -} - -export interface RNFBPerfHttpMetricData { - attributes: Record; - httpResponseCode?: number; - requestPayloadSize?: number; - responsePayloadSize?: number; - responseContentType?: string; -} - -export interface RNFBPerfNativeModule { - isPerformanceCollectionEnabled: boolean; - isInstrumentationEnabled: boolean; - setPerformanceCollectionEnabled(enabled: boolean): Promise; - instrumentationEnabled(enabled: boolean): Promise; - startTrace(id: number, identifier: string): Promise; - stopTrace(id: number, traceData: RNFBPerfTraceData): Promise; - startHttpMetric( - id: number, - url: string, - httpMethod: FirebasePerformanceTypes.HttpMethod, - ): Promise; - stopHttpMetric(id: number, metricData: RNFBPerfHttpMetricData): Promise; - startScreenTrace(id: number, identifier: string): Promise; - stopScreenTrace(id: number): Promise; -} - -/** - * Firebase Performance Monitoring package for React Native. - * - * @firebase perf - */ -// eslint-disable-next-line @typescript-eslint/no-namespace -export declare namespace FirebasePerformanceTypes { - /** - * Valid HTTP methods. - */ - export type HttpMethod = - | 'GET' - | 'HEAD' - | 'PUT' - | 'POST' - | 'PATCH' - | 'TRACE' - | 'DELETE' - | 'CONNECT' - | 'OPTIONS'; - - /** - * Trace allows you to time the beginning to end of a certain action in your app with additional metric values and attributes. - */ - export class Trace { - getAttribute(attribute: string): string | null; - putAttribute(attribute: string, value: string): void; - getMetric(metricName: string): number; - getMetrics(): { [key: string]: number }; - putMetric(metricName: string, value: number): void; - incrementMetric(metricName: string, incrementBy: number): void; - removeMetric(metricName: string): void; - start(): Promise; - stop(): Promise; - } - - /** - * ScreenTrace allows you to record a custom screen rendering trace of slow and frozen frames. - */ - export class ScreenTrace { - start(): Promise; - stop(): Promise; - } - - /** - * Metric used to collect data for network requests/responses. A new instance must be used for every request/response. - */ - export class HttpMetric { - getAttribute(attribute: string): string | null; - getAttributes(): { [key: string]: string }; - putAttribute(attribute: string, value: string): void; - removeAttribute(attribute: string): void; - setHttpResponseCode(code: number | null): void; - setRequestPayloadSize(bytes: number | null): void; - setResponsePayloadSize(bytes: number | null): void; - setResponseContentType(contentType: string | null): void; - start(): Promise; - stop(): Promise; - } - - export interface Statics { - SDK_VERSION: string; - } - - /** - * The Firebase Performance Monitoring service interface. - * - * > This module is available for the default app only. - */ - export class Module extends ReactNativeFirebase.FirebaseModule { - app: ReactNativeFirebase.FirebaseApp; - isPerformanceCollectionEnabled: boolean; - instrumentationEnabled: boolean; - dataCollectionEnabled: boolean; - /** - * @deprecated prefer setting `dataCollectionEnabled = boolean`. - */ - setPerformanceCollectionEnabled(enabled: boolean): Promise; - newTrace(identifier: string): Trace; - startTrace(identifier: string): Promise; - newScreenTrace(identifier: string): ScreenTrace; - startScreenTrace(identifier: string): Promise; - newHttpMetric(url: string, httpMethod: HttpMethod): HttpMetric; - } -} - -const statics: FirebasePerformanceTypes.Statics = { - SDK_VERSION: version, -}; - -const namespace = 'perf'; - -const nativeModuleName = 'RNFBPerfModule'; - -const VALID_HTTP_METHODS: FirebasePerformanceTypes.HttpMethod[] = [ - 'CONNECT', - 'DELETE', - 'GET', - 'HEAD', - 'OPTIONS', - 'PATCH', - 'POST', - 'PUT', - 'TRACE', -]; - -class FirebasePerfModule extends FirebaseModule { - private _isPerformanceCollectionEnabled: boolean; - private _instrumentationEnabled: boolean; - - constructor( - app: ReactNativeFirebase.FirebaseAppBase, - config: ModuleConfig, - customUrlOrRegion?: string | null, - ) { - super(app, config, customUrlOrRegion); - this._isPerformanceCollectionEnabled = this.perfNative.isPerformanceCollectionEnabled; - this._instrumentationEnabled = this.perfNative.isInstrumentationEnabled; - } - - private get perfNative(): RNFBPerfNativeModule { - return this.native as RNFBPerfNativeModule; - } - - get isPerformanceCollectionEnabled(): boolean { - return this._isPerformanceCollectionEnabled; - } - - get instrumentationEnabled(): boolean { - return this._instrumentationEnabled; - } - - set instrumentationEnabled(enabled: boolean) { - if (!isBoolean(enabled)) { - throw new Error("firebase.perf().instrumentationEnabled = 'enabled' must be a boolean."); - } - if (Platform.OS === 'ios') { - // We don't change for android as it cannot be set from code, it is set at gradle build time. - this._instrumentationEnabled = enabled; - // No need to await, as it only takes effect on the next app run. - this.perfNative.instrumentationEnabled(enabled); - } - } - - get dataCollectionEnabled(): boolean { - return this._isPerformanceCollectionEnabled; - } - - set dataCollectionEnabled(enabled: boolean) { - if (!isBoolean(enabled)) { - throw new Error("firebase.perf().dataCollectionEnabled = 'enabled' must be a boolean."); - } - this._isPerformanceCollectionEnabled = enabled; - // Keep setter behavior fire-and-forget; callers that need completion should use setPerformanceCollectionEnabled(). - void this.perfNative.setPerformanceCollectionEnabled(enabled); - } - - async setPerformanceCollectionEnabled(enabled: boolean): Promise { - if (!isBoolean(enabled)) { - throw new Error( - "firebase.perf().setPerformanceCollectionEnabled(*) 'enabled' must be a boolean.", - ); - } - - if (Platform.OS === 'ios') { - // '_instrumentationEnabled' is updated here as well to maintain backward compatibility. See: - // https://github.com/invertase/react-native-firebase/commit/b705622e64d6ebf4ee026d50841e2404cf692f85 - this._instrumentationEnabled = enabled; - await this.perfNative.instrumentationEnabled(enabled); - } - - this._isPerformanceCollectionEnabled = enabled; - return this.perfNative.setPerformanceCollectionEnabled(enabled); - } - - newTrace(identifier: string): Trace { - // TODO(VALIDATION): identifier: no leading or trailing whitespace, no leading underscore '_' - if (!isString(identifier) || identifier.length > 100) { - throw new Error( - "firebase.perf().newTrace(*) 'identifier' must be a string with a maximum length of 100 characters.", - ); - } - - return new Trace(this.perfNative, identifier); - } - - startTrace(identifier: string): Promise { - const trace = this.newTrace(identifier); - return trace.start().then(() => trace); - } - - newScreenTrace(identifier: string): ScreenTrace { - if (!isString(identifier) || identifier.length > 100) { - throw new Error( - "firebase.perf().newScreenTrace(*) 'identifier' must be a string with a maximum length of 100 characters.", - ); - } - - return new ScreenTrace(this.perfNative, identifier); - } - - startScreenTrace(identifier: string): Promise { - const screenTrace = this.newScreenTrace(identifier); - return screenTrace.start().then(() => screenTrace); - } - - newHttpMetric(url: string, httpMethod: FirebasePerformanceTypes.HttpMethod): HttpMetric { - if (!isString(url)) { - throw new Error("firebase.perf().newHttpMetric(*, _) 'url' must be a string."); - } - - if (!isOneOf(httpMethod, VALID_HTTP_METHODS)) { - throw new Error( - `firebase.perf().newHttpMetric(_, *) 'httpMethod' must be one of ${VALID_HTTP_METHODS.join( - ', ', - )}.`, - ); - } - - return new HttpMetric(this.perfNative, url, httpMethod); - } -} - -// import { SDK_VERSION } from '@react-native-firebase/perf'; -export const SDK_VERSION = version; - -export type PerfNamespace = ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< - FirebasePerformanceTypes.Module, - FirebasePerformanceTypes.Statics -> & { - firebase: ReactNativeFirebase.Module; - app(name?: string): ReactNativeFirebase.FirebaseApp; -}; - -const defaultExport = createModuleNamespace({ - statics, - version, - namespace, - nativeModuleName, - nativeEvents: false, - hasMultiAppSupport: false, - hasCustomUrlOrRegionSupport: false, - ModuleClass: FirebasePerfModule, -}) as unknown as PerfNamespace; - -export default defaultExport; +export type * from './types/perf'; export * from './modular'; -// import perf, { firebase } from '@react-native-firebase/perf'; -// perf().X(...); -// firebase.perf().X(...); -export const firebase = - getFirebaseRoot() as unknown as ReactNativeFirebase.FirebaseNamespacedExport< - 'perf', - FirebasePerformanceTypes.Module, - FirebasePerformanceTypes.Statics - >; +export type { FirebasePerformanceTypes } from './types/namespaced'; -/** - * Attach namespace to `firebase.` and `FirebaseApp.`. - */ -declare module '@react-native-firebase/app' { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace ReactNativeFirebase { - import FirebaseModuleWithStatics = ReactNativeFirebase.FirebaseModuleWithStatics; - interface Module { - perf: FirebaseModuleWithStatics< - FirebasePerformanceTypes.Module, - FirebasePerformanceTypes.Statics - >; - } - interface FirebaseApp { - perf(): FirebasePerformanceTypes.Module; - } - } -} +export * from './namespaced'; +export { default } from './namespaced'; diff --git a/packages/perf/lib/modular/index.ts b/packages/perf/lib/modular.ts similarity index 93% rename from packages/perf/lib/modular/index.ts rename to packages/perf/lib/modular.ts index e5f3fe163e..316ddcfb90 100644 --- a/packages/perf/lib/modular/index.ts +++ b/packages/perf/lib/modular.ts @@ -19,13 +19,7 @@ import { getApp } from '@react-native-firebase/app'; import { MODULAR_DEPRECATION_ARG } from '@react-native-firebase/app/dist/module/common'; import type { FirebaseApp } from '@react-native-firebase/app'; -import type { FirebasePerformanceTypes } from '..'; - -type Performance = FirebasePerformanceTypes.Module; -type Trace = FirebasePerformanceTypes.Trace; -type HttpMethod = FirebasePerformanceTypes.HttpMethod; -type HttpMetric = FirebasePerformanceTypes.HttpMetric; -type ScreenTrace = FirebasePerformanceTypes.ScreenTrace; +import type { HttpMetric, HttpMethod, Performance, ScreenTrace, Trace } from './types/perf'; type PerformanceSettings = { dataCollectionEnabled: boolean; diff --git a/packages/perf/lib/namespaced.ts b/packages/perf/lib/namespaced.ts new file mode 100644 index 0000000000..b4a255fd1b --- /dev/null +++ b/packages/perf/lib/namespaced.ts @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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 { isBoolean, isOneOf, isString } from '@react-native-firebase/app/dist/module/common'; +import { + createModuleNamespace, + FirebaseModule, + getFirebaseRoot, +} from '@react-native-firebase/app/dist/module/internal'; +import { Platform } from 'react-native'; + +import HttpMetric from './HttpMetric'; +import ScreenTrace from './ScreenTrace'; +import Trace from './Trace'; +import { version } from './version'; + +import type { ReactNativeFirebase } from '@react-native-firebase/app'; +import type { ModuleConfig } from '@react-native-firebase/app/dist/module/types/internal'; +import type { RNFBPerfNativeModule } from './types/internal'; +import type { FirebasePerformanceTypes } from './types/namespaced'; + +const statics: FirebasePerformanceTypes.Statics = { + SDK_VERSION: version, +}; + +const namespace = 'perf'; + +const nativeModuleName = 'RNFBPerfModule'; + +const VALID_HTTP_METHODS: FirebasePerformanceTypes.HttpMethod[] = [ + 'CONNECT', + 'DELETE', + 'GET', + 'HEAD', + 'OPTIONS', + 'PATCH', + 'POST', + 'PUT', + 'TRACE', +]; + +class FirebasePerfModule extends FirebaseModule { + private _isPerformanceCollectionEnabled: boolean; + private _instrumentationEnabled: boolean; + + constructor( + app: ReactNativeFirebase.FirebaseAppBase, + config: ModuleConfig, + customUrlOrRegion?: string | null, + ) { + super(app, config, customUrlOrRegion); + this._isPerformanceCollectionEnabled = this.perfNative.isPerformanceCollectionEnabled; + this._instrumentationEnabled = this.perfNative.isInstrumentationEnabled; + } + + private get perfNative(): RNFBPerfNativeModule { + return this.native as RNFBPerfNativeModule; + } + + get isPerformanceCollectionEnabled(): boolean { + return this._isPerformanceCollectionEnabled; + } + + get instrumentationEnabled(): boolean { + return this._instrumentationEnabled; + } + + set instrumentationEnabled(enabled: boolean) { + if (!isBoolean(enabled)) { + throw new Error("firebase.perf().instrumentationEnabled = 'enabled' must be a boolean."); + } + if (Platform.OS === 'ios') { + // We don't change for android as it cannot be set from code, it is set at gradle build time. + this._instrumentationEnabled = enabled; + // No need to await, as it only takes effect on the next app run. + this.perfNative.instrumentationEnabled(enabled); + } + } + + get dataCollectionEnabled(): boolean { + return this._isPerformanceCollectionEnabled; + } + + set dataCollectionEnabled(enabled: boolean) { + if (!isBoolean(enabled)) { + throw new Error("firebase.perf().dataCollectionEnabled = 'enabled' must be a boolean."); + } + this._isPerformanceCollectionEnabled = enabled; + // Keep setter behavior fire-and-forget; callers that need completion should use setPerformanceCollectionEnabled(). + void this.perfNative.setPerformanceCollectionEnabled(enabled); + } + + async setPerformanceCollectionEnabled(enabled: boolean): Promise { + if (!isBoolean(enabled)) { + throw new Error( + "firebase.perf().setPerformanceCollectionEnabled(*) 'enabled' must be a boolean.", + ); + } + + if (Platform.OS === 'ios') { + // '_instrumentationEnabled' is updated here as well to maintain backward compatibility. See: + // https://github.com/invertase/react-native-firebase/commit/b705622e64d6ebf4ee026d50841e2404cf692f85 + this._instrumentationEnabled = enabled; + await this.perfNative.instrumentationEnabled(enabled); + } + + this._isPerformanceCollectionEnabled = enabled; + return this.perfNative.setPerformanceCollectionEnabled(enabled); + } + + newTrace(identifier: string): Trace { + // TODO(VALIDATION): identifier: no leading or trailing whitespace, no leading underscore '_' + if (!isString(identifier) || identifier.length > 100) { + throw new Error( + "firebase.perf().newTrace(*) 'identifier' must be a string with a maximum length of 100 characters.", + ); + } + + return new Trace(this.perfNative, identifier); + } + + startTrace(identifier: string): Promise { + const traceInstance = this.newTrace(identifier); + return traceInstance.start().then(() => traceInstance); + } + + newScreenTrace(identifier: string): ScreenTrace { + if (!isString(identifier) || identifier.length > 100) { + throw new Error( + "firebase.perf().newScreenTrace(*) 'identifier' must be a string with a maximum length of 100 characters.", + ); + } + + return new ScreenTrace(this.perfNative, identifier); + } + + startScreenTrace(identifier: string): Promise { + const screenTrace = this.newScreenTrace(identifier); + return screenTrace.start().then(() => screenTrace); + } + + newHttpMetric(url: string, httpMethod: FirebasePerformanceTypes.HttpMethod): HttpMetric { + if (!isString(url)) { + throw new Error("firebase.perf().newHttpMetric(*, _) 'url' must be a string."); + } + + if (!isOneOf(httpMethod, VALID_HTTP_METHODS)) { + throw new Error( + `firebase.perf().newHttpMetric(_, *) 'httpMethod' must be one of ${VALID_HTTP_METHODS.join( + ', ', + )}.`, + ); + } + + return new HttpMetric(this.perfNative, url, httpMethod); + } +} + +// import { SDK_VERSION } from '@react-native-firebase/perf'; +export const SDK_VERSION = version; + +export type PerfNamespace = ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< + FirebasePerformanceTypes.Module, + FirebasePerformanceTypes.Statics +> & { + firebase: ReactNativeFirebase.Module; + app(name?: string): ReactNativeFirebase.FirebaseApp; +}; + +const defaultExport = createModuleNamespace({ + statics, + version, + namespace, + nativeModuleName, + nativeEvents: false, + hasMultiAppSupport: false, + hasCustomUrlOrRegionSupport: false, + ModuleClass: FirebasePerfModule, +}) as unknown as PerfNamespace; + +export default defaultExport; + +// import perf, { firebase } from '@react-native-firebase/perf'; +// perf().X(...); +// firebase.perf().X(...); +export const firebase = + getFirebaseRoot() as unknown as ReactNativeFirebase.FirebaseNamespacedExport< + 'perf', + FirebasePerformanceTypes.Module, + FirebasePerformanceTypes.Statics + >; diff --git a/packages/perf/lib/types/internal.ts b/packages/perf/lib/types/internal.ts new file mode 100644 index 0000000000..b82878b598 --- /dev/null +++ b/packages/perf/lib/types/internal.ts @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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 type { HttpMethod } from './perf'; + +export interface RNFBPerfTraceData { + metrics: Record; + attributes: Record; +} + +export interface RNFBPerfHttpMetricData { + attributes: Record; + httpResponseCode?: number; + requestPayloadSize?: number; + responsePayloadSize?: number; + responseContentType?: string; +} + +export interface RNFBPerfNativeModule { + isPerformanceCollectionEnabled: boolean; + isInstrumentationEnabled: boolean; + setPerformanceCollectionEnabled(enabled: boolean): Promise; + instrumentationEnabled(enabled: boolean): Promise; + startTrace(id: number, identifier: string): Promise; + stopTrace(id: number, traceData: RNFBPerfTraceData): Promise; + startHttpMetric(id: number, url: string, httpMethod: HttpMethod): Promise; + stopHttpMetric(id: number, metricData: RNFBPerfHttpMetricData): Promise; + startScreenTrace(id: number, identifier: string): Promise; + stopScreenTrace(id: number): Promise; +} diff --git a/packages/perf/lib/types/namespaced.ts b/packages/perf/lib/types/namespaced.ts new file mode 100644 index 0000000000..51be795ce8 --- /dev/null +++ b/packages/perf/lib/types/namespaced.ts @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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 type { ReactNativeFirebase } from '@react-native-firebase/app'; + +/** + * Firebase Performance Monitoring package for React Native. + * + * #### Example 1 + * + * Access the firebase export from the `perf` package: + * + * ```js + * import { firebase } from '@react-native-firebase/perf'; + * + * // firebase.perf().X + * ``` + * + * #### Example 2 + * + * Using the default export from the `perf` package: + * + * ```js + * import perf from '@react-native-firebase/perf'; + * + * // perf().X + * ``` + * + * #### Example 3 + * + * Using the default export from the `app` package: + * + * ```js + * import firebase from '@react-native-firebase/app'; + * import '@react-native-firebase/perf'; + * + * // firebase.perf().X + * ``` + * + * @firebase perf + */ +/** + * @deprecated Use the modular type exports from `@react-native-firebase/perf` instead. + * `FirebasePerformanceTypes` is kept for backwards compatibility with the namespaced API. + */ +/* eslint-disable @typescript-eslint/no-namespace */ +export namespace FirebasePerformanceTypes { + type FirebaseModule = ReactNativeFirebase.FirebaseModule; + + /** + * @deprecated Use the `HttpMethod` type from the modular package exports instead. + */ + export type HttpMethod = + | 'GET' + | 'HEAD' + | 'PUT' + | 'POST' + | 'PATCH' + | 'TRACE' + | 'DELETE' + | 'CONNECT' + | 'OPTIONS'; + + /** + * @deprecated Use the `Trace` type from the modular package exports instead. + */ + export interface Trace { + getAttribute(attribute: string): string | null; + putAttribute(attribute: string, value: string): void; + getMetric(metricName: string): number; + getMetrics(): { [key: string]: number }; + putMetric(metricName: string, value: number): void; + incrementMetric(metricName: string, incrementBy: number): void; + removeMetric(metricName: string): void; + start(): Promise; + stop(): Promise; + } + + /** + * @deprecated Use the `ScreenTrace` type from the modular package exports instead. + */ + export interface ScreenTrace { + start(): Promise; + stop(): Promise; + } + + /** + * @deprecated Use the `HttpMetric` type from the modular package exports instead. + */ + export interface HttpMetric { + getAttribute(attribute: string): string | null; + getAttributes(): { [key: string]: string }; + putAttribute(attribute: string, value: string): void; + removeAttribute(attribute: string): void; + setHttpResponseCode(code: number | null): void; + setRequestPayloadSize(bytes: number | null): void; + setResponsePayloadSize(bytes: number | null): void; + setResponseContentType(contentType: string | null): void; + start(): Promise; + stop(): Promise; + } + + /** + * @deprecated Use static exports from the package root instead. + */ + export interface Statics { + SDK_VERSION: string; + } + + /** + * @deprecated Use the `Performance` type from the modular package exports instead. + */ + export interface Module extends FirebaseModule { + app: ReactNativeFirebase.FirebaseApp; + isPerformanceCollectionEnabled: boolean; + instrumentationEnabled: boolean; + dataCollectionEnabled: boolean; + /** + * @deprecated prefer setting `dataCollectionEnabled = boolean`. + */ + setPerformanceCollectionEnabled(enabled: boolean): Promise; + newTrace(identifier: string): Trace; + startTrace(identifier: string): Promise; + newScreenTrace(identifier: string): ScreenTrace; + startScreenTrace(identifier: string): Promise; + newHttpMetric(url: string, httpMethod: HttpMethod): HttpMetric; + } +} +/* eslint-enable @typescript-eslint/no-namespace */ + +/** + * Attach namespace to `firebase.` and `FirebaseApp.`. + */ +declare module '@react-native-firebase/app' { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace ReactNativeFirebase { + import FirebaseModuleWithStatics = ReactNativeFirebase.FirebaseModuleWithStatics; + interface Module { + perf: FirebaseModuleWithStatics< + FirebasePerformanceTypes.Module, + FirebasePerformanceTypes.Statics + >; + } + interface FirebaseApp { + perf(): FirebasePerformanceTypes.Module; + } + } +} diff --git a/packages/perf/lib/types/perf.ts b/packages/perf/lib/types/perf.ts new file mode 100644 index 0000000000..be39712d10 --- /dev/null +++ b/packages/perf/lib/types/perf.ts @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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 type { FirebaseApp } from '@react-native-firebase/app'; + +/** + * Valid HTTP methods for HTTP metrics. + */ +export type HttpMethod = + | 'GET' + | 'HEAD' + | 'PUT' + | 'POST' + | 'PATCH' + | 'TRACE' + | 'DELETE' + | 'CONNECT' + | 'OPTIONS'; + +/** + * Trace allows you to time the beginning to end of a certain action in your app with additional metric values and attributes. + */ +export interface Trace { + getAttribute(attribute: string): string | null; + putAttribute(attribute: string, value: string): void; + getMetric(metricName: string): number; + getMetrics(): { [key: string]: number }; + putMetric(metricName: string, value: number): void; + incrementMetric(metricName: string, incrementBy: number): void; + removeMetric(metricName: string): void; + start(): Promise; + stop(): Promise; +} + +/** + * ScreenTrace allows you to record a custom screen rendering trace of slow and frozen frames. + */ +export interface ScreenTrace { + start(): Promise; + stop(): Promise; +} + +/** + * Metric used to collect data for network requests/responses. A new instance must be used for every request/response. + */ +export interface HttpMetric { + getAttribute(attribute: string): string | null; + getAttributes(): { [key: string]: string }; + putAttribute(attribute: string, value: string): void; + removeAttribute(attribute: string): void; + setHttpResponseCode(code: number | null): void; + setRequestPayloadSize(bytes: number | null): void; + setResponsePayloadSize(bytes: number | null): void; + setResponseContentType(contentType: string | null): void; + start(): Promise; + stop(): Promise; +} + +/** + * The Firebase Performance Monitoring service interface (modular API). + * + * > This module is available for the default app only. + */ +export interface Performance { + /** The FirebaseApp this Performance instance is associated with */ + app: FirebaseApp; + isPerformanceCollectionEnabled: boolean; + instrumentationEnabled: boolean; + dataCollectionEnabled: boolean; + /** + * @deprecated prefer setting `dataCollectionEnabled = boolean`. + */ + setPerformanceCollectionEnabled(enabled: boolean): Promise; + newTrace(identifier: string): Trace; + startTrace(identifier: string): Promise; + newScreenTrace(identifier: string): ScreenTrace; + startScreenTrace(identifier: string): Promise; + newHttpMetric(url: string, httpMethod: HttpMethod): HttpMetric; +} From 50d07b8da42dfb063f9118e4d19a144668b243e7 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 12:29:38 +0100 Subject: [PATCH 03/17] refactor(perf): type namespaced module and native bridge --- packages/perf/lib/namespaced.ts | 29 ++++++++++++----------------- packages/perf/lib/types/internal.ts | 6 ++++++ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/perf/lib/namespaced.ts b/packages/perf/lib/namespaced.ts index b4a255fd1b..e14770f275 100644 --- a/packages/perf/lib/namespaced.ts +++ b/packages/perf/lib/namespaced.ts @@ -20,6 +20,7 @@ import { createModuleNamespace, FirebaseModule, getFirebaseRoot, + type ModuleConfig, } from '@react-native-firebase/app/dist/module/internal'; import { Platform } from 'react-native'; @@ -29,8 +30,6 @@ import Trace from './Trace'; import { version } from './version'; import type { ReactNativeFirebase } from '@react-native-firebase/app'; -import type { ModuleConfig } from '@react-native-firebase/app/dist/module/types/internal'; -import type { RNFBPerfNativeModule } from './types/internal'; import type { FirebasePerformanceTypes } from './types/namespaced'; const statics: FirebasePerformanceTypes.Statics = { @@ -39,7 +38,7 @@ const statics: FirebasePerformanceTypes.Statics = { const namespace = 'perf'; -const nativeModuleName = 'RNFBPerfModule'; +const nativeModuleName = 'RNFBPerfModule' as const; const VALID_HTTP_METHODS: FirebasePerformanceTypes.HttpMethod[] = [ 'CONNECT', @@ -53,7 +52,7 @@ const VALID_HTTP_METHODS: FirebasePerformanceTypes.HttpMethod[] = [ 'TRACE', ]; -class FirebasePerfModule extends FirebaseModule { +class FirebasePerfModule extends FirebaseModule { private _isPerformanceCollectionEnabled: boolean; private _instrumentationEnabled: boolean; @@ -63,12 +62,8 @@ class FirebasePerfModule extends FirebaseModule { customUrlOrRegion?: string | null, ) { super(app, config, customUrlOrRegion); - this._isPerformanceCollectionEnabled = this.perfNative.isPerformanceCollectionEnabled; - this._instrumentationEnabled = this.perfNative.isInstrumentationEnabled; - } - - private get perfNative(): RNFBPerfNativeModule { - return this.native as RNFBPerfNativeModule; + this._isPerformanceCollectionEnabled = this.native.isPerformanceCollectionEnabled; + this._instrumentationEnabled = this.native.isInstrumentationEnabled; } get isPerformanceCollectionEnabled(): boolean { @@ -87,7 +82,7 @@ class FirebasePerfModule extends FirebaseModule { // We don't change for android as it cannot be set from code, it is set at gradle build time. this._instrumentationEnabled = enabled; // No need to await, as it only takes effect on the next app run. - this.perfNative.instrumentationEnabled(enabled); + this.native.instrumentationEnabled(enabled); } } @@ -101,7 +96,7 @@ class FirebasePerfModule extends FirebaseModule { } this._isPerformanceCollectionEnabled = enabled; // Keep setter behavior fire-and-forget; callers that need completion should use setPerformanceCollectionEnabled(). - void this.perfNative.setPerformanceCollectionEnabled(enabled); + void this.native.setPerformanceCollectionEnabled(enabled); } async setPerformanceCollectionEnabled(enabled: boolean): Promise { @@ -115,11 +110,11 @@ class FirebasePerfModule extends FirebaseModule { // '_instrumentationEnabled' is updated here as well to maintain backward compatibility. See: // https://github.com/invertase/react-native-firebase/commit/b705622e64d6ebf4ee026d50841e2404cf692f85 this._instrumentationEnabled = enabled; - await this.perfNative.instrumentationEnabled(enabled); + await this.native.instrumentationEnabled(enabled); } this._isPerformanceCollectionEnabled = enabled; - return this.perfNative.setPerformanceCollectionEnabled(enabled); + return this.native.setPerformanceCollectionEnabled(enabled); } newTrace(identifier: string): Trace { @@ -130,7 +125,7 @@ class FirebasePerfModule extends FirebaseModule { ); } - return new Trace(this.perfNative, identifier); + return new Trace(this.native, identifier); } startTrace(identifier: string): Promise { @@ -145,7 +140,7 @@ class FirebasePerfModule extends FirebaseModule { ); } - return new ScreenTrace(this.perfNative, identifier); + return new ScreenTrace(this.native, identifier); } startScreenTrace(identifier: string): Promise { @@ -166,7 +161,7 @@ class FirebasePerfModule extends FirebaseModule { ); } - return new HttpMetric(this.perfNative, url, httpMethod); + return new HttpMetric(this.native, url, httpMethod); } } diff --git a/packages/perf/lib/types/internal.ts b/packages/perf/lib/types/internal.ts index b82878b598..565bcddccc 100644 --- a/packages/perf/lib/types/internal.ts +++ b/packages/perf/lib/types/internal.ts @@ -42,3 +42,9 @@ export interface RNFBPerfNativeModule { startScreenTrace(id: number, identifier: string): Promise; stopScreenTrace(id: number): Promise; } + +declare module '@react-native-firebase/app/dist/module/internal/NativeModules' { + interface ReactNativeFirebaseNativeModules { + RNFBPerfModule: RNFBPerfNativeModule; + } +} From 7775575cfc0fd2b53792d72f2d76ce7560d4421d Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 12:33:45 +0100 Subject: [PATCH 04/17] refactor(perf): align modular API with firebase-js-sdk --- packages/perf/e2e/HttpMetric.e2e.js | 8 +- packages/perf/e2e/Trace.e2e.js | 8 +- packages/perf/lib/MetricWithAttributes.ts | 4 +- packages/perf/lib/Trace.ts | 6 +- packages/perf/lib/modular.ts | 126 +++++++++------------- packages/perf/lib/types/internal.ts | 32 +++++- packages/perf/lib/types/namespaced.ts | 10 +- packages/perf/lib/types/perf.ts | 67 ++++++++---- packages/perf/type-test.ts | 5 +- 9 files changed, 149 insertions(+), 117 deletions(-) diff --git a/packages/perf/e2e/HttpMetric.e2e.js b/packages/perf/e2e/HttpMetric.e2e.js index e2e27ab7a6..e78f244ca9 100644 --- a/packages/perf/e2e/HttpMetric.e2e.js +++ b/packages/perf/e2e/HttpMetric.e2e.js @@ -112,10 +112,10 @@ describe('HttpMetric modular', function () { // }); describe('getAttribute()', function () { - it('should return null if attribute does not exist', async function () { + it('should return undefined if attribute does not exist', async function () { const httpMetric = firebase.perf().newHttpMetric(aCoolUrl, 'GET'); const value = httpMetric.getAttribute('inver'); - should.equal(value, null); + should.equal(value, undefined); }); it('should return an attribute string value', async function () { @@ -415,12 +415,12 @@ describe('HttpMetric modular', function () { // }); describe('getAttribute()', function () { - it('should return null if attribute does not exist', async function () { + it('should return undefined if attribute does not exist', async function () { const { getPerformance, httpMetric } = perfModular; const perf = getPerformance(); const httpMetricRequest = httpMetric(perf, aCoolUrl, 'GET'); const value = httpMetricRequest.getAttribute('inver'); - should.equal(value, null); + should.equal(value, undefined); }); it('should return an attribute string value', async function () { diff --git a/packages/perf/e2e/Trace.e2e.js b/packages/perf/e2e/Trace.e2e.js index c461d66f19..08b21018ab 100644 --- a/packages/perf/e2e/Trace.e2e.js +++ b/packages/perf/e2e/Trace.e2e.js @@ -92,10 +92,10 @@ describe('Trace modular', function () { // }); // }); describe('getAttribute()', function () { - it('should return null if attribute does not exist', async function () { + it('should return undefined if attribute does not exist', async function () { const trace = firebase.perf().newTrace('invertase'); const value = trace.getAttribute('inver'); - should.equal(value, null); + should.equal(value, undefined); }); it('should return an attribute string value', async function () { @@ -417,12 +417,12 @@ describe('Trace modular', function () { // }); // }); describe('getAttribute()', function () { - it('should return null if attribute does not exist', async function () { + it('should return undefined if attribute does not exist', async function () { const { getPerformance, trace } = perfModular; const perf = getPerformance(); const traceInvertase = trace(perf, 'invertase'); const value = traceInvertase.getAttribute('inver'); - should.equal(value, null); + should.equal(value, undefined); }); it('should return an attribute string value', async function () { diff --git a/packages/perf/lib/MetricWithAttributes.ts b/packages/perf/lib/MetricWithAttributes.ts index 619f963228..168c01c863 100644 --- a/packages/perf/lib/MetricWithAttributes.ts +++ b/packages/perf/lib/MetricWithAttributes.ts @@ -32,11 +32,11 @@ export default class MetricWithAttributes { this._attributes = {}; } - getAttribute(attribute: string): string | null { + getAttribute(attribute: string): string | undefined { if (!isString(attribute)) { throw new Error("firebase.perf.*.getAttribute(*) 'attribute' must be a string."); } - return this._attributes[attribute] || null; + return hasOwnProperty(this._attributes, attribute) ? this._attributes[attribute]! : undefined; } getAttributes(): Record { diff --git a/packages/perf/lib/Trace.ts b/packages/perf/lib/Trace.ts index b4bfb47cd4..f973afc79c 100644 --- a/packages/perf/lib/Trace.ts +++ b/packages/perf/lib/Trace.ts @@ -63,15 +63,17 @@ export default class Trace extends MetricWithAttributes { this._metrics[metricName] = value; } - incrementMetric(metricName: string, incrementBy: number): void { + incrementMetric(metricName: string, num?: number): void { // TODO(VALIDATION): metricName: no leading or trailing whitespace, no leading underscore '_' character, max length is 32 characters // TODO(VALIDATION): value: >= 0 if (!isString(metricName)) { throw new Error("firebase.perf.Trace.incrementMetric(*, _) 'metricName' must be a string."); } + const incrementBy = num ?? 1; + if (!isNumber(incrementBy)) { - throw new Error("firebase.perf.Trace.incrementMetric(_, *) 'incrementBy' must be a number."); + throw new Error("firebase.perf.Trace.incrementMetric(_, *) 'num' must be a number."); } this._metrics[metricName] = this.getMetric(metricName) + incrementBy; diff --git a/packages/perf/lib/modular.ts b/packages/perf/lib/modular.ts index 316ddcfb90..f185a996e4 100644 --- a/packages/perf/lib/modular.ts +++ b/packages/perf/lib/modular.ts @@ -19,119 +19,95 @@ import { getApp } from '@react-native-firebase/app'; import { MODULAR_DEPRECATION_ARG } from '@react-native-firebase/app/dist/module/common'; import type { FirebaseApp } from '@react-native-firebase/app'; -import type { HttpMetric, HttpMethod, Performance, ScreenTrace, Trace } from './types/perf'; +import type { + FirebasePerformance, + HttpMetric, + HttpMethod, + PerformanceSettings, + PerformanceTrace, + ScreenTrace, +} from './types/perf'; -type PerformanceSettings = { - dataCollectionEnabled: boolean; -}; - -type PerformanceSettingsInternal = Partial< - PerformanceSettings & { - instrumentationEnabled: boolean; - } ->; - -type WithModularDeprecationArg = F extends (...args: infer P) => infer R - ? (...args: [...P, typeof MODULAR_DEPRECATION_ARG]) => R - : never; +import { perfInternal as toPerfInternal } from './types/internal'; /** - * Returns a Performance instance for the given app. - * @param app - FirebaseApp. Optional. - * @returns {Performance} + * Returns a {@link FirebasePerformance} instance for the given app. + * @param app - The FirebaseApp to use. Optional; defaults to the default app. */ -export function getPerformance(app?: FirebaseApp): Performance { +export function getPerformance(app?: FirebaseApp): FirebasePerformance { if (app) { - return getApp(app.name).perf(); + return getApp(app.name).perf() as FirebasePerformance; } - return getApp().perf(); + return getApp().perf() as FirebasePerformance; } /** - * Returns a Performance instance for the given app. - * @param app - FirebaseApp. Required. - * @param settings - Optional PerformanceSettings. Set "dataCollectionEnabled" which will enable/disable Performance collection. - * @returns {Performance} + * Returns a {@link FirebasePerformance} instance for the given app (aligned with the Firebase JS SDK). + * Can only be called once per app during initialization in typical usage. + * + * @param app - The FirebaseApp to use. + * @param settings - Optional {@link PerformanceSettings}. */ -export async function initializePerformance( +export function initializePerformance( app: FirebaseApp, settings?: PerformanceSettings, -): Promise { - const perf = getApp(app.name).perf(); - const resolvedSettings = settings as PerformanceSettingsInternal | undefined; +): FirebasePerformance { + const perf = getPerformance(app); - if (resolvedSettings && resolvedSettings.dataCollectionEnabled !== undefined) { - perf.dataCollectionEnabled = resolvedSettings.dataCollectionEnabled; + if (settings?.dataCollectionEnabled !== undefined) { + perf.dataCollectionEnabled = settings.dataCollectionEnabled; } - if (resolvedSettings && resolvedSettings.instrumentationEnabled !== undefined) { - perf.instrumentationEnabled = resolvedSettings.instrumentationEnabled; + if (settings?.instrumentationEnabled !== undefined) { + perf.instrumentationEnabled = settings.instrumentationEnabled; } return perf; } /** - * Returns a Trace instance. - * @param perf - Performance instance - * @param identifier - A String to identify the Trace instance - * @returns {Trace} + * Returns a new {@link PerformanceTrace} instance. + * @param performance - The {@link FirebasePerformance} instance to use. + * @param name - The name of the trace. */ -export function trace(perf: Performance, identifier: string): Trace { - return (perf.newTrace as WithModularDeprecationArg).call( - perf, - identifier, - MODULAR_DEPRECATION_ARG, - ); +export function trace(performance: FirebasePerformance, name: string): PerformanceTrace { + return toPerfInternal(performance).newTrace(name, MODULAR_DEPRECATION_ARG); } /** - * Returns a HttpMetric instance. - * @param perf - Performance instance - * @param identifier - A String to identify the HttpMetric instance - * @returns {HttpMetric} + * Creates an {@link HttpMetric} for a URL and HTTP method (React Native). + * @param performance - The {@link FirebasePerformance} instance to use. + * @param url - Request URL. + * @param httpMethod - HTTP method. */ export function httpMetric( - perf: Performance, - identifier: string, + performance: FirebasePerformance, + url: string, httpMethod: HttpMethod, ): HttpMetric { - return (perf.newHttpMetric as WithModularDeprecationArg).call( - perf, - identifier, - httpMethod, - MODULAR_DEPRECATION_ARG, - ); + return toPerfInternal(performance).newHttpMetric(url, httpMethod, MODULAR_DEPRECATION_ARG); } /** - * Creates a ScreenTrace instance with the given identifier. + * Creates a {@link ScreenTrace} with the given screen name. * Throws if hardware acceleration is disabled or if Android is 9.0 or 9.1. * @platform android Android !== 9.0.0 && Android !== 9.1.0 - * @param perf - Performance instance - * @param identifier Name of the trace, no leading or trailing whitespace allowed, no leading underscore '_' character allowed, max length is 100. - * @returns {ScreenTrace} + * @param performance - The {@link FirebasePerformance} instance to use. + * @param screenName - Screen name; no leading/trailing whitespace, no leading `_`, max length 100. */ -export function newScreenTrace(perf: Performance, identifier: string): ScreenTrace { - return (perf.newScreenTrace as WithModularDeprecationArg).call( - perf, - identifier, - MODULAR_DEPRECATION_ARG, - ); +export function newScreenTrace(performance: FirebasePerformance, screenName: string): ScreenTrace { + return toPerfInternal(performance).newScreenTrace(screenName, MODULAR_DEPRECATION_ARG); } /** - * Creates a ScreenTrace instance with the given identifier and immediately starts it. - * Throws if hardware acceleration is disabled or if Android is 9.0 or 9.1. + * Creates a {@link ScreenTrace} and starts it immediately. * @platform android Android !== 9.0.0 && Android !== 9.1.0 - * @param perf - Performance instance - * @param identifier Name of the screen - * @returns {Promise} + * @param performance - The {@link FirebasePerformance} instance to use. + * @param screenName - Name of the screen. */ -export function startScreenTrace(perf: Performance, identifier: string): Promise { - return (perf.startScreenTrace as WithModularDeprecationArg).call( - perf, - identifier, - MODULAR_DEPRECATION_ARG, - ); +export function startScreenTrace( + performance: FirebasePerformance, + screenName: string, +): Promise { + return toPerfInternal(performance).startScreenTrace(screenName, MODULAR_DEPRECATION_ARG); } diff --git a/packages/perf/lib/types/internal.ts b/packages/perf/lib/types/internal.ts index 565bcddccc..041717bd6e 100644 --- a/packages/perf/lib/types/internal.ts +++ b/packages/perf/lib/types/internal.ts @@ -15,7 +15,37 @@ * */ -import type { HttpMethod } from './perf'; +import type { + FirebasePerformance, + HttpMetric, + HttpMethod, + PerformanceTrace, + ScreenTrace, +} from './perf'; + +export type PerfModularDeprecationArg = string; + +/** + * Namespaced perf instance used by modular entrypoints; methods accept an optional modular deprecation token. + */ +export interface PerfInternal extends FirebasePerformance { + newTrace(name: string, deprecationArg?: PerfModularDeprecationArg): PerformanceTrace; + startTrace(name: string, deprecationArg?: PerfModularDeprecationArg): Promise; + newScreenTrace(screenName: string, deprecationArg?: PerfModularDeprecationArg): ScreenTrace; + startScreenTrace( + screenName: string, + deprecationArg?: PerfModularDeprecationArg, + ): Promise; + newHttpMetric( + url: string, + httpMethod: HttpMethod, + deprecationArg?: PerfModularDeprecationArg, + ): HttpMetric; +} + +export function perfInternal(performance: FirebasePerformance): PerfInternal { + return performance as PerfInternal; +} export interface RNFBPerfTraceData { metrics: Record; diff --git a/packages/perf/lib/types/namespaced.ts b/packages/perf/lib/types/namespaced.ts index 51be795ce8..bea342e5a6 100644 --- a/packages/perf/lib/types/namespaced.ts +++ b/packages/perf/lib/types/namespaced.ts @@ -76,15 +76,15 @@ export namespace FirebasePerformanceTypes { | 'OPTIONS'; /** - * @deprecated Use the `Trace` type from the modular package exports instead. + * @deprecated Use the `PerformanceTrace` type from the modular package exports instead. */ export interface Trace { - getAttribute(attribute: string): string | null; + getAttribute(attribute: string): string | undefined; putAttribute(attribute: string, value: string): void; getMetric(metricName: string): number; getMetrics(): { [key: string]: number }; putMetric(metricName: string, value: number): void; - incrementMetric(metricName: string, incrementBy: number): void; + incrementMetric(metricName: string, incrementBy?: number): void; removeMetric(metricName: string): void; start(): Promise; stop(): Promise; @@ -102,7 +102,7 @@ export namespace FirebasePerformanceTypes { * @deprecated Use the `HttpMetric` type from the modular package exports instead. */ export interface HttpMetric { - getAttribute(attribute: string): string | null; + getAttribute(attribute: string): string | undefined; getAttributes(): { [key: string]: string }; putAttribute(attribute: string, value: string): void; removeAttribute(attribute: string): void; @@ -122,7 +122,7 @@ export namespace FirebasePerformanceTypes { } /** - * @deprecated Use the `Performance` type from the modular package exports instead. + * @deprecated Use the `FirebasePerformance` type from the modular package exports instead. */ export interface Module extends FirebaseModule { app: ReactNativeFirebase.FirebaseApp; diff --git a/packages/perf/lib/types/perf.ts b/packages/perf/lib/types/perf.ts index be39712d10..6bdc06dc2a 100644 --- a/packages/perf/lib/types/perf.ts +++ b/packages/perf/lib/types/perf.ts @@ -17,6 +17,16 @@ import type { FirebaseApp } from '@react-native-firebase/app'; +/** + * Configuration options for Performance Monitoring (aligned with {@link https://firebase.google.com/docs/reference/js/performance.performancesettings | PerformanceSettings} in the Firebase JS SDK). + */ +export interface PerformanceSettings { + /** Whether to collect custom events. */ + dataCollectionEnabled?: boolean; + /** Whether to collect out-of-the-box events. */ + instrumentationEnabled?: boolean; +} + /** * Valid HTTP methods for HTTP metrics. */ @@ -32,22 +42,29 @@ export type HttpMethod = | 'OPTIONS'; /** - * Trace allows you to time the beginning to end of a certain action in your app with additional metric values and attributes. + * A performance trace (aligned with {@link https://firebase.google.com/docs/reference/js/performance.performancetrace | PerformanceTrace} in the Firebase JS SDK). + * + * @remarks React Native uses async `start`/`stop` (native bridge). The web SDK uses synchronous `start`/`stop`. React Native also exposes `getMetrics` and `removeMetric`, which are not on the web `PerformanceTrace` type. */ -export interface Trace { - getAttribute(attribute: string): string | null; - putAttribute(attribute: string, value: string): void; +export interface PerformanceTrace { + getAttribute(attr: string): string | undefined; + putAttribute(attr: string, value: string): void; getMetric(metricName: string): number; getMetrics(): { [key: string]: number }; - putMetric(metricName: string, value: number): void; - incrementMetric(metricName: string, incrementBy: number): void; + putMetric(metricName: string, num: number): void; + incrementMetric(metricName: string, num?: number): void; removeMetric(metricName: string): void; start(): Promise; stop(): Promise; } /** - * ScreenTrace allows you to record a custom screen rendering trace of slow and frozen frames. + * @deprecated Use {@link PerformanceTrace} instead. Kept for older modular imports. + */ +export type Trace = PerformanceTrace; + +/** + * Screen trace for slow/frozen frames (React Native). */ export interface ScreenTrace { start(): Promise; @@ -55,13 +72,13 @@ export interface ScreenTrace { } /** - * Metric used to collect data for network requests/responses. A new instance must be used for every request/response. + * Network request metric (React Native; not part of the web Performance modular surface). */ export interface HttpMetric { - getAttribute(attribute: string): string | null; + getAttribute(attr: string): string | undefined; getAttributes(): { [key: string]: string }; - putAttribute(attribute: string, value: string): void; - removeAttribute(attribute: string): void; + putAttribute(attr: string, value: string): void; + removeAttribute(attr: string): void; setHttpResponseCode(code: number | null): void; setRequestPayloadSize(bytes: number | null): void; setResponsePayloadSize(bytes: number | null): void; @@ -71,23 +88,31 @@ export interface HttpMetric { } /** - * The Firebase Performance Monitoring service interface (modular API). + * Firebase Performance Monitoring service (aligned with {@link https://firebase.google.com/docs/reference/js/performance.firebaseperformance | FirebasePerformance} in the Firebase JS SDK). * - * > This module is available for the default app only. + * @remarks React Native adds collection/read APIs backed by native (`isPerformanceCollectionEnabled`, custom traces, HTTP metrics, screen traces). */ -export interface Performance { - /** The FirebaseApp this Performance instance is associated with */ +export interface FirebasePerformance { + /** The FirebaseApp this Performance instance is associated with. */ app: FirebaseApp; - isPerformanceCollectionEnabled: boolean; + /** Controls logging of automatic traces and HTTP/S network monitoring. */ instrumentationEnabled: boolean; + /** Controls logging of custom traces. */ dataCollectionEnabled: boolean; + /** Whether performance data collection is enabled for this app instance (native). */ + isPerformanceCollectionEnabled: boolean; /** - * @deprecated prefer setting `dataCollectionEnabled = boolean`. + * @deprecated Prefer assigning {@link FirebasePerformance.dataCollectionEnabled}. */ setPerformanceCollectionEnabled(enabled: boolean): Promise; - newTrace(identifier: string): Trace; - startTrace(identifier: string): Promise; - newScreenTrace(identifier: string): ScreenTrace; - startScreenTrace(identifier: string): Promise; + newTrace(name: string): PerformanceTrace; + startTrace(name: string): Promise; + newScreenTrace(screenName: string): ScreenTrace; + startScreenTrace(screenName: string): Promise; newHttpMetric(url: string, httpMethod: HttpMethod): HttpMetric; } + +/** + * @deprecated Use {@link FirebasePerformance} instead. Kept for older modular imports. + */ +export type Performance = FirebasePerformance; diff --git a/packages/perf/type-test.ts b/packages/perf/type-test.ts index 6e552c0361..38d3f556f3 100644 --- a/packages/perf/type-test.ts +++ b/packages/perf/type-test.ts @@ -111,9 +111,8 @@ console.log(modularPerf1.app.name); const modularPerf2 = getPerformance(firebase.app()); console.log(modularPerf2.app.name); -initializePerformance(firebase.app(), { dataCollectionEnabled: true }).then(initializedPerf => { - console.log(initializedPerf.app.name); -}); +const initializedPerf = initializePerformance(firebase.app(), { dataCollectionEnabled: true }); +console.log(initializedPerf.app.name); const modularTrace = trace(modularPerf1, 'modular-trace'); modularTrace.putAttribute('modular-key', 'modular-value'); From 90bb7de6cf600eba4565473823da41985edd4b10 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 12:38:01 +0100 Subject: [PATCH 05/17] test(perf): add type coverage and compare-types wiring --- .../compare-types/packages/perf/config.ts | 79 +++++++++ .../packages/perf/firebase-sdk.d.ts | 162 ++++++++++++++++++ .github/scripts/compare-types/src/registry.ts | 24 +-- packages/perf/type-test.ts | 9 + tsconfig.json | 4 +- 5 files changed, 264 insertions(+), 14 deletions(-) create mode 100644 .github/scripts/compare-types/packages/perf/config.ts create mode 100644 .github/scripts/compare-types/packages/perf/firebase-sdk.d.ts diff --git a/.github/scripts/compare-types/packages/perf/config.ts b/.github/scripts/compare-types/packages/perf/config.ts new file mode 100644 index 0000000000..52ec26edf0 --- /dev/null +++ b/.github/scripts/compare-types/packages/perf/config.ts @@ -0,0 +1,79 @@ +/** + * Known differences between the firebase-js-sdk @firebase/performance modular + * API and the @react-native-firebase/perf modular API. + */ + +import type { PackageConfig } from '../../src/types'; + +const config: PackageConfig = { + missingInRN: [], + extraInRN: [ + { + name: 'HttpMethod', + reason: + 'String union of HTTP verbs used by React Native HTTP metrics. The web Performance ' + + 'modular API does not expose HTTP metrics in the public tree-shakeable surface.', + }, + { + name: 'Trace', + reason: + 'Deprecated type alias for `PerformanceTrace` kept for older modular imports. The ' + + 'firebase-js-sdk only exports `PerformanceTrace`.', + }, + { + name: 'ScreenTrace', + reason: + 'React Native screen trace type for slow/frozen frame reporting. No equivalent exists ' + + 'in the firebase-js-sdk modular Performance API.', + }, + { + name: 'HttpMetric', + reason: + 'React Native HTTP request metric type backed by native instrumentation. The web SDK ' + + 'does not expose this type on the modular public surface.', + }, + { + name: 'Performance', + reason: + 'Deprecated type alias for `FirebasePerformance` kept for older modular imports. The ' + + 'firebase-js-sdk only exports `FirebasePerformance`.', + }, + { + name: 'httpMetric', + reason: + 'React Native modular helper that constructs an `HttpMetric`. No equivalent exists in ' + + 'the firebase-js-sdk modular Performance API.', + }, + { + name: 'newScreenTrace', + reason: + 'React Native modular helper that constructs a `ScreenTrace`. No equivalent exists in ' + + 'the firebase-js-sdk modular Performance API.', + }, + { + name: 'startScreenTrace', + reason: + 'React Native modular helper that creates and starts a `ScreenTrace`. No equivalent ' + + 'exists in the firebase-js-sdk modular Performance API.', + }, + ], + differentShape: [ + { + name: 'FirebasePerformance', + reason: + 'React Native extends the service interface with native collection state (`isPerformanceCollectionEnabled`), ' + + 'a deprecated `setPerformanceCollectionEnabled` bridge helper, and factory methods for traces, ' + + 'screen traces, and HTTP metrics. The firebase-js-sdk web type only exposes `app`, ' + + '`instrumentationEnabled`, and `dataCollectionEnabled`.', + }, + { + name: 'PerformanceTrace', + reason: + 'React Native uses async `start`/`stop` (`Promise`) because work crosses the native bridge. ' + + 'The web SDK uses synchronous `start`/`stop` and exposes `record()`. RN Firebase exposes ' + + '`getMetrics` and `removeMetric` for native-backed custom metrics instead of the web shape.', + }, + ], +}; + +export default config; diff --git a/.github/scripts/compare-types/packages/perf/firebase-sdk.d.ts b/.github/scripts/compare-types/packages/perf/firebase-sdk.d.ts new file mode 100644 index 0000000000..2b0aae0bf1 --- /dev/null +++ b/.github/scripts/compare-types/packages/perf/firebase-sdk.d.ts @@ -0,0 +1,162 @@ +/** + * Public types snapshot from the Firebase JS SDK (@firebase/performance). + * + * Source: firebase-js-sdk `node_modules/@firebase/performance/dist/src/index.d.ts` + * and `node_modules/@firebase/performance/dist/src/public_types.d.ts` + * Modality: modular (tree-shakeable) API only + * + * This file is the reference snapshot used to detect API drift between the + * firebase-js-sdk and the @react-native-firebase/perf modular API. + * + * When a new version of the firebase-js-sdk ships with type changes, update + * this file with the new public modular types from @firebase/performance. + */ + +import { FirebaseApp } from '@firebase/app'; + +/** + * Defines configuration options for the Performance Monitoring SDK. + * + * @public + */ +export interface PerformanceSettings { + /** Whether to collect custom events. */ + dataCollectionEnabled?: boolean; + /** Whether to collect out of the box events. */ + instrumentationEnabled?: boolean; +} + +/** + * The Firebase Performance Monitoring service interface. + * + * @public + */ +export interface FirebasePerformance { + /** + * The {@link @firebase/app#FirebaseApp} this `FirebasePerformance` instance is associated with. + */ + app: FirebaseApp; + /** + * Controls the logging of automatic traces and HTTP/S network monitoring. + */ + instrumentationEnabled: boolean; + /** + * Controls the logging of custom traces. + */ + dataCollectionEnabled: boolean; +} + +/** + * The interface representing a `Trace`. + * + * @public + */ +export interface PerformanceTrace { + /** + * Starts the timing for the trace instance. + */ + start(): void; + /** + * Stops the timing of the trace instance and logs the data of the instance. + */ + stop(): void; + /** + * Records a trace from given parameters. This provides a direct way to use trace without a need to + * start/stop. This is useful for use cases in which the trace cannot directly be used + * (e.g. if the duration was captured before the Performance SDK was loaded). + * + * @param startTime - trace start time since epoch in millisec. + * @param duration - The duration of the trace in millisec. + * @param options - An object which can optionally hold maps of custom metrics and + * custom attributes. + */ + record( + startTime: number, + duration: number, + options?: { + metrics?: { + [key: string]: number; + }; + attributes?: { + [key: string]: string; + }; + }, + ): void; + /** + * Adds to the value of a custom metric. If a custom metric with the provided name does not + * exist, it creates one with that name and the value equal to the given number. The value will be floored down to an + * integer. + * + * @param metricName - The name of the custom metric. + * @param num - The number to be added to the value of the custom metric. If not provided, it + * uses a default value of one. + */ + incrementMetric(metricName: string, num?: number): void; + /** + * Sets the value of the specified custom metric to the given number regardless of whether + * a metric with that name already exists on the trace instance or not. The value will be floored down to an + * integer. + * + * @param metricName - Name of the custom metric. + * @param num - Value to of the custom metric. + */ + putMetric(metricName: string, num: number): void; + /** + * Returns the value of the custom metric by that name. If a custom metric with that name does + * not exist will return zero. + * + * @param metricName - Name of the custom metric. + */ + getMetric(metricName: string): number; + /** + * Set a custom attribute of a trace to a certain value. + * + * @param attr - Name of the custom attribute. + * @param value - Value of the custom attribute. + */ + putAttribute(attr: string, value: string): void; + /** + * Retrieves the value which a custom attribute is set to. + * + * @param attr - Name of the custom attribute. + */ + getAttribute(attr: string): string | undefined; + /** + * Removes the specified custom attribute from a trace instance. + * + * @param attr - Name of the custom attribute. + */ + removeAttribute(attr: string): void; + /** + * Returns a map of all custom attributes of a trace instance. + */ + getAttributes(): { + [key: string]: string; + }; +} + +/** + * Returns a {@link FirebasePerformance} instance for the given app. + * @param app - The {@link @firebase/app#FirebaseApp} to use. + * @public + */ +export declare function getPerformance(app?: FirebaseApp): FirebasePerformance; + +/** + * Returns a {@link FirebasePerformance} instance for the given app. Can only be called once. + * @param app - The {@link @firebase/app#FirebaseApp} to use. + * @param settings - Optional settings for the {@link FirebasePerformance} instance. + * @public + */ +export declare function initializePerformance( + app: FirebaseApp, + settings?: PerformanceSettings, +): FirebasePerformance; + +/** + * Returns a new `PerformanceTrace` instance. + * @param performance - The {@link FirebasePerformance} instance to use. + * @param name - The name of the trace. + * @public + */ +export declare function trace(performance: FirebasePerformance, name: string): PerformanceTrace; diff --git a/.github/scripts/compare-types/src/registry.ts b/.github/scripts/compare-types/src/registry.ts index 5c6520a4e2..ecde9e749a 100644 --- a/.github/scripts/compare-types/src/registry.ts +++ b/.github/scripts/compare-types/src/registry.ts @@ -19,6 +19,7 @@ import databaseConfig from '../packages/database/config'; import appCheckConfig from '../packages/app-check/config'; import firestoreConfig from '../packages/firestore/config'; import firestorePipelinesConfig from '../packages/firestore-pipelines/config'; +import perfConfig from '../packages/perf/config'; import remoteConfigConfig from '../packages/remote-config/config'; const SCRIPT_DIR = path.resolve(__dirname, '..'); @@ -54,12 +55,7 @@ function rnDist(packageName: string): string { export const packages: PackageEntry[] = [ { name: 'storage', - firebaseSdkTypesPaths: [path.join( - SCRIPT_DIR, - 'packages', - 'storage', - 'storage-js-sdk.d.ts', - )], + firebaseSdkTypesPaths: [path.join(SCRIPT_DIR, 'packages', 'storage', 'storage-js-sdk.d.ts')], rnFirebaseModularFiles: [ path.join(rnDist('storage'), 'types', 'storage.d.ts'), path.join(rnDist('storage'), 'modular.d.ts'), @@ -143,9 +139,7 @@ export const packages: PackageEntry[] = [ }, { name: 'app-check', - firebaseSdkTypesPaths: [ - path.join(SCRIPT_DIR, 'packages', 'app-check', 'app-check-sdk.d.ts'), - ], + firebaseSdkTypesPaths: [path.join(SCRIPT_DIR, 'packages', 'app-check', 'app-check-sdk.d.ts')], rnFirebaseModularFiles: [ path.join(rnDist('app-check'), 'types', 'appcheck.d.ts'), path.join(rnDist('app-check'), 'modular.d.ts'), @@ -211,6 +205,14 @@ export const packages: PackageEntry[] = [ ], config: firestorePipelinesConfig, }, + { + name: 'perf', + firebaseSdkTypesPaths: [path.join(SCRIPT_DIR, 'packages', 'perf', 'firebase-sdk.d.ts')], + rnFirebaseModularFiles: [ + path.join(rnDist('perf'), 'types', 'perf.d.ts'), + path.join(rnDist('perf'), 'modular.d.ts'), + ], + rnFirebaseSupportFiles: [], + config: perfConfig, + }, ]; - - diff --git a/packages/perf/type-test.ts b/packages/perf/type-test.ts index 38d3f556f3..dadb390178 100644 --- a/packages/perf/type-test.ts +++ b/packages/perf/type-test.ts @@ -7,6 +7,9 @@ import perf, { httpMetric, newScreenTrace, startScreenTrace, + type Performance as ModularPerformance, + type PerformanceTrace, + type Trace as ModularTrace, } from '.'; console.log(perf().app); @@ -146,3 +149,9 @@ startScreenTrace(modularPerf1, 'modular-async-screen').then(startedModularScreen console.log(startedModularScreenTrace); startedModularScreenTrace.stop(); }); + +// Deprecated modular type aliases (still exported for compatibility) +const _modularTraceAlias: ModularTrace = modularTrace; +const _modularPerfAlias: ModularPerformance = modularPerf1; +void (_modularTraceAlias satisfies PerformanceTrace); +void (_modularPerfAlias satisfies typeof modularPerf1); diff --git a/tsconfig.json b/tsconfig.json index 2a4298e5e0..8164f2205f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,8 +21,6 @@ "packages/installations/lib/modular/index.d.ts", "packages/ml/lib/index.d.ts", "packages/ml/lib/modular/index.d.ts", - "packages/perf/lib/index.d.ts", - "packages/perf/lib/modular/index.d.ts", "packages/functions/type-test.ts", "packages/app/type-test.ts", "packages/app-check/type-test.ts", @@ -70,7 +68,7 @@ ], "@react-native-firebase/firestore/pipelines": [ "./packages/firestore/dist/typescript/lib/pipelines/index.d.ts" - ], + ] } }, "exclude": ["node_modules", "**/*.spec.ts", "packages/**/dist"] From c3ddf2cefe56844543b910cb93296061bc6d3297 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 12:41:19 +0100 Subject: [PATCH 06/17] refactor(perf): reduce compare-types drift from firebase-js-sdk --- .../scripts/compare-types/packages/perf/config.ts | 12 ------------ packages/perf/lib/index.ts | 9 ++++++++- packages/perf/lib/types/perf.ts | 10 ---------- packages/perf/type-test.ts | 9 --------- 4 files changed, 8 insertions(+), 32 deletions(-) diff --git a/.github/scripts/compare-types/packages/perf/config.ts b/.github/scripts/compare-types/packages/perf/config.ts index 52ec26edf0..6da32fe60a 100644 --- a/.github/scripts/compare-types/packages/perf/config.ts +++ b/.github/scripts/compare-types/packages/perf/config.ts @@ -14,12 +14,6 @@ const config: PackageConfig = { 'String union of HTTP verbs used by React Native HTTP metrics. The web Performance ' + 'modular API does not expose HTTP metrics in the public tree-shakeable surface.', }, - { - name: 'Trace', - reason: - 'Deprecated type alias for `PerformanceTrace` kept for older modular imports. The ' + - 'firebase-js-sdk only exports `PerformanceTrace`.', - }, { name: 'ScreenTrace', reason: @@ -32,12 +26,6 @@ const config: PackageConfig = { 'React Native HTTP request metric type backed by native instrumentation. The web SDK ' + 'does not expose this type on the modular public surface.', }, - { - name: 'Performance', - reason: - 'Deprecated type alias for `FirebasePerformance` kept for older modular imports. The ' + - 'firebase-js-sdk only exports `FirebasePerformance`.', - }, { name: 'httpMetric', reason: diff --git a/packages/perf/lib/index.ts b/packages/perf/lib/index.ts index b1f7adf0bc..874aebeeef 100644 --- a/packages/perf/lib/index.ts +++ b/packages/perf/lib/index.ts @@ -15,7 +15,14 @@ * */ -export type * from './types/perf'; +export type { + FirebasePerformance, + HttpMetric, + HttpMethod, + PerformanceSettings, + PerformanceTrace, + ScreenTrace, +} from './types/perf'; export * from './modular'; diff --git a/packages/perf/lib/types/perf.ts b/packages/perf/lib/types/perf.ts index 6bdc06dc2a..7fe8381eef 100644 --- a/packages/perf/lib/types/perf.ts +++ b/packages/perf/lib/types/perf.ts @@ -58,11 +58,6 @@ export interface PerformanceTrace { stop(): Promise; } -/** - * @deprecated Use {@link PerformanceTrace} instead. Kept for older modular imports. - */ -export type Trace = PerformanceTrace; - /** * Screen trace for slow/frozen frames (React Native). */ @@ -111,8 +106,3 @@ export interface FirebasePerformance { startScreenTrace(screenName: string): Promise; newHttpMetric(url: string, httpMethod: HttpMethod): HttpMetric; } - -/** - * @deprecated Use {@link FirebasePerformance} instead. Kept for older modular imports. - */ -export type Performance = FirebasePerformance; diff --git a/packages/perf/type-test.ts b/packages/perf/type-test.ts index dadb390178..38d3f556f3 100644 --- a/packages/perf/type-test.ts +++ b/packages/perf/type-test.ts @@ -7,9 +7,6 @@ import perf, { httpMetric, newScreenTrace, startScreenTrace, - type Performance as ModularPerformance, - type PerformanceTrace, - type Trace as ModularTrace, } from '.'; console.log(perf().app); @@ -149,9 +146,3 @@ startScreenTrace(modularPerf1, 'modular-async-screen').then(startedModularScreen console.log(startedModularScreenTrace); startedModularScreenTrace.stop(); }); - -// Deprecated modular type aliases (still exported for compatibility) -const _modularTraceAlias: ModularTrace = modularTrace; -const _modularPerfAlias: ModularPerformance = modularPerf1; -void (_modularTraceAlias satisfies PerformanceTrace); -void (_modularPerfAlias satisfies typeof modularPerf1); From 7b4aa4f35f8561d47ddfdd3d4f45b5d7fc55c66a Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 14:59:56 +0100 Subject: [PATCH 07/17] chore: move function into modular --- packages/perf/lib/modular.ts | 14 +++++++++----- packages/perf/lib/types/internal.ts | 4 ---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/perf/lib/modular.ts b/packages/perf/lib/modular.ts index f185a996e4..85d462c423 100644 --- a/packages/perf/lib/modular.ts +++ b/packages/perf/lib/modular.ts @@ -28,7 +28,11 @@ import type { ScreenTrace, } from './types/perf'; -import { perfInternal as toPerfInternal } from './types/internal'; +import type { PerfInternal } from './types/internal'; + +function perfInternal(performance: FirebasePerformance): PerfInternal { + return performance as PerfInternal; +} /** * Returns a {@link FirebasePerformance} instance for the given app. @@ -71,7 +75,7 @@ export function initializePerformance( * @param name - The name of the trace. */ export function trace(performance: FirebasePerformance, name: string): PerformanceTrace { - return toPerfInternal(performance).newTrace(name, MODULAR_DEPRECATION_ARG); + return perfInternal(performance).newTrace(name, MODULAR_DEPRECATION_ARG); } /** @@ -85,7 +89,7 @@ export function httpMetric( url: string, httpMethod: HttpMethod, ): HttpMetric { - return toPerfInternal(performance).newHttpMetric(url, httpMethod, MODULAR_DEPRECATION_ARG); + return perfInternal(performance).newHttpMetric(url, httpMethod, MODULAR_DEPRECATION_ARG); } /** @@ -96,7 +100,7 @@ export function httpMetric( * @param screenName - Screen name; no leading/trailing whitespace, no leading `_`, max length 100. */ export function newScreenTrace(performance: FirebasePerformance, screenName: string): ScreenTrace { - return toPerfInternal(performance).newScreenTrace(screenName, MODULAR_DEPRECATION_ARG); + return perfInternal(performance).newScreenTrace(screenName, MODULAR_DEPRECATION_ARG); } /** @@ -109,5 +113,5 @@ export function startScreenTrace( performance: FirebasePerformance, screenName: string, ): Promise { - return toPerfInternal(performance).startScreenTrace(screenName, MODULAR_DEPRECATION_ARG); + return perfInternal(performance).startScreenTrace(screenName, MODULAR_DEPRECATION_ARG); } diff --git a/packages/perf/lib/types/internal.ts b/packages/perf/lib/types/internal.ts index 041717bd6e..9a1dae80a8 100644 --- a/packages/perf/lib/types/internal.ts +++ b/packages/perf/lib/types/internal.ts @@ -43,10 +43,6 @@ export interface PerfInternal extends FirebasePerformance { ): HttpMetric; } -export function perfInternal(performance: FirebasePerformance): PerfInternal { - return performance as PerfInternal; -} - export interface RNFBPerfTraceData { metrics: Record; attributes: Record; From 7b7bf51808c6a477d5baa9c78632a57d86dc2fba Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 15:00:11 +0100 Subject: [PATCH 08/17] chore: revert to existing behaviour --- packages/perf/e2e/HttpMetric.e2e.js | 8 ++++---- packages/perf/e2e/Trace.e2e.js | 8 ++++---- packages/perf/lib/MetricWithAttributes.ts | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/perf/e2e/HttpMetric.e2e.js b/packages/perf/e2e/HttpMetric.e2e.js index e78f244ca9..e2e27ab7a6 100644 --- a/packages/perf/e2e/HttpMetric.e2e.js +++ b/packages/perf/e2e/HttpMetric.e2e.js @@ -112,10 +112,10 @@ describe('HttpMetric modular', function () { // }); describe('getAttribute()', function () { - it('should return undefined if attribute does not exist', async function () { + it('should return null if attribute does not exist', async function () { const httpMetric = firebase.perf().newHttpMetric(aCoolUrl, 'GET'); const value = httpMetric.getAttribute('inver'); - should.equal(value, undefined); + should.equal(value, null); }); it('should return an attribute string value', async function () { @@ -415,12 +415,12 @@ describe('HttpMetric modular', function () { // }); describe('getAttribute()', function () { - it('should return undefined if attribute does not exist', async function () { + it('should return null if attribute does not exist', async function () { const { getPerformance, httpMetric } = perfModular; const perf = getPerformance(); const httpMetricRequest = httpMetric(perf, aCoolUrl, 'GET'); const value = httpMetricRequest.getAttribute('inver'); - should.equal(value, undefined); + should.equal(value, null); }); it('should return an attribute string value', async function () { diff --git a/packages/perf/e2e/Trace.e2e.js b/packages/perf/e2e/Trace.e2e.js index 08b21018ab..c461d66f19 100644 --- a/packages/perf/e2e/Trace.e2e.js +++ b/packages/perf/e2e/Trace.e2e.js @@ -92,10 +92,10 @@ describe('Trace modular', function () { // }); // }); describe('getAttribute()', function () { - it('should return undefined if attribute does not exist', async function () { + it('should return null if attribute does not exist', async function () { const trace = firebase.perf().newTrace('invertase'); const value = trace.getAttribute('inver'); - should.equal(value, undefined); + should.equal(value, null); }); it('should return an attribute string value', async function () { @@ -417,12 +417,12 @@ describe('Trace modular', function () { // }); // }); describe('getAttribute()', function () { - it('should return undefined if attribute does not exist', async function () { + it('should return null if attribute does not exist', async function () { const { getPerformance, trace } = perfModular; const perf = getPerformance(); const traceInvertase = trace(perf, 'invertase'); const value = traceInvertase.getAttribute('inver'); - should.equal(value, undefined); + should.equal(value, null); }); it('should return an attribute string value', async function () { diff --git a/packages/perf/lib/MetricWithAttributes.ts b/packages/perf/lib/MetricWithAttributes.ts index 168c01c863..892aca5c85 100644 --- a/packages/perf/lib/MetricWithAttributes.ts +++ b/packages/perf/lib/MetricWithAttributes.ts @@ -32,11 +32,11 @@ export default class MetricWithAttributes { this._attributes = {}; } - getAttribute(attribute: string): string | undefined { + getAttribute(attribute: string): string | null { if (!isString(attribute)) { throw new Error("firebase.perf.*.getAttribute(*) 'attribute' must be a string."); } - return hasOwnProperty(this._attributes, attribute) ? this._attributes[attribute]! : undefined; + return hasOwnProperty(this._attributes, attribute) ? this._attributes[attribute]! : null; } getAttributes(): Record { From 7ce15298802f0afe4a0bdbb7fec646311dcd54f4 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 15:02:35 +0100 Subject: [PATCH 09/17] chore: wildcard type export --- packages/perf/lib/index.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/perf/lib/index.ts b/packages/perf/lib/index.ts index 874aebeeef..b1f7adf0bc 100644 --- a/packages/perf/lib/index.ts +++ b/packages/perf/lib/index.ts @@ -15,14 +15,7 @@ * */ -export type { - FirebasePerformance, - HttpMetric, - HttpMethod, - PerformanceSettings, - PerformanceTrace, - ScreenTrace, -} from './types/perf'; +export type * from './types/perf'; export * from './modular'; From d922b5eddb61e54280531879f0a0befae5fa4a7d Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 15:03:03 +0100 Subject: [PATCH 10/17] chore: move methods of Perf instance on to Internal --- packages/perf/lib/types/internal.ts | 4 ++++ packages/perf/lib/types/perf.ts | 9 --------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/perf/lib/types/internal.ts b/packages/perf/lib/types/internal.ts index 9a1dae80a8..52bc86f14b 100644 --- a/packages/perf/lib/types/internal.ts +++ b/packages/perf/lib/types/internal.ts @@ -29,6 +29,10 @@ export type PerfModularDeprecationArg = string; * Namespaced perf instance used by modular entrypoints; methods accept an optional modular deprecation token. */ export interface PerfInternal extends FirebasePerformance { + /** + * @deprecated Prefer assigning {@link FirebasePerformance.dataCollectionEnabled}. + */ + setPerformanceCollectionEnabled(enabled: boolean): Promise; newTrace(name: string, deprecationArg?: PerfModularDeprecationArg): PerformanceTrace; startTrace(name: string, deprecationArg?: PerfModularDeprecationArg): Promise; newScreenTrace(screenName: string, deprecationArg?: PerfModularDeprecationArg): ScreenTrace; diff --git a/packages/perf/lib/types/perf.ts b/packages/perf/lib/types/perf.ts index 7fe8381eef..247132ab43 100644 --- a/packages/perf/lib/types/perf.ts +++ b/packages/perf/lib/types/perf.ts @@ -96,13 +96,4 @@ export interface FirebasePerformance { dataCollectionEnabled: boolean; /** Whether performance data collection is enabled for this app instance (native). */ isPerformanceCollectionEnabled: boolean; - /** - * @deprecated Prefer assigning {@link FirebasePerformance.dataCollectionEnabled}. - */ - setPerformanceCollectionEnabled(enabled: boolean): Promise; - newTrace(name: string): PerformanceTrace; - startTrace(name: string): Promise; - newScreenTrace(screenName: string): ScreenTrace; - startScreenTrace(screenName: string): Promise; - newHttpMetric(url: string, httpMethod: HttpMethod): HttpMetric; } From 815ffd201b0a406a2505eade855f664f35197232 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 15:07:22 +0100 Subject: [PATCH 11/17] chore: does not exist currently --- packages/perf/lib/MetricWithAttributes.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/perf/lib/MetricWithAttributes.ts b/packages/perf/lib/MetricWithAttributes.ts index 892aca5c85..ca86b4d3b4 100644 --- a/packages/perf/lib/MetricWithAttributes.ts +++ b/packages/perf/lib/MetricWithAttributes.ts @@ -65,12 +65,4 @@ export default class MetricWithAttributes { this._attributes[attribute] = value; } - - removeAttribute(attribute: string): void { - if (!isString(attribute)) { - throw new Error("firebase.perf.*.removeAttribute(*) 'attribute' must be a string."); - } - - delete this._attributes[attribute]; - } } From 7ed402666b73963d94a2dc238a5417d09cf4ea5e Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 15:17:08 +0100 Subject: [PATCH 12/17] test: fix tests that combine modular and namespace --- packages/perf/__tests__/perf.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/perf/__tests__/perf.test.ts b/packages/perf/__tests__/perf.test.ts index ad6bbe1eb1..133685a052 100644 --- a/packages/perf/__tests__/perf.test.ts +++ b/packages/perf/__tests__/perf.test.ts @@ -200,6 +200,7 @@ describe('Performance Monitoring', function () { const perf = getPerformance(); perfV9Deprecation( () => trace(perf, 'invertase'), + // @ts-expect-error Combines modular and namespace API () => perf.newTrace('invertase'), 'newTrace', ); @@ -209,6 +210,7 @@ describe('Performance Monitoring', function () { const perf = getPerformance(); perfV9Deprecation( () => httpMetric(perf, 'https://invertase.io', 'GET'), + // @ts-expect-error Combines modular and namespace API () => perf.newHttpMetric('https://invertase.io', 'GET'), 'newHttpMetric', ); @@ -218,6 +220,7 @@ describe('Performance Monitoring', function () { const perf = getPerformance(); perfV9Deprecation( () => newScreenTrace(perf, 'invertase'), + // @ts-expect-error Combines modular and namespace API () => perf.newScreenTrace('invertase'), 'newScreenTrace', ); @@ -227,6 +230,7 @@ describe('Performance Monitoring', function () { const perf = getPerformance(); perfV9Deprecation( () => startScreenTrace(perf, 'invertase'), + // @ts-expect-error Combines modular and namespace API () => perf.startScreenTrace('invertase'), 'startScreenTrace', ); From 5cd91b5fe72209f9c299c42188d300667ea252cf Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 1 May 2026 15:17:16 +0100 Subject: [PATCH 13/17] chore: typedoc update --- packages/perf/lib/modular.ts | 5 ++--- packages/perf/typedoc.json | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/perf/lib/modular.ts b/packages/perf/lib/modular.ts index 85d462c423..b5d713e373 100644 --- a/packages/perf/lib/modular.ts +++ b/packages/perf/lib/modular.ts @@ -94,8 +94,7 @@ export function httpMetric( /** * Creates a {@link ScreenTrace} with the given screen name. - * Throws if hardware acceleration is disabled or if Android is 9.0 or 9.1. - * @platform android Android !== 9.0.0 && Android !== 9.1.0 + * Android only; throws if hardware acceleration is disabled or if Android is 9.0 or 9.1. * @param performance - The {@link FirebasePerformance} instance to use. * @param screenName - Screen name; no leading/trailing whitespace, no leading `_`, max length 100. */ @@ -105,7 +104,7 @@ export function newScreenTrace(performance: FirebasePerformance, screenName: str /** * Creates a {@link ScreenTrace} and starts it immediately. - * @platform android Android !== 9.0.0 && Android !== 9.1.0 + * Android only; throws if hardware acceleration is disabled or if Android is 9.0 or 9.1. * @param performance - The {@link FirebasePerformance} instance to use. * @param screenName - Name of the screen. */ diff --git a/packages/perf/typedoc.json b/packages/perf/typedoc.json index ce7104d0e0..55ee480416 100644 --- a/packages/perf/typedoc.json +++ b/packages/perf/typedoc.json @@ -1,5 +1,5 @@ { "$schema": "https://typedoc.org/schema.json", - "entryPoints": ["lib/index.d.ts"], - "intentionallyNotExported": ["PerfNamespace"] + "entryPoints": ["lib/modular.ts", "lib/types/perf.ts"], + "tsconfig": "tsconfig.json" } From 14959ba61a4adc54ef023319fc33d056222c7945 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 12 May 2026 14:30:39 +0100 Subject: [PATCH 14/17] chore: add perf-config --- .../compare-types/configs/perf-config.ts | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/scripts/compare-types/configs/perf-config.ts diff --git a/.github/scripts/compare-types/configs/perf-config.ts b/.github/scripts/compare-types/configs/perf-config.ts new file mode 100644 index 0000000000..6da32fe60a --- /dev/null +++ b/.github/scripts/compare-types/configs/perf-config.ts @@ -0,0 +1,67 @@ +/** + * Known differences between the firebase-js-sdk @firebase/performance modular + * API and the @react-native-firebase/perf modular API. + */ + +import type { PackageConfig } from '../../src/types'; + +const config: PackageConfig = { + missingInRN: [], + extraInRN: [ + { + name: 'HttpMethod', + reason: + 'String union of HTTP verbs used by React Native HTTP metrics. The web Performance ' + + 'modular API does not expose HTTP metrics in the public tree-shakeable surface.', + }, + { + name: 'ScreenTrace', + reason: + 'React Native screen trace type for slow/frozen frame reporting. No equivalent exists ' + + 'in the firebase-js-sdk modular Performance API.', + }, + { + name: 'HttpMetric', + reason: + 'React Native HTTP request metric type backed by native instrumentation. The web SDK ' + + 'does not expose this type on the modular public surface.', + }, + { + name: 'httpMetric', + reason: + 'React Native modular helper that constructs an `HttpMetric`. No equivalent exists in ' + + 'the firebase-js-sdk modular Performance API.', + }, + { + name: 'newScreenTrace', + reason: + 'React Native modular helper that constructs a `ScreenTrace`. No equivalent exists in ' + + 'the firebase-js-sdk modular Performance API.', + }, + { + name: 'startScreenTrace', + reason: + 'React Native modular helper that creates and starts a `ScreenTrace`. No equivalent ' + + 'exists in the firebase-js-sdk modular Performance API.', + }, + ], + differentShape: [ + { + name: 'FirebasePerformance', + reason: + 'React Native extends the service interface with native collection state (`isPerformanceCollectionEnabled`), ' + + 'a deprecated `setPerformanceCollectionEnabled` bridge helper, and factory methods for traces, ' + + 'screen traces, and HTTP metrics. The firebase-js-sdk web type only exposes `app`, ' + + '`instrumentationEnabled`, and `dataCollectionEnabled`.', + }, + { + name: 'PerformanceTrace', + reason: + 'React Native uses async `start`/`stop` (`Promise`) because work crosses the native bridge. ' + + 'The web SDK uses synchronous `start`/`stop` and exposes `record()`. RN Firebase exposes ' + + '`getMetrics` and `removeMetric` for native-backed custom metrics instead of the web shape.', + }, + ], +}; + +export default config; From c4fe09c892d5ebbf18a31e0ddd3202076cab84f3 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 12 May 2026 14:32:47 +0100 Subject: [PATCH 15/17] yarn.lock --- yarn.lock | 269 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 232 insertions(+), 37 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4832e300ed..425ee1acc5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1233,7 +1233,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-flow-strip-types@npm:^7.25.2, @babel/plugin-transform-flow-strip-types@npm:^7.27.1": +"@babel/plugin-transform-flow-strip-types@npm:^7.25.2, @babel/plugin-transform-flow-strip-types@npm:^7.26.5, @babel/plugin-transform-flow-strip-types@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-flow-strip-types@npm:7.27.1" dependencies: @@ -1375,6 +1375,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-systemjs@npm:^7.29.4": + version: 7.29.4 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.29.4" + dependencies: + "@babel/helper-module-transforms": "npm:^7.28.6" + "@babel/helper-plugin-utils": "npm:^7.28.6" + "@babel/helper-validator-identifier": "npm:^7.28.5" + "@babel/traverse": "npm:^7.29.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/79269e6ec8ec831bb63bf1c7cc1a980e28da785e92b36d42612f0139e4044499b99aa109fca849e1a156c092aabf6c24d145f4cabf2ac9ea84ef468852fe4c03 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-umd@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-modules-umd@npm:7.27.1" @@ -1804,7 +1818,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-strict-mode@npm:^7.27.1": +"@babel/plugin-transform-strict-mode@npm:^7.24.7, @babel/plugin-transform-strict-mode@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-strict-mode@npm:7.27.1" dependencies: @@ -1899,6 +1913,87 @@ __metadata: languageName: node linkType: hard +"@babel/preset-env@npm:^7.25.2": + version: 7.29.5 + resolution: "@babel/preset-env@npm:7.29.5" + dependencies: + "@babel/compat-data": "npm:^7.29.3" + "@babel/helper-compilation-targets": "npm:^7.28.6" + "@babel/helper-plugin-utils": "npm:^7.28.6" + "@babel/helper-validator-option": "npm:^7.27.1" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "npm:^7.28.5" + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "npm:^7.27.1" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.27.1" + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "npm:^7.29.3" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.27.1" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.28.6" + "@babel/plugin-proposal-private-property-in-object": "npm:7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-import-assertions": "npm:^7.28.6" + "@babel/plugin-syntax-import-attributes": "npm:^7.28.6" + "@babel/plugin-syntax-unicode-sets-regex": "npm:^7.18.6" + "@babel/plugin-transform-arrow-functions": "npm:^7.27.1" + "@babel/plugin-transform-async-generator-functions": "npm:^7.29.0" + "@babel/plugin-transform-async-to-generator": "npm:^7.28.6" + "@babel/plugin-transform-block-scoped-functions": "npm:^7.27.1" + "@babel/plugin-transform-block-scoping": "npm:^7.28.6" + "@babel/plugin-transform-class-properties": "npm:^7.28.6" + "@babel/plugin-transform-class-static-block": "npm:^7.28.6" + "@babel/plugin-transform-classes": "npm:^7.28.6" + "@babel/plugin-transform-computed-properties": "npm:^7.28.6" + "@babel/plugin-transform-destructuring": "npm:^7.28.5" + "@babel/plugin-transform-dotall-regex": "npm:^7.28.6" + "@babel/plugin-transform-duplicate-keys": "npm:^7.27.1" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "npm:^7.29.0" + "@babel/plugin-transform-dynamic-import": "npm:^7.27.1" + "@babel/plugin-transform-explicit-resource-management": "npm:^7.28.6" + "@babel/plugin-transform-exponentiation-operator": "npm:^7.28.6" + "@babel/plugin-transform-export-namespace-from": "npm:^7.27.1" + "@babel/plugin-transform-for-of": "npm:^7.27.1" + "@babel/plugin-transform-function-name": "npm:^7.27.1" + "@babel/plugin-transform-json-strings": "npm:^7.28.6" + "@babel/plugin-transform-literals": "npm:^7.27.1" + "@babel/plugin-transform-logical-assignment-operators": "npm:^7.28.6" + "@babel/plugin-transform-member-expression-literals": "npm:^7.27.1" + "@babel/plugin-transform-modules-amd": "npm:^7.27.1" + "@babel/plugin-transform-modules-commonjs": "npm:^7.28.6" + "@babel/plugin-transform-modules-systemjs": "npm:^7.29.4" + "@babel/plugin-transform-modules-umd": "npm:^7.27.1" + "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.29.0" + "@babel/plugin-transform-new-target": "npm:^7.27.1" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.28.6" + "@babel/plugin-transform-numeric-separator": "npm:^7.28.6" + "@babel/plugin-transform-object-rest-spread": "npm:^7.28.6" + "@babel/plugin-transform-object-super": "npm:^7.27.1" + "@babel/plugin-transform-optional-catch-binding": "npm:^7.28.6" + "@babel/plugin-transform-optional-chaining": "npm:^7.28.6" + "@babel/plugin-transform-parameters": "npm:^7.27.7" + "@babel/plugin-transform-private-methods": "npm:^7.28.6" + "@babel/plugin-transform-private-property-in-object": "npm:^7.28.6" + "@babel/plugin-transform-property-literals": "npm:^7.27.1" + "@babel/plugin-transform-regenerator": "npm:^7.29.0" + "@babel/plugin-transform-regexp-modifiers": "npm:^7.28.6" + "@babel/plugin-transform-reserved-words": "npm:^7.27.1" + "@babel/plugin-transform-shorthand-properties": "npm:^7.27.1" + "@babel/plugin-transform-spread": "npm:^7.28.6" + "@babel/plugin-transform-sticky-regex": "npm:^7.27.1" + "@babel/plugin-transform-template-literals": "npm:^7.27.1" + "@babel/plugin-transform-typeof-symbol": "npm:^7.27.1" + "@babel/plugin-transform-unicode-escapes": "npm:^7.27.1" + "@babel/plugin-transform-unicode-property-regex": "npm:^7.28.6" + "@babel/plugin-transform-unicode-regex": "npm:^7.27.1" + "@babel/plugin-transform-unicode-sets-regex": "npm:^7.28.6" + "@babel/preset-modules": "npm:0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2: "npm:^0.4.15" + babel-plugin-polyfill-corejs3: "npm:^0.14.0" + babel-plugin-polyfill-regenerator: "npm:^0.6.6" + core-js-compat: "npm:^3.48.0" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/2e54630764b6650d81df5ce5a47fa260acd3695dc95a6b989b713bf6c0713fb320e3ae3f76f0c636bfda058ee5c582a3de7f5d58d691c68ca566129c7d3d0f0a + languageName: node + linkType: hard + "@babel/preset-env@npm:^7.29.2, @babel/preset-env@npm:^7.29.3": version: 7.29.3 resolution: "@babel/preset-env@npm:7.29.3" @@ -2006,7 +2101,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-react@npm:^7.22.15, @babel/preset-react@npm:^7.28.5": +"@babel/preset-react@npm:^7.22.15, @babel/preset-react@npm:^7.24.7, @babel/preset-react@npm:^7.28.5": version: 7.28.5 resolution: "@babel/preset-react@npm:7.28.5" dependencies: @@ -6058,6 +6153,7 @@ __metadata: resolution: "@react-native-firebase/perf@workspace:packages/perf" dependencies: expo: "npm:^55.0.18" + react-native-builder-bob: "npm:^0.40.13" peerDependencies: "@react-native-firebase/app": 24.0.0 expo: ">=47.0.0" @@ -8139,7 +8235,7 @@ __metadata: languageName: node linkType: hard -"arktype@npm:^2.2.0": +"arktype@npm:^2.1.15, arktype@npm:^2.2.0": version: 2.2.0 resolution: "arktype@npm:2.2.0" dependencies: @@ -8204,6 +8300,13 @@ __metadata: languageName: node linkType: hard +"array-union@npm:^2.1.0": + version: 2.1.0 + resolution: "array-union@npm:2.1.0" + checksum: 10/5bee12395cba82da674931df6d0fea23c4aa4660cb3b338ced9f828782a65caa232573e6bf3968f23e0c5eb301764a382cef2f128b170a9dc59de0e36c39f98d + languageName: node + linkType: hard + "array.prototype.findlast@npm:^1.2.5": version: 1.2.5 resolution: "array.prototype.findlast@npm:1.2.5" @@ -8599,7 +8702,7 @@ __metadata: languageName: node linkType: hard -"babel-plugin-syntax-hermes-parser@npm:0.28.1": +"babel-plugin-syntax-hermes-parser@npm:0.28.1, babel-plugin-syntax-hermes-parser@npm:^0.28.0": version: 0.28.1 resolution: "babel-plugin-syntax-hermes-parser@npm:0.28.1" dependencies: @@ -9043,6 +9146,21 @@ __metadata: languageName: node linkType: hard +"browserslist@npm:^4.20.4, browserslist@npm:^4.28.1, browserslist@npm:^4.28.2": + version: 4.28.2 + resolution: "browserslist@npm:4.28.2" + dependencies: + baseline-browser-mapping: "npm:^2.10.12" + caniuse-lite: "npm:^1.0.30001782" + electron-to-chromium: "npm:^1.5.328" + node-releases: "npm:^2.0.36" + update-browserslist-db: "npm:^1.2.3" + bin: + browserslist: cli.js + checksum: 10/cff88386e5b5ba5614c9063bd32ef94865bba22b6a381844c7d09ea1eea62a2247e7106e516abdbfda6b75b9986044c991dfe45f92f10add5ad63dccc07589ec + languageName: node + linkType: hard + "browserslist@npm:^4.24.0, browserslist@npm:^4.25.0, browserslist@npm:^4.28.0": version: 4.28.1 resolution: "browserslist@npm:4.28.1" @@ -9058,21 +9176,6 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.28.1, browserslist@npm:^4.28.2": - version: 4.28.2 - resolution: "browserslist@npm:4.28.2" - dependencies: - baseline-browser-mapping: "npm:^2.10.12" - caniuse-lite: "npm:^1.0.30001782" - electron-to-chromium: "npm:^1.5.328" - node-releases: "npm:^2.0.36" - update-browserslist-db: "npm:^1.2.3" - bin: - browserslist: cli.js - checksum: 10/cff88386e5b5ba5614c9063bd32ef94865bba22b6a381844c7d09ea1eea62a2247e7106e516abdbfda6b75b9986044c991dfe45f92f10add5ad63dccc07589ec - languageName: node - linkType: hard - "bs-logger@npm:^0.2.6": version: 0.2.6 resolution: "bs-logger@npm:0.2.6" @@ -10763,6 +10866,13 @@ __metadata: languageName: node linkType: hard +"dedent@npm:^0.7.0": + version: 0.7.0 + resolution: "dedent@npm:0.7.0" + checksum: 10/87de191050d9a40dd70cad01159a0bcf05ecb59750951242070b6abf9569088684880d00ba92a955b4058804f16eeaf91d604f283929b4f614d181cd7ae633d2 + languageName: node + linkType: hard + "dedent@npm:^1.6.0": version: 1.7.0 resolution: "dedent@npm:1.7.0" @@ -10883,6 +10993,22 @@ __metadata: languageName: node linkType: hard +"del@npm:^6.1.1": + version: 6.1.1 + resolution: "del@npm:6.1.1" + dependencies: + globby: "npm:^11.0.1" + graceful-fs: "npm:^4.2.4" + is-glob: "npm:^4.0.1" + is-path-cwd: "npm:^2.2.0" + is-path-inside: "npm:^3.0.2" + p-map: "npm:^4.0.0" + rimraf: "npm:^3.0.2" + slash: "npm:^3.0.0" + checksum: 10/563288b73b8b19a7261c47fd21a330eeab6e2acd7c6208c49790dfd369127120dd7836cdf0c1eca216b77c94782a81507eac6b4734252d3bef2795cb366996b6 + languageName: node + linkType: hard + "del@npm:^8.0.1": version: 8.0.1 resolution: "del@npm:8.0.1" @@ -11123,6 +11249,15 @@ __metadata: languageName: node linkType: hard +"dir-glob@npm:^3.0.1": + version: 3.0.1 + resolution: "dir-glob@npm:3.0.1" + dependencies: + path-type: "npm:^4.0.0" + checksum: 10/fa05e18324510d7283f55862f3161c6759a3f2f8dbce491a2fc14c8324c498286c54282c1f0e933cb930da8419b30679389499b919122952a4f8592362ef4615 + languageName: node + linkType: hard + "discontinuous-range@npm:1.0.0": version: 1.0.0 resolution: "discontinuous-range@npm:1.0.0" @@ -12416,29 +12551,29 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.3.2": - version: 3.3.2 - resolution: "fast-glob@npm:3.3.2" +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.3": + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" dependencies: "@nodelib/fs.stat": "npm:^2.0.2" "@nodelib/fs.walk": "npm:^1.2.3" glob-parent: "npm:^5.1.2" merge2: "npm:^1.3.0" - micromatch: "npm:^4.0.4" - checksum: 10/222512e9315a0efca1276af9adb2127f02105d7288fa746145bf45e2716383fb79eb983c89601a72a399a56b7c18d38ce70457c5466218c5f13fad957cee16df + micromatch: "npm:^4.0.8" + checksum: 10/dcc6432b269762dd47381d8b8358bf964d8f4f60286ac6aa41c01ade70bda459ff2001b516690b96d5365f68a49242966112b5d5cc9cd82395fa8f9d017c90ad languageName: node linkType: hard -"fast-glob@npm:^3.3.3": - version: 3.3.3 - resolution: "fast-glob@npm:3.3.3" +"fast-glob@npm:^3.3.2": + version: 3.3.2 + resolution: "fast-glob@npm:3.3.2" dependencies: "@nodelib/fs.stat": "npm:^2.0.2" "@nodelib/fs.walk": "npm:^1.2.3" glob-parent: "npm:^5.1.2" merge2: "npm:^1.3.0" - micromatch: "npm:^4.0.8" - checksum: 10/dcc6432b269762dd47381d8b8358bf964d8f4f60286ac6aa41c01ade70bda459ff2001b516690b96d5365f68a49242966112b5d5cc9cd82395fa8f9d017c90ad + micromatch: "npm:^4.0.4" + checksum: 10/222512e9315a0efca1276af9adb2127f02105d7288fa746145bf45e2716383fb79eb983c89601a72a399a56b7c18d38ce70457c5466218c5f13fad957cee16df languageName: node linkType: hard @@ -13650,6 +13785,20 @@ __metadata: languageName: node linkType: hard +"globby@npm:^11.0.1": + version: 11.1.0 + resolution: "globby@npm:11.1.0" + dependencies: + array-union: "npm:^2.1.0" + dir-glob: "npm:^3.0.1" + fast-glob: "npm:^3.2.9" + ignore: "npm:^5.2.0" + merge2: "npm:^1.4.1" + slash: "npm:^3.0.0" + checksum: 10/288e95e310227bbe037076ea81b7c2598ccbc3122d87abc6dab39e1eec309aa14f0e366a98cdc45237ffcfcbad3db597778c0068217dcb1950fef6249104e1b1 + languageName: node + linkType: hard + "globby@npm:^14.0.2": version: 14.1.0 resolution: "globby@npm:14.1.0" @@ -14948,7 +15097,7 @@ __metadata: languageName: node linkType: hard -"is-git-dirty@npm:^2.0.2": +"is-git-dirty@npm:^2.0.1, is-git-dirty@npm:^2.0.2": version: 2.0.2 resolution: "is-git-dirty@npm:2.0.2" dependencies: @@ -15056,6 +15205,13 @@ __metadata: languageName: node linkType: hard +"is-path-cwd@npm:^2.2.0": + version: 2.2.0 + resolution: "is-path-cwd@npm:2.2.0" + checksum: 10/46a840921bb8cc0dc7b5b423a14220e7db338072a4495743a8230533ce78812dc152548c86f4b828411fe98c5451959f07cf841c6a19f611e46600bd699e8048 + languageName: node + linkType: hard + "is-path-cwd@npm:^3.0.0": version: 3.0.0 resolution: "is-path-cwd@npm:3.0.0" @@ -16433,7 +16589,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:^2.2.2, json5@npm:^2.2.3": +"json5@npm:^2.2.1, json5@npm:^2.2.2, json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" bin: @@ -16617,7 +16773,7 @@ __metadata: languageName: node linkType: hard -"kleur@npm:^4.1.5": +"kleur@npm:^4.1.4, kleur@npm:^4.1.5": version: 4.1.5 resolution: "kleur@npm:4.1.5" checksum: 10/44d84cc4eedd4311099402ef6d4acd9b2d16e08e499d6ef3bb92389bd4692d7ef09e35248c26e27f98acac532122acb12a1bfee645994ae3af4f0a37996da7df @@ -17726,7 +17882,7 @@ __metadata: languageName: node linkType: hard -"merge2@npm:^1.3.0": +"merge2@npm:^1.3.0, merge2@npm:^1.4.1": version: 1.4.1 resolution: "merge2@npm:1.4.1" checksum: 10/7268db63ed5169466540b6fb947aec313200bcf6d40c5ab722c22e242f651994619bcd85601602972d3c85bd2cc45a358a4c61937e9f11a061919a1da569b0c2 @@ -20673,7 +20829,7 @@ __metadata: languageName: node linkType: hard -"p-map@npm:4.0.0": +"p-map@npm:4.0.0, p-map@npm:^4.0.0": version: 4.0.0 resolution: "p-map@npm:4.0.0" dependencies: @@ -21161,6 +21317,13 @@ __metadata: languageName: node linkType: hard +"path-type@npm:^4.0.0": + version: 4.0.0 + resolution: "path-type@npm:4.0.0" + checksum: 10/5b1e2daa247062061325b8fdbfd1fb56dde0a448fb1455453276ea18c60685bdad23a445dc148cf87bc216be1573357509b7d4060494a6fd768c7efad833ee45 + languageName: node + linkType: hard + "path-type@npm:^6.0.0": version: 6.0.0 resolution: "path-type@npm:6.0.0" @@ -21976,6 +22139,38 @@ __metadata: languageName: node linkType: hard +"react-native-builder-bob@npm:^0.40.13": + version: 0.40.18 + resolution: "react-native-builder-bob@npm:0.40.18" + dependencies: + "@babel/core": "npm:^7.25.2" + "@babel/plugin-transform-flow-strip-types": "npm:^7.26.5" + "@babel/plugin-transform-strict-mode": "npm:^7.24.7" + "@babel/preset-env": "npm:^7.25.2" + "@babel/preset-react": "npm:^7.24.7" + "@babel/preset-typescript": "npm:^7.24.7" + arktype: "npm:^2.1.15" + babel-plugin-syntax-hermes-parser: "npm:^0.28.0" + browserslist: "npm:^4.20.4" + cross-spawn: "npm:^7.0.3" + dedent: "npm:^0.7.0" + del: "npm:^6.1.1" + escape-string-regexp: "npm:^4.0.0" + fs-extra: "npm:^10.1.0" + glob: "npm:^10.5.0" + is-git-dirty: "npm:^2.0.1" + json5: "npm:^2.2.1" + kleur: "npm:^4.1.4" + prompts: "npm:^2.4.2" + react-native-monorepo-config: "npm:^0.3.3" + which: "npm:^2.0.2" + yargs: "npm:^17.5.1" + bin: + bob: bin/bob + checksum: 10/06eddba046a508dff0aa322c823fa57a280e2ed6a6f586de2114ebc11b6bb7863714eaaff8e93d681bb851fb4f9e792b102541211847f0e546f4e9546df6c3de + languageName: node + linkType: hard + "react-native-builder-bob@npm:^0.41.0": version: 0.41.0 resolution: "react-native-builder-bob@npm:0.41.0" @@ -26230,7 +26425,7 @@ __metadata: languageName: node linkType: hard -"which@npm:^2.0.1": +"which@npm:^2.0.1, which@npm:^2.0.2": version: 2.0.2 resolution: "which@npm:2.0.2" dependencies: @@ -26637,7 +26832,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.2, yargs@npm:^17.0.0, yargs@npm:^17.6.2, yargs@npm:^17.7.2": +"yargs@npm:17.7.2, yargs@npm:^17.0.0, yargs@npm:^17.5.1, yargs@npm:^17.6.2, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: From e73807a7079613764339d65083f51a38f2f8078c Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 12 May 2026 14:53:22 +0100 Subject: [PATCH 16/17] fix: use correct namespace in registry --- .github/scripts/compare-types/src/registry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/compare-types/src/registry.ts b/.github/scripts/compare-types/src/registry.ts index 752cd639e4..c936186094 100644 --- a/.github/scripts/compare-types/src/registry.ts +++ b/.github/scripts/compare-types/src/registry.ts @@ -250,7 +250,7 @@ export const packages: PackageEntry[] = [ })), { name: 'perf', - firebaseSdkTypesPaths: [requiredFirebaseTypes('perf')], + firebaseSdkTypesPaths: [requiredFirebaseTypes('performance')], rnFirebaseModularFiles: [ path.join(rnDist('perf'), 'types', 'perf.d.ts'), path.join(rnDist('perf'), 'modular.d.ts'), From dcc096937fa04b982ebb0b6cca5ec8b798239486 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 12 May 2026 17:20:27 +0100 Subject: [PATCH 17/17] fix: remove deprecated baseUrl --- packages/perf/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/perf/tsconfig.json b/packages/perf/tsconfig.json index 3e4e85585b..4e2b0c4906 100644 --- a/packages/perf/tsconfig.json +++ b/packages/perf/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "../../tsconfig.packages.base.json", "compilerOptions": { - "baseUrl": ".", "rootDir": ".", "paths": { "@react-native-firebase/app/dist/module/common/*": ["../app/dist/typescript/lib/common/*"],