@dynatrace/react-native-plugin 2.313.1 → 2.317.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.
package/README.md CHANGED
@@ -27,13 +27,13 @@ If you want to start using this plugin and are not a Dynatrace customer yet, hea
27
27
  * Android Gradle plugin version 7.0+
28
28
  * Java 11
29
29
  * For iOS users: Minimum iOS 12
30
- * NodeJS 18+
30
+ * NodeJS 18.18+
31
31
 
32
32
  ## Agent Versions
33
33
  This agent versions are configured in this plugin:
34
34
 
35
- * Android Agent: 8.313.1.1004
36
- * iOS Agent: 8.313.1.1016
35
+ * Android Agent: 8.317.1.1007
36
+ * iOS Agent: 8.317.1.1003
37
37
 
38
38
  ## Quick Setup
39
39
 
@@ -1461,6 +1461,9 @@ If you are struggling with a problem, submit a support ticket to Dynatrace (supp
1461
1461
  <br/><br/>
1462
1462
  ## Changelog
1463
1463
 
1464
+ 2.317.1
1465
+ * Updated Android (8.317.1.1007) & iOS Agent (8.317.1.1003)
1466
+
1464
1467
  2.313.1
1465
1468
  * Updated Android (8.313.1.1004) & iOS Agent (8.313.1.1016)
1466
1469
  * Expo transformer is now automatically used, if available.
@@ -72,7 +72,7 @@ repositories {
72
72
  }
73
73
 
74
74
  dependencies {
75
- implementation 'com.dynatrace.agent:agent-android:8.313.1.1004'
75
+ implementation 'com.dynatrace.agent:agent-android:8.317.1.1007'
76
76
  implementation "com.facebook.react:react-native:${safeExtGet('reactNative', '+')}"
77
77
  }
78
78
 
@@ -31,6 +31,7 @@ private val buffer: Queue<AppStartMeasurement> = ConcurrentLinkedQueue()
31
31
  * Setup to register all time points which we want to use to report correct app startup time.
32
32
  */
33
33
  fun setupAppStartListener() {
34
+ val initTimestamp = System.currentTimeMillis() - SystemClock.elapsedRealtime();
34
35
  ReactMarker.addListener { name: ReactMarkerConstants?, _: String?, _: Int ->
35
36
  when (name) {
36
37
  ReactMarkerConstants.RELOAD -> {
@@ -38,7 +39,7 @@ fun setupAppStartListener() {
38
39
  buffer.add(
39
40
  AppStartMeasurement(
40
41
  AppStartMeasurementType.RELOAD.value,
41
- SystemClock.uptimeMillis()
42
+ initTimestamp + SystemClock.elapsedRealtime()
42
43
  )
43
44
  )
44
45
  }
@@ -46,35 +47,35 @@ fun setupAppStartListener() {
46
47
  ReactMarkerConstants.CONTENT_APPEARED -> buffer.add(
47
48
  AppStartMeasurement(
48
49
  AppStartMeasurementType.CONTENT_APPEARED.value,
49
- SystemClock.uptimeMillis()
50
+ initTimestamp + SystemClock.elapsedRealtime()
50
51
  )
51
52
  )
52
53
 
53
54
  ReactMarkerConstants.DOWNLOAD_END -> buffer.add(
54
55
  AppStartMeasurement(
55
56
  AppStartMeasurementType.DOWNLOAD_END.value,
56
- SystemClock.uptimeMillis()
57
+ initTimestamp + SystemClock.elapsedRealtime()
57
58
  )
58
59
  )
59
60
 
60
61
  ReactMarkerConstants.DOWNLOAD_START -> buffer.add(
61
62
  AppStartMeasurement(
62
63
  AppStartMeasurementType.DOWNLOAD_START.value,
63
- SystemClock.uptimeMillis()
64
+ initTimestamp + SystemClock.elapsedRealtime()
64
65
  )
65
66
  )
66
67
 
67
68
  ReactMarkerConstants.RUN_JS_BUNDLE_END -> buffer.add(
68
69
  AppStartMeasurement(
69
70
  AppStartMeasurementType.RUN_JS_BUNDLE_END.value,
70
- SystemClock.uptimeMillis()
71
+ initTimestamp + SystemClock.elapsedRealtime()
71
72
  )
72
73
  )
73
74
 
74
75
  ReactMarkerConstants.RUN_JS_BUNDLE_START -> buffer.add(
75
76
  AppStartMeasurement(
76
77
  AppStartMeasurementType.RUN_JS_BUNDLE_START.value,
77
- SystemClock.uptimeMillis()
78
+ initTimestamp + SystemClock.elapsedRealtime()
78
79
  )
79
80
  )
80
81
 
@@ -9,6 +9,7 @@ import com.facebook.react.bridge.Arguments
9
9
  import com.facebook.react.bridge.Promise
10
10
  import com.facebook.react.bridge.ReactApplicationContext
11
11
  import com.facebook.react.bridge.ReadableMap
12
+ import com.facebook.react.bridge.ReadableArray
12
13
  import org.json.JSONObject
13
14
  import java.net.URI
14
15
  import java.net.URISyntaxException
@@ -291,6 +292,10 @@ class DynatraceRNBridgeImpl(
291
292
  HybridBridge.forwardEvent(JSONObject(DynatraceUtils.toHashMap(attributes)))
292
293
  }
293
294
 
295
+ fun forwardAppStartEvent(attributes: ReadableMap, appStartKeys: ReadableArray) {
296
+ HybridBridge.forwardAppStartEvent(JSONObject(DynatraceUtils.toHashMap(attributes)), appStartKeys.toArrayList() as ArrayList<String>)
297
+ }
298
+
294
299
  fun startView(name: String) {
295
300
  Dynatrace.startView(name)
296
301
  }
@@ -3,6 +3,7 @@ package com.dynatrace.android.agent
3
3
  import com.facebook.react.bridge.Promise
4
4
  import com.facebook.react.bridge.ReactApplicationContext
5
5
  import com.facebook.react.bridge.ReadableMap
6
+ import com.facebook.react.bridge.ReadableArray
6
7
 
7
8
  class DynatraceRNBridge(
8
9
  reactContext: ReactApplicationContext,
@@ -174,6 +175,10 @@ class DynatraceRNBridge(
174
175
  impl.forwardEvent(attributes)
175
176
  }
176
177
 
178
+ override fun forwardAppStartEvent(attributes: ReadableMap, appStartKeys: ReadableArray) {
179
+ impl.forwardAppStartEvent(attributes, appStartKeys)
180
+ }
181
+
177
182
  override fun startView(name: String) {
178
183
  impl.startView(name)
179
184
  }
@@ -5,6 +5,7 @@ import com.facebook.react.bridge.ReactApplicationContext
5
5
  import com.facebook.react.bridge.ReactContextBaseJavaModule
6
6
  import com.facebook.react.bridge.ReactMethod
7
7
  import com.facebook.react.bridge.ReadableMap
8
+ import com.facebook.react.bridge.ReadableArray
8
9
 
9
10
  class DynatraceRNBridge(
10
11
  reactContext: ReactApplicationContext,
@@ -191,6 +192,11 @@ class DynatraceRNBridge(
191
192
  impl.forwardEvent(attributes)
192
193
  }
193
194
 
195
+ @ReactMethod
196
+ fun forwardAppStartEvent(attributes: ReadableMap, appStartKeys: ReadableArray) {
197
+ impl.forwardAppStartEvent(attributes, appStartKeys)
198
+ }
199
+
194
200
  @ReactMethod
195
201
  fun startView(name: String) {
196
202
  impl.startView(name)
@@ -1,3 +1,3 @@
1
1
  dependencies {
2
- classpath 'com.dynatrace.tools.android:gradle-plugin:8.313.1.1004'
2
+ classpath 'com.dynatrace.tools.android:gradle-plugin:8.317.1.1007'
3
3
  }
@@ -1 +1 @@
1
- "use strict";var u,n=require("@babel/runtime/helpers/interopRequireDefault"),r=n(require("@babel/runtime/helpers/toConsumableArray")),a=(Object.defineProperty(exports,"t",{value:!0}),exports.instrument=void 0,require("path")),c=require("jscodeshift"),i=require("jscodeshift/src/Collection"),e=require("../scripts/FileOperationHelper"),t=require("../scripts/PathsConstants"),o=require("./libs/react-native/Touchables.InstrInfo"),l=require("./libs/react-native/RefreshControl.InstrInfo"),f=require("./libs/react-native/Switch.InstrInfo"),s=require("./libs/community/gesture-handler/Touchables.InstrInfo"),d=require("./libs/community/Picker.InstrInfo"),v=require("./parser/Babel"),p=require("./model/Types"),m=(!function(n){n[n.i=-1]="Filtered",n[n.o=0]="Normal",n[n.u=1]="ReactNative",n[n.l=2]="React"}(u=u||{}),[]),y=(m.push.apply(m,(0,r.default)(o.instrumentationInfo)),m.push.apply(m,(0,r.default)(l.instrumentationInfo)),m.push.apply(m,(0,r.default)(f.instrumentationInfo)),m.push.apply(m,(0,r.default)(s.instrumentationInfo)),m.push.apply(m,(0,r.default)(d.instrumentationInfo)),["AppRegistry","renderApplication","setUpErrorHandling"]),g="@dynatrace/react-native-plugin/instrumentation/libs",instrument=function(n,r,e){r=w(r);var t=V(r);if(t!==u.i){var i=!1,o=N(r,n);if(t===u.l)T(o),i=!0;else if(t===u.u)r.endsWith("AppRegistry.js")?void 0!==e&&e.autoStart&&(U(o),i=!0):r.endsWith("renderApplication.js")?(q(o),i=!0):r.endsWith("setUpErrorHandling.js")&&void 0!==e&&e.autoStart&&e.errorHandler.enabled&&(J(o,e.autoStart,e.errorHandler.reportFatalErrorAsCrash),i=!0);else{var t=B(r,e);if(e.custom.reactnavigation&&b(r,o))i=!0;else{if(!t.input&&!t.lifecycle)return null!=e&&e.debug&&console.log("Dynatrace - Filtered All: ".concat(r)),E(r),n;t.lifecycle&&q(o)&&(i=!0),t.input&&m.forEach(function(n){n=K(o,n);o=n.root,i=i||n.v})}}i?(n=o.toSource({quote:"single"}),H(n,r)):E(r),null!=e&&e.debug&&i&&console.log("Dynatrace - Modified Filename: "+r)}else r.includes(a.join("@dynatrace","react-native-plugin"))&&r.endsWith(a.join("lib","core","configuration","ConfigurationPreset.js"))&&void 0!==e&&(t=N(r,n),void 0!==e.lifecycle&&h(t,"getLifecycleUpdate",e.lifecycle.includeUpdate),void 0!==e.debug&&h(t,"getLogLevel",e.debug?0:1),void 0!==e.bundleName&&h(t,"getBundleName",e.bundleName),void 0!==e.bundleVersion&&h(t,"getBundleVersion",e.bundleVersion),void 0!==e.input&&void 0!==e.input.actionNamePrivacy&&h(t,"getActionNamePrivacy",e.input.actionNamePrivacy),void 0!==e.errorHandler&&(h(t,"isErrorHandlerEnabled",e.errorHandler.enabled),h(t,"isReportFatalErrorAsCrash",e.errorHandler.reportFatalErrorAsCrash)),e.autoStart&&h(t,"isAutoStartupEnabled",e.autoStart),n=t.toSource({quote:"single"}),H(n,r));return n},b=(exports.instrument=instrument,function(n,r){return!!O(n,r)&&(n="import { registerListener } from '".concat(g,"/react-navigation/ReactNavigation';"),r.find(c.ImportDeclaration).at(0).insertBefore(n),!0)}),O=function(n,r){var e=!1;return n.includes("react-navigation")&&n.includes("NavigationContainer.tsx")&&r.find(c.VariableDeclarator).forEach(function(n){e||null==n.value||null==n.value.id||"refContainer"!==n.value.id.name||null!=n.parent&&null!=n.parent.value&&null!=n.parent.value.type&&"VariableDeclaration"===n.parent.value.type&&(n.parent.insertAfter("registerListener(refContainer);"),e=!0)}),e},q=function(n){var r=n.findJSXElements(),t=!1;return 0<r.length&&(n.find(c.FunctionDeclaration).forEach(function(n){var r,e=(0,i.fromPaths)([n]);0<e.findJSXElements().length&&null!=n&&null!=n.value&&null!=n.value.id&&n.value.id.name&&(r=e.find(c.ClassDeclaration),e=e.find(c.ClassExpression),0===r.length)&&0===e.length&&(A(n,p.Types.FunctionalComponent,n.value.id.name),t=!0)}),n.find(c.ClassDeclaration).forEach(function(n){0<(0,i.fromPaths)([n]).findJSXElements().length&&null!=n&&null!=n.value&&n.value.id&&n.value.id.name&&(A(n,p.Types.ClassComponent,n.value.id.name),t=!0)}),n.find(c.ArrowFunctionExpression).forEach(function(n){0<(0,i.fromPaths)([n]).findJSXElements().length&&null!=n.parent&&null!=n.parent.value&&null!=n.parent.value.id&&null!=n.parent.value.id.name&&(A(n,p.Types.FunctionalComponent,n.parent.value.id.name),t=!0)})),t},A=function(n,r,e){for(r=c.expressionStatement(c.assignmentExpression("=",c.memberExpression(c.identifier(e),c.identifier("_dtInfo")),P(r,e)));void 0!==n.parentPath&&"body"!==n.parentPath.name;)n=n.parentPath;void 0!==n.parentPath&&n.insertAfter(r)},P=function(n,r){return c.objectExpression([c.objectProperty(c.identifier("type"),c.numericLiteral(n)),c.objectProperty(c.identifier("name"),c.stringLiteral(r))])},h=function(n,r,e){var n=n.find(c.Identifier).filter(function(n){return n.node.name===r});1===n.length&&"ReturnStatement"===(n=n.paths()[0].parent.value.body.body[0]).type&&("boolean"==typeof e&&(n.argument=c.booleanLiteral(e)),"string"==typeof e&&(n.argument=c.stringLiteral(e)),"number"==typeof e)&&(n.argument=c.numericLiteral(e))},N=function(n,r){return c.withParser((0,v.babelParser)(a.extname(n)))(r)},w=function(n){return a.isAbsolute(n)?n.replace(t.default.getApplicationPath()+a.sep,""):n},E=function(n){try{var r=a.join(t.default.getBuildPath(),n+".dtx");e.default.checkIfFileExistsSync(r),e.default.deleteFileSync(r)}catch(n){}},H=function(n,r){r=a.join(t.default.getBuildPath(),r);try{e.default.checkIfFileExistsSync(a.dirname(r))}catch(n){e.default.createDirectorySync(a.dirname(r))}e.default.writeTextToFileSync(r+".dtx",n)},B=function(n,r){var e={input:!1,lifecycle:!1};return void 0!==r&&(void 0!==r.lifecycle&&void 0!==r.lifecycle.instrument&&r.lifecycle.instrument(n)&&(e.lifecycle=!0),void 0!==r.input)&&void 0!==r.input.instrument&&r.input.instrument(n)&&(e.input=!0),e},T=function(n){var r,n=n.find(c.Program);1===n.length&&(r=c.expressionStatement(c.callExpression(c.memberExpression(c.callExpression(c.identifier("require"),[c.stringLiteral("@dynatrace/react-native-plugin/instrumentation/jsx/ElementHelper")]),c.identifier("instrumentCreateElement")),[c.memberExpression(c.identifier("module"),c.identifier("exports"))])),n.paths()[0].node.body.push(r))},U=function(n){var r=M(n,"runApplication",!0);1===r.length&&(en(n,{p:"_DynatraceApplicationHandler",module:"@dynatrace/react-native-plugin",reference:"ApplicationHandler"}),I(r.paths()[0].parent.value.body.body,0,D("_DynatraceApplicationHandler","startup",[])))},J=function(n,r,e){n=n.paths()[0].value.program.body;null!=n&&(I(n,n.length,_({p:"_DynatraceErrorHandler",module:"@dynatrace/react-native-plugin/lib/core/ErrorHandler",reference:""})),I(n,n.length,D("_DynatraceErrorHandler","registerErrorHandler",[c.literal(e)])))},I=function(n,r){for(var e=arguments.length,t=new Array(2<e?e-2:0),i=2;i<e;i++)t[i-2]=arguments[i];return n.splice.apply(n,[r,0].concat(t))},M=function(n,r,t){for(var e=arguments.length,i=new Array(3<e?e-3:0),o=3;o<e;o++)i[o-3]=arguments[o];return n.find(c.Identifier).filter(function(n){return n.node.name===r}).filter(function(n){return void 0!==n.parent&&void 0!==n.parent.value&&void 0!==n.parent.value.params}).filter(function(n){var r=void 0!==n.parent&&void 0!==n.parent.value;t||(r=r&&n.parent.value.params.length===i.length);for(var e=0;e<0;e++)r=r&&n.parent.value.params[e].name===i[e];return r})},V=function(n){if(n.includes("@dynatrace"))return u.i;var r=a.extname(n);if(".js"!==r&&".ts"!==r&&".tsx"!==r&&".jsx"!==r)return u.i;for(var e=a.parse(n),t=e.dir.split(a.sep),i=0;i<t.length;i++)if("node_modules"===t[i]){if("react-native"===t[i+1]||"create-react-class"===t[i+1]||"react-clone-referenced-element"===t[i+1])return y.includes(e.name)?u.u:u.i;if("react"===t[i+1]&&"index"===e.name)return u.l}return u.o},k=function(n,r,e){var t=z(n,r,e);return G(n,r,e)||t},z=function(n,r,e){var t=Q(n,r);return 0<t.length&&(void 0!==(t=C(t,r.reference,!1))&&(e.p=t.localName),nn(n,e),!0)},G=function(n,r,e){var t=j(n,r.module);if(1===t.length){t=C(t,r.reference,!0);if(void 0!==t)return rn(n,e.defaultImport,t.localName,"ImportNamespaceSpecifier"===t.type),!0}return!1},K=function(n,r){var e=JSON.parse(JSON.stringify(r.new));return{root:n,v:k(n,r.old,e)||W(n,r.old,r.new.defaultImport)}},Q=function(n,r){return n.find(c.ImportDeclaration).filter(function(n){return n.node.source.value===r.module&&null!=n.node.specifiers&&n.node.specifiers.some(function(n){return x(n)&&n.imported.name===r.reference||n.local&&n.local.name===r.reference})})},x=function(n){return void 0!==n.imported},W=function(n,r,e){var t=!1;return n.find(c.CallExpression).filter(function(n){return X(n.node.callee)&&Y(n.node.arguments[0])&&n.node.arguments[0].value===r.module&&void 0!==n.parent}).forEach(function(n){(void 0===n.parent.value.property||void 0!==n.parent.value.property&&void 0!==n.parent.value.property.name&&n.parent.value.property.name===r.reference)&&(n.node.arguments[0].value=e,t=t||!0)}),t},X=function(n){return"require"===n.name},Y=function(n){return"StringLiteral"===n.type||"Literal"===n.type},j=function(n,r){return n.find(c.ImportDeclaration).filter(function(n){return n.node.source.value===r})},C=function(n,e,t){var i;return n.forEach(function(n){void 0!==n.node.specifiers&&(n.node.specifiers=n.node.specifiers.filter(function(n){var r;return x(n)&&!t?((r=n.imported.name!==e)||null==n.local||n.imported.name===n.local.name||(i={localName:n.local.name,type:n.type}),r):!(!x(n)&&t&&(null!=n.local&&(i={localName:n.local.name,type:n.type}),1))}),0===n.node.specifiers.length)&&n.prune()}),i},Z=function(n,r){n.find(c.ImportDeclaration).filter(function(n){return n.node.source.value===r.module}).forEach(function(n){null!=n.node.specifiers&&n.node.specifiers.push(F(r))})},$=function(n,r,e){n.find(c.ImportDeclaration).filter(function(n){return n.node.source.value===r}).forEach(function(n){null!=n.node.specifiers&&n.node.specifiers.push(e)})},R=function(n,r,e){var t=n.find(c.ImportDeclaration);0<t.length?c(t.paths()[0]).insertAfter(S(r,e)):1===(t=n.find(c.Program)).length&&t.paths()[0].node.body.unshift(S(r,e))},nn=function(n,r){0<j(n,r.module).length?Z(n,r):R(n,r.module,[F(r)])},rn=function(n,r,e,t){var i=j(n,r),t=(t?ln:cn)(e);0<i.length?$(n,r,t):R(n,r,[t])},en=function(n,r){n=n.find(c.VariableDeclaration);0<n.length&&c(n.paths()[0]).insertAfter(_(r))},D=function(n,r,e){return c.expressionStatement(tn(n,r,e))},tn=function(n,r,e){return c.callExpression(an(n,r),e)},_=function(n){return c.variableDeclaration("var",[on(n)])},on=function(n){return c.variableDeclarator(void 0!==n.p?c.identifier(n.p):c.identifier(n.reference),(0<n.reference.length?un:L)(n))},un=function(n){return c.memberExpression(L(n),c.identifier(n.reference))},an=function(n,r){return c.memberExpression(c.identifier(n),c.identifier(r))},L=function(n){return c.callExpression(c.identifier("require"),[c.literal(n.module)])},S=function(n,r){return c.importDeclaration(r,c.literal(n))},F=function(n){return void 0!==n.p?c.importSpecifier(c.identifier(n.reference),c.identifier(n.p)):c.importSpecifier(c.identifier(n.reference))},cn=function(n){return c.importDefaultSpecifier(c.identifier(n))},ln=function(n){return c.importNamespaceSpecifier(c.identifier(n))};
1
+ "use strict";var a,n=require("@babel/runtime/helpers/interopRequireDefault"),r=n(require("@babel/runtime/helpers/toConsumableArray")),l=(Object.defineProperty(exports,"t",{value:!0}),exports.instrument=void 0,require("path")),o=require("jscodeshift"),i=require("jscodeshift/src/Collection"),e=require("../scripts/FileOperationHelper"),c=require("../scripts/PathsConstants"),f=require("../lib/core/util/GetValuesFromPackage"),t=require("../scripts/util/InstrumentUtil"),u=require("./libs/react-native/Touchables.InstrInfo"),s=require("./libs/react-native/RefreshControl.InstrInfo"),d=require("./libs/react-native/Switch.InstrInfo"),v=require("./libs/community/gesture-handler/Touchables.InstrInfo"),p=require("./libs/community/Picker.InstrInfo"),m=require("./parser/Babel"),g=require("./model/Types"),y=(!function(n){n[n.i=-1]="Filtered",n[n.u=0]="Normal",n[n.o=1]="ReactNative",n[n.l=2]="React"}(a=a||{}),[]),b=(y.push.apply(y,(0,r.default)(u.instrumentationInfo)),y.push.apply(y,(0,r.default)(s.instrumentationInfo)),y.push.apply(y,(0,r.default)(d.instrumentationInfo)),y.push.apply(y,(0,r.default)(v.instrumentationInfo)),y.push.apply(y,(0,r.default)(p.instrumentationInfo)),["AppRegistry","renderApplication","setUpErrorHandling"]),q="@dynatrace/react-native-plugin/instrumentation/libs",instrument=function(n,r,e){r=V(r);var t,i=G(r);if(i!==a.i){var u=!1,o=I(r,n);if(i===a.l)T(o),u=!0;else if(i===a.o)r.endsWith("AppRegistry.js")?void 0!==e&&e.autoStart&&(k(o),u=!0):r.endsWith("renderApplication.js")?(A(o),u=!0):r.endsWith("setUpErrorHandling.js")&&void 0!==e&&e.autoStart&&e.errorHandler.enabled&&(J(o,e.autoStart,e.errorHandler.reportFatalErrorAsCrash),u=!0);else{var i=w(r,e);if(e.custom.reactnavigation&&P(r,o))u=!0;else{if(!i.input&&!i.lifecycle)return null!=e&&e.debug&&console.log("Dynatrace - Filtered All: ".concat(r)),E(r),n;i.lifecycle&&A(o)&&(u=!0),i.input&&y.forEach(function(n){n=W(o,n);o=n.root,u=u||n.v})}}u?(n=o.toSource({quote:"single"}),H(n,r)):E(r),null!=e&&e.debug&&u&&console.log("Dynatrace - Modified Filename: "+r)}else r.includes(l.join("@dynatrace","react-native-plugin"))&&r.endsWith(l.join("lib","core","configuration","ConfigurationPreset.js"))&&void 0!==e&&(i=(0,f.getHostAppBundleInfo)(c.default.getPackageJsonFile()),t=I(r,n),void 0!==e.lifecycle&&N(t,"getLifecycleUpdate",e.lifecycle.includeUpdate),void 0!==e.debug&&N(t,"getLogLevel",e.debug?0:1),void 0!==e.bundleName?N(t,"getBundleName",e.bundleName):null!==i&&N(t,"getBundleName",null==i?void 0:i.name),void 0!==e.bundleVersion?N(t,"getBundleVersion",e.bundleVersion):null!==i&&N(t,"getBundleVersion",null==i?void 0:i.version),void 0!==e.input&&void 0!==e.input.actionNamePrivacy&&N(t,"getActionNamePrivacy",e.input.actionNamePrivacy),void 0!==e.errorHandler&&(N(t,"isErrorHandlerEnabled",e.errorHandler.enabled),N(t,"isReportFatalErrorAsCrash",e.errorHandler.reportFatalErrorAsCrash)),e.autoStart&&N(t,"isAutoStartupEnabled",e.autoStart),n=t.toSource({quote:"single"}),H(n,r));return n},P=(exports.instrument=instrument,function(n,r){return!!O(n,r)&&(n="import { registerListener } from '".concat(q,"/react-navigation/ReactNavigation';"),r.find(o.ImportDeclaration).at(0).insertBefore(n),!0)}),O=function(n,r){var e=!1;return n.includes("react-navigation")&&n.includes("NavigationContainer.tsx")&&r.find(o.VariableDeclarator).forEach(function(n){e||null==n.value||null==n.value.id||"refContainer"!==n.value.id.name||null!=n.parent&&null!=n.parent.value&&null!=n.parent.value.type&&"VariableDeclaration"===n.parent.value.type&&(n.parent.insertAfter("registerListener(refContainer);"),e=!0)}),e},A=function(n){var r=n.findJSXElements(),t=!1;return 0<r.length&&(n.find(o.FunctionDeclaration).forEach(function(n){var r,e=(0,i.fromPaths)([n]);0<e.findJSXElements().length&&null!=n&&null!=n.value&&null!=n.value.id&&n.value.id.name&&(r=e.find(o.ClassDeclaration),e=e.find(o.ClassExpression),0===r.length)&&0===e.length&&(h(n,g.Types.FunctionalComponent,n.value.id.name),t=!0)}),n.find(o.ClassDeclaration).forEach(function(n){0<(0,i.fromPaths)([n]).findJSXElements().length&&null!=n&&null!=n.value&&n.value.id&&n.value.id.name&&(h(n,g.Types.ClassComponent,n.value.id.name),t=!0)}),n.find(o.ArrowFunctionExpression).forEach(function(n){0<(0,i.fromPaths)([n]).findJSXElements().length&&null!=n.parent&&null!=n.parent.value&&null!=n.parent.value.id&&null!=n.parent.value.id.name&&(h(n,g.Types.FunctionalComponent,n.parent.value.id.name),t=!0)})),t},h=function(n,r,e){for(r=o.expressionStatement(o.assignmentExpression("=",o.memberExpression(o.identifier(e),o.identifier("_dtInfo")),U(r,e)));void 0!==n.parentPath&&"body"!==n.parentPath.name;)n=n.parentPath;void 0!==n.parentPath&&n.insertAfter(r)},U=function(n,r){return o.objectExpression([o.objectProperty(o.identifier("type"),o.numericLiteral(n)),o.objectProperty(o.identifier("name"),o.stringLiteral(r))])},N=function(n,r,e){var n=n.find(o.Identifier).filter(function(n){return n.node.name===r});1===n.length&&"ReturnStatement"===(n=n.paths()[0].parent.value.body.body[0]).type&&("boolean"==typeof e&&(n.argument=o.booleanLiteral(e)),"string"==typeof e&&(n.argument=o.stringLiteral(e)),"number"==typeof e)&&(n.argument=o.numericLiteral(e))},I=function(n,r){return o.withParser((0,m.babelParser)(l.extname(n)))(r)},V=function(n){return l.isAbsolute(n)?n.replace(c.default.getApplicationPath()+l.sep,""):n},E=function(n){try{var r=l.join(c.default.getBuildPath(),n+t.INSTRUMENTED_FILE_EXTENSION);e.default.checkIfFileExistsSync(r),e.default.deleteFileSync(r)}catch(n){}},H=function(n,r){r=l.join(c.default.getBuildPath(),r);try{e.default.checkIfFileExistsSync(l.dirname(r))}catch(n){e.default.createDirectorySync(l.dirname(r))}e.default.writeTextToFileSync(r+t.INSTRUMENTED_FILE_EXTENSION,n)},w=function(n,r){var e={input:!1,lifecycle:!1};return void 0!==r&&(void 0!==r.lifecycle&&void 0!==r.lifecycle.instrument&&r.lifecycle.instrument(n)&&(e.lifecycle=!0),void 0!==r.input)&&void 0!==r.input.instrument&&r.input.instrument(n)&&(e.input=!0),e},T=function(n){var r,n=n.find(o.Program);1===n.length&&(r=o.expressionStatement(o.callExpression(o.memberExpression(o.callExpression(o.identifier("require"),[o.stringLiteral("@dynatrace/react-native-plugin/instrumentation/jsx/ElementHelper")]),o.identifier("instrumentCreateElement")),[o.memberExpression(o.identifier("module"),o.identifier("exports"))])),n.paths()[0].node.body.push(r))},k=function(n){var r=M(n,"runApplication",!0);1===r.length&&(un(n,{p:"_DynatraceApplicationHandler",module:"@dynatrace/react-native-plugin",reference:"ApplicationHandler"}),j(r.paths()[0].parent.value.body.body,0,_("_DynatraceApplicationHandler","startup",[])))},J=function(n,r,e){n=n.paths()[0].value.program.body;null!=n&&(j(n,n.length,L({p:"_DynatraceErrorHandler",module:"@dynatrace/react-native-plugin/lib/core/ErrorHandler",reference:""})),j(n,n.length,_("_DynatraceErrorHandler","registerErrorHandler",[o.literal(e)])))},j=function(n,r){for(var e=arguments.length,t=new Array(2<e?e-2:0),i=2;i<e;i++)t[i-2]=arguments[i];return n.splice.apply(n,[r,0].concat(t))},M=function(n,r,t){for(var e=arguments.length,i=new Array(3<e?e-3:0),u=3;u<e;u++)i[u-3]=arguments[u];return n.find(o.Identifier).filter(function(n){return n.node.name===r}).filter(function(n){return void 0!==n.parent&&void 0!==n.parent.value&&void 0!==n.parent.value.params}).filter(function(n){var r=void 0!==n.parent&&void 0!==n.parent.value;t||(r=r&&n.parent.value.params.length===i.length);for(var e=0;e<0;e++)r=r&&n.parent.value.params[e].name===i[e];return r})},G=function(n){if(n.includes("@dynatrace"))return a.i;var r=l.extname(n);if(".js"!==r&&".ts"!==r&&".tsx"!==r&&".jsx"!==r)return a.i;for(var e=l.parse(n),t=e.dir.split(l.sep),i=0;i<t.length;i++)if("node_modules"===t[i]){if("react-native"===t[i+1]||"create-react-class"===t[i+1]||"react-clone-referenced-element"===t[i+1])return b.includes(e.name)?a.o:a.i;if("react"===t[i+1]&&"index"===e.name)return a.l}return a.u},z=function(n,r,e){var t=K(n,r,e);return Q(n,r,e)||t},K=function(n,r,e){var t=X(n,r);return 0<t.length&&(void 0!==(t=R(t,r.reference,!1))&&(e.p=t.localName),en(n,e),!0)},Q=function(n,r,e){var t=x(n,r.module);if(1===t.length){t=R(t,r.reference,!0);if(void 0!==t)return tn(n,e.defaultImport,t.localName,"ImportNamespaceSpecifier"===t.type),!0}return!1},W=function(n,r){var e=JSON.parse(JSON.stringify(r.new));return{root:n,v:z(n,r.old,e)||Y(n,r.old,r.new.defaultImport)}},X=function(n,r){return n.find(o.ImportDeclaration).filter(function(n){return n.node.source.value===r.module&&null!=n.node.specifiers&&n.node.specifiers.some(function(n){return C(n)&&n.imported.name===r.reference||n.local&&n.local.name===r.reference})})},C=function(n){return void 0!==n.imported},Y=function(n,r,e){var t=!1;return n.find(o.CallExpression).filter(function(n){return Z(n.node.callee)&&$(n.node.arguments[0])&&n.node.arguments[0].value===r.module&&void 0!==n.parent}).forEach(function(n){(void 0===n.parent.value.property||void 0!==n.parent.value.property&&void 0!==n.parent.value.property.name&&n.parent.value.property.name===r.reference)&&(n.node.arguments[0].value=e,t=t||!0)}),t},Z=function(n){return"require"===n.name},$=function(n){return"StringLiteral"===n.type||"Literal"===n.type},x=function(n,r){return n.find(o.ImportDeclaration).filter(function(n){return n.node.source.value===r})},R=function(n,e,t){var i;return n.forEach(function(n){void 0!==n.node.specifiers&&(n.node.specifiers=n.node.specifiers.filter(function(n){var r;return C(n)&&!t?((r=n.imported.name!==e)||null==n.local||n.imported.name===n.local.name||(i={localName:n.local.name,type:n.type}),r):!(!C(n)&&t&&(null!=n.local&&(i={localName:n.local.name,type:n.type}),1))}),0===n.node.specifiers.length)&&n.prune()}),i},nn=function(n,r){n.find(o.ImportDeclaration).filter(function(n){return n.node.source.value===r.module}).forEach(function(n){null!=n.node.specifiers&&n.node.specifiers.push(B(r))})},rn=function(n,r,e){n.find(o.ImportDeclaration).filter(function(n){return n.node.source.value===r}).forEach(function(n){null!=n.node.specifiers&&n.node.specifiers.push(e)})},D=function(n,r,e){var t=n.find(o.ImportDeclaration);0<t.length?o(t.paths()[0]).insertAfter(F(r,e)):1===(t=n.find(o.Program)).length&&t.paths()[0].node.body.unshift(F(r,e))},en=function(n,r){0<x(n,r.module).length?nn(n,r):D(n,r.module,[B(r)])},tn=function(n,r,e,t){var i=x(n,r),t=(t?sn:fn)(e);0<i.length?rn(n,r,t):D(n,r,[t])},un=function(n,r){n=n.find(o.VariableDeclaration);0<n.length&&o(n.paths()[0]).insertAfter(L(r))},_=function(n,r,e){return o.expressionStatement(on(n,r,e))},on=function(n,r,e){return o.callExpression(cn(n,r),e)},L=function(n){return o.variableDeclaration("var",[an(n)])},an=function(n){return o.variableDeclarator(void 0!==n.p?o.identifier(n.p):o.identifier(n.reference),(0<n.reference.length?ln:S)(n))},ln=function(n){return o.memberExpression(S(n),o.identifier(n.reference))},cn=function(n,r){return o.memberExpression(o.identifier(n),o.identifier(r))},S=function(n){return o.callExpression(o.identifier("require"),[o.literal(n.module)])},F=function(n,r){return o.importDeclaration(r,o.literal(n))},B=function(n){return void 0!==n.p?o.importSpecifier(o.identifier(n.reference),o.identifier(n.p)):o.importSpecifier(o.identifier(n.reference))},fn=function(n){return o.importDefaultSpecifier(o.identifier(n))},sn=function(n){return o.importNamespaceSpecifier(o.identifier(n))};
@@ -17,6 +17,7 @@
17
17
 
18
18
  @interface HybridBridge : NSObject
19
19
  + (void)forwardEvent:(NSDictionary<NSString*,id>* _Nullable)fields NS_SWIFT_NAME(forwardEvent(fields:));
20
+ + (void)forwardAppStartEvent:(NSDictionary<NSString*,id>* _Nullable)fields keys:(NSArray<NSString*>* _Nullable)keys NS_SWIFT_NAME(forwardEvent(fields:keys:));
20
21
  @end
21
22
 
22
23
  typedef enum : NSUInteger {
@@ -13,6 +13,9 @@
13
13
  #import <CoreLocation/CoreLocation.h>
14
14
  #import <React/RCTPerformanceLogger.h>
15
15
  #import <React/RCTRootView.h>
16
+ #import <cxxreact/ReactMarker.h>
17
+
18
+ #include <chrono>
16
19
 
17
20
  @implementation DynatraceRNBridge
18
21
 
@@ -44,21 +47,28 @@ NSString *const ContentAppeared = @"contentAppeared";
44
47
  /**
45
48
  * Telling us if we can actually emit events
46
49
  */
47
- bool hasListeners = NO;
50
+ bool hasListeners;
48
51
 
49
52
  /**
50
53
  * When events have been emitted this value will be true, so we don't emit again
51
54
  */
52
- bool didEmit = NO;
55
+ bool didEmit;
56
+
57
+ /**
58
+ * When content finally appeared
59
+ */
60
+ int64_t contentAppeared;
53
61
 
54
62
  RCT_EXPORT_MODULE(DynatraceBridge);
55
63
 
56
- - (id) init
64
+ - (instancetype) init
57
65
  {
58
- self = [super init];
59
- if (self) {
66
+ if (self = [super init]) {
60
67
  actionDict = [[NSMutableDictionary alloc] init];
61
68
  webTimingsDict = [[NSMutableDictionary alloc] init];
69
+ hasListeners = NO;
70
+ didEmit = NO;
71
+ contentAppeared = -1;
62
72
  }
63
73
  return self;
64
74
  }
@@ -67,6 +77,12 @@ RCT_EXPORT_MODULE(DynatraceBridge);
67
77
  return @[EmitAppMeasurementsIdentifier];
68
78
  }
69
79
 
80
+ int64_t GetTimestampUnix()
81
+ {
82
+ auto millisecondsUTC = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
83
+ return millisecondsUTC;
84
+ }
85
+
70
86
  /**
71
87
  * When bridge is starting we will add an observer for content did appear so we can emit our app start measurements
72
88
  */
@@ -74,72 +90,45 @@ RCT_EXPORT_MODULE(DynatraceBridge);
74
90
  {
75
91
  [super setBridge:bridge];
76
92
  [[NSNotificationCenter defaultCenter] addObserver:self
77
- selector:@selector(emitMeasurements)
93
+ selector:@selector(contentAppeared)
78
94
  name:RCTContentDidAppearNotification
79
95
  object:nil];
80
96
  }
81
97
 
98
+ - (BOOL)isReady
99
+ {
100
+ return contentAppeared != -1 && !std::isnan(facebook::react::ReactMarker::StartupLogger::getInstance().getRunJSBundleEndTime());
101
+ }
102
+
103
+ - (void) contentAppeared
104
+ {
105
+ contentAppeared = GetTimestampUnix();
106
+ [self emitMeasurements];
107
+ }
108
+
82
109
  /**
83
110
  * Emitting all events which are available and important for our app start measurements
84
111
  */
85
112
  - (void)emitMeasurements
86
113
  {
87
- if (!didEmit && hasListeners && [self.bridge.performanceLogger valueForTag:RCTPLTTI] != 0) {
114
+ if (!didEmit && hasListeners && [self isReady]) {
88
115
  didEmit = YES;
89
-
116
+
90
117
  NSMutableDictionary<NSString*, NSNumber*> *appStartMeasurements = [[NSMutableDictionary alloc] init];
91
-
92
- [self addStartMeasurement:RCTPLScriptDownload withName:DownloadStart intoDict:appStartMeasurements];
93
- [self addEndMeasurement:RCTPLScriptDownload withName:DownloadEnd intoDict:appStartMeasurements];
94
- [self addStartMeasurement:RCTPLScriptExecution withName:RunJSBundleStart intoDict:appStartMeasurements];
95
- [self addEndMeasurement:RCTPLScriptExecution withName:RunJSBundleEnd intoDict:appStartMeasurements];
96
- [self addStartMeasurement:RCTPLBridgeStartup withName:Reload intoDict:appStartMeasurements];
97
- [self addStartMeasurement:RCTPLTTI withName:ContentAppeared intoDict:appStartMeasurements];
98
-
118
+
119
+ [appStartMeasurements setObject:[self correctionOfTimestamp:facebook::react::ReactMarker::StartupLogger::getInstance().getRunJSBundleStartTime()] forKey:RunJSBundleStart];
120
+ [appStartMeasurements setObject:[self correctionOfTimestamp:facebook::react::ReactMarker::StartupLogger::getInstance().getRunJSBundleEndTime()] forKey:RunJSBundleEnd];
121
+ [appStartMeasurements setObject:[NSNumber numberWithLongLong:contentAppeared] forKey:ContentAppeared];
122
+
99
123
  if (hasListeners) {
100
124
  [self sendEventWithName:EmitAppMeasurementsIdentifier body:appStartMeasurements];
101
125
  }
102
126
  }
103
127
  }
104
128
 
105
- /**
106
- * Adding a start measurement
107
- *
108
- * @param type Tag which is used for the internal react native performance logger
109
- * @param name Name which should be used for inserting a specific timestamp into the dict
110
- * @param measurements Dictionary which is used to insert the timing
111
- */
112
- - (void)addStartMeasurement:(RCTPLTag)type withName:(NSString *)name intoDict:(NSMutableDictionary *) measurements {
113
- int64_t duration = [self.bridge.performanceLogger durationForTag:type];
114
- int64_t end = [self.bridge.performanceLogger valueForTag:type];
115
-
116
- if (duration == 0 || end == 0) {
117
- return;
118
- }
119
-
120
- [measurements setObject:[self correctionOfTimestamp:end-duration] forKey:name];
121
- }
122
-
123
- /**
124
- * Adding a end measurement
125
- *
126
- * @param type Tag which is used for the internal react native performance logger
127
- * @param name Name which should be used for inserting a specific timestamp into the dict
128
- * @param measurements Dictionary which is used to insert the timing
129
- */
130
- - (void)addEndMeasurement:(RCTPLTag)type withName:(NSString *)name intoDict:(NSMutableDictionary *) measurements {
131
- int64_t end = [self.bridge.performanceLogger valueForTag:type];
132
-
133
- if (end == 0) {
134
- return;
135
- }
136
-
137
- [measurements setObject:[self correctionOfTimestamp:end] forKey:name];
138
- }
139
-
140
129
  - (NSNumber* )correctionOfTimestamp:(int64_t) timestamp
141
130
  {
142
- return [NSNumber numberWithLongLong:timestamp + [[NSDate date] timeIntervalSince1970] - (CACurrentMediaTime() * 1000)];
131
+ return [NSNumber numberWithLongLong:timestamp + GetTimestampUnix() - (CACurrentMediaTime() * 1000)];
143
132
  }
144
133
 
145
134
  /**
@@ -209,7 +198,10 @@ RCT_EXPORT_METHOD(start:(NSDictionary *) options)
209
198
  }
210
199
 
211
200
  if (properties[@"DTXBeaconURL"] != NULL && properties[@"DTXApplicationID"] != NULL) {
212
- [Dynatrace startupWithConfig:properties];
201
+ dispatch_async(dispatch_get_main_queue(), ^{
202
+ NSLog(@"Dynatrace Agent runs on the main thread.");
203
+ [Dynatrace startupWithConfig:properties];
204
+ });
213
205
  }
214
206
  }
215
207
 
@@ -460,6 +452,11 @@ RCT_EXPORT_METHOD(forwardEvent:(NSDictionary<NSString*, id>*) attributes)
460
452
  [HybridBridge forwardEvent:attributes];
461
453
  }
462
454
 
455
+ RCT_EXPORT_METHOD(forwardAppStartEvent:(NSDictionary<NSString*, id>*) attributes withAppStartKeys:(NSArray<NSString*>* _Nullable) appStartKeys)
456
+ {
457
+ [HybridBridge forwardAppStartEvent:attributes keys:appStartKeys];
458
+ }
459
+
463
460
  RCT_EXPORT_METHOD(setGPSLocation:(double)latitude andLongitude: (double)longitude platform: (NSString *) platform)
464
461
  {
465
462
  if ([self shouldWorkOnIosWithPlatform: platform])
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ApplicationHandler = void 0;
4
+ const AppStartObserver_1 = require("../next/appstart/AppStartObserver");
4
5
  const ConfigurationBuilder_1 = require("./configuration/ConfigurationBuilder");
5
6
  const ConfigurationHandler_1 = require("./configuration/ConfigurationHandler");
6
7
  const ConsoleLogger_1 = require("./logging/ConsoleLogger");
8
+ AppStartObserver_1.AppStartObserver.call();
7
9
  const logger = new ConsoleLogger_1.ConsoleLogger('ApplicationHandler');
8
10
  exports.ApplicationHandler = {
9
11
  startup: (configuration) => {
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getHostAppBundleInfo = void 0;
4
+ const fs_1 = require("fs");
5
+ const pluginName = '@dynatrace/react-native-plugin';
6
+ const getHostAppBundleInfo = (path, allowInternal = false) => {
7
+ try {
8
+ const pkg = JSON.parse((0, fs_1.readFileSync)(path, 'utf8'));
9
+ if (pkg.name === pluginName && !allowInternal) {
10
+ return null;
11
+ }
12
+ return { name: pkg.name, version: pkg.version };
13
+ }
14
+ catch (_a) {
15
+ return null;
16
+ }
17
+ };
18
+ exports.getHostAppBundleInfo = getHostAppBundleInfo;
@@ -2,13 +2,14 @@
2
2
  const pathNode = require('path');
3
3
  const pathsConstants = require('@dynatrace/react-native-plugin/scripts/PathsConstants').default;
4
4
  const fileOperation = require('@dynatrace/react-native-plugin/scripts/FileOperationHelper').default;
5
+ const INSTRUMENTED_FILE_EXTENSION = require('@dynatrace/react-native-plugin/scripts/util/InstrumentUtil').INSTRUMENTED_FILE_EXTENSION;
5
6
  const originalSourceMapInfo = require('./getSourceMapInfoOrig');
6
7
  const getSourceMapInfo = (module, options) => {
7
8
  const dataOrig = originalSourceMapInfo(module, options);
8
9
  try {
9
- if (!Boolean(options.excludeSource)) {
10
+ if (!options.excludeSource) {
10
11
  const correctPath = module.path.replace(pathsConstants.getApplicationPath(), '') +
11
- '.dtx';
12
+ INSTRUMENTED_FILE_EXTENSION;
12
13
  dataOrig.source = fileOperation.readTextFromFileSync(pathNode.join(pathsConstants.getBuildPath(), correctPath));
13
14
  }
14
15
  }
@@ -54,20 +54,26 @@ const createErrorEvent = (type, message, stacktrace) => {
54
54
  exports.createErrorEvent = createErrorEvent;
55
55
  const createAppStartEvent = (appStartMeasurements) => {
56
56
  const event = {
57
- 'characteristics.is_app_start': true,
57
+ 'characteristics.has_app_start': true,
58
58
  };
59
- let timings = [];
59
+ let timingsApp = [];
60
60
  for (const { type, key } of AppStartType_1.AppStartTypeKeyMapping) {
61
61
  if (appStartMeasurements[type] !== undefined) {
62
- event[key] = appStartMeasurements[type];
63
- timings.push(appStartMeasurements[type]);
62
+ timingsApp.push({
63
+ name: key,
64
+ timestamp: appStartMeasurements[type],
65
+ });
64
66
  }
65
67
  }
66
- timings = timings.sort((a, b) => a - b);
67
- if (timings.length > 0) {
68
- event["start_time"] = timings[0];
68
+ timingsApp = timingsApp.sort((a, b) => a.timestamp - b.timestamp);
69
+ if (timingsApp.length > 0) {
70
+ event["start_time"] = timingsApp[0].timestamp;
69
71
  event["duration"] =
70
- timings[timings.length - 1] - timings[0];
72
+ timingsApp[timingsApp.length - 1].timestamp -
73
+ timingsApp[0].timestamp;
74
+ for (const timing of timingsApp) {
75
+ event[timing.name] = timing.timestamp - timingsApp[0].timestamp;
76
+ }
71
77
  }
72
78
  else {
73
79
  return null;
@@ -7,6 +7,7 @@ const BaseDataEventModifier_1 = require("./modifier/BaseDataEventModifier");
7
7
  const ValueRestrictionModifier_1 = require("./modifier/ValueRestrictionModifier");
8
8
  const ModifyEventValidation_1 = require("./modifier/ModifyEventValidation");
9
9
  const ViewInfoCreator_1 = require("./ViewInfoCreator");
10
+ const EventSpecContstants_1 = require("./spec/EventSpecContstants");
10
11
  class EventPipelineImpl {
11
12
  constructor() {
12
13
  this.customEventModifierChain = new ModifyEventValidation_1.ModifyEventValidation();
@@ -27,14 +28,18 @@ class EventPipelineImpl {
27
28
  }
28
29
  catch (error) {
29
30
  if (event != null) {
30
- event["dt.rum.api.has_enrich_exception"] =
31
- true;
31
+ event["dt.internal.api.has_enrich_exception"] = true;
32
32
  }
33
33
  }
34
34
  });
35
35
  if (event != null) {
36
36
  this.logger.debug(`forwardEvent(${JSON.stringify(event)})`);
37
- DynatraceBridge_1.DynatraceNative.forwardEvent(event);
37
+ if (event["characteristics.has_app_start"] === true) {
38
+ DynatraceBridge_1.DynatraceNative.forwardAppStartEvent(event, EventSpecContstants_1.ALL_APP_START_KEYS);
39
+ }
40
+ else {
41
+ DynatraceBridge_1.DynatraceNative.forwardEvent(event);
42
+ }
38
43
  }
39
44
  }
40
45
  addEventModifier(eventModifier) {
@@ -76,7 +76,7 @@ class ModifyEventValidation {
76
76
  this.logger.debug(`sanitizeUserEnrichedEvent(${originalEvent}, ${userEnrichedEvent}): Event has not been changed`);
77
77
  return originalEvent;
78
78
  }
79
- let overriddenKeys = originalEvent["dt.rum.overridden_keys"];
79
+ let overriddenKeys = originalEvent["dt.internal.api.overridden_keys"];
80
80
  if (overriddenKeys == null) {
81
81
  overriddenKeys = [];
82
82
  }
@@ -97,19 +97,19 @@ class ModifyEventValidation {
97
97
  const sizedEntries = new EventLimitation_1.EventLimitation().limitEventProperties(finalEntries);
98
98
  if (sizedEntries.length < userEnrichedEventEntries.length) {
99
99
  sizedEntries.push([
100
- "dt.rum.api.has_dropped_fields",
100
+ "dt.internal.api.has_dropped_custom_properties",
101
101
  true,
102
102
  ]);
103
103
  }
104
104
  if (Array.isArray(overriddenKeys) && overriddenKeys.length > 0) {
105
105
  sizedEntries.push([
106
- "dt.rum.overridden_keys",
106
+ "dt.internal.api.overridden_keys",
107
107
  overriddenKeys,
108
108
  ]);
109
109
  }
110
110
  if (externalException) {
111
111
  sizedEntries.push([
112
- "dt.rum.api.has_enrich_exception",
112
+ "dt.internal.api.has_enrich_exception",
113
113
  true,
114
114
  ]);
115
115
  }
@@ -31,7 +31,7 @@ class SendEventValidationImpl {
31
31
  this.applyOverriddenKeys(sizedEntries);
32
32
  if (sizedEntries.length < eventCopy.length) {
33
33
  sizedEntries.push([
34
- "dt.rum.api.has_dropped_fields",
34
+ "dt.internal.api.has_dropped_custom_properties",
35
35
  true,
36
36
  ]);
37
37
  }
@@ -55,7 +55,7 @@ class SendEventValidationImpl {
55
55
  }
56
56
  if (overriddenKeys.length > 0) {
57
57
  eventEntries.push([
58
- "dt.rum.overridden_keys",
58
+ "dt.internal.api.overridden_keys",
59
59
  overriddenKeys,
60
60
  ]);
61
61
  }
@@ -9,7 +9,7 @@ class ValueRestrictionModifierImpl {
9
9
  }
10
10
  modifyEvent(event) {
11
11
  if (event != null && this.eventHasRestrictedValues(event)) {
12
- event["dt.rum.has_nfn_values"] = true;
12
+ event["dt.internal.has_nfn_values"] = true;
13
13
  }
14
14
  return JSON.parse(JSON.stringify(event, (_key, value) => {
15
15
  if (value === undefined) {
@@ -1,33 +1,34 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MODIFY_EVENT_WHITELIST_NAMESPACE = exports.MODIFY_EVENT_WHITELIST_FIELDS = exports.SEND_SESSION_PROPERTY_EVENT_WHITELIST_FIELDS = exports.SEND_EVENT_WHITELIST_FIELDS = exports.SEND_SESSION_PROPERTY_EVENT_WHITELIST_NAMESPACES = exports.SEND_EVENT_WHITELIST_NAMESPACES = exports.AllCharacteristicsKeys = exports.KEY_NAME_REGEX = exports.MAX_CUSTOM_EVENT_VALUE_LENGTH = exports.MAX_CUSTOM_EVENT_KEY_LENGTH = exports.MAX_CUSTOM_EVENT_FIELDS = void 0;
4
- const SPECIFICATION_VERSION = '0.18';
3
+ exports.ALL_APP_START_KEYS = exports.MODIFY_EVENT_WHITELIST_NAMESPACE = exports.MODIFY_EVENT_WHITELIST_FIELDS = exports.SEND_SESSION_PROPERTY_EVENT_WHITELIST_FIELDS = exports.SEND_EVENT_WHITELIST_FIELDS = exports.SEND_SESSION_PROPERTY_EVENT_WHITELIST_NAMESPACES = exports.SEND_EVENT_WHITELIST_NAMESPACES = exports.AllCharacteristicsKeys = exports.KEY_NAME_REGEX = exports.MAX_CUSTOM_EVENT_VALUE_LENGTH = exports.MAX_CUSTOM_EVENT_KEY_LENGTH = exports.MAX_CUSTOM_EVENT_FIELDS = void 0;
4
+ const SPECIFICATION_VERSION = '0.19';
5
5
  exports.MAX_CUSTOM_EVENT_FIELDS = 50;
6
6
  exports.MAX_CUSTOM_EVENT_KEY_LENGTH = 100;
7
7
  exports.MAX_CUSTOM_EVENT_VALUE_LENGTH = 5000;
8
8
  exports.KEY_NAME_REGEX = RegExp('^[a-z0-9]+(?:\\.[a-z][a-z0-9]*|_[a-z0-9]+)*$');
9
- exports.AllCharacteristicsKeys = [
10
- "characteristics.has_anr",
11
- "characteristics.has_crash",
12
- "characteristics.has_csp_violation",
13
- "characteristics.has_error",
14
- "characteristics.has_exception",
15
- "characteristics.has_failed_request",
16
- "characteristics.has_request",
17
- "characteristics.has_w3c_navigation_timings",
18
- "characteristics.has_w3c_resource_timings",
19
- "characteristics.has_navigation",
20
- "characteristics.has_page_summary",
21
- "characteristics.has_user_interaction",
22
- "characteristics.has_view_summary",
23
- "characteristics.is_api_reported",
24
- "characteristics.is_app_start",
25
- "characteristics.is_internal",
26
- "characteristics.is_self_monitoring",
27
- "characteristics.is_visibility_change",
28
- "characteristics.has_event_properties",
29
- "characteristics.has_session_properties",
30
- ];
9
+ const characteristicsKeyMap = {
10
+ ["characteristics.has_anr"]: null,
11
+ ["characteristics.has_crash"]: null,
12
+ ["characteristics.has_csp_violation"]: null,
13
+ ["characteristics.has_error"]: null,
14
+ ["characteristics.has_exception"]: null,
15
+ ["characteristics.has_failed_request"]: null,
16
+ ["characteristics.has_request"]: null,
17
+ ["characteristics.has_w3c_navigation_timings"]: null,
18
+ ["characteristics.has_w3c_resource_timings"]: null,
19
+ ["characteristics.has_navigation"]: null,
20
+ ["characteristics.has_page_summary"]: null,
21
+ ["characteristics.has_user_interaction"]: null,
22
+ ["characteristics.has_view_summary"]: null,
23
+ ["characteristics.is_api_reported"]: null,
24
+ ["characteristics.has_app_start"]: null,
25
+ ["characteristics.is_internal"]: null,
26
+ ["characteristics.is_self_monitoring"]: null,
27
+ ["characteristics.has_visibility_change"]: null,
28
+ ["characteristics.has_event_properties"]: null,
29
+ ["characteristics.has_session_properties"]: null,
30
+ };
31
+ exports.AllCharacteristicsKeys = Object.keys(characteristicsKeyMap);
31
32
  exports.SEND_EVENT_WHITELIST_NAMESPACES = [
32
33
  "event_properties",
33
34
  ];
@@ -45,3 +46,12 @@ exports.MODIFY_EVENT_WHITELIST_NAMESPACE = [
45
46
  "session_properties",
46
47
  "event_properties",
47
48
  ];
49
+ const appStartKeyMap = {
50
+ ["app_start.react_native.content_appeared"]: null,
51
+ ["app_start.react_native.download.end_time"]: null,
52
+ ["app_start.react_native.download.start_time"]: null,
53
+ ["app_start.react_native.run_js_bundle.load_time"]: null,
54
+ ["app_start.react_native.run_js_bundle.end_time"]: null,
55
+ ["app_start.react_native.run_js_bundle.start_time"]: null,
56
+ };
57
+ exports.ALL_APP_START_KEYS = Object.keys(appStartKeyMap);
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@dynatrace/react-native-plugin",
3
- "version": "2.313.1",
3
+ "version": "2.317.1",
4
4
  "description": "This plugin gives you the ability to use the Dynatrace Mobile agent in your react native application.",
5
5
  "main": "index.js",
6
6
  "types": "typings/react-native-dynatrace.d.ts",
7
7
  "bin": {
8
8
  "instrumentDynatrace": "scripts/Instrument.js",
9
- "configDynatrace": "scripts/CheckConfig.js"
9
+ "configDynatrace": "scripts/CheckConfig.js",
10
+ "lineOffsetDynatrace": "scripts/LineOffsetAnalyze.js"
10
11
  },
11
12
  "keywords": [
12
13
  "react-native",
@@ -82,7 +83,7 @@
82
83
  "author": "Dynatrace",
83
84
  "license": "SEE LICENSE IN LICENSE.md",
84
85
  "dependencies": {
85
- "@babel/runtime": "^7.27.1",
86
+ "@babel/runtime": "^7.27.6",
86
87
  "jscodeshift": "^17.3.0",
87
88
  "plist": "^3.1.0",
88
89
  "proxy-polyfill": "^0.3.2",
@@ -96,7 +97,7 @@
96
97
  "react-native": ">=0.62.0"
97
98
  },
98
99
  "devDependencies": {
99
- "@babel/cli": "^7.25.7",
100
+ "@babel/cli": "^7.27.2",
100
101
  "@babel/plugin-proposal-class-properties": "^7.8.3",
101
102
  "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
102
103
  "@babel/plugin-proposal-optional-chaining": "^7.8.3",
@@ -108,6 +109,7 @@
108
109
  "@types/jest": "^29.5.1",
109
110
  "@types/jscodeshift": "^0.11.6",
110
111
  "@types/libxmljs": "^0.18.3",
112
+ "@types/mock-fs": "^4.13.4",
111
113
  "@types/node": "^18.19.71",
112
114
  "@types/plist": "^3.0.2",
113
115
  "@types/react-native": "^0.63.32",
@@ -117,7 +119,8 @@
117
119
  "@typescript-eslint/eslint-plugin": "^8.22.0",
118
120
  "@typescript-eslint/parser": "^8.22.0",
119
121
  "compressing": "^1.5.1",
120
- "eslint": "^9.19.0",
122
+ "diff": "^8.0.2",
123
+ "eslint": "^9.29.0",
121
124
  "eslint-config-prettier": "^8.5.0",
122
125
  "eslint-plugin-import": "^2.31.0",
123
126
  "eslint-plugin-jsdoc": "^50.6.3",
@@ -128,7 +131,8 @@
128
131
  "jest-each": "^28.1.3",
129
132
  "jest-junit": "^14.0.0",
130
133
  "jest-mock": "^28.1.3",
131
- "npm-check-updates": "^16.10.12",
134
+ "mock-fs": "^5.5.0",
135
+ "npm-check-updates": "^18.0.1",
132
136
  "prettier": "^2.6.1",
133
137
  "shelljs": "^0.8.5",
134
138
  "ts-jest": "^28.0.7",
@@ -111,7 +111,7 @@ Pod::Spec.new do |s|
111
111
  #
112
112
 
113
113
  s.dependency "React"
114
- s.dependency 'Dynatrace', '~> 8.313.1.1016'
114
+ s.dependency 'Dynatrace', '~> 8.317.1.1003'
115
115
 
116
116
  # Allows for better compatibility for older and newer versions
117
117
  if defined?(install_modules_dependencies)
@@ -1,7 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
3
12
  Object.defineProperty(exports, "__esModule", { value: true });
4
13
  const fs = require("fs");
14
+ const fsPromise = require("fs/promises");
5
15
  const path = require("path");
6
16
  const checkIfFileExists = (_file) => new Promise((resolve, reject) => {
7
17
  fs.stat(_file, (err) => {
@@ -147,9 +157,32 @@ const copyDirectory = (from, to) => {
147
157
  }
148
158
  });
149
159
  };
160
+ const allowedRootFolders = ['src', 'node_modules'];
161
+ const getAllFiles = (log, dir, base = '', isRoot = false) => __awaiter(void 0, void 0, void 0, function* () {
162
+ const entries = yield fsPromise.readdir(dir);
163
+ let files = [];
164
+ for (const entry of entries) {
165
+ const fullPath = path.join(dir, entry);
166
+ const relPath = path.join(base, entry);
167
+ const stat = yield fsPromise.stat(fullPath);
168
+ if (stat.isDirectory()) {
169
+ if (isRoot && !allowedRootFolders.includes(entry)) {
170
+ yield log(`🔸 Skipping folder: ${entry}`);
171
+ continue;
172
+ }
173
+ const subFiles = yield getAllFiles(log, fullPath, relPath);
174
+ files = files.concat(subFiles);
175
+ }
176
+ else {
177
+ files.push(relPath);
178
+ }
179
+ }
180
+ return files;
181
+ });
150
182
  exports.default = {
151
183
  checkIfFileExists,
152
184
  checkIfFileExistsSync,
185
+ getAllFiles,
153
186
  readTextFromFile,
154
187
  readTextFromFileSync,
155
188
  writeTextToFile,
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const GetValuesFromPackage_1 = require("../lib/core/util/GetValuesFromPackage");
5
+ const PathsConstants_1 = require("../scripts/PathsConstants");
6
+ const LineOffsetAnalyzeCall_1 = require("./core/LineOffsetAnalyzeCall");
7
+ module.exports = (() => {
8
+ const args = process.argv.slice(2);
9
+ const appBundleInfo = Object.assign({}, (0, GetValuesFromPackage_1.getHostAppBundleInfo)(PathsConstants_1.default.getPackageJsonFile()));
10
+ const pluginInfo = Object.assign({}, (0, GetValuesFromPackage_1.getHostAppBundleInfo)(PathsConstants_1.default.getInternalPackageJsonFile(), true));
11
+ appBundleInfo.pluginVersion = ((pluginInfo === null || pluginInfo === void 0 ? void 0 : pluginInfo.version) != null) ? pluginInfo === null || pluginInfo === void 0 ? void 0 : pluginInfo.version : '0';
12
+ const projectRoot = args[0] || PathsConstants_1.default.getApplicationPath();
13
+ const buildPath = args[1] || PathsConstants_1.default.getBuildPath();
14
+ const analyzer = new LineOffsetAnalyzeCall_1.LineOffsetAnalyzer(projectRoot, buildPath, appBundleInfo);
15
+ analyzer.run();
16
+ })();
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.LineOffsetAnalyzer = void 0;
13
+ const fs = require("fs/promises");
14
+ const path_1 = require("path");
15
+ const diff_1 = require("diff");
16
+ const Logger_1 = require("../Logger");
17
+ const InstrumentUtil_1 = require("../util/InstrumentUtil");
18
+ const FileOperationHelper_1 = require("../../scripts/FileOperationHelper");
19
+ class LineOffsetAnalyzer {
20
+ constructor(rootPath, instrumentedPath, appBundleInfo) {
21
+ this.rootDir = (0, path_1.resolve)(rootPath);
22
+ this.instrumentedDir = (0, path_1.resolve)(instrumentedPath);
23
+ this.outputFile = (0, path_1.resolve)(this.instrumentedDir, 'line-offsets.json');
24
+ this.logFile = (0, path_1.resolve)(this.instrumentedDir, 'debug.log');
25
+ this.appBundleInfo = appBundleInfo;
26
+ }
27
+ run() {
28
+ return __awaiter(this, void 0, void 0, function* () {
29
+ try {
30
+ yield fs.writeFile(this.logFile, '');
31
+ const buildExists = yield FileOperationHelper_1.default.checkIfFileExists(this.instrumentedDir);
32
+ if (!buildExists) {
33
+ yield this.log(`❌ Build directory not found at: ${this.instrumentedDir}`);
34
+ yield this.log('🛠️ Please run the instrumentation step first!');
35
+ console.error('🚫 Build directory missing. Make and instrument the project first.');
36
+ process.exit(1);
37
+ }
38
+ const instrumentedFiles = (yield FileOperationHelper_1.default.getAllFiles(this.log, this.instrumentedDir))
39
+ .filter((filePath) => filePath.endsWith(InstrumentUtil_1.INSTRUMENTED_FILE_EXTENSION));
40
+ const mappings = {};
41
+ yield this.log(`🔍 Found ${instrumentedFiles.length} instrumented files`);
42
+ for (const instrRelPath of instrumentedFiles) {
43
+ const baseRelPath = instrRelPath.slice(0, -InstrumentUtil_1.INSTRUMENTED_FILE_EXTENSION.length);
44
+ const origPath = (0, path_1.join)(this.rootDir, baseRelPath);
45
+ const instrPath = (0, path_1.join)(this.instrumentedDir, instrRelPath);
46
+ const origExists = yield FileOperationHelper_1.default.checkIfFileExists(origPath);
47
+ if (!origExists) {
48
+ yield this.log(`⚠️ Original file not found for: ${baseRelPath}`);
49
+ continue;
50
+ }
51
+ yield this.log(`📄 Comparing: ${baseRelPath}`);
52
+ const [originalContent, instrumentedContent] = yield Promise.all([
53
+ FileOperationHelper_1.default.readTextFromFile(origPath),
54
+ FileOperationHelper_1.default.readTextFromFile(instrPath),
55
+ ]);
56
+ const offsets = this.computeLineOffsets(originalContent, instrumentedContent);
57
+ if (offsets.length > 0) {
58
+ mappings[baseRelPath] = offsets;
59
+ yield this.log(`✅ Offsets found for ${baseRelPath}: ${offsets.length} entries`);
60
+ }
61
+ else {
62
+ yield this.log(`ℹ️ No offsets needed for ${baseRelPath}`);
63
+ }
64
+ }
65
+ const finalOutput = {
66
+ generationTime: new Date().toISOString(),
67
+ appVersion: this.appBundleInfo ? this.appBundleInfo.version : 'application version not defined',
68
+ pluginVersion: this.appBundleInfo ? this.appBundleInfo.pluginVersion : 'plugin version not defined',
69
+ mappings: {
70
+ [this.appBundleInfo ? this.appBundleInfo.name : 'app name not defined']: mappings,
71
+ },
72
+ };
73
+ yield fs.writeFile(this.outputFile, JSON.stringify(finalOutput, null, 2));
74
+ yield this.log(`✅ Line offsets written to ${this.outputFile}`);
75
+ }
76
+ catch (err) {
77
+ console.error('❌ Unexpected error:', err);
78
+ }
79
+ });
80
+ }
81
+ log(msg) {
82
+ return __awaiter(this, void 0, void 0, function* () {
83
+ Logger_1.default.logMessageSync(msg, Logger_1.default.INFO);
84
+ yield fs.appendFile(this.logFile, msg + '\n');
85
+ });
86
+ }
87
+ computeLineOffsets(originalContent, instrumentedContent) {
88
+ const diffs = (0, diff_1.diffLines)(originalContent, instrumentedContent);
89
+ let origLine = 0;
90
+ let instrLine = 0;
91
+ let offset = 0;
92
+ let lastRecordedOffset = null;
93
+ const offsets = [];
94
+ this.log('🔍 Diff result:');
95
+ for (const part of diffs) {
96
+ const lines = part.value.split('\n').slice(0, -1);
97
+ if (part.added) {
98
+ this.log(`➕ Added ${lines.length} lines at instrumented line ${instrLine + 1}`);
99
+ lines.forEach((line, i) => this.log(` + ${instrLine + 1 + i}: ${line}`));
100
+ instrLine += lines.length;
101
+ offset -= lines.length;
102
+ }
103
+ else if (part.removed) {
104
+ this.log(`➖ Removed ${lines.length} lines at original line ${origLine + 1}`);
105
+ lines.forEach((line, i) => this.log(` - ${origLine + 1 + i}: ${line}`));
106
+ origLine += lines.length;
107
+ offset += lines.length;
108
+ }
109
+ else {
110
+ for (let i = 0; i < lines.length; i++) {
111
+ instrLine++;
112
+ origLine++;
113
+ if (offset !== 0 && offset !== lastRecordedOffset) {
114
+ offsets.push({ [instrLine]: offset });
115
+ lastRecordedOffset = offset;
116
+ }
117
+ }
118
+ }
119
+ }
120
+ return offsets;
121
+ }
122
+ }
123
+ exports.LineOffsetAnalyzer = LineOffsetAnalyzer;
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.showVersionOfPlugin = exports.isPlatformAvailable = void 0;
3
+ exports.showVersionOfPlugin = exports.isPlatformAvailable = exports.INSTRUMENTED_FILE_EXTENSION = void 0;
4
4
  const Logger_1 = require("../Logger");
5
5
  const FileOperationHelper_1 = require("../FileOperationHelper");
6
6
  const PathsConstants_1 = require("../PathsConstants");
7
+ exports.INSTRUMENTED_FILE_EXTENSION = '.dtx';
7
8
  const isPlatformAvailable = (path, platform) => {
8
9
  try {
9
10
  FileOperationHelper_1.default.checkIfFileExistsSync(path);
@@ -115,6 +115,11 @@ export interface Spec extends TurboModule {
115
115
 
116
116
  forwardEvent(attributes: UnsafeObject): void;
117
117
 
118
+ forwardAppStartEvent(
119
+ attributes: UnsafeObject,
120
+ appStartKeys: string[],
121
+ ): void;
122
+
118
123
  setGPSLocation(
119
124
  latitude: number,
120
125
  longitude: number,
@@ -1395,6 +1395,11 @@ export declare interface IConfiguration {
1395
1395
  * Will define the bundle name which will prefix internal action ids.
1396
1396
  */
1397
1397
  readonly bundleName?: string;
1398
+
1399
+ /**
1400
+ * Will define the bundle version which will extend bundleName signature.
1401
+ */
1402
+ readonly bundleVersion?: string;
1398
1403
  }
1399
1404
 
1400
1405
  /**
@@ -1413,12 +1418,13 @@ export declare class ManualStartupConfiguration implements IConfiguration {
1413
1418
  * @param {boolean} userOptIn Activates the privacy mode when set to `true`. User consent must be queried and set.
1414
1419
  * @param {boolean} actionNamePrivacy Activates a privacy mode especially for Touchables and Buttons. Setting this option to true means that a name for the control will no longer be shown, e.g. "Touch on Button". When setting a dtActionName onto the component this setting will be ignored.
1415
1420
  * @param {string} bundleName Will define the bundle name which will prefix internal action ids.
1421
+ * @param {string} bundleVersion Will define the bundle version which will extend bundleName signature.
1416
1422
  *
1417
1423
  * @deprecated Use ConfigurationBuilder and IConfiguration instead
1418
1424
  */
1419
1425
  constructor(beaconUrl: string, applicationId: string, reportCrash?: boolean,
1420
1426
  logLevel?: LogLevel, lifecycleUpdate?: boolean, userOptIn?: boolean,
1421
- actionNamePrivacy?: boolean, bundleName?: string);
1427
+ actionNamePrivacy?: boolean, bundleName?: string, bundleVersion?: string);
1422
1428
 
1423
1429
  /**
1424
1430
  * Beacon url which is used for communicate with the beacon endpoint. This value is mandatory.
@@ -1495,6 +1501,11 @@ export declare class ManualStartupConfiguration implements IConfiguration {
1495
1501
  * Will define the bundle name which will prefix internal action ids.
1496
1502
  */
1497
1503
  readonly bundleName?: string;
1504
+
1505
+ /**
1506
+ * Will define the bundle version which will extend bundleName signature.
1507
+ */
1508
+ readonly bundleVersion?: string;
1498
1509
  }
1499
1510
 
1500
1511
  /**
@@ -1672,6 +1683,25 @@ export declare class ConfigurationBuilder {
1672
1683
  */
1673
1684
  public withBundleName(bundleName: string): ConfigurationBuilder;
1674
1685
 
1686
+
1687
+ /**
1688
+ * Builder function to handle bundle name property
1689
+ *
1690
+ * @param {string} bundleName Will define the bundle name which will prefix internal action ids.
1691
+ *
1692
+ * Usage:
1693
+ *
1694
+ * ```ts
1695
+ * import { ConfigurationBuilder, Dynatrace } from '@dynatrace/react-native-plugin';
1696
+ *
1697
+ * const configurationBuilder = new ConfigurationBuilder("beaconUrl", "applicationId");
1698
+ * Dynatrace.start(configurationBuilder.withBundleName("bundleName").buildConfiguration());
1699
+ * ```
1700
+ *
1701
+ * @see https://www.npmjs.com/package/@dynatrace/react-native-plugin#plugin-startup
1702
+ */
1703
+ public withBundleVersion(bundleName: string): ConfigurationBuilder;
1704
+
1675
1705
  /**
1676
1706
  * Build configuration which is used for startup
1677
1707
  *