@dynatrace/react-native-plugin 2.333.1 → 2.337.1

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.
Files changed (50) hide show
  1. package/README.md +181 -232
  2. package/android/build.gradle +1 -1
  3. package/files/plugin-runtime.gradle +27 -13
  4. package/files/plugin.gradle +1 -1
  5. package/instrumentation/BabelPluginDynatrace.js +1 -0
  6. package/instrumentation/DynatraceInstrumentation.js +1 -1
  7. package/instrumentation/jsx/JsxRuntime.js +4 -4
  8. package/instrumentation/libs/UserInteraction.js +114 -0
  9. package/instrumentation/libs/community/Picker.js +1 -1
  10. package/instrumentation/libs/community/gesture-handler/Touchables.InstrInfo.js +2 -0
  11. package/instrumentation/libs/community/gesture-handler/Touchables.js +3 -1
  12. package/instrumentation/libs/community/gesture-handler/index.js +3 -1
  13. package/instrumentation/libs/react-navigation/ReactNavigation.js +9 -0
  14. package/instrumentation/libs/withOnPressMonitoring.js +65 -14
  15. package/lib/core/Dynatrace.js +3 -0
  16. package/lib/core/DynatraceBridge.js +5 -7
  17. package/lib/core/configuration/ActionNameOptions.js +2 -0
  18. package/lib/core/configuration/Configuration.js +5 -1
  19. package/lib/core/configuration/ConfigurationBuilder.js +11 -1
  20. package/lib/core/configuration/ConfigurationDefaults.js +3 -1
  21. package/lib/core/configuration/ConfigurationHandler.js +64 -0
  22. package/lib/core/configuration/ConfigurationPreset.js +6 -0
  23. package/lib/core/configuration/ManualStartupConfiguration.js +9 -1
  24. package/lib/dynatrace-reporter.js +0 -14
  25. package/lib/dynatrace-transformer.js +10 -13
  26. package/lib/features/ui-interaction/Config.js +8 -2
  27. package/lib/features/ui-interaction/Plugin.Fragment.Test.js +170 -0
  28. package/lib/features/ui-interaction/Plugin.js +226 -882
  29. package/lib/features/ui-interaction/Run.js +11 -7
  30. package/lib/features/ui-interaction/Runtime.js +258 -913
  31. package/lib/features/ui-interaction/TouchMetaResolver.js +492 -0
  32. package/lib/features/ui-interaction/Types.js +1 -62
  33. package/lib/next/Dynatrace.js +44 -0
  34. package/lib/next/configuration/INativeRuntimeConfiguration.js +9 -0
  35. package/lib/next/configuration/RuntimeConfigurationObserver.js +50 -6
  36. package/lib/next/events/EventPipeline.js +14 -6
  37. package/lib/next/events/HttpRequestEventData.js +26 -30
  38. package/lib/next/provider/TimestampProvider.js +20 -7
  39. package/lib/next/util/TraceContextUtils.js +108 -0
  40. package/package.json +8 -10
  41. package/react-native-dynatrace.podspec +1 -1
  42. package/scripts/Android.js +96 -66
  43. package/scripts/Config.js +11 -1
  44. package/scripts/Ios.js +288 -71
  45. package/scripts/PathsConstants.js +34 -20
  46. package/scripts/core/InstrumentCall.js +8 -3
  47. package/scripts/core/LineOffsetAnalyzeCall.js +9 -15
  48. package/scripts/util/SourceMapUtil.js +49 -11
  49. package/types.d.ts +178 -39
  50. package/scripts/util/ReactOptions.js +0 -21
