@truewatchtech/react-native-mobile 0.4.3-alpha.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FTMobileReactNativeSDK.podspec +1 -1
- package/README.md +0 -34
- package/android/build.gradle +2 -2
- package/android/src/main/java/com/ft/sdk/reactnative/FTLogImpl.java +10 -2
- package/android/src/main/java/com/ft/sdk/reactnative/FTMobileImpl.java +45 -3
- package/android/src/main/java/com/ft/sdk/reactnative/FTRUMImpl.java +51 -9
- package/android/src/main/java/com/ft/sdk/reactnative/FTTraceImpl.java +5 -2
- package/android/src/main/java/com/ft/sdk/reactnative/ViewTiming.java +17 -0
- package/android/src/main/java/com/ft/sdk/reactnative/VisitorIdConfigMapper.java +50 -0
- package/android/src/newarch/java/com/ft/sdk/reactnative/FTMobileModule.java +5 -0
- package/android/src/newarch/java/com/ft/sdk/reactnative/FTRUMModule.java +18 -0
- package/android/src/oldarch/java/com/ft/sdk/reactnative/FTMobileModule.java +5 -0
- package/android/src/oldarch/java/com/ft/sdk/reactnative/FTRUMModule.java +15 -0
- package/android/src/test/java/com/ft/sdk/reactnative/FTMobileImplTest.java +53 -0
- package/ios/FTMobileReactNative.mm +37 -2
- package/ios/FTReactNativeLog.mm +7 -2
- package/ios/FTReactNativeRUM.mm +94 -4
- package/ios/FTReactNativeResource.m +6 -0
- package/ios/FTReactNativeTrace.mm +4 -2
- package/ios/FTReactNativeViewTiming.h +18 -0
- package/ios/FTWebSocketResourceData.h +4 -0
- package/ios/FTWebSocketResourceData.m +31 -0
- package/lib/commonjs/ft_logger.js +3 -1
- package/lib/commonjs/ft_logger.js.map +1 -1
- package/lib/commonjs/ft_mobile_agent.js +13 -1
- package/lib/commonjs/ft_mobile_agent.js.map +1 -1
- package/lib/commonjs/ft_rum.js +19 -3
- package/lib/commonjs/ft_rum.js.map +1 -1
- package/lib/commonjs/ft_tracing.js +2 -1
- package/lib/commonjs/ft_tracing.js.map +1 -1
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/rum/FTRumViewTracking.js +147 -0
- package/lib/commonjs/rum/FTRumViewTracking.js.map +1 -0
- package/lib/commonjs/rum/FTRumWebSocketNative.js.map +1 -1
- package/lib/commonjs/rum/FTRumWebSocketTiming.js +19 -1
- package/lib/commonjs/rum/FTRumWebSocketTiming.js.map +1 -1
- package/lib/commonjs/rum/FTRumWebSocketTracking.js +7 -11
- package/lib/commonjs/rum/FTRumWebSocketTracking.js.map +1 -1
- package/lib/commonjs/specs/NativeFTMobileReactNative.js.map +1 -1
- package/lib/commonjs/specs/NativeFTReactNativeRUM.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/commonjs/version.js.map +1 -1
- package/lib/commonjs/visitorIdConfig.js +37 -0
- package/lib/commonjs/visitorIdConfig.js.map +1 -0
- package/lib/module/ft_logger.js +3 -1
- package/lib/module/ft_logger.js.map +1 -1
- package/lib/module/ft_mobile_agent.js +13 -1
- package/lib/module/ft_mobile_agent.js.map +1 -1
- package/lib/module/ft_rum.js +19 -3
- package/lib/module/ft_rum.js.map +1 -1
- package/lib/module/ft_tracing.js +2 -1
- package/lib/module/ft_tracing.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/rum/FTRumViewTracking.js +140 -0
- package/lib/module/rum/FTRumViewTracking.js.map +1 -0
- package/lib/module/rum/FTRumWebSocketNative.js.map +1 -1
- package/lib/module/rum/FTRumWebSocketTiming.js +19 -1
- package/lib/module/rum/FTRumWebSocketTiming.js.map +1 -1
- package/lib/module/rum/FTRumWebSocketTracking.js +7 -11
- package/lib/module/rum/FTRumWebSocketTracking.js.map +1 -1
- package/lib/module/specs/NativeFTMobileReactNative.js.map +1 -1
- package/lib/module/specs/NativeFTReactNativeRUM.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/module/version.js.map +1 -1
- package/lib/module/visitorIdConfig.js +31 -0
- package/lib/module/visitorIdConfig.js.map +1 -0
- package/lib/typescript/ft_logger.d.ts +11 -1
- package/lib/typescript/ft_mobile_agent.d.ts +25 -0
- package/lib/typescript/ft_rum.d.ts +33 -5
- package/lib/typescript/ft_tracing.d.ts +9 -1
- package/lib/typescript/index.d.ts +2 -0
- package/lib/typescript/rum/FTRumViewTracking.d.ts +32 -0
- package/lib/typescript/rum/FTRumWebSocketNative.d.ts +2 -3
- package/lib/typescript/rum/FTRumWebSocketTiming.d.ts +6 -1
- package/lib/typescript/rum/FTRumWebSocketTracking.d.ts +2 -4
- package/lib/typescript/specs/NativeFTMobileReactNative.d.ts +1 -0
- package/lib/typescript/specs/NativeFTReactNativeRUM.d.ts +7 -1
- package/lib/typescript/version.d.ts +1 -1
- package/lib/typescript/visitorIdConfig.d.ts +11 -0
- package/package.json +4 -3
- package/src/ft_logger.tsx +11 -1
- package/src/ft_mobile_agent.tsx +34 -1
- package/src/ft_rum.tsx +50 -5
- package/src/ft_tracing.tsx +9 -1
- package/src/index.tsx +4 -0
- package/src/rum/FTRumViewTracking.ts +179 -0
- package/src/rum/FTRumWebSocketNative.ts +2 -1
- package/src/rum/FTRumWebSocketTiming.ts +34 -2
- package/src/rum/FTRumWebSocketTracking.ts +9 -14
- package/src/specs/NativeFTMobileReactNative.ts +1 -0
- package/src/specs/NativeFTReactNativeRUM.ts +15 -1
- package/src/version.ts +1 -1
- package/src/visitorIdConfig.ts +123 -0
|
@@ -33,6 +33,7 @@ export interface Spec extends TurboModule {
|
|
|
33
33
|
* @returns a Promise.
|
|
34
34
|
*/
|
|
35
35
|
unbindRUMUserData(): Promise<void>;
|
|
36
|
+
resetVisitorId(): Promise<void>;
|
|
36
37
|
/**
|
|
37
38
|
* Add custom global parameters. Applies to RUM and Log data
|
|
38
39
|
* @param context custom global parameters.
|
|
@@ -32,10 +32,16 @@ export interface Spec extends TurboModule {
|
|
|
32
32
|
/**
|
|
33
33
|
* view load duration.
|
|
34
34
|
* @param viewName view name
|
|
35
|
-
* @param loadTime view load duration
|
|
35
|
+
* @param loadTime monotonic view load duration in nanoseconds
|
|
36
36
|
* @returns a Promise.
|
|
37
37
|
*/
|
|
38
38
|
onCreateView(viewName: string, loadTime: number): Promise<void>;
|
|
39
|
+
/** Replace the current View's loading duration; non-negative safe integer nanoseconds. */
|
|
40
|
+
updateViewLoadingTime(duration: number): Promise<void>;
|
|
41
|
+
/** Framework integration: duration in ns; epoch start in ms, converted natively. */
|
|
42
|
+
onCreateViewWithLoadStart(viewName: string, loadTime: number, loadStartEpochMs: number): Promise<void>;
|
|
43
|
+
/** Framework integration: Manual = 0, Automatic = 1. */
|
|
44
|
+
startViewWithTrackingType(viewName: string, property: Object | undefined, trackingType: number): Promise<void>;
|
|
39
45
|
/**
|
|
40
46
|
* view start.
|
|
41
47
|
* @param viewName page name
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "0.4.3
|
|
1
|
+
export declare const version = "0.4.3";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Anonymous Visitor ID header configuration for HTTP(S), excluding WebSockets. */
|
|
2
|
+
export interface FTVisitorIdConfig {
|
|
3
|
+
header: string;
|
|
4
|
+
/** Fixed lifetime in hours; defaults to 24. Access does not renew it. */
|
|
5
|
+
ttlHours?: number;
|
|
6
|
+
/** Complete HTTP(S) prefixes constrained to the same scheme, host and port. */
|
|
7
|
+
urlPrefixes?: string[];
|
|
8
|
+
/** Native regular expressions searched against the complete URL; rules use OR. */
|
|
9
|
+
regularExpressions?: string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function validateVisitorIdConfig(config: FTVisitorIdConfig): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truewatchtech/react-native-mobile",
|
|
3
|
-
"version": "0.4.3
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Base on TrueWatch Android iOS React Native SDK",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -78,7 +78,8 @@
|
|
|
78
78
|
[
|
|
79
79
|
"typescript",
|
|
80
80
|
{
|
|
81
|
-
"tsc": "../../node_modules/.bin/tsc"
|
|
81
|
+
"tsc": "../../node_modules/.bin/tsc",
|
|
82
|
+
"project": "tsconfig.build.json"
|
|
82
83
|
}
|
|
83
84
|
]
|
|
84
85
|
]
|
|
@@ -97,5 +98,5 @@
|
|
|
97
98
|
"directories": {
|
|
98
99
|
"lib": "lib"
|
|
99
100
|
},
|
|
100
|
-
"gitHead": "
|
|
101
|
+
"gitHead": "61ed39427761a210cbfe6b339e71296f952f9697"
|
|
101
102
|
}
|
package/src/ft_logger.tsx
CHANGED
|
@@ -18,8 +18,10 @@ export enum FTLogCacheDiscard {
|
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Configure log output configuration.
|
|
21
|
-
* @param
|
|
21
|
+
* @param logSamplingRate Log sampling percentage in [0, 100], default 100. Supports fractional percentages and takes precedence over sampleRate.
|
|
22
|
+
* @param sampleRate Deprecated log sampling probability in [0, 1], default 1. Used only when logSamplingRate is omitted; multiplied by 100 before passing to Native.
|
|
22
23
|
* @param enableLinkRumData whether to link with RUM
|
|
24
|
+
* @param enableWebViewLog Collect Browser Logs SDK events from WebViews, default false
|
|
23
25
|
* @param enableCustomLog whether to enable custom logs
|
|
24
26
|
* @param discardStrategy log discard strategy
|
|
25
27
|
* @param logLevelFilters log level filter
|
|
@@ -27,9 +29,17 @@ export enum FTLogCacheDiscard {
|
|
|
27
29
|
* @param logCacheLimitCount get maximum log entry count limit [1000,), default 5000
|
|
28
30
|
*/
|
|
29
31
|
export interface FTLogConfig {
|
|
32
|
+
/**
|
|
33
|
+
* Log sampling percentage in [0, 100], default 100.
|
|
34
|
+
* Supports fractional percentages: 12.5 means 12.5%, not 0.125.
|
|
35
|
+
* Takes precedence over sampleRate when both are supplied, including 0.
|
|
36
|
+
*/
|
|
37
|
+
logSamplingRate?: number;
|
|
38
|
+
/** @deprecated Use logSamplingRate. Probability in [0, 1], default 1; 0.125 maps to 12.5%. Ignored when logSamplingRate is supplied. */
|
|
30
39
|
sampleRate?: number;
|
|
31
40
|
enableLinkRumData?: boolean;
|
|
32
41
|
enableCustomLog?: boolean;
|
|
42
|
+
enableWebViewLog?: boolean;
|
|
33
43
|
discardStrategy?: FTLogCacheDiscard;
|
|
34
44
|
logLevelFilters?: Array<FTLogStatus>;
|
|
35
45
|
globalContext?: object;
|
package/src/ft_mobile_agent.tsx
CHANGED
|
@@ -163,6 +163,8 @@ export type FTRemoteConfigOverrideMatch = {
|
|
|
163
163
|
/**
|
|
164
164
|
* Values that can be modified by remote config override rules.
|
|
165
165
|
* These values will adjust the fetched remote configuration before it is applied.
|
|
166
|
+
* Omitted fields are not changed by this rule. Sampling percentages use [0, 100]
|
|
167
|
+
* and support fractions (12.5 means 12.5%); legacy probabilities use [0, 1].
|
|
166
168
|
*/
|
|
167
169
|
export type FTRemoteConfigOverrideValues = {
|
|
168
170
|
env?: string;
|
|
@@ -171,7 +173,21 @@ export type FTRemoteConfigOverrideValues = {
|
|
|
171
173
|
compressIntakeRequests?: boolean;
|
|
172
174
|
syncPageSize?: number;
|
|
173
175
|
syncSleepTime?: number;
|
|
176
|
+
/** RUM session sampling percentage override in [0, 100]; takes precedence over rumSampleRate, including 0. */
|
|
177
|
+
sessionSamplingRate?: number;
|
|
178
|
+
/** Error session sampling percentage override in [0, 100]; takes precedence over rumSessionOnErrorSampleRate, including 0. */
|
|
179
|
+
sessionOnErrorSamplingRate?: number;
|
|
180
|
+
/** Trace sampling percentage override in [0, 100]; takes precedence over traceSampleRate, including 0. */
|
|
181
|
+
traceSamplingRate?: number;
|
|
182
|
+
/** Log sampling percentage override in [0, 100]; takes precedence over logSampleRate, including 0. */
|
|
183
|
+
logSamplingRate?: number;
|
|
184
|
+
/** Session Replay sampling percentage override in [0, 100]; fractional percentages are supported. */
|
|
185
|
+
sessionReplaySamplingRate?: number;
|
|
186
|
+
/** Error replay sampling percentage override in [0, 100]; fractional percentages are supported. */
|
|
187
|
+
sessionReplayOnErrorSamplingRate?: number;
|
|
188
|
+
/** @deprecated Use sessionSamplingRate. Legacy RUM probability override in [0, 1]; 0.125 maps to 12.5%. */
|
|
174
189
|
rumSampleRate?: number;
|
|
190
|
+
/** @deprecated Use sessionOnErrorSamplingRate. Legacy error session probability override in [0, 1]. */
|
|
175
191
|
rumSessionOnErrorSampleRate?: number;
|
|
176
192
|
rumEnableTraceUserAction?: boolean;
|
|
177
193
|
rumEnableTraceUserView?: boolean;
|
|
@@ -183,12 +199,15 @@ export type FTRemoteConfigOverrideValues = {
|
|
|
183
199
|
rumEnableTrackAppANR?: boolean;
|
|
184
200
|
rumEnableTraceWebView?: boolean;
|
|
185
201
|
rumAllowWebViewHost?: Array<string>;
|
|
202
|
+
/** @deprecated Use traceSamplingRate. Legacy Trace probability override in [0, 1]. */
|
|
186
203
|
traceSampleRate?: number;
|
|
187
204
|
traceEnableAutoTrace?: boolean;
|
|
188
205
|
traceType?: string;
|
|
206
|
+
/** @deprecated Use logSamplingRate. Legacy log probability override in [0, 1]. */
|
|
189
207
|
logSampleRate?: number;
|
|
190
208
|
logLevelFilters?: Array<string>;
|
|
191
209
|
logEnableCustomLog?: boolean;
|
|
210
|
+
logEnableWebViewLog?: boolean;
|
|
192
211
|
logEnableConsoleLog?: boolean;
|
|
193
212
|
};
|
|
194
213
|
|
|
@@ -269,6 +288,10 @@ export interface FTMobileConfig {
|
|
|
269
288
|
compressIntakeRequests?: boolean;
|
|
270
289
|
enableDataFilter?: boolean;
|
|
271
290
|
dataFilters?: Record<string, Array<string>>;
|
|
291
|
+
/** Shared WebView host allowlist for RUM and Log. Omitted uses the legacy RUM
|
|
292
|
+
* config; null allows every host; [] disables bridge access. Remote config wins.
|
|
293
|
+
*/
|
|
294
|
+
allowWebViewHost?: Array<string> | null;
|
|
272
295
|
globalContext?: object;
|
|
273
296
|
groupIdentifiers?: Array<string>;
|
|
274
297
|
enableLimitWithDbSize?: boolean;
|
|
@@ -320,6 +343,8 @@ type FTMobileReactNativeType = {
|
|
|
320
343
|
* @returns a Promise.
|
|
321
344
|
*/
|
|
322
345
|
unbindRUMUserData(): Promise<void>;
|
|
346
|
+
/** Rotate the native anonymous Visitor ID for subsequent requests. */
|
|
347
|
+
resetVisitorId(): Promise<void>;
|
|
323
348
|
/**
|
|
324
349
|
* Add custom global parameters. Applies to RUM and Log data
|
|
325
350
|
* @param context custom global parameters.
|
|
@@ -415,7 +440,12 @@ class FTMobileReactNativeWrapper implements FTMobileReactNativeType {
|
|
|
415
440
|
) {
|
|
416
441
|
config.datakitUrl = config.serverUrl;
|
|
417
442
|
}
|
|
418
|
-
|
|
443
|
+
const nativeConfig = { ...config };
|
|
444
|
+
// Keep undefined equivalent to omission; explicit null overrides legacy hosts.
|
|
445
|
+
if (nativeConfig.allowWebViewHost === undefined) {
|
|
446
|
+
delete nativeConfig.allowWebViewHost;
|
|
447
|
+
}
|
|
448
|
+
return this.sdk.sdkConfig(nativeConfig);
|
|
419
449
|
}
|
|
420
450
|
setDatakitURL(datakitUrl: string): Promise<void> {
|
|
421
451
|
return this.sdk.setDatakitURL(datakitUrl);
|
|
@@ -431,6 +461,9 @@ class FTMobileReactNativeWrapper implements FTMobileReactNativeType {
|
|
|
431
461
|
): Promise<void> {
|
|
432
462
|
return this.sdk.bindRUMUserData(userId, userName, userEmail, extra);
|
|
433
463
|
}
|
|
464
|
+
resetVisitorId(): Promise<void> {
|
|
465
|
+
return this.sdk.resetVisitorId();
|
|
466
|
+
}
|
|
434
467
|
unbindRUMUserData(): Promise<void> {
|
|
435
468
|
return this.sdk.unbindRUMUserData();
|
|
436
469
|
}
|
package/src/ft_rum.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FTVisitorIdConfig, validateVisitorIdConfig } from './visitorIdConfig';
|
|
1
2
|
// import { NativeModules } from 'react-native';
|
|
2
3
|
import { Platform } from 'react-native';
|
|
3
4
|
import { FTRumErrorTracking } from './rum/FTRumErrorTracking';
|
|
@@ -76,8 +77,10 @@ export enum IOSCrashMonitoringType {
|
|
|
76
77
|
* Set RUM tracking conditions.
|
|
77
78
|
* @param androidAppId appId, apply during monitoring
|
|
78
79
|
* @param iOSAppId appId, apply during monitoring
|
|
79
|
-
* @param
|
|
80
|
-
* @param
|
|
80
|
+
* @param sessionSamplingRate RUM session sampling percentage in [0, 100], default 100. Supports fractional percentages and takes precedence over sampleRate.
|
|
81
|
+
* @param sessionOnErrorSamplingRate Sampling percentage in [0, 100] for otherwise unsampled sessions when an error occurs, default 0. Supports fractional percentages and takes precedence over sessionOnErrorSampleRate.
|
|
82
|
+
* @param sampleRate Deprecated RUM session sampling probability in [0, 1], default 1. Used only when sessionSamplingRate is omitted; multiplied by 100 before passing to Native.
|
|
83
|
+
* @param sessionOnErrorSampleRate Deprecated error session sampling probability in [0, 1], default 0. Used only when sessionOnErrorSamplingRate is omitted; multiplied by 100 before passing to Native.
|
|
81
84
|
* @param enableAutoTrackUserAction whether to automatically collect react-native control click events, can set actionName with accessibilityLabel when enabled
|
|
82
85
|
* @param enableTrackError whether to automatically collect react-native Error
|
|
83
86
|
* @param enableTrackNativeCrash whether to collect Native Error
|
|
@@ -101,13 +104,30 @@ export enum IOSCrashMonitoringType {
|
|
|
101
104
|
* @param rumCacheLimitCount RUM max cache size, default 100_000
|
|
102
105
|
* @param rumDiscardStrategy RUM data discard strategy
|
|
103
106
|
* @param enableTraceWebView Set whether to enable WebView data collection, default true
|
|
104
|
-
* @param allowWebViewHost
|
|
107
|
+
* @param allowWebViewHost Deprecated; use FTMobileConfig.allowWebViewHost for shared WebView host filtering.
|
|
105
108
|
* @param iosCrashMonitoringType iOS crash monitoring type , default is highCompatibility, which does not include Mach exceptions for better compatibility. you must enable system crash monitoring to get crash stack traces and crash information.
|
|
106
109
|
*/
|
|
107
110
|
export interface FTRUMConfig {
|
|
111
|
+
/** Valid config enables Visitor ID; omitted/null disables it. */
|
|
112
|
+
visitorIdConfig?: FTVisitorIdConfig | null;
|
|
108
113
|
androidAppId: string;
|
|
109
114
|
iOSAppId: string;
|
|
115
|
+
/**
|
|
116
|
+
* RUM session sampling percentage in [0, 100], default 100.
|
|
117
|
+
* Supports fractional percentages: 12.5 means 12.5%, not 0.125.
|
|
118
|
+
* Takes precedence over sampleRate when both are supplied, including 0.
|
|
119
|
+
* Setting 0 disables regular session sampling; error session sampling is separate.
|
|
120
|
+
*/
|
|
121
|
+
sessionSamplingRate?: number;
|
|
122
|
+
/** @deprecated Use sessionSamplingRate. Probability in [0, 1], default 1; 0.125 maps to 12.5%. Ignored when sessionSamplingRate is supplied. */
|
|
110
123
|
sampleRate?: number;
|
|
124
|
+
/**
|
|
125
|
+
* Sampling percentage for otherwise unsampled RUM sessions when an error occurs.
|
|
126
|
+
* Range [0, 100], default 0; fractional percentages are supported.
|
|
127
|
+
* Takes precedence over sessionOnErrorSampleRate when both are supplied, including 0.
|
|
128
|
+
*/
|
|
129
|
+
sessionOnErrorSamplingRate?: number;
|
|
130
|
+
/** @deprecated Use sessionOnErrorSamplingRate. Probability in [0, 1], default 0; multiplied by 100. Ignored when sessionOnErrorSamplingRate is supplied. */
|
|
111
131
|
sessionOnErrorSampleRate?: number;
|
|
112
132
|
enableAutoTrackUserAction?: boolean;
|
|
113
133
|
enableAutoTrackError?: boolean;
|
|
@@ -129,7 +149,8 @@ export interface FTRUMConfig {
|
|
|
129
149
|
rumCacheLimitCount?: number;
|
|
130
150
|
rumDiscardStrategy?: FTRUMCacheDiscard;
|
|
131
151
|
enableTraceWebView?: boolean;
|
|
132
|
-
allowWebViewHost
|
|
152
|
+
/** @deprecated Configure the shared host allowlist in FTMobileConfig.allowWebViewHost. */
|
|
153
|
+
allowWebViewHost?: Array<string> | null;
|
|
133
154
|
iosCrashMonitoringType?: IOSCrashMonitoringType;
|
|
134
155
|
}
|
|
135
156
|
/**
|
|
@@ -205,10 +226,17 @@ type FTReactNativeRUMType = {
|
|
|
205
226
|
/**
|
|
206
227
|
* view load duration.
|
|
207
228
|
* @param viewName view name
|
|
208
|
-
* @param loadTime view load duration
|
|
229
|
+
* @param loadTime monotonic view load duration in nanoseconds
|
|
209
230
|
* @returns a Promise.
|
|
210
231
|
*/
|
|
211
232
|
onCreateView(viewName: string, loadTime: number): Promise<void>;
|
|
233
|
+
/**
|
|
234
|
+
* Replace the current active View's loading duration, between startView and stopView.
|
|
235
|
+
* @param duration monotonic elapsed time in nanoseconds; a non-negative safe integer.
|
|
236
|
+
* Zero clears loading time. Late calls can update a newer View; no View ID is supplied.
|
|
237
|
+
* @returns a Promise for the native call, not for data upload.
|
|
238
|
+
*/
|
|
239
|
+
updateViewLoadingTime(duration: number): Promise<void>;
|
|
212
240
|
/**
|
|
213
241
|
* view start.
|
|
214
242
|
* @param viewName page name
|
|
@@ -289,6 +317,13 @@ class FTReactNativeRUMWrapper implements FTReactNativeRUMType {
|
|
|
289
317
|
.default;
|
|
290
318
|
|
|
291
319
|
setConfig(config: FTRUMConfig): Promise<void> {
|
|
320
|
+
if (config.visitorIdConfig != null) {
|
|
321
|
+
try {
|
|
322
|
+
validateVisitorIdConfig(config.visitorIdConfig);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
return Promise.reject(error);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
292
327
|
bridgeContextManager.configureLongTaskContext(
|
|
293
328
|
config.enableLongTask === true
|
|
294
329
|
);
|
|
@@ -356,6 +391,16 @@ class FTReactNativeRUMWrapper implements FTReactNativeRUMType {
|
|
|
356
391
|
onCreateView(viewName: string, loadTime: number): Promise<void> {
|
|
357
392
|
return this.rum.onCreateView(viewName, loadTime);
|
|
358
393
|
}
|
|
394
|
+
updateViewLoadingTime(duration: number): Promise<void> {
|
|
395
|
+
if (!Number.isSafeInteger(duration) || duration < 0) {
|
|
396
|
+
return Promise.reject(
|
|
397
|
+
new RangeError(
|
|
398
|
+
'duration must be a non-negative safe integer in nanoseconds'
|
|
399
|
+
)
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
return this.rum.updateViewLoadingTime(duration);
|
|
403
|
+
}
|
|
359
404
|
startView(viewName: string, property?: object): Promise<void> {
|
|
360
405
|
// Automatically merge bridge context properties with local properties
|
|
361
406
|
const mergedProperties =
|
package/src/ft_tracing.tsx
CHANGED
|
@@ -35,12 +35,20 @@ export enum TraceType {
|
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
37
|
* Configure trace.
|
|
38
|
-
* @param
|
|
38
|
+
* @param traceSamplingRate Trace sampling percentage in [0, 100], default 100. Supports fractional percentages and takes precedence over sampleRate.
|
|
39
|
+
* @param sampleRate Deprecated Trace sampling probability in [0, 1], default 1. Used only when traceSamplingRate is omitted; multiplied by 100 before passing to Native.
|
|
39
40
|
* @param traceType trace type
|
|
40
41
|
* @param enableLinkRUMData whether to link with RUM data
|
|
41
42
|
* @param enableNativeAutoTrace whether to enable automatic tracing
|
|
42
43
|
*/
|
|
43
44
|
export interface FTTraceConfig {
|
|
45
|
+
/**
|
|
46
|
+
* Trace sampling percentage in [0, 100], default 100.
|
|
47
|
+
* Supports fractional percentages: 12.5 means 12.5%, not 0.125.
|
|
48
|
+
* Takes precedence over sampleRate when both are supplied, including 0.
|
|
49
|
+
*/
|
|
50
|
+
traceSamplingRate?: number;
|
|
51
|
+
/** @deprecated Use traceSamplingRate. Probability in [0, 1], default 1; 0.125 maps to 12.5%. Ignored when traceSamplingRate is supplied. */
|
|
44
52
|
sampleRate?: number;
|
|
45
53
|
traceType?: TraceType;
|
|
46
54
|
enableLinkRUMData?: boolean;
|
package/src/index.tsx
CHANGED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { AppState } from 'react-native';
|
|
2
|
+
import type { AppStateStatus } from 'react-native';
|
|
3
|
+
import { FTReactNativeRUM } from '../ft_rum';
|
|
4
|
+
import { bridgeContextManager } from '../ft_mobile_agent';
|
|
5
|
+
import type { Spec as NativeRUMSpec } from '../specs/NativeFTReactNativeRUM';
|
|
6
|
+
|
|
7
|
+
type Route = { key: string; name: string };
|
|
8
|
+
type Loading = { monotonicMs: number; epochMs: number };
|
|
9
|
+
const MAX_PENDING_LOAD_MS = 15 * 60 * 1000;
|
|
10
|
+
|
|
11
|
+
/** Shared lifecycle coordinator for navigation adapters. Use one per navigator. */
|
|
12
|
+
export class FTRumViewTracking {
|
|
13
|
+
private current?: Route;
|
|
14
|
+
private active?: { key: string; generation: number };
|
|
15
|
+
private generation = 0;
|
|
16
|
+
private lifecycle = 0;
|
|
17
|
+
private state: AppStateStatus | null = null;
|
|
18
|
+
private subscription?: { remove(): void };
|
|
19
|
+
private loading = new Map<string, Loading>();
|
|
20
|
+
|
|
21
|
+
constructor(private readonly onBackground?: () => void) {}
|
|
22
|
+
|
|
23
|
+
start(): void {
|
|
24
|
+
if (this.subscription) return;
|
|
25
|
+
this.state = AppState.currentState;
|
|
26
|
+
const lifecycle = ++this.lifecycle;
|
|
27
|
+
const listener = (state: AppStateStatus): void => {
|
|
28
|
+
if (lifecycle === this.lifecycle) this.onAppState(state);
|
|
29
|
+
};
|
|
30
|
+
const subscription = AppState.addEventListener('change', listener);
|
|
31
|
+
// RN < 0.65 uses removeEventListener instead of returning a subscription.
|
|
32
|
+
const legacyAppState = AppState as typeof AppState & {
|
|
33
|
+
removeEventListener?: (type: string, callback: typeof listener) => void;
|
|
34
|
+
};
|
|
35
|
+
this.subscription = subscription || {
|
|
36
|
+
remove: () => legacyAppState.removeEventListener?.('change', listener),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Capture both clocks at the loading boundary, before any async work. */
|
|
41
|
+
beginLoad(key: string): void {
|
|
42
|
+
this.start();
|
|
43
|
+
this.pruneLoading();
|
|
44
|
+
if (this.state === 'background' || this.loading.has(key)) return;
|
|
45
|
+
if (
|
|
46
|
+
typeof performance === 'undefined' ||
|
|
47
|
+
typeof performance.now !== 'function'
|
|
48
|
+
)
|
|
49
|
+
return;
|
|
50
|
+
// Only the latest transition can supply loading for this navigator.
|
|
51
|
+
this.loading.clear();
|
|
52
|
+
this.loading.set(key, {
|
|
53
|
+
monotonicMs: performance.now(),
|
|
54
|
+
epochMs: Date.now(),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Remember a transitioning route so foreground recovery uses the current page. */
|
|
59
|
+
setCurrent(route: Route): void {
|
|
60
|
+
this.start();
|
|
61
|
+
this.current = { key: route.key, name: route.name };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
show(route: Route, loadingKey: string = route.key): number | undefined {
|
|
65
|
+
this.setCurrent(route);
|
|
66
|
+
this.pruneLoading();
|
|
67
|
+
const loading = this.loading.get(loadingKey);
|
|
68
|
+
this.loading.clear();
|
|
69
|
+
if (this.state !== 'active') return undefined;
|
|
70
|
+
if (this.active?.key === route.key) return this.active.generation;
|
|
71
|
+
this.active = { key: route.key, generation: ++this.generation };
|
|
72
|
+
if (loading) {
|
|
73
|
+
const durationNs = Math.floor(
|
|
74
|
+
(performance.now() - loading.monotonicMs) * 1000000
|
|
75
|
+
);
|
|
76
|
+
if (Number.isFinite(durationNs) && durationNs > 0) {
|
|
77
|
+
this.report(
|
|
78
|
+
this.nativeRUM.onCreateViewWithLoadStart(
|
|
79
|
+
route.name,
|
|
80
|
+
durationNs,
|
|
81
|
+
Number.isFinite(loading.epochMs) ? loading.epochMs : 0
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Enqueue these calls together. Waiting for a Promise here could start a stale
|
|
87
|
+
// page after background/route changes; native consumes loading before start.
|
|
88
|
+
this.report(
|
|
89
|
+
this.nativeRUM.startViewWithTrackingType(
|
|
90
|
+
route.name,
|
|
91
|
+
bridgeContextManager.mergeWithLocalPropertiesSync(),
|
|
92
|
+
1 // Automatic is an internal framework bridge detail.
|
|
93
|
+
)
|
|
94
|
+
);
|
|
95
|
+
return this.active.generation;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Apply a completed interval only to the appearance that started it. */
|
|
99
|
+
updateLoadingTime(
|
|
100
|
+
key: string,
|
|
101
|
+
generation: number | undefined,
|
|
102
|
+
durationNs: number
|
|
103
|
+
): void {
|
|
104
|
+
if (
|
|
105
|
+
this.state !== 'active' ||
|
|
106
|
+
!this.active ||
|
|
107
|
+
this.active.key !== key ||
|
|
108
|
+
this.active.generation !== generation ||
|
|
109
|
+
!Number.isSafeInteger(durationNs) ||
|
|
110
|
+
durationNs <= 0
|
|
111
|
+
)
|
|
112
|
+
return;
|
|
113
|
+
// Enqueue immediately; never let a Promise continuation update another View.
|
|
114
|
+
this.report(FTReactNativeRUM.updateViewLoadingTime(durationNs));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Only the appearance that owns the active generation may close it. */
|
|
118
|
+
hide(key: string, generation: number | undefined): void {
|
|
119
|
+
if (this.state !== 'active') return;
|
|
120
|
+
if (
|
|
121
|
+
!this.active ||
|
|
122
|
+
this.active.key !== key ||
|
|
123
|
+
this.active.generation !== generation
|
|
124
|
+
)
|
|
125
|
+
return;
|
|
126
|
+
this.active = undefined;
|
|
127
|
+
this.current = undefined;
|
|
128
|
+
this.report(FTReactNativeRUM.stopView());
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
stop(): void {
|
|
132
|
+
++this.lifecycle;
|
|
133
|
+
this.subscription?.remove();
|
|
134
|
+
this.subscription = undefined;
|
|
135
|
+
if (this.active && this.state === 'active')
|
|
136
|
+
this.report(FTReactNativeRUM.stopView());
|
|
137
|
+
this.active = undefined;
|
|
138
|
+
this.current = undefined;
|
|
139
|
+
this.loading.clear();
|
|
140
|
+
// Never reuse generation values: callbacks from a previous subscription can arrive late.
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private onAppState = (state: AppStateStatus): void => {
|
|
144
|
+
if (state === this.state) return;
|
|
145
|
+
this.state = state;
|
|
146
|
+
if (state === 'background') {
|
|
147
|
+
// Native closes Automatic Views at its own history boundary. No JS stop.
|
|
148
|
+
this.active = undefined;
|
|
149
|
+
this.loading.clear();
|
|
150
|
+
this.onBackground?.();
|
|
151
|
+
} else if (state === 'active' && this.current) {
|
|
152
|
+
this.show(this.current);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
private pruneLoading(): void {
|
|
157
|
+
const now =
|
|
158
|
+
typeof performance !== 'undefined' &&
|
|
159
|
+
typeof performance.now === 'function'
|
|
160
|
+
? performance.now()
|
|
161
|
+
: NaN;
|
|
162
|
+
for (const [key, load] of this.loading) {
|
|
163
|
+
const age = now - load.monotonicMs;
|
|
164
|
+
if (!Number.isFinite(age) || age < 0 || age >= MAX_PENDING_LOAD_MS)
|
|
165
|
+
this.loading.delete(key);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private get nativeRUM(): NativeRUMSpec {
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
171
|
+
return require('../specs/NativeFTReactNativeRUM').default;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private report(result: Promise<void>): void {
|
|
175
|
+
result.catch((error) =>
|
|
176
|
+
console.warn('[TrueWatch] Automatic View tracking failed', error)
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import NativeWebSocket from '../specs/NativeFTReactNativeWebSocket';
|
|
2
2
|
import { bridgeContextManager } from '../ft_mobile_agent';
|
|
3
|
+
import type { WebSocketTimingMetrics } from './FTRumWebSocketTiming';
|
|
3
4
|
|
|
4
5
|
export interface NativeWebSocketResource {
|
|
5
6
|
url: string;
|
|
@@ -51,7 +52,7 @@ export class NativeWebSocketSession {
|
|
|
51
52
|
addResource(
|
|
52
53
|
key: string,
|
|
53
54
|
content: NativeWebSocketResource,
|
|
54
|
-
metrics:
|
|
55
|
+
metrics: Partial<WebSocketTimingMetrics>
|
|
55
56
|
): Promise<void> {
|
|
56
57
|
return NativeWebSocket!.addResource(key, content, metrics, this.session);
|
|
57
58
|
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
export interface WebSocketTimingMetrics {
|
|
2
|
+
duration: number;
|
|
3
|
+
// Decimal int64 nanoseconds: epoch nanoseconds exceed JS number precision.
|
|
4
|
+
fetchStartNsTimeInterval?: string;
|
|
5
|
+
fetchEndNsTimeInterval?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
1
8
|
/** Measures only the JS-observed opening handshake; bridge delays are excluded. */
|
|
2
|
-
export function startWebSocketTiming(): () =>
|
|
9
|
+
export function startWebSocketTiming(): () =>
|
|
10
|
+
| WebSocketTimingMetrics
|
|
11
|
+
| undefined {
|
|
3
12
|
const wallStart = readWallTime();
|
|
4
13
|
let monotonicNow: (() => number) | undefined;
|
|
5
14
|
let monotonicStart: number | undefined;
|
|
@@ -36,10 +45,33 @@ export function startWebSocketTiming(): () => number | undefined {
|
|
|
36
45
|
}
|
|
37
46
|
// Resource metrics use nanoseconds. Do not send an invalid bridge number.
|
|
38
47
|
const durationNs = Math.round(durationMs * 1e6);
|
|
39
|
-
|
|
48
|
+
if (!Number.isSafeInteger(durationNs)) return undefined;
|
|
49
|
+
const metrics: WebSocketTimingMetrics = { duration: durationNs };
|
|
50
|
+
if (
|
|
51
|
+
wallStart !== undefined &&
|
|
52
|
+
Number.isSafeInteger(wallStart) &&
|
|
53
|
+
wallStart > 0
|
|
54
|
+
) {
|
|
55
|
+
// Add the monotonic elapsed interval to the start epoch, not Date.now()
|
|
56
|
+
// at completion. Split milliseconds/remainder to avoid epoch-ns doubles.
|
|
57
|
+
const endMs = wallStart + Math.floor(durationNs / 1e6);
|
|
58
|
+
const start = `${wallStart}000000`;
|
|
59
|
+
const end = `${endMs}${String(durationNs % 1e6).padStart(6, '0')}`;
|
|
60
|
+
if (Number.isSafeInteger(endMs) && fitsInt64(start) && fitsInt64(end)) {
|
|
61
|
+
metrics.fetchStartNsTimeInterval = start;
|
|
62
|
+
metrics.fetchEndNsTimeInterval = end;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return metrics;
|
|
40
66
|
};
|
|
41
67
|
}
|
|
42
68
|
|
|
69
|
+
function fitsInt64(value: string): boolean {
|
|
70
|
+
return (
|
|
71
|
+
value.length < 19 || (value.length === 19 && value <= '9223372036854775807')
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
43
75
|
function readWallTime(): number | undefined {
|
|
44
76
|
try {
|
|
45
77
|
const time = Date.now();
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Platform } from 'react-native';
|
|
3
3
|
import NativeFTReactNativeTrace from '../specs/NativeFTReactNativeTrace';
|
|
4
4
|
import { startWebSocketTiming } from './FTRumWebSocketTiming';
|
|
5
|
+
import type { WebSocketTimingMetrics } from './FTRumWebSocketTiming';
|
|
5
6
|
import {
|
|
6
7
|
clearNativeWebSocketSessions,
|
|
7
8
|
startNativeWebSocketSession,
|
|
@@ -42,7 +43,7 @@ interface FTRumWebSocketResourceReporter {
|
|
|
42
43
|
addResource(
|
|
43
44
|
key: string,
|
|
44
45
|
resource: FTRUMWebSocketResource,
|
|
45
|
-
metrics?:
|
|
46
|
+
metrics?: WebSocketTimingMetrics
|
|
46
47
|
): Promise<void>;
|
|
47
48
|
}
|
|
48
49
|
|
|
@@ -330,7 +331,7 @@ export class FTRumWebSocketTracking {
|
|
|
330
331
|
return;
|
|
331
332
|
}
|
|
332
333
|
completed = true;
|
|
333
|
-
const
|
|
334
|
+
const timing = stopTiming();
|
|
334
335
|
// Freeze the observed boundary before inspecting or parsing error data.
|
|
335
336
|
const error = getError?.();
|
|
336
337
|
if (
|
|
@@ -368,7 +369,7 @@ export class FTRumWebSocketTracking {
|
|
|
368
369
|
requestHeader: requestHeaders,
|
|
369
370
|
webSocketEvent: event,
|
|
370
371
|
},
|
|
371
|
-
|
|
372
|
+
timing,
|
|
372
373
|
error,
|
|
373
374
|
errorReady
|
|
374
375
|
);
|
|
@@ -383,7 +384,7 @@ export class FTRumWebSocketTracking {
|
|
|
383
384
|
requestHeaders,
|
|
384
385
|
resourceStatus,
|
|
385
386
|
state,
|
|
386
|
-
|
|
387
|
+
timing,
|
|
387
388
|
error,
|
|
388
389
|
errorReady
|
|
389
390
|
);
|
|
@@ -620,7 +621,7 @@ export class FTRumWebSocketTracking {
|
|
|
620
621
|
requestHeader: Record<string, unknown>,
|
|
621
622
|
resourceStatus: number,
|
|
622
623
|
webSocketHandshakeState: WebSocketHandshakeState,
|
|
623
|
-
|
|
624
|
+
timing: WebSocketTimingMetrics | undefined,
|
|
624
625
|
error?: WebSocketHandshakeError,
|
|
625
626
|
errorReady?: Promise<void>
|
|
626
627
|
): Promise<void> {
|
|
@@ -657,14 +658,8 @@ export class FTRumWebSocketTracking {
|
|
|
657
658
|
webSocketHandshakeState,
|
|
658
659
|
};
|
|
659
660
|
if (error?.message) resource.errorMessage = error.message;
|
|
660
|
-
const metrics: { duration?: number } = {};
|
|
661
|
-
if (duration !== undefined) metrics.duration = duration;
|
|
662
661
|
if (isCurrentLifecycle()) {
|
|
663
|
-
await resourceReporter.addResource(
|
|
664
|
-
resourceKey,
|
|
665
|
-
resource,
|
|
666
|
-
Object.keys(metrics).length ? metrics : undefined
|
|
667
|
-
);
|
|
662
|
+
await resourceReporter.addResource(resourceKey, resource, timing);
|
|
668
663
|
}
|
|
669
664
|
} catch {
|
|
670
665
|
if (isCurrentLifecycle()) {
|
|
@@ -679,7 +674,7 @@ export class FTRumWebSocketTracking {
|
|
|
679
674
|
key: string,
|
|
680
675
|
started: Promise<boolean>,
|
|
681
676
|
content: NativeWebSocketResource,
|
|
682
|
-
|
|
677
|
+
timing: WebSocketTimingMetrics | undefined,
|
|
683
678
|
error?: WebSocketHandshakeError,
|
|
684
679
|
errorReady?: Promise<void>
|
|
685
680
|
): Promise<void> {
|
|
@@ -703,7 +698,7 @@ export class FTRumWebSocketTracking {
|
|
|
703
698
|
await session.addResource(
|
|
704
699
|
key,
|
|
705
700
|
error?.message ? { ...content, errorMessage: error.message } : content,
|
|
706
|
-
|
|
701
|
+
timing ?? {}
|
|
707
702
|
);
|
|
708
703
|
} catch {
|
|
709
704
|
session.release(key);
|
|
@@ -41,6 +41,7 @@ export interface Spec extends TurboModule {
|
|
|
41
41
|
* @returns a Promise.
|
|
42
42
|
*/
|
|
43
43
|
unbindRUMUserData(): Promise<void>;
|
|
44
|
+
resetVisitorId(): Promise<void>;
|
|
44
45
|
/**
|
|
45
46
|
* Add custom global parameters. Applies to RUM and Log data
|
|
46
47
|
* @param context custom global parameters.
|