Skip to content

Commit 4aedfe8

Browse files
committed
refactor(perf)!: migrate to TypeScript
BREAKING CHANGE: perf types now match firebase-js-sdk as closely as possible Please see https://rnfirebase.io/migrating-to-v25 for help migrating if needed. react-native-firebase has a goal to be a drop-in replacement for firebase-js-sdk, with native extensions and performance. It has always worked that way at the javascript level but the typescript types have been divergent. We are fixing that as we refactor to typescript. Please bear with us as we get closer to our goal of react-native-firebase matching firebase-js-sdk both in functionality where possible, but also in exact typescript typing. Specifics for Performance: - changed modular `initializePerformance(app, settings)` to return `FirebasePerformance` synchronously instead of `Promise<Performance>`, matching firebase-js-sdk; TypeScript consumers that call `.then(...)` on it will need to use the returned instance directly. - aligned the modular `FirebasePerformance` type with firebase-js-sdk, so it no longer exposes older namespaced instance-style methods such as `newTrace`, `startTrace`, `newHttpMetric`, `newScreenTrace`, `startScreenTrace`, or `setPerformanceCollectionEnabled` in the modular typings; use `trace(perf, name)`, `httpMetric(perf, url, method)`, `newScreenTrace(perf, name)`, `startScreenTrace(perf, name)`, and the `dataCollectionEnabled` property instead. - changed `PerformanceSettings` to the firebase-js-sdk shape, with optional `dataCollectionEnabled` and `instrumentationEnabled`. - changed modular trace and metric `getAttribute(...)` typings from `string | null` to `string | undefined`, matching firebase-js-sdk. - kept React Native-only modular exports for native functionality: `httpMetric`, `newScreenTrace`, `startScreenTrace`, plus `HttpMethod`, `HttpMetric`, and `ScreenTrace`. - kept the deprecated namespaced API under `FirebasePerformanceTypes`, but split it from the modular public types and marked it as deprecated for compatibility.
1 parent 83ec435 commit 4aedfe8

24 files changed

