@dreamhorizonorg/pulse-react-native 0.0.1 → 0.0.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/PulseReactNativeOtel.podspec +1 -1
- package/README.md +34 -879
- package/android/build.gradle +10 -15
- package/android/proguard-rules.pro +3 -99
- package/android/src/main/java/com/pulsereactnativeotel/Pulse.kt +89 -0
- package/android/src/main/java/com/pulsereactnativeotel/PulseOtelConstants.kt +1 -1
- package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelLogger.kt +3 -1
- package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelModule.kt +69 -3
- package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelPackage.kt +1 -1
- package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelTracer.kt +24 -8
- package/android/src/main/java/com/pulsereactnativeotel/ReactNativeScreenAttributesLogRecordProcessor.kt +21 -0
- package/android/src/main/java/com/pulsereactnativeotel/ReactNativeScreenAttributesSpanProcessor.kt +30 -0
- package/android/src/main/java/com/pulsereactnativeotel/ReactNativeScreenNameTracker.kt +17 -0
- package/app.plugin.js +1 -0
- package/ios/PulseReactNativeOtel.mm +7 -1
- package/lib/module/NativePulseReactNativeOtel.js.map +1 -1
- package/lib/module/config.js +57 -19
- package/lib/module/config.js.map +1 -1
- package/lib/module/errorBoundary.js.map +1 -1
- package/lib/module/events.js +6 -0
- package/lib/module/events.js.map +1 -1
- package/lib/module/index.js +4 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/navigation/index.js +179 -0
- package/lib/module/navigation/index.js.map +1 -0
- package/lib/module/navigation/navigation.interface.js +8 -0
- package/lib/module/navigation/navigation.interface.js.map +1 -0
- package/lib/module/navigation/screen-interactive.js +101 -0
- package/lib/module/navigation/screen-interactive.js.map +1 -0
- package/lib/module/navigation/screen-load.js +67 -0
- package/lib/module/navigation/screen-load.js.map +1 -0
- package/lib/module/navigation/screen-session.js +60 -0
- package/lib/module/navigation/screen-session.js.map +1 -0
- package/lib/module/navigation/useNavigationTracking.js +34 -0
- package/lib/module/navigation/useNavigationTracking.js.map +1 -0
- package/lib/module/navigation/utils.js +17 -0
- package/lib/module/navigation/utils.js.map +1 -0
- package/lib/module/network-interceptor/graphql-helper.js +92 -0
- package/lib/module/network-interceptor/graphql-helper.js.map +1 -0
- package/lib/module/network-interceptor/header-helper.js +24 -0
- package/lib/module/network-interceptor/header-helper.js.map +1 -0
- package/lib/module/network-interceptor/initialization.js +18 -1
- package/lib/module/network-interceptor/initialization.js.map +1 -1
- package/lib/module/network-interceptor/request-tracker-xhr.js +61 -4
- package/lib/module/network-interceptor/request-tracker-xhr.js.map +1 -1
- package/lib/module/network-interceptor/span-helpers.js +36 -16
- package/lib/module/network-interceptor/span-helpers.js.map +1 -1
- package/lib/module/network-interceptor/url-helper.js +58 -2
- package/lib/module/network-interceptor/url-helper.js.map +1 -1
- package/lib/module/pulse.constants.js +47 -0
- package/lib/module/pulse.constants.js.map +1 -0
- package/lib/module/trace.js +17 -2
- package/lib/module/trace.js.map +1 -1
- package/lib/typescript/plugin/src/index.d.ts +5 -0
- package/lib/typescript/plugin/src/index.d.ts.map +1 -0
- package/lib/typescript/plugin/src/types.d.ts +27 -0
- package/lib/typescript/plugin/src/types.d.ts.map +1 -0
- package/lib/typescript/plugin/src/utils.d.ts +10 -0
- package/lib/typescript/plugin/src/utils.d.ts.map +1 -0
- package/lib/typescript/plugin/src/withAndroidPulse.d.ts +4 -0
- package/lib/typescript/plugin/src/withAndroidPulse.d.ts.map +1 -0
- package/lib/typescript/src/NativePulseReactNativeOtel.d.ts +15 -2
- package/lib/typescript/src/NativePulseReactNativeOtel.d.ts.map +1 -1
- package/lib/typescript/src/config.d.ts +14 -8
- package/lib/typescript/src/config.d.ts.map +1 -1
- package/lib/typescript/src/errorBoundary.d.ts.map +1 -1
- package/lib/typescript/src/events.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +6 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation/index.d.ts +13 -0
- package/lib/typescript/src/navigation/index.d.ts.map +1 -0
- package/lib/typescript/src/navigation/navigation.interface.d.ts +18 -0
- package/lib/typescript/src/navigation/navigation.interface.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen-interactive.d.ts +16 -0
- package/lib/typescript/src/navigation/screen-interactive.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen-load.d.ts +13 -0
- package/lib/typescript/src/navigation/screen-load.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen-session.d.ts +15 -0
- package/lib/typescript/src/navigation/screen-session.d.ts.map +1 -0
- package/lib/typescript/src/navigation/useNavigationTracking.d.ts +5 -0
- package/lib/typescript/src/navigation/useNavigationTracking.d.ts.map +1 -0
- package/lib/typescript/src/navigation/utils.d.ts +8 -0
- package/lib/typescript/src/navigation/utils.d.ts.map +1 -0
- package/lib/typescript/src/network-interceptor/graphql-helper.d.ts +8 -0
- package/lib/typescript/src/network-interceptor/graphql-helper.d.ts.map +1 -0
- package/lib/typescript/src/network-interceptor/header-helper.d.ts +15 -0
- package/lib/typescript/src/network-interceptor/header-helper.d.ts.map +1 -0
- package/lib/typescript/src/network-interceptor/initialization.d.ts +4 -1
- package/lib/typescript/src/network-interceptor/initialization.d.ts.map +1 -1
- package/lib/typescript/src/network-interceptor/network.interface.d.ts +3 -0
- package/lib/typescript/src/network-interceptor/network.interface.d.ts.map +1 -1
- package/lib/typescript/src/network-interceptor/request-tracker-xhr.d.ts.map +1 -1
- package/lib/typescript/src/network-interceptor/span-helpers.d.ts +1 -1
- package/lib/typescript/src/network-interceptor/span-helpers.d.ts.map +1 -1
- package/lib/typescript/src/network-interceptor/url-helper.d.ts +9 -0
- package/lib/typescript/src/network-interceptor/url-helper.d.ts.map +1 -1
- package/lib/typescript/src/pulse.constants.d.ts +43 -0
- package/lib/typescript/src/pulse.constants.d.ts.map +1 -0
- package/lib/typescript/src/pulse.interface.d.ts +2 -1
- package/lib/typescript/src/pulse.interface.d.ts.map +1 -1
- package/lib/typescript/src/trace.d.ts +7 -0
- package/lib/typescript/src/trace.d.ts.map +1 -1
- package/package.json +29 -9
- package/plugin/build/index.d.ts +4 -0
- package/plugin/build/index.js +10 -0
- package/plugin/build/types.d.ts +26 -0
- package/plugin/build/types.js +2 -0
- package/plugin/build/utils.d.ts +9 -0
- package/plugin/build/utils.js +102 -0
- package/plugin/build/withAndroidPulse.d.ts +3 -0
- package/plugin/build/withAndroidPulse.js +53 -0
- package/scripts/pulse-cli.js +82 -0
- package/scripts/uploadService.js +122 -0
- package/scripts/utils.js +125 -0
- package/src/NativePulseReactNativeOtel.ts +18 -2
- package/src/config.ts +94 -23
- package/src/errorBoundary.tsx +11 -5
- package/src/events.ts +7 -0
- package/src/global.d.ts +0 -1
- package/src/index.tsx +7 -4
- package/src/navigation/index.ts +335 -0
- package/src/navigation/navigation.interface.ts +26 -0
- package/src/navigation/screen-interactive.ts +149 -0
- package/src/navigation/screen-load.ts +97 -0
- package/src/navigation/screen-session.ts +87 -0
- package/src/navigation/useNavigationTracking.ts +59 -0
- package/src/navigation/utils.ts +19 -0
- package/src/network-interceptor/graphql-helper.ts +110 -0
- package/src/network-interceptor/header-helper.ts +26 -0
- package/src/network-interceptor/initialization.ts +22 -1
- package/src/network-interceptor/network.interface.ts +3 -0
- package/src/network-interceptor/request-tracker-xhr.ts +93 -3
- package/src/network-interceptor/span-helpers.ts +47 -18
- package/src/network-interceptor/url-helper.ts +67 -1
- package/src/pulse.constants.ts +52 -0
- package/src/pulse.interface.ts +6 -1
- package/src/trace.ts +25 -2
- package/LICENSE +0 -20
- package/lib/module/network-interceptor/request-tracker-fetch.js +0 -72
- package/lib/module/network-interceptor/request-tracker-fetch.js.map +0 -1
- package/lib/module/reactNavigation.js +0 -100
- package/lib/module/reactNavigation.js.map +0 -1
- package/lib/typescript/src/network-interceptor/request-tracker-fetch.d.ts +0 -7
- package/lib/typescript/src/network-interceptor/request-tracker-fetch.d.ts.map +0 -1
- package/lib/typescript/src/reactNavigation.d.ts +0 -10
- package/lib/typescript/src/reactNavigation.d.ts.map +0 -1
- package/src/network-interceptor/request-tracker-fetch.ts +0 -96
- package/src/reactNavigation.tsx +0 -146
package/scripts/utils.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const packageJson = require('../package.json');
|
|
4
|
+
|
|
5
|
+
const FILE_TYPE_TO_BACKEND_TYPE = {
|
|
6
|
+
'js-sourcemap': 'JS',
|
|
7
|
+
'mapping': 'mapping',
|
|
8
|
+
'android-ndk': 'ndk',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function checkAndAssertNodeVersion() {
|
|
12
|
+
const nodeVersion = process.versions.node.split('.');
|
|
13
|
+
const majorVersion = parseInt(nodeVersion[0], 10);
|
|
14
|
+
|
|
15
|
+
const requiredVersion = packageJson.engines?.node;
|
|
16
|
+
let minMajorVersion = 18; // fallback
|
|
17
|
+
|
|
18
|
+
if (requiredVersion) {
|
|
19
|
+
const match = requiredVersion.match(/>=(\d+)/);
|
|
20
|
+
if (match) {
|
|
21
|
+
minMajorVersion = parseInt(match[1], 10);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (majorVersion < minMajorVersion) {
|
|
26
|
+
console.error(
|
|
27
|
+
`✗ Error: Pulse CLI requires Node.js ${minMajorVersion}.0.0 or higher.`
|
|
28
|
+
);
|
|
29
|
+
console.error(` Current version: ${process.versions.node}`);
|
|
30
|
+
console.error(
|
|
31
|
+
` Required: ${requiredVersion || `>=${minMajorVersion}.0.0`}`
|
|
32
|
+
);
|
|
33
|
+
console.error(` Please upgrade Node.js: https://nodejs.org/`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getPlatform(commandName) {
|
|
39
|
+
if (commandName.includes('android')) {
|
|
40
|
+
return 'android';
|
|
41
|
+
}
|
|
42
|
+
if (commandName.includes('ios')) {
|
|
43
|
+
return 'ios';
|
|
44
|
+
}
|
|
45
|
+
return 'Unknown';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function validateFiles(options) {
|
|
49
|
+
const files = [];
|
|
50
|
+
const errors = [];
|
|
51
|
+
|
|
52
|
+
Object.keys(FILE_TYPE_TO_BACKEND_TYPE).forEach((fileOption) => {
|
|
53
|
+
const optionKey = fileOption.replace(/-([a-z])/g, (_, letter) =>
|
|
54
|
+
letter.toUpperCase()
|
|
55
|
+
);
|
|
56
|
+
const optionValue = options[optionKey];
|
|
57
|
+
|
|
58
|
+
if (!optionValue) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const filePath = path.resolve(optionValue);
|
|
63
|
+
if (!fs.existsSync(filePath)) {
|
|
64
|
+
errors.push(`File not found for filepath: ${filePath}`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
files.push({
|
|
69
|
+
optionName: fileOption,
|
|
70
|
+
path: filePath,
|
|
71
|
+
fileName: path.basename(filePath),
|
|
72
|
+
metadataType: FILE_TYPE_TO_BACKEND_TYPE[fileOption],
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (errors.length > 0) {
|
|
77
|
+
throw new Error(`Validation errors:\n ${errors.join('\n ')}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (files.length === 0) {
|
|
81
|
+
throw new Error('No files to upload');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return files;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function validateVersionVersionCodeBundleId(options, commandName) {
|
|
88
|
+
const platform = getPlatform(commandName);
|
|
89
|
+
const isIOS = platform === 'ios';
|
|
90
|
+
const version = isIOS ? options.bundleVersion : options.appVersion;
|
|
91
|
+
|
|
92
|
+
if (!options.versionCode) {
|
|
93
|
+
throw new Error('Version code is required');
|
|
94
|
+
}
|
|
95
|
+
const versionCodeNum = parseInt(options.versionCode, 10);
|
|
96
|
+
if (isNaN(versionCodeNum) || versionCodeNum <= 0) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Invalid version code: "${options.versionCode}". Must be a positive integer.`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!version || typeof version !== 'string' || version.trim().length === 0) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
!version
|
|
105
|
+
? `Missing required option: ${isIOS ? '--bundle-version' : '--app-version'}`
|
|
106
|
+
: `Invalid ${isIOS ? 'bundle version' : 'app version'}: "${version}". Must be a non-empty string.`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (options.bundleId) {
|
|
111
|
+
const bundleIdPattern = /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/i;
|
|
112
|
+
if (!bundleIdPattern.test(options.bundleId)) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Invalid bundle-id: "${options.bundleId}". Must be in reverse domain notation (e.g., com.example.app).`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = {
|
|
121
|
+
checkAndAssertNodeVersion,
|
|
122
|
+
getPlatform,
|
|
123
|
+
validateFiles,
|
|
124
|
+
validateVersionVersionCodeBundleId,
|
|
125
|
+
};
|
|
@@ -22,8 +22,8 @@ export interface Spec extends TurboModule {
|
|
|
22
22
|
attributes?: Object
|
|
23
23
|
): boolean;
|
|
24
24
|
|
|
25
|
-
/** Start an active span; returns spanId
|
|
26
|
-
startSpan(name: string, attributes?: Object): string;
|
|
25
|
+
/** Start an active span; returns spanId. */
|
|
26
|
+
startSpan(name: string, inheritContext: boolean, attributes?: Object): string;
|
|
27
27
|
|
|
28
28
|
/** End a span with optional status code. */
|
|
29
29
|
endSpan(spanId: string, statusCode?: string): boolean;
|
|
@@ -41,6 +41,9 @@ export interface Spec extends TurboModule {
|
|
|
41
41
|
stackTrace?: string
|
|
42
42
|
): boolean;
|
|
43
43
|
|
|
44
|
+
/** Discard a span without sending it to backend. */
|
|
45
|
+
discardSpan(spanId: string): boolean;
|
|
46
|
+
|
|
44
47
|
/** Set user id for the session. Setting null will reset the id */
|
|
45
48
|
setUserId(id: string | null): void;
|
|
46
49
|
|
|
@@ -52,6 +55,19 @@ export interface Spec extends TurboModule {
|
|
|
52
55
|
|
|
53
56
|
/** Trigger ANR test (freezes main thread for 6 seconds) */
|
|
54
57
|
triggerAnr(): void;
|
|
58
|
+
|
|
59
|
+
/** Set the current React Native screen name to sync active screen name on Android/iOS */
|
|
60
|
+
setCurrentScreenName(screenName: string): boolean;
|
|
61
|
+
|
|
62
|
+
/** Get all SDK Remote Config features */
|
|
63
|
+
getAllFeatures(): {
|
|
64
|
+
rn_screen_load: boolean;
|
|
65
|
+
screen_session: boolean;
|
|
66
|
+
rn_screen_interactive: boolean;
|
|
67
|
+
network_instrumentation: boolean;
|
|
68
|
+
custom_events: boolean;
|
|
69
|
+
js_crash: boolean;
|
|
70
|
+
} | null;
|
|
55
71
|
}
|
|
56
72
|
|
|
57
73
|
export default TurboModuleRegistry.getEnforcing<Spec>('PulseReactNativeOtel');
|
package/src/config.ts
CHANGED
|
@@ -3,29 +3,53 @@ import { isSupportedPlatform } from './initialization';
|
|
|
3
3
|
import {
|
|
4
4
|
createReactNavigationIntegration,
|
|
5
5
|
type ReactNavigationIntegration,
|
|
6
|
-
|
|
6
|
+
type NavigationIntegrationOptions,
|
|
7
|
+
} from './navigation';
|
|
7
8
|
import { initializeNetworkInterceptor } from './network-interceptor/initialization';
|
|
9
|
+
import PulseReactNativeOtel from './NativePulseReactNativeOtel';
|
|
10
|
+
import type { PulseFeatureConfig } from './pulse.interface';
|
|
11
|
+
import { PULSE_FEATURE_NAMES } from './pulse.constants';
|
|
8
12
|
|
|
9
|
-
export type
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
autoDetectNetwork?: boolean;
|
|
13
|
+
export type NetworkHeaderConfig = {
|
|
14
|
+
requestHeaders?: string[];
|
|
15
|
+
responseHeaders?: string[];
|
|
13
16
|
};
|
|
14
17
|
|
|
15
|
-
export type
|
|
18
|
+
export type PulseConfig = {
|
|
16
19
|
autoDetectExceptions?: boolean;
|
|
17
20
|
autoDetectNavigation?: boolean;
|
|
18
21
|
autoDetectNetwork?: boolean;
|
|
22
|
+
networkHeaders?: NetworkHeaderConfig;
|
|
19
23
|
};
|
|
20
24
|
|
|
21
|
-
const defaultConfig: PulseConfig = {
|
|
25
|
+
const defaultConfig: Required<PulseConfig> = {
|
|
22
26
|
autoDetectExceptions: true,
|
|
23
27
|
autoDetectNavigation: true,
|
|
24
28
|
autoDetectNetwork: true,
|
|
29
|
+
networkHeaders: {
|
|
30
|
+
requestHeaders: [],
|
|
31
|
+
responseHeaders: [],
|
|
32
|
+
},
|
|
25
33
|
};
|
|
26
34
|
|
|
27
35
|
let currentConfig: PulseConfig = { ...defaultConfig };
|
|
28
36
|
|
|
37
|
+
// Cache for features from remote SDK config
|
|
38
|
+
let cachedFeatures: PulseFeatureConfig;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Gets all features from the remote SDK config.
|
|
42
|
+
* @returns Record of feature names to their enabled status, or null if config not available
|
|
43
|
+
*/
|
|
44
|
+
export function getFeaturesFromRemoteConfig(): PulseFeatureConfig {
|
|
45
|
+
if (cachedFeatures !== undefined) {
|
|
46
|
+
return cachedFeatures;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
cachedFeatures = PulseReactNativeOtel.getAllFeatures();
|
|
50
|
+
return cachedFeatures;
|
|
51
|
+
}
|
|
52
|
+
|
|
29
53
|
function configure(config: PulseConfig): void {
|
|
30
54
|
currentConfig = {
|
|
31
55
|
...currentConfig,
|
|
@@ -34,28 +58,74 @@ function configure(config: PulseConfig): void {
|
|
|
34
58
|
setupErrorHandler(currentConfig.autoDetectExceptions ?? true);
|
|
35
59
|
|
|
36
60
|
if (currentConfig.autoDetectNetwork) {
|
|
37
|
-
initializeNetworkInterceptor(
|
|
61
|
+
initializeNetworkInterceptor(
|
|
62
|
+
currentConfig.networkHeaders ?? {
|
|
63
|
+
requestHeaders: [],
|
|
64
|
+
responseHeaders: [],
|
|
65
|
+
}
|
|
66
|
+
);
|
|
38
67
|
}
|
|
39
68
|
}
|
|
40
69
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
70
|
+
function resolveFeatureState(
|
|
71
|
+
features: PulseFeatureConfig,
|
|
72
|
+
featureName: string,
|
|
73
|
+
optionValue: boolean
|
|
74
|
+
): boolean {
|
|
75
|
+
if (features !== undefined && features !== null)
|
|
76
|
+
return features[featureName] ?? optionValue;
|
|
77
|
+
return optionValue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function resolveNavigationState(
|
|
81
|
+
features: PulseFeatureConfig,
|
|
82
|
+
optionValue: boolean
|
|
83
|
+
): boolean {
|
|
84
|
+
if (features !== undefined && features !== null) {
|
|
85
|
+
const hasAny =
|
|
86
|
+
features[PULSE_FEATURE_NAMES.SCREEN_SESSION] === true ||
|
|
87
|
+
features[PULSE_FEATURE_NAMES.RN_SCREEN_LOAD] === true ||
|
|
88
|
+
features[PULSE_FEATURE_NAMES.RN_SCREEN_INTERACTIVE] === true;
|
|
89
|
+
return hasAny ?? optionValue;
|
|
44
90
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
91
|
+
return optionValue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function start(options?: PulseConfig): void {
|
|
95
|
+
if (!isSupportedPlatform()) return;
|
|
96
|
+
|
|
97
|
+
const features = getFeaturesFromRemoteConfig();
|
|
98
|
+
const config: PulseConfig = {
|
|
99
|
+
autoDetectExceptions: resolveFeatureState(
|
|
100
|
+
features,
|
|
101
|
+
PULSE_FEATURE_NAMES.JS_CRASH,
|
|
102
|
+
options?.autoDetectExceptions ?? defaultConfig.autoDetectExceptions
|
|
103
|
+
),
|
|
104
|
+
autoDetectNavigation: resolveNavigationState(
|
|
105
|
+
features,
|
|
106
|
+
options?.autoDetectNavigation ?? defaultConfig.autoDetectNavigation
|
|
107
|
+
),
|
|
108
|
+
autoDetectNetwork: resolveFeatureState(
|
|
109
|
+
features,
|
|
110
|
+
PULSE_FEATURE_NAMES.NETWORK_INSTRUMENTATION,
|
|
111
|
+
options?.autoDetectNetwork ?? defaultConfig.autoDetectNetwork
|
|
112
|
+
),
|
|
113
|
+
networkHeaders: options?.networkHeaders ?? {
|
|
114
|
+
requestHeaders: [],
|
|
115
|
+
responseHeaders: [],
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
configure(config);
|
|
53
120
|
}
|
|
54
121
|
|
|
55
|
-
export function createNavigationIntegrationWithConfig(
|
|
122
|
+
export function createNavigationIntegrationWithConfig(
|
|
123
|
+
options?: NavigationIntegrationOptions
|
|
124
|
+
): ReactNavigationIntegration {
|
|
56
125
|
if (!isSupportedPlatform()) {
|
|
57
126
|
return {
|
|
58
|
-
registerNavigationContainer: (_: unknown) => {},
|
|
127
|
+
registerNavigationContainer: (_: unknown) => () => {},
|
|
128
|
+
markContentReady: () => {},
|
|
59
129
|
};
|
|
60
130
|
}
|
|
61
131
|
if (!currentConfig.autoDetectNavigation) {
|
|
@@ -63,13 +133,14 @@ export function createNavigationIntegrationWithConfig(): ReactNavigationIntegrat
|
|
|
63
133
|
'[Pulse Navigation] auto-detection disabled via Pulse.start; createNavigationIntegration() returning no-op.'
|
|
64
134
|
);
|
|
65
135
|
const noop: ReactNavigationIntegration = {
|
|
66
|
-
registerNavigationContainer: (_: unknown) => {
|
|
136
|
+
registerNavigationContainer: (_: unknown) => () => {
|
|
67
137
|
console.warn(
|
|
68
138
|
'[Pulse Navigation] auto-detection disabled via Pulse.start; registerNavigationContainer() returning no-op.'
|
|
69
139
|
);
|
|
70
140
|
},
|
|
141
|
+
markContentReady: () => {},
|
|
71
142
|
};
|
|
72
143
|
return noop;
|
|
73
144
|
}
|
|
74
|
-
return createReactNavigationIntegration();
|
|
145
|
+
return createReactNavigationIntegration(options);
|
|
75
146
|
}
|
package/src/errorBoundary.tsx
CHANGED
|
@@ -30,17 +30,22 @@ const INITIAL_STATE: ErrorBoundaryState = {
|
|
|
30
30
|
error: null,
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
export class ErrorBoundary extends React.Component<
|
|
33
|
+
export class ErrorBoundary extends React.Component<
|
|
34
|
+
ErrorBoundaryProps,
|
|
35
|
+
ErrorBoundaryState
|
|
36
|
+
> {
|
|
34
37
|
public state: ErrorBoundaryState = INITIAL_STATE;
|
|
35
38
|
|
|
36
39
|
public componentDidCatch(error: unknown, errorInfo: React.ErrorInfo): void {
|
|
37
|
-
const componentStack =
|
|
40
|
+
const componentStack =
|
|
41
|
+
errorInfo.componentStack || COMPONENT_STACK_UNAVAILABLE;
|
|
38
42
|
const { onError } = this.props;
|
|
39
43
|
|
|
40
44
|
// Error is handled if a fallback is provided, otherwise it's unhandled (fatal)
|
|
41
45
|
const handled = !!this.props.fallback;
|
|
42
46
|
|
|
43
|
-
const errorToReport =
|
|
47
|
+
const errorToReport =
|
|
48
|
+
error instanceof Error ? error : new Error(String(error));
|
|
44
49
|
Pulse.reportException(errorToReport, !handled);
|
|
45
50
|
|
|
46
51
|
if (onError) {
|
|
@@ -76,9 +81,10 @@ export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoun
|
|
|
76
81
|
|
|
77
82
|
export function withErrorBoundary<P extends Record<string, any>>(
|
|
78
83
|
WrappedComponent: React.ComponentType<P>,
|
|
79
|
-
errorBoundaryOptions: ErrorBoundaryProps
|
|
84
|
+
errorBoundaryOptions: ErrorBoundaryProps
|
|
80
85
|
): React.FC<P> {
|
|
81
|
-
const componentDisplayName =
|
|
86
|
+
const componentDisplayName =
|
|
87
|
+
WrappedComponent.displayName || WrappedComponent.name || UNKNOWN_COMPONENT;
|
|
82
88
|
|
|
83
89
|
const Wrapped = React.memo((props: P) => (
|
|
84
90
|
<ErrorBoundary {...errorBoundaryOptions}>
|
package/src/events.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import PulseReactNativeOtel from './NativePulseReactNativeOtel';
|
|
2
|
+
import { getFeaturesFromRemoteConfig } from './config';
|
|
2
3
|
import { mergeWithGlobalAttributes } from './globalAttributes';
|
|
3
4
|
import { isSupportedPlatform } from './initialization';
|
|
4
5
|
import type { PulseAttributes } from './pulse.interface';
|
|
@@ -7,6 +8,12 @@ export function trackEvent(event: string, attributes?: PulseAttributes): void {
|
|
|
7
8
|
if (!isSupportedPlatform()) {
|
|
8
9
|
return;
|
|
9
10
|
}
|
|
11
|
+
const features = getFeaturesFromRemoteConfig();
|
|
12
|
+
const customEventsEnabled = features?.custom_events ?? true;
|
|
13
|
+
|
|
14
|
+
if (!customEventsEnabled) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
10
17
|
|
|
11
18
|
const observedTimeMs = Date.now();
|
|
12
19
|
const mergedAttributes = mergeWithGlobalAttributes(attributes || {});
|
package/src/global.d.ts
CHANGED
package/src/index.tsx
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { startSpan, trackSpan } from './trace';
|
|
2
2
|
import { reportException } from './errorHandler';
|
|
3
3
|
import { trackEvent } from './events';
|
|
4
|
-
import { start
|
|
4
|
+
import { start } from './config';
|
|
5
5
|
import { isInitialized } from './initialization';
|
|
6
6
|
import { setGlobalAttribute } from './globalAttributes';
|
|
7
7
|
import { setUserId, setUserProperty, setUserProperties } from './user';
|
|
8
8
|
import { ErrorBoundary, withErrorBoundary } from './errorBoundary';
|
|
9
|
+
import { useNavigationTracking, markContentReady } from './navigation';
|
|
9
10
|
|
|
10
11
|
export type { Span } from './trace';
|
|
11
|
-
export type { PulseConfig
|
|
12
|
+
export type { PulseConfig } from './config';
|
|
12
13
|
export type { PulseAttributes, PulseAttributeValue } from './pulse.interface';
|
|
13
14
|
export type {
|
|
14
15
|
ReactNavigationIntegration,
|
|
15
16
|
NavigationRoute,
|
|
16
|
-
|
|
17
|
+
NavigationIntegrationOptions,
|
|
18
|
+
} from './navigation';
|
|
17
19
|
|
|
18
20
|
export type { ErrorBoundaryProps, FallbackRender } from './errorBoundary';
|
|
19
21
|
|
|
@@ -21,7 +23,8 @@ export { SpanStatusCode } from './trace';
|
|
|
21
23
|
export const Pulse = {
|
|
22
24
|
start,
|
|
23
25
|
isInitialized,
|
|
24
|
-
|
|
26
|
+
useNavigationTracking,
|
|
27
|
+
markContentReady,
|
|
25
28
|
trackEvent,
|
|
26
29
|
reportException,
|
|
27
30
|
trackSpan,
|