@@ -5,6 +5,43 @@ const RuntimeConfigurationObserver_1 = require("../../next/configuration/Runtime
5
5
  const LogLevel_1 = require("../logging/LogLevel");
6
6
  let _configuration;
7
7
  let _uiiEnabledLastKnownGood = undefined;
8
+ const getRuntimeConfiguration = () => RuntimeConfigurationObserver_1.RuntimeConfigurationObserver.getCurrentRuntimeConfiguration();
9
+ const getBaseThirdGenDecision = () => {
10
+ if (!RuntimeConfigurationObserver_1.RuntimeConfigurationObserver.isInitiated()) {
11
+ return {
12
+ allowed: true,
13
+ reason: 'runtime_config_unavailable',
14
+ };
15
+ }
16
+ const runtimeConfiguration = getRuntimeConfiguration();
17
+ if (!runtimeConfiguration['3rd_gen_enabled']) {
18
+ return {
19
+ allowed: false,
20
+ reason: 'third_gen_disabled',
21
+ };
22
+ }
23
+ if (runtimeConfiguration.data_collection_level === 0) {
24
+ return {
25
+ allowed: false,
26
+ reason: 'data_collection_off',
27
+ };
28
+ }
29
+ if (!runtimeConfiguration.agent_status) {
30
+ return {
31
+ allowed: false,
32
+ reason: 'agent_inactive',
33
+ };
34
+ }
35
+ if (runtimeConfiguration.traffic_control) {
36
+ return {
37
+ allowed: false,
38
+ reason: 'traffic_control_active',
39
+ };
40
+ }
41
+ return {
42
+ allowed: true,
43
+ };
44
+ };
8
45
  exports.ConfigurationHandler = {
9
46
  setConfiguration(configuration) {
10
47
  _configuration = configuration;
@@ -19,7 +56,34 @@ exports.ConfigurationHandler = {
19
56
  _configuration.logLevel === LogLevel_1.LogLevel.Debug,
20
57
  isLifecycleUpdateEnabled: () => _configuration.lifecycleUpdate,
21
58
  isActionNamePrivacyEnabled: () => _configuration.actionNamePrivacy,
59
+ getActionNamePreference: () => _configuration.actionNamePreference,
60
+ getActionNameAlgorithm: () => _configuration.actionNameAlgorithm,
22
61
  isGrailEnabled: () => RuntimeConfigurationObserver_1.RuntimeConfigurationObserver.getCurrentRuntimeConfiguration()['3rd_gen_enabled'],
62
+ getThirdGenDataReportingDecision: () => getBaseThirdGenDecision(),
63
+ getThirdGenHeaderPropagationDecision: () => getBaseThirdGenDecision(),
64
+ getThirdGenCrashReportingDecision: () => {
65
+ const decision = getBaseThirdGenDecision();
66
+ if (!decision.allowed) {
67
+ return decision;
68
+ }
69
+ if (decision.reason === 'runtime_config_unavailable') {
70
+ return decision;
71
+ }
72
+ if (!getRuntimeConfiguration().crash_reporting) {
73
+ return {
74
+ allowed: false,
75
+ reason: 'crash_reporting_disabled',
76
+ };
77
+ }
78
+ return {
79
+ allowed: true,
80
+ };
81
+ },
82
+ getInstanceId: () => RuntimeConfigurationObserver_1.RuntimeConfigurationObserver.getCurrentRuntimeConfiguration()['instance_id'],
83
+ getSessionId: () => RuntimeConfigurationObserver_1.RuntimeConfigurationObserver.getCurrentRuntimeConfiguration()['session_id'],
84
+ getApplicationId: () => RuntimeConfigurationObserver_1.RuntimeConfigurationObserver.getCurrentRuntimeConfiguration()['application_id'],
85
+ getTracestateKeyPrefix: () => RuntimeConfigurationObserver_1.RuntimeConfigurationObserver.getCurrentRuntimeConfiguration()['tracestate_key_prefix'],
86
+ getTraceContextEnabled: () => RuntimeConfigurationObserver_1.RuntimeConfigurationObserver.getCurrentRuntimeConfiguration()['trace_context_enabled'],
23
87
  isUserInteractionEnabled: () => {
24
88
  const isRuntimeConfigInitiated = RuntimeConfigurationObserver_1.RuntimeConfigurationObserver.isInitiated();
25
89
  if (isRuntimeConfigInitiated) {
@@ -21,6 +21,12 @@ class ConfigurationPreset {
21
21
  getActionNamePrivacy() {
22
22
  return ConfigurationDefaults_1.DEFAULT_ACTION_NAME_PRIVACY;
23
23
  }
24
+ getActionNamePreference() {
25
+ return ConfigurationDefaults_1.DEFAULT_ACTION_NAME_PREFERENCE;
26
+ }
27
+ getActionNameAlgorithm() {
28
+ return ConfigurationDefaults_1.DEFAULT_ACTION_NAME_ALGORITHM;
29
+ }
24
30
  getBundleName() {
25
31
  return undefined;
26
32
  }
@@ -5,7 +5,7 @@ const LogLevelUtil_1 = require("../logging/LogLevelUtil");
5
5
  const ConfigurationDefaults_1 = require("./ConfigurationDefaults");
6
6
  const ConfigurationPreset_1 = require("./ConfigurationPreset");
7
7
  class ManualStartupConfiguration {
8
- constructor(beaconUrl, applicationId, reportCrash, logLevel, lifecycleUpdate, userOptIn, actionNamePrivacy, bundleName, bundleVersion) {
8
+ constructor(beaconUrl, applicationId, reportCrash, logLevel, lifecycleUpdate, userOptIn, actionNamePrivacy, actionNamePreference, actionNameAlgorithm, bundleName, bundleVersion) {
9
9
  this.reportCrash = ConfigurationDefaults_1.DEFAULT_REPORT_CRASH;
10
10
  this.userOptIn = ConfigurationDefaults_1.DEFAULT_USER_OPT_IN;
11
11
  if (!applicationId || !beaconUrl) {
@@ -19,6 +19,8 @@ class ManualStartupConfiguration {
19
19
  this.reportFatalErrorAsCrash = preset.isReportFatalErrorAsCrash();
20
20
  this.lifecycleUpdate = preset.getLifecycleUpdate();
21
21
  this.actionNamePrivacy = preset.getActionNamePrivacy();
22
+ this.actionNamePreference = preset.getActionNamePreference();
23
+ this.actionNameAlgorithm = preset.getActionNameAlgorithm();
22
24
  this.bundleName = preset.getBundleName();
23
25
  if (reportCrash != null) {
24
26
  this.reportCrash = reportCrash;
@@ -35,6 +37,12 @@ class ManualStartupConfiguration {
35
37
  if (actionNamePrivacy != null) {
36
38
  this.actionNamePrivacy = actionNamePrivacy;
37
39
  }
40
+ if (actionNamePreference != null) {
41
+ this.actionNamePreference = actionNamePreference;
42
+ }
43
+ if (actionNameAlgorithm != null) {
44
+ this.actionNameAlgorithm = actionNameAlgorithm;
45
+ }
38
46
  if (bundleName != null) {
39
47
  this.bundleName = bundleName;
40
48
  }
@@ -9,7 +9,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- const child_process_1 = require("child_process");
13
12
  const TerminalReporter = (() => {
14
13
  try {
15
14
  return require('metro/src/lib/TerminalReporter');
@@ -24,19 +23,6 @@ const paths = require('../scripts/PathsConstants');
24
23
  const reporter = new TerminalReporter(new Terminal(process.stdout));
25
24
  const updateOrig = TerminalReporter.prototype.update;
26
25
  TerminalReporter.prototype.update = function update(event) {
27
- if ((event === null || event === void 0 ? void 0 : event.type) === 'bundle_build_done') {
28
- (0, child_process_1.exec)('npx lineOffsetDynatrace env prod', (error, stdout, stderr) => {
29
- if (error) {
30
- console.error(`Error: ${error.message}`);
31
- return;
32
- }
33
- if (stderr) {
34
- console.error(`stderr: ${stderr}`);
35
- return;
36
- }
37
- console.log(`${stdout}`);
38
- });
39
- }
40
26
  return updateOrig === null || updateOrig === void 0 ? void 0 : updateOrig.apply(this, arguments);
41
27
  };
42
28
  const update = (event) => __awaiter(void 0, void 0, void 0, function* () {
@@ -1,24 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getUpstreamTransformer = exports.transform = void 0;
4
- const nodePath = require("path");
5
- const PathsConstants_1 = require("../scripts/PathsConstants");
6
4
  const config = require("../scripts/Config");
7
5
  const Logger_1 = require("../scripts/Logger");
8
- const CustomArgumentUtil_1 = require("../scripts/util/CustomArgumentUtil");
6
+ const BabelPluginDynatrace_1 = require("../instrumentation/BabelPluginDynatrace");
9
7
  const instrumentor = require("../instrumentation/DynatraceInstrumentation");
10
- const customArguments = (0, CustomArgumentUtil_1.readCustomArguments)();
11
- const transform = (src, filename, options) => {
12
- if (typeof src === 'object') {
13
- ({ src, filename, options } = src);
14
- }
8
+ const Config_1 = require("../scripts/Config");
9
+ const transform = (args) => {
10
+ var _a;
11
+ let src = args.src;
12
+ const { filename, options } = args;
15
13
  let reactOptions;
16
14
  try {
17
- if (customArguments.isCustomConfigurationPathSet()) {
18
- reactOptions = config.readConfig(nodePath.join(PathsConstants_1.default.getApplicationPath(), customArguments.getCustomConfigurationPath()));
19
- }
20
- else {
21
- reactOptions = config.readConfig(nodePath.join(PathsConstants_1.default.getConfigFilePath()));
15
+ reactOptions = (0, Config_1.readConfigDefault)();
16
+ if (!reactOptions.react.useLegacyJscodeshift) {
17
+ args.plugins = [BabelPluginDynatrace_1.default, ...((_a = args.plugins) !== null && _a !== void 0 ? _a : [])];
18
+ return (0, exports.getUpstreamTransformer)(reactOptions === null || reactOptions === void 0 ? void 0 : reactOptions.react).transform(args);
22
19
  }
23
20
  src = instrumentor.instrument(src, filename, reactOptions.react);
24
21
  }
@@ -7,12 +7,18 @@ exports.DEFAULT_DT_CONFIG = {
7
7
  artifactsDir: Types_1.Literals.DefaultArtifactsDir,
8
8
  runtimeImport: Types_1.Literals.DefaultRuntimeImport,
9
9
  componentNames: {
10
- segment: Types_1.Literals.DefaultSegmentName,
11
- tabButton: Types_1.Literals.DefaultTabButtonName,
12
10
  root: Types_1.Literals.DefaultRootName,
13
11
  },
14
12
  ui: {
15
13
  skipTags: ['NavigationContainer'],
14
+ pathId: {
15
+ enabled: true,
16
+ propName: 'dtPathID',
17
+ uiRootPropName: 'dtUIRoot',
18
+ injectUiRoot: false,
19
+ skipProviderTags: true,
20
+ blacklist: ['AnalyticsRoot', 'Stack', 'React.Fragment', 'Fragment'],
21
+ },
16
22
  },
17
23
  };
18
24
  const getDTConfig = () => exports.DEFAULT_DT_CONFIG;
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsc = require("jscodeshift");
4
+ const Plugin_1 = require("./Plugin");
5
+ const j = jsc.withParser('tsx');
6
+ const runPlugin = (src, filename = 'FragmentCase.tsx') => {
7
+ const root = j(src);
8
+ const out = (0, Plugin_1.applyDTPathIdInstrumentation)(root, filename);
9
+ return out.root.toSource({ quote: 'single' });
10
+ };
11
+ describe('UI interaction Fragment blacklist', () => {
12
+ it('does not inject dtUIRoot by default', () => {
13
+ const code = runPlugin(`
14
+ import React from 'react';
15
+ import { View, Text } from 'react-native';
16
+
17
+ export default function Screen() {
18
+ return (
19
+ <View>
20
+ <Text>Hello</Text>
21
+ </View>
22
+ );
23
+ }
24
+ `);
25
+ expect(code).toContain('<View dtPathID=');
26
+ expect(code).not.toContain('dtUIRoot');
27
+ });
28
+ it('does not instrument React.Fragment', () => {
29
+ const code = runPlugin(`
30
+ import React from 'react';
31
+ import { View, Text } from 'react-native';
32
+
33
+ export default function Screen() {
34
+ return (
35
+ <View>
36
+ <React.Fragment>
37
+ <Text>Hello</Text>
38
+ </React.Fragment>
39
+ </View>
40
+ );
41
+ }
42
+ `);
43
+ expect(code).toContain('<React.Fragment>');
44
+ expect(code).not.toContain('<React.Fragment dtPathID=');
45
+ expect(code).toContain('<View dtPathID=');
46
+ expect(code).toContain('<Text dtPathID=');
47
+ });
48
+ it('does not instrument imported Fragment', () => {
49
+ const code = runPlugin(`
50
+ import React, { Fragment } from 'react';
51
+ import { View, Text } from 'react-native';
52
+
53
+ export default function Screen() {
54
+ return (
55
+ <View>
56
+ <Fragment>
57
+ <Text>Hello</Text>
58
+ </Fragment>
59
+ </View>
60
+ );
61
+ }
62
+ `);
63
+ expect(code).toContain('<Fragment>');
64
+ expect(code).not.toContain('<Fragment dtPathID=');
65
+ expect(code).toContain('<View dtPathID=');
66
+ expect(code).toContain('<Text dtPathID=');
67
+ });
68
+ it('does not instrument aliased Fragment imports', () => {
69
+ const code = runPlugin(`
70
+ import React, { Fragment as F } from 'react';
71
+ import { View, Text } from 'react-native';
72
+
73
+ export default function Screen() {
74
+ return (
75
+ <View>
76
+ <F>
77
+ <Text>Hello</Text>
78
+ </F>
79
+ </View>
80
+ );
81
+ }
82
+ `);
83
+ expect(code).toContain('<F>');
84
+ expect(code).not.toContain('<F dtPathID=');
85
+ expect(code).toContain('<View dtPathID=');
86
+ expect(code).toContain('<Text dtPathID=');
87
+ });
88
+ it('does not instrument empty fragment tags', () => {
89
+ const code = runPlugin(`
90
+ import React from 'react';
91
+ import { View, Text } from 'react-native';
92
+
93
+ export default function Screen() {
94
+ return (
95
+ <>
96
+ <View>
97
+ <Text>Hello</Text>
98
+ </View>
99
+ </>
100
+ );
101
+ }
102
+ `);
103
+ expect(code).toContain('<>');
104
+ expect(code).toContain('<View dtPathID=');
105
+ expect(code).toContain('<Text dtPathID=');
106
+ });
107
+ it('removes existing dtPathID from React.Fragment JSX', () => {
108
+ const code = runPlugin(`
109
+ import React from 'react';
110
+ export default function Screen() {
111
+ return <React.Fragment dtPathID='bad'>Hello</React.Fragment>;
112
+ }
113
+ `);
114
+ expect(code).toContain('<React.Fragment>');
115
+ expect(code).not.toContain('dtPathID=');
116
+ });
117
+ it('removes dtPathID from transpiled jsx Fragment call', () => {
118
+ const code = runPlugin(`
119
+ import { Fragment as _Fragment, jsx as _jsx } from 'react/jsx-runtime';
120
+ export default function Screen() {
121
+ return _jsx(_Fragment, { dtPathID: 'bad', children: 'Hello' });
122
+ }
123
+ `);
124
+ expect(code).toContain('_jsx(_Fragment');
125
+ expect(code).not.toContain('dtPathID');
126
+ });
127
+ it('removes dtPathID from transpiled jsxDEV Fragment call', () => {
128
+ const code = runPlugin(`
129
+ import { Fragment as _Fragment, jsxDEV as _jsxDEV } from 'react/jsx-dev-runtime';
130
+ export default function Screen() {
131
+ return _jsxDEV(_Fragment, { dtPathID: 'bad', children: 'Hello' }, void 0, false, void 0, this);
132
+ }
133
+ `);
134
+ expect(code).toContain('_jsxDEV(_Fragment');
135
+ expect(code).not.toContain('dtPathID');
136
+ });
137
+ it('does not instrument Stack elements', () => {
138
+ const code = runPlugin(`
139
+ import React from 'react';
140
+ import { Stack } from 'expo-router';
141
+ import { View, Text } from 'react-native';
142
+
143
+ export default function Screen() {
144
+ return (
145
+ <View>
146
+ <Stack>
147
+ <Text>Hello</Text>
148
+ </Stack>
149
+ </View>
150
+ );
151
+ }
152
+ `);
153
+ expect(code).toContain('<Stack>');
154
+ expect(code).not.toContain('<Stack dtPathID=');
155
+ expect(code).toContain('<View dtPathID=');
156
+ expect(code).toContain('<Text dtPathID=');
157
+ });
158
+ it('skips instrumentation for Expo package files', () => {
159
+ const src = `
160
+ import React from 'react';
161
+ import { View, Text } from 'react-native';
162
+ export default function Screen() {
163
+ return <View><Text>Hello</Text></View>;
164
+ }
165
+ `;
166
+ const code = runPlugin(src, 'node_modules/expo-router/build/qualified-entry.js');
167
+ expect(code).toEqual(j(src).toSource({ quote: 'single' }));
168
+ expect(code).not.toContain('dtPathID');
169
+ });
170
+ });