@dynatrace/react-native-plugin 2.313.1 → 2.317.2
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 +7 -3
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/dynatrace/android/agent/DynatraceAppStartModule.kt +7 -6
- package/android/src/main/java/com/dynatrace/android/agent/DynatraceRNBridgeImpl.kt +5 -0
- package/android/src/new/java/com/dynatrace/android/agent/DynatraceRNBridge.kt +5 -0
- package/android/src/old/java/com/dynatrace/android/agent/DynatraceRNBridge.kt +6 -0
- package/files/plugin.gradle +1 -1
- package/instrumentation/DynatraceInstrumentation.js +1 -1
- package/ios/DynatraceRNBridge.h +1 -0
- package/ios/DynatraceRNBridge.mm +50 -53
- package/lib/core/Application.js +2 -0
- package/lib/core/util/GetValuesFromPackage.js +18 -0
- package/lib/metro/getSourceMapInfo.js +3 -2
- package/lib/next/events/EventCreator.js +14 -8
- package/lib/next/events/EventPipeline.js +8 -3
- package/lib/next/events/modifier/ModifyEventValidation.js +4 -4
- package/lib/next/events/modifier/SendEventValidation.js +2 -2
- package/lib/next/events/modifier/ValueRestrictionModifier.js +1 -1
- package/lib/next/events/spec/EventSpecContstants.js +34 -24
- package/package.json +10 -6
- package/react-native-dynatrace.podspec +1 -1
- package/scripts/FileOperationHelper.js +33 -0
- package/scripts/Ios.js +10 -9
- package/scripts/LineOffsetAnalyze.js +16 -0
- package/scripts/core/LineOffsetAnalyzeCall.js +123 -0
- package/scripts/util/InstrumentUtil.js +2 -1
- package/scripts/util/PlistConstants.js +2 -1
- package/src/lib/core/interface/NativeDynatraceBridge.ts +5 -0
- package/typings/react-native-dynatrace.d.ts +31 -1
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.
|
|
36
|
-
* iOS Agent: 8.
|
|
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,10 @@ 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.2
|
|
1465
|
+
* Updated Android (8.317.1.1007) & iOS Agent (8.317.1.1003)
|
|
1466
|
+
* Webrequest correlation with user action fixed for iOS
|
|
1467
|
+
|
|
1464
1468
|
2.313.1
|
|
1465
1469
|
* Updated Android (8.313.1.1004) & iOS Agent (8.313.1.1016)
|
|
1466
1470
|
* Expo transformer is now automatically used, if available.
|
package/android/build.gradle
CHANGED
|
@@ -72,7 +72,7 @@ repositories {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
dependencies {
|
|
75
|
-
implementation 'com.dynatrace.agent:agent-android:8.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|
package/files/plugin.gradle
CHANGED
|
@@ -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))};
|
package/ios/DynatraceRNBridge.h
CHANGED
|
@@ -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 {
|
package/ios/DynatraceRNBridge.mm
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
- (
|
|
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(
|
|
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
|
|
114
|
+
if (!didEmit && hasListeners && [self isReady]) {
|
|
88
115
|
didEmit = YES;
|
|
89
|
-
|
|
116
|
+
|
|
90
117
|
NSMutableDictionary<NSString*, NSNumber*> *appStartMeasurements = [[NSMutableDictionary alloc] init];
|
|
91
|
-
|
|
92
|
-
[
|
|
93
|
-
[
|
|
94
|
-
[
|
|
95
|
-
|
|
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 +
|
|
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
|
-
|
|
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])
|
package/lib/core/Application.js
CHANGED
|
@@ -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 (!
|
|
10
|
+
if (!options.excludeSource) {
|
|
10
11
|
const correctPath = module.path.replace(pathsConstants.getApplicationPath(), '') +
|
|
11
|
-
|
|
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.
|
|
57
|
+
'characteristics.has_app_start': true,
|
|
58
58
|
};
|
|
59
|
-
let
|
|
59
|
+
let timingsApp = [];
|
|
60
60
|
for (const { type, key } of AppStartType_1.AppStartTypeKeyMapping) {
|
|
61
61
|
if (appStartMeasurements[type] !== undefined) {
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
timingsApp.push({
|
|
63
|
+
name: key,
|
|
64
|
+
timestamp: appStartMeasurements[type],
|
|
65
|
+
});
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
event["start_time"] =
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
106
|
+
"dt.internal.api.overridden_keys",
|
|
107
107
|
overriddenKeys,
|
|
108
108
|
]);
|
|
109
109
|
}
|
|
110
110
|
if (externalException) {
|
|
111
111
|
sizedEntries.push([
|
|
112
|
-
"dt.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
25
|
-
"characteristics.is_internal",
|
|
26
|
-
"characteristics.is_self_monitoring",
|
|
27
|
-
"characteristics.
|
|
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.
|
|
3
|
+
"version": "2.317.2",
|
|
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.
|
|
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.
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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.
|
|
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,
|
package/scripts/Ios.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const nodePath = require("path");
|
|
5
|
-
const
|
|
5
|
+
const plist_1 = require("plist");
|
|
6
6
|
const Logger_1 = require("./Logger");
|
|
7
7
|
const FileOperationHelper_1 = require("./FileOperationHelper");
|
|
8
8
|
const PathsConstants_1 = require("./PathsConstants");
|
|
@@ -62,20 +62,20 @@ const modifyPListFile = (pathToPList, iosConfig, removeOnly) => {
|
|
|
62
62
|
};
|
|
63
63
|
const removePListConfig = (file) => {
|
|
64
64
|
const pListContent = FileOperationHelper_1.default.readTextFromFileSync(file);
|
|
65
|
-
const pListObj =
|
|
65
|
+
const pListObj = (0, plist_1.parse)(pListContent);
|
|
66
66
|
const pListObjCopy = Object.assign({}, pListObj);
|
|
67
67
|
for (const property in pListObj) {
|
|
68
68
|
if (property.startsWith('DTX')) {
|
|
69
69
|
delete pListObjCopy[property];
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
FileOperationHelper_1.default.writeTextToFileSync(file,
|
|
72
|
+
FileOperationHelper_1.default.writeTextToFileSync(file, (0, plist_1.build)(pListObjCopy));
|
|
73
73
|
Logger_1.default.logMessageSync('Removed old configuration in plist file: ' + file, Logger_1.default.INFO);
|
|
74
74
|
};
|
|
75
75
|
const addAgentConfigToPListFile = (file, config) => {
|
|
76
76
|
const pListContent = FileOperationHelper_1.default.readTextFromFileSync(file);
|
|
77
77
|
const newPListContent = PlistConstants_1.START_PLIST + config + PlistConstants_1.END_PLIST;
|
|
78
|
-
FileOperationHelper_1.default.writeTextToFileSync(file,
|
|
78
|
+
FileOperationHelper_1.default.writeTextToFileSync(file, (0, plist_1.build)(Object.assign(Object.assign({}, (0, plist_1.parse)(pListContent)), (0, plist_1.parse)(newPListContent))));
|
|
79
79
|
Logger_1.default.logMessageSync('Updated configuration in plist file: ' + file, Logger_1.default.INFO);
|
|
80
80
|
};
|
|
81
81
|
const findPListFile = () => {
|
|
@@ -106,13 +106,13 @@ const findPListFile = () => {
|
|
|
106
106
|
};
|
|
107
107
|
const parsePList = (file) => {
|
|
108
108
|
const pListContent = FileOperationHelper_1.default.readTextFromFileSync(file);
|
|
109
|
-
let pListObj =
|
|
109
|
+
let pListObj = (0, plist_1.parse)(pListContent);
|
|
110
110
|
return pListObj = Object.assign({}, pListObj);
|
|
111
111
|
};
|
|
112
112
|
const isAutoStartEnabled = (config) => {
|
|
113
113
|
if (config !== undefined && config.indexOf(PlistConstants_1.AUTO_START_PROP) >= 0) {
|
|
114
114
|
const configObj = PlistConstants_1.START_PLIST + config + PlistConstants_1.END_PLIST;
|
|
115
|
-
const configObjCopy =
|
|
115
|
+
const configObjCopy = (0, plist_1.parse)(configObj);
|
|
116
116
|
const configKeys = Object.keys(configObjCopy);
|
|
117
117
|
const configValues = Object.values(configObjCopy);
|
|
118
118
|
for (const key in configKeys) {
|
|
@@ -142,7 +142,7 @@ const updatedExcludedStr = (config) => {
|
|
|
142
142
|
};
|
|
143
143
|
const isPropertyCountEqual = (parsedPList, config) => {
|
|
144
144
|
const configObj = PlistConstants_1.START_PLIST + config + PlistConstants_1.END_PLIST;
|
|
145
|
-
const configObjCopy =
|
|
145
|
+
const configObjCopy = (0, plist_1.parse)(configObj);
|
|
146
146
|
return Object.keys(parsedPList)
|
|
147
147
|
.filter((pListDtxKeys) => pListDtxKeys.startsWith('DTX')).length === Object.keys(configObjCopy)
|
|
148
148
|
.filter((configDtxKeys) => configDtxKeys.startsWith('DTX')).length;
|
|
@@ -162,7 +162,7 @@ const areConfigsEqual = (parsedPList, configObj) => {
|
|
|
162
162
|
};
|
|
163
163
|
const comparePListAndConfig = (pListObj, config) => {
|
|
164
164
|
const configObj = PlistConstants_1.START_PLIST + config + PlistConstants_1.END_PLIST;
|
|
165
|
-
const configObjCopy =
|
|
165
|
+
const configObjCopy = (0, plist_1.parse)(configObj);
|
|
166
166
|
removeNonDTXProperties(pListObj);
|
|
167
167
|
removeNonDTXProperties(configObjCopy);
|
|
168
168
|
return areConfigsEqual(pListObj, configObjCopy);
|
|
@@ -183,7 +183,7 @@ const hasDuplicateProperties = (config) => {
|
|
|
183
183
|
return !newConfigArr.every((property) => newConfigArr.indexOf(property) === newConfigArr.lastIndexOf(property));
|
|
184
184
|
};
|
|
185
185
|
const createNewPListIfRequired = (parsedPList, configProps, pathToPList) => {
|
|
186
|
-
const configIncludingFlavor = configProps +
|
|
186
|
+
const configIncludingFlavor = configProps + getAdditionalInternalPluginKeys() + updatedExcludedStr(configProps);
|
|
187
187
|
if (isPropertyCountEqual(parsedPList, configIncludingFlavor) && comparePListAndConfig(parsedPList, configIncludingFlavor)) {
|
|
188
188
|
Logger_1.default.logMessageSync('Not generating a new plist as the current plist and ' +
|
|
189
189
|
' dynatrace.config.js iOS properties are identical!', Logger_1.default.INFO);
|
|
@@ -195,6 +195,7 @@ const createNewPListIfRequired = (parsedPList, configProps, pathToPList) => {
|
|
|
195
195
|
addAgentConfigToPListFile(pathToPList, configIncludingFlavor);
|
|
196
196
|
}
|
|
197
197
|
};
|
|
198
|
+
const getAdditionalInternalPluginKeys = () => PlistConstants_1.FLAVOR_PROP + PlistConstants_1.WEBREQUEST_FALLBACK_PROP;
|
|
198
199
|
exports.default = {
|
|
199
200
|
modifyPListFile,
|
|
200
201
|
findPListFile,
|
|
@@ -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);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CONTROLS_PROP_OPTIONS = exports.END_CONTROLS_PROP = exports.START_CONTROLS_PROP = exports.DEFAULT_CONTROLS_PROP = exports.FLAVOR_PROP = exports.AUTO_START_PROP = exports.END_PLIST = exports.START_PLIST = void 0;
|
|
3
|
+
exports.CONTROLS_PROP_OPTIONS = exports.END_CONTROLS_PROP = exports.START_CONTROLS_PROP = exports.DEFAULT_CONTROLS_PROP = exports.WEBREQUEST_FALLBACK_PROP = exports.FLAVOR_PROP = exports.AUTO_START_PROP = exports.END_PLIST = exports.START_PLIST = void 0;
|
|
4
4
|
exports.START_PLIST = '<plist><dict>';
|
|
5
5
|
exports.END_PLIST = '</dict></plist>';
|
|
6
6
|
exports.AUTO_START_PROP = '<key>DTXAutoStart</key>';
|
|
7
7
|
exports.FLAVOR_PROP = '<key>DTXFlavor</key>\n<string>react_native</string>';
|
|
8
|
+
exports.WEBREQUEST_FALLBACK_PROP = '<key>DTXDisableWebRequestsInstrumentationV2</key>\n<true/>';
|
|
8
9
|
exports.DEFAULT_CONTROLS_PROP = '<key>DTXExcludedControls</key>\n<array>\n\t<string>PickerView</string>\n\t<string>Switch</string>\n</array>';
|
|
9
10
|
exports.START_CONTROLS_PROP = '<key>DTXExcludedControls</key>\n<array>';
|
|
10
11
|
exports.END_CONTROLS_PROP = '\n</array>';
|
|
@@ -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
|
*
|