Lines changed: 1146 additions & 886 deletions
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* Known differences between the firebase-js-sdk @firebase/performance modular
3+
* API and the @react-native-firebase/perf modular API.
4+
*/
5+
6+
import type { PackageConfig } from '../../src/types';
7+
8+
const config: PackageConfig = {
9+
missingInRN: [],
10+
extraInRN: [
11+
{
12+
name: 'HttpMethod',
13+
reason:
14+
'String union of HTTP verbs used by React Native HTTP metrics. The web Performance ' +
15+
'modular API does not expose HTTP metrics in the public tree-shakeable surface.',
16+
},
17+
{
18+
name: 'ScreenTrace',
19+
reason:
20+
'React Native screen trace type for slow/frozen frame reporting. No equivalent exists ' +
21+
'in the firebase-js-sdk modular Performance API.',
22+
},
23+
{
24+
name: 'HttpMetric',
25+
reason:
26+
'React Native HTTP request metric type backed by native instrumentation. The web SDK ' +
27+
'does not expose this type on the modular public surface.',
28+
},
29+
{
30+
name: 'httpMetric',
31+
reason:
32+
'React Native modular helper that constructs an `HttpMetric`. No equivalent exists in ' +
33+
'the firebase-js-sdk modular Performance API.',
34+
},
35+
{
36+
name: 'newScreenTrace',
37+
reason:
38+
'React Native modular helper that constructs a `ScreenTrace`. No equivalent exists in ' +
39+
'the firebase-js-sdk modular Performance API.',
40+
},
41+
{
42+
name: 'startScreenTrace',
43+
reason:
44+
'React Native modular helper that creates and starts a `ScreenTrace`. No equivalent ' +
45+
'exists in the firebase-js-sdk modular Performance API.',
46+
},
47+
],
48+
differentShape: [
49+
{
50+
name: 'FirebasePerformance',
51+
reason:
52+
'React Native extends the service interface with native collection state (`isPerformanceCollectionEnabled`), ' +
53+
'a deprecated `setPerformanceCollectionEnabled` bridge helper, and factory methods for traces, ' +
54+
'screen traces, and HTTP metrics. The firebase-js-sdk web type only exposes `app`, ' +
55+
'`instrumentationEnabled`, and `dataCollectionEnabled`.',
56+
},
57+
{
58+
name: 'PerformanceTrace',
59+
reason:
60+
'React Native uses async `start`/`stop` (`Promise<null>`) because work crosses the native bridge. ' +
61+
'The web SDK uses synchronous `start`/`stop` and exposes `record()`. RN Firebase exposes ' +
62+
'`getMetrics` and `removeMetric` for native-backed custom metrics instead of the web shape.',
63+
},
64+
],
65+
};
66+
67+
export default config;
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/**
2+
* Public types snapshot from the Firebase JS SDK (@firebase/performance).
3+
*
4+
* Source: firebase-js-sdk `node_modules/@firebase/performance/dist/src/index.d.ts`
5+
* and `node_modules/@firebase/performance/dist/src/public_types.d.ts`
6+
* Modality: modular (tree-shakeable) API only
7+
*
8+
* This file is the reference snapshot used to detect API drift between the
9+
* firebase-js-sdk and the @react-native-firebase/perf modular API.
10+
*
11+
* When a new version of the firebase-js-sdk ships with type changes, update
12+
* this file with the new public modular types from @firebase/performance.
13+
*/
14+
15+
import { FirebaseApp } from '@firebase/app';
16+
17+
/**
18+
* Defines configuration options for the Performance Monitoring SDK.
19+
*
20+
* @public
21+
*/
22+
export interface PerformanceSettings {
23+
/** Whether to collect custom events. */
24+
dataCollectionEnabled?: boolean;
25+
/** Whether to collect out of the box events. */
26+
instrumentationEnabled?: boolean;
27+
}
28+
29+
/**
30+
* The Firebase Performance Monitoring service interface.
31+
*
32+
* @public
33+
*/
34+
export interface FirebasePerformance {
35+
/**
36+
* The {@link @firebase/app#FirebaseApp} this `FirebasePerformance` instance is associated with.
37+
*/
38+
app: FirebaseApp;
39+
/**
40+
* Controls the logging of automatic traces and HTTP/S network monitoring.
41+
*/
42+
instrumentationEnabled: boolean;
43+
/**
44+
* Controls the logging of custom traces.
45+
*/
46+
dataCollectionEnabled: boolean;
47+
}
48+
49+
/**
50+
* The interface representing a `Trace`.
51+
*
52+
* @public
53+
*/
54+
export interface PerformanceTrace {
55+
/**
56+
* Starts the timing for the trace instance.
57+
*/
58+
start(): void;
59+
/**
60+
* Stops the timing of the trace instance and logs the data of the instance.
61+
*/
62+
stop(): void;
63+
/**
64+
* Records a trace from given parameters. This provides a direct way to use trace without a need to
65+
* start/stop. This is useful for use cases in which the trace cannot directly be used
66+
* (e.g. if the duration was captured before the Performance SDK was loaded).
67+
*
68+
* @param startTime - trace start time since epoch in millisec.
69+
* @param duration - The duration of the trace in millisec.
70+
* @param options - An object which can optionally hold maps of custom metrics and
71+
* custom attributes.
72+
*/
73+
record(
74+
startTime: number,
75+
duration: number,
76+
options?: {
77+
metrics?: {
78+
[key: string]: number;
79+
};
80+
attributes?: {
81+
[key: string]: string;
82+
};
83+
},
84+
): void;
85+
/**
86+
* Adds to the value of a custom metric. If a custom metric with the provided name does not
87+
* exist, it creates one with that name and the value equal to the given number. The value will be floored down to an
88+
* integer.
89+
*
90+
* @param metricName - The name of the custom metric.
91+
* @param num - The number to be added to the value of the custom metric. If not provided, it
92+
* uses a default value of one.
93+
*/
94+
incrementMetric(metricName: string, num?: number): void;
95+
/**
96+
* Sets the value of the specified custom metric to the given number regardless of whether
97+
* a metric with that name already exists on the trace instance or not. The value will be floored down to an
98+
* integer.
99+
*
100+
* @param metricName - Name of the custom metric.
101+
* @param num - Value to of the custom metric.
102+
*/
103+
putMetric(metricName: string, num: number): void;
104+
/**
105+
* Returns the value of the custom metric by that name. If a custom metric with that name does
106+
* not exist will return zero.
107+
*
108+
* @param metricName - Name of the custom metric.
109+
*/
110+
getMetric(metricName: string): number;
111+
/**
112+
* Set a custom attribute of a trace to a certain value.
113+
*
114+
* @param attr - Name of the custom attribute.
115+
* @param value - Value of the custom attribute.
116+
*/
117+
putAttribute(attr: string, value: string): void;
118+
/**
119+
* Retrieves the value which a custom attribute is set to.
120+
*
121+
* @param attr - Name of the custom attribute.
122+
*/
123+
getAttribute(attr: string): string | undefined;
124+
/**
125+
* Removes the specified custom attribute from a trace instance.
126+
*
127+
* @param attr - Name of the custom attribute.
128+
*/
129+
removeAttribute(attr: string): void;
130+
/**
131+
* Returns a map of all custom attributes of a trace instance.
132+
*/
133+
getAttributes(): {
134+
[key: string]: string;
135+
};
136+
}
137+
138+
/**
139+
* Returns a {@link FirebasePerformance} instance for the given app.
140+
* @param app - The {@link @firebase/app#FirebaseApp} to use.
141+
* @public
142+
*/
143+
export declare function getPerformance(app?: FirebaseApp): FirebasePerformance;
144+
145+
/**
146+
* Returns a {@link FirebasePerformance} instance for the given app. Can only be called once.
147+
* @param app - The {@link @firebase/app#FirebaseApp} to use.
148+
* @param settings - Optional settings for the {@link FirebasePerformance} instance.
149+
* @public
150+
*/
151+
export declare function initializePerformance(
152+
app: FirebaseApp,
153+
settings?: PerformanceSettings,
154+
): FirebasePerformance;
155+
156+
/**
157+
* Returns a new `PerformanceTrace` instance.
158+
* @param performance - The {@link FirebasePerformance} instance to use.
159+
* @param name - The name of the trace.
160+
* @public
161+
*/
162+
export declare function trace(performance: FirebasePerformance, name: string): PerformanceTrace;

.github/scripts/compare-types/src/registry.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import appCheckConfig from '../configs/app-check';
1818
import firestoreConfig from '../configs/firestore';
1919
import firestorePipelinesConfig from '../configs/firestore-pipelines';
2020
import remoteConfigConfig from '../configs/remote-config';
21+
import perfConfig from '../configs/perf-config';
2122

2223
const SCRIPT_DIR = path.resolve(__dirname, '..');
2324
const REPO_ROOT = path.resolve(SCRIPT_DIR, '..', '..', '..');
@@ -247,6 +248,14 @@ export const packages: PackageEntry[] = [
247248
],
248249
config: firestorePipelinesConfig,
249250
})),
251+
{
252+
name: 'perf',
253+
firebaseSdkTypesPaths: [requiredFirebaseTypes('performance')],
254+
rnFirebaseModularFiles: [
255+
path.join(rnDist('perf'), 'types', 'perf.d.ts'),
256+
path.join(rnDist('perf'), 'modular.d.ts'),
257+
],
258+
rnFirebaseSupportFiles: [],
259+
config: perfConfig,
260+
},
250261
];
251-
252-

packages/perf/__tests__/perf.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ describe('Performance Monitoring', function () {
200200
const perf = getPerformance();
201201
perfV9Deprecation(
202202
() => trace(perf, 'invertase'),
203+
// @ts-expect-error Combines modular and namespace API
203204
() => perf.newTrace('invertase'),
204205
'newTrace',
205206
);
@@ -209,6 +210,7 @@ describe('Performance Monitoring', function () {
209210
const perf = getPerformance();
210211
perfV9Deprecation(
211212
() => httpMetric(perf, 'https://invertase.io', 'GET'),
213+
// @ts-expect-error Combines modular and namespace API
212214
() => perf.newHttpMetric('https://invertase.io', 'GET'),
213215
'newHttpMetric',
214216
);
@@ -218,6 +220,7 @@ describe('Performance Monitoring', function () {
218220
const perf = getPerformance();
219221
perfV9Deprecation(
220222
() => newScreenTrace(perf, 'invertase'),
223+
// @ts-expect-error Combines modular and namespace API
221224
() => perf.newScreenTrace('invertase'),
222225
'newScreenTrace',
223226
);
@@ -227,6 +230,7 @@ describe('Performance Monitoring', function () {
227230
const perf = getPerformance();
228231
perfV9Deprecation(
229232
() => startScreenTrace(perf, 'invertase'),
233+
// @ts-expect-error Combines modular and namespace API
230234
() => perf.startScreenTrace('invertase'),
231235
'startScreenTrace',
232236
);
Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,23 @@
1616
*/
1717

1818
import { isNull, isNumber, isString } from '@react-native-firebase/app/dist/module/common';
19+
1920
import MetricWithAttributes from './MetricWithAttributes';
2021

22+
import type { RNFBPerfHttpMetricData, RNFBPerfNativeModule } from './types/internal';
23+
import type { HttpMethod } from './types/perf';
24+
2125
export default class HttpMetric extends MetricWithAttributes {
22-
constructor(native, url, httpMethod) {
26+
private readonly _url: string;
27+
private readonly _httpMethod: HttpMethod;
28+
private _httpResponseCode: number | null;
29+
private _requestPayloadSize: number | null;
30+
private _responsePayloadSize: number | null;
31+
private _responseContentType: string | null;
32+
private _started: boolean;
33+
private _stopped: boolean;
34+
35+
constructor(native: RNFBPerfNativeModule, url: string, httpMethod: HttpMethod) {
2336
super(native);
2437

2538
this._url = url;
@@ -34,7 +47,7 @@ export default class HttpMetric extends MetricWithAttributes {
3447
this._stopped = false;
3548
}
3649

37-
setHttpResponseCode(code) {
50+
setHttpResponseCode(code: number | null): void {
3851
if (!isNumber(code) && !isNull(code)) {
3952
throw new Error(
4053
"firebase.perf.HttpMetric.setHttpResponseCode(*) 'code' must be a number or null.",
@@ -44,7 +57,7 @@ export default class HttpMetric extends MetricWithAttributes {
4457
this._httpResponseCode = code;
4558
}
4659

47-
setRequestPayloadSize(bytes) {
60+
setRequestPayloadSize(bytes: number | null): void {
4861
if (!isNumber(bytes) && !isNull(bytes)) {
4962
throw new Error(
5063
"firebase.perf.HttpMetric.setRequestPayloadSize(*) 'bytes' must be a number or null.",
@@ -54,7 +67,7 @@ export default class HttpMetric extends MetricWithAttributes {
5467
this._requestPayloadSize = bytes;
5568
}
5669

57-
setResponsePayloadSize(bytes) {
70+
setResponsePayloadSize(bytes: number | null): void {
5871
if (!isNumber(bytes) && !isNull(bytes)) {
5972
throw new Error(
6073
"firebase.perf.HttpMetric.setResponsePayloadSize(*) 'bytes' must be a number or null.",
@@ -64,7 +77,7 @@ export default class HttpMetric extends MetricWithAttributes {
6477
this._responsePayloadSize = bytes;
6578
}
6679

67-
setResponseContentType(contentType) {
80+
setResponseContentType(contentType: string | null): void {
6881
if (!isString(contentType) && !isNull(contentType)) {
6982
throw new Error(
7083
"firebase.perf.HttpMetric.setResponseContentType(*) 'contentType' must be a string or null.",
@@ -74,7 +87,7 @@ export default class HttpMetric extends MetricWithAttributes {
7487
this._responseContentType = contentType;
7588
}
7689

77-
start() {
90+
start(): Promise<null> {
7891
if (this._started) {
7992
return Promise.resolve(null);
8093
}
@@ -83,13 +96,13 @@ export default class HttpMetric extends MetricWithAttributes {
8396
return this.native.startHttpMetric(this._id, this._url, this._httpMethod);
8497
}
8598

86-
stop() {
99+
stop(): Promise<null> {
87100
if (this._stopped) {
88101
return Promise.resolve(null);
89102
}
90103
this._stopped = true;
91104

92-
const metricData = {
105+
const metricData: RNFBPerfHttpMetricData = {
93106
attributes: Object.assign({}, this._attributes),
94107
};
95108

0 commit comments

Comments
 (0)