@dreamhorizonorg/pulse-react-native 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/LICENSE +20 -0
  2. package/PulseReactNativeOtel.podspec +21 -0
  3. package/README.md +900 -0
  4. package/android/build.gradle +97 -0
  5. package/android/gradle.properties +5 -0
  6. package/android/proguard-rules.pro +108 -0
  7. package/android/src/main/AndroidManifest.xml +2 -0
  8. package/android/src/main/java/com/pulsereactnativeotel/PulseOtelConstants.kt +20 -0
  9. package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelLogger.kt +73 -0
  10. package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelModule.kt +85 -0
  11. package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelPackage.kt +33 -0
  12. package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelTracer.kt +127 -0
  13. package/ios/PulseReactNativeOtel.h +5 -0
  14. package/ios/PulseReactNativeOtel.mm +100 -0
  15. package/lib/module/NativePulseReactNativeOtel.js +5 -0
  16. package/lib/module/NativePulseReactNativeOtel.js.map +1 -0
  17. package/lib/module/config.js +55 -0
  18. package/lib/module/config.js.map +1 -0
  19. package/lib/module/errorBoundary.js +62 -0
  20. package/lib/module/errorBoundary.js.map +1 -0
  21. package/lib/module/errorHandler.js +112 -0
  22. package/lib/module/errorHandler.js.map +1 -0
  23. package/lib/module/events.js +14 -0
  24. package/lib/module/events.js.map +1 -0
  25. package/lib/module/global.d.js +4 -0
  26. package/lib/module/global.d.js.map +1 -0
  27. package/lib/module/globalAttributes.js +13 -0
  28. package/lib/module/globalAttributes.js.map +1 -0
  29. package/lib/module/index.js +27 -0
  30. package/lib/module/index.js.map +1 -0
  31. package/lib/module/initialization.js +18 -0
  32. package/lib/module/initialization.js.map +1 -0
  33. package/lib/module/network-interceptor/initialization.js +25 -0
  34. package/lib/module/network-interceptor/initialization.js.map +1 -0
  35. package/lib/module/network-interceptor/network.interface.js +2 -0
  36. package/lib/module/network-interceptor/network.interface.js.map +1 -0
  37. package/lib/module/network-interceptor/request-tracker-fetch.js +72 -0
  38. package/lib/module/network-interceptor/request-tracker-fetch.js.map +1 -0
  39. package/lib/module/network-interceptor/request-tracker-xhr.js +75 -0
  40. package/lib/module/network-interceptor/request-tracker-xhr.js.map +1 -0
  41. package/lib/module/network-interceptor/request-tracker.js +25 -0
  42. package/lib/module/network-interceptor/request-tracker.js.map +1 -0
  43. package/lib/module/network-interceptor/span-helpers.js +62 -0
  44. package/lib/module/network-interceptor/span-helpers.js.map +1 -0
  45. package/lib/module/network-interceptor/url-helper.js +127 -0
  46. package/lib/module/network-interceptor/url-helper.js.map +1 -0
  47. package/lib/module/pulse.interface.js +2 -0
  48. package/lib/module/pulse.interface.js.map +1 -0
  49. package/lib/module/reactNavigation.js +100 -0
  50. package/lib/module/reactNavigation.js.map +1 -0
  51. package/lib/module/trace.js +75 -0
  52. package/lib/module/trace.js.map +1 -0
  53. package/lib/module/user.js +23 -0
  54. package/lib/module/user.js.map +1 -0
  55. package/lib/module/utility.js +34 -0
  56. package/lib/module/utility.js.map +1 -0
  57. package/lib/typescript/src/NativePulseReactNativeOtel.d.ts +31 -0
  58. package/lib/typescript/src/NativePulseReactNativeOtel.d.ts.map +1 -0
  59. package/lib/typescript/src/config.d.ts +14 -0
  60. package/lib/typescript/src/config.d.ts.map +1 -0
  61. package/lib/typescript/src/errorBoundary.d.ts +26 -0
  62. package/lib/typescript/src/errorBoundary.d.ts.map +1 -0
  63. package/lib/typescript/src/errorHandler.d.ts +4 -0
  64. package/lib/typescript/src/errorHandler.d.ts.map +1 -0
  65. package/lib/typescript/src/events.d.ts +3 -0
  66. package/lib/typescript/src/events.d.ts.map +1 -0
  67. package/lib/typescript/src/globalAttributes.d.ts +4 -0
  68. package/lib/typescript/src/globalAttributes.d.ts.map +1 -0
  69. package/lib/typescript/src/index.d.ts +30 -0
  70. package/lib/typescript/src/index.d.ts.map +1 -0
  71. package/lib/typescript/src/initialization.d.ts +3 -0
  72. package/lib/typescript/src/initialization.d.ts.map +1 -0
  73. package/lib/typescript/src/network-interceptor/initialization.d.ts +3 -0
  74. package/lib/typescript/src/network-interceptor/initialization.d.ts.map +1 -0
  75. package/lib/typescript/src/network-interceptor/network.interface.d.ts +29 -0
  76. package/lib/typescript/src/network-interceptor/network.interface.d.ts.map +1 -0
  77. package/lib/typescript/src/network-interceptor/request-tracker-fetch.d.ts +7 -0
  78. package/lib/typescript/src/network-interceptor/request-tracker-fetch.d.ts.map +1 -0
  79. package/lib/typescript/src/network-interceptor/request-tracker-xhr.d.ts +4 -0
  80. package/lib/typescript/src/network-interceptor/request-tracker-xhr.d.ts.map +1 -0
  81. package/lib/typescript/src/network-interceptor/request-tracker.d.ts +9 -0
  82. package/lib/typescript/src/network-interceptor/request-tracker.d.ts.map +1 -0
  83. package/lib/typescript/src/network-interceptor/span-helpers.d.ts +7 -0
  84. package/lib/typescript/src/network-interceptor/span-helpers.d.ts.map +1 -0
  85. package/lib/typescript/src/network-interceptor/url-helper.d.ts +38 -0
  86. package/lib/typescript/src/network-interceptor/url-helper.d.ts.map +1 -0
  87. package/lib/typescript/src/pulse.interface.d.ts +8 -0
  88. package/lib/typescript/src/pulse.interface.d.ts.map +1 -0
  89. package/lib/typescript/src/reactNavigation.d.ts +10 -0
  90. package/lib/typescript/src/reactNavigation.d.ts.map +1 -0
  91. package/lib/typescript/src/trace.d.ts +19 -0
  92. package/lib/typescript/src/trace.d.ts.map +1 -0
  93. package/lib/typescript/src/user.d.ts +5 -0
  94. package/lib/typescript/src/user.d.ts.map +1 -0
  95. package/lib/typescript/src/utility.d.ts +8 -0
  96. package/lib/typescript/src/utility.d.ts.map +1 -0
  97. package/package.json +165 -0
  98. package/src/NativePulseReactNativeOtel.ts +57 -0
  99. package/src/config.ts +75 -0
  100. package/src/errorBoundary.tsx +92 -0
  101. package/src/errorHandler.ts +169 -0
  102. package/src/events.ts +15 -0
  103. package/src/global.d.ts +9 -0
  104. package/src/globalAttributes.ts +16 -0
  105. package/src/index.tsx +35 -0
  106. package/src/initialization.ts +18 -0
  107. package/src/network-interceptor/initialization.ts +28 -0
  108. package/src/network-interceptor/network.interface.ts +36 -0
  109. package/src/network-interceptor/request-tracker-fetch.ts +96 -0
  110. package/src/network-interceptor/request-tracker-xhr.ts +108 -0
  111. package/src/network-interceptor/request-tracker.ts +31 -0
  112. package/src/network-interceptor/span-helpers.ts +84 -0
  113. package/src/network-interceptor/url-helper.ts +155 -0
  114. package/src/pulse.interface.ts +14 -0
  115. package/src/reactNavigation.tsx +146 -0
  116. package/src/trace.ts +108 -0
  117. package/src/user.ts +24 -0
  118. package/src/utility.ts +36 -0
@@ -0,0 +1,97 @@
1
+ buildscript {
2
+ ext.getExtOrDefault = {name ->
3
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['PulseReactNativeOtel_' + name]
4
+ }
5
+
6
+ repositories {
7
+ google()
8
+ mavenCentral()
9
+ }
10
+
11
+ dependencies {
12
+ classpath "com.android.tools.build:gradle:8.7.2"
13
+ // noinspection DifferentKotlinGradleVersion
14
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
15
+ }
16
+ }
17
+
18
+
19
+ apply plugin: "com.android.library"
20
+ apply plugin: "kotlin-android"
21
+
22
+ apply plugin: "com.facebook.react"
23
+
24
+ def getExtOrIntegerDefault(name) {
25
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["PulseReactNativeOtel_" + name]).toInteger()
26
+ }
27
+
28
+ android {
29
+ namespace "com.pulsereactnativeotel"
30
+
31
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
32
+
33
+ defaultConfig {
34
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
35
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
36
+ }
37
+
38
+ buildFeatures {
39
+ buildConfig true
40
+ }
41
+
42
+ buildTypes {
43
+ release {
44
+ minifyEnabled false
45
+ consumerProguardFiles 'proguard-rules.pro'
46
+ }
47
+ debug {
48
+ consumerProguardFiles 'proguard-rules.pro'
49
+ }
50
+ }
51
+
52
+ lintOptions {
53
+ disable "GradleCompatible"
54
+ }
55
+
56
+ compileOptions {
57
+ sourceCompatibility JavaVersion.VERSION_1_8
58
+ targetCompatibility JavaVersion.VERSION_1_8
59
+ }
60
+
61
+ sourceSets {
62
+ main {
63
+ java.srcDirs += [
64
+ "generated/java",
65
+ "generated/jni"
66
+ ]
67
+ }
68
+ }
69
+ }
70
+
71
+ repositories {
72
+ mavenCentral()
73
+ google()
74
+ }
75
+
76
+ def kotlin_version = getExtOrDefault("kotlinVersion")
77
+
78
+ dependencies {
79
+ implementation("com.facebook.react:react-android")
80
+ implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
81
+
82
+ api("org.dreamhorizon:pulse-android-sdk:0.0.1-alpha") {
83
+ exclude group: "com.squareup.okhttp3", module: "okhttp-jvm"
84
+ }
85
+ api("com.squareup.okhttp3:okhttp:5.0.0-alpha.14")
86
+ }
87
+
88
+ configurations.all {
89
+ resolutionStrategy {
90
+ eachDependency {
91
+ if (requested.group == "com.squareup.okhttp3" && requested.name == "okhttp-jvm") {
92
+ useTarget("com.squareup.okhttp3:okhttp:${requested.version}")
93
+ because("Resolving okhttp-jvm to okhttp for Android compatibility")
94
+ }
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,5 @@
1
+ PulseReactNativeOtel_kotlinVersion=2.0.21
2
+ PulseReactNativeOtel_minSdkVersion=24
3
+ PulseReactNativeOtel_targetSdkVersion=35
4
+ PulseReactNativeOtel_compileSdkVersion=35
5
+ PulseReactNativeOtel_ndkVersion=27.1.12297006
@@ -0,0 +1,108 @@
1
+ # Pulse React Native OpenTelemetry - Consumer ProGuard Rules
2
+ # These rules are automatically applied to consuming applications
3
+
4
+ # ===== OpenTelemetry ProGuard Rules =====
5
+ # OpenTelemetry is heavily reflection-based and needs these classes preserved
6
+
7
+ # Keep all OpenTelemetry classes
8
+ -keep class io.opentelemetry.** { *; }
9
+ -dontwarn io.opentelemetry.**
10
+
11
+ # Keep OpenTelemetry API
12
+ -keep class io.opentelemetry.api.** { *; }
13
+ -keep interface io.opentelemetry.api.** { *; }
14
+
15
+ # Keep OpenTelemetry SDK
16
+ -keep class io.opentelemetry.sdk.** { *; }
17
+
18
+ # Keep OpenTelemetry Context (critical for span propagation)
19
+ -keep class io.opentelemetry.context.** { *; }
20
+ -keep class io.opentelemetry.context.Context { *; }
21
+ -keep class io.opentelemetry.context.Scope { *; }
22
+ -keep class io.opentelemetry.context.ImplicitContextKeyed { *; }
23
+ -keep interface io.opentelemetry.context.** { *; }
24
+
25
+ # ===== Missing Compile-Time Annotations =====
26
+ # These annotations are compile-time only and not needed at runtime
27
+ # R8/ProGuard should ignore them when missing
28
+
29
+ -dontwarn com.google.errorprone.annotations.**
30
+ -dontwarn javax.annotation.**
31
+ -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
32
+ -dontwarn edu.umd.cs.findbugs.annotations.**
33
+ -dontwarn org.checkerframework.checker.**
34
+ -dontwarn com.google.j2objc.annotations.**
35
+
36
+ # Specifically ignore the MustBeClosed annotation that R8 complains about
37
+ -dontwarn com.google.errorprone.annotations.MustBeClosed
38
+ -dontwarn com.google.errorprone.annotations.CanIgnoreReturnValue
39
+ -dontwarn com.google.errorprone.annotations.CheckReturnValue
40
+ -dontwarn com.google.errorprone.annotations.concurrent.**
41
+
42
+ # ===== Missing Java SE Classes (not available in Android) =====
43
+ # java.beans package is part of Java SE but not Android
44
+ # These are referenced by libraries like SnakeYAML and Jackson but not used at runtime
45
+
46
+ -dontwarn java.beans.**
47
+ -dontwarn java.beans.BeanInfo
48
+ -dontwarn java.beans.ConstructorProperties
49
+ -dontwarn java.beans.FeatureDescriptor
50
+ -dontwarn java.beans.IntrospectionException
51
+ -dontwarn java.beans.Introspector
52
+ -dontwarn java.beans.PropertyDescriptor
53
+ -dontwarn java.beans.Transient
54
+
55
+ # SnakeYAML uses java.beans for reflection but has fallbacks
56
+ -dontwarn org.yaml.snakeyaml.introspector.MethodProperty
57
+
58
+ # Jackson uses java.beans for annotations but has fallbacks
59
+ -dontwarn com.fasterxml.jackson.databind.ext.Java7SupportImpl
60
+
61
+ # ===== Pulse React Native OTEL =====
62
+ # Keep our library classes
63
+ -keep class com.pulsereactnativeotel.** { *; }
64
+ -keepclassmembers class com.pulsereactnativeotel.** { *; }
65
+
66
+ # Keep Kotlin metadata
67
+ -keep class kotlin.Metadata { *; }
68
+
69
+ # ===== General Rules =====
70
+ # Keep attributes needed for proper functioning
71
+ -keepattributes Signature
72
+ -keepattributes *Annotation*
73
+ -keepattributes InnerClasses
74
+ -keepattributes EnclosingMethod
75
+ -keepattributes Exceptions
76
+ -keepattributes SourceFile
77
+ -keepattributes LineNumberTable
78
+
79
+ # Keep generic signatures for proper type checking
80
+ -keepattributes Signature
81
+
82
+ # ===== React Native Specific =====
83
+ # Keep native method names
84
+ -keepclasseswithmembernames class * {
85
+ native <methods>;
86
+ }
87
+
88
+ # Keep React Native annotations
89
+ -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
90
+ -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
91
+ -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
92
+ -keep,allowobfuscation @interface com.facebook.jni.annotations.DoNotStrip
93
+
94
+ -keep @com.facebook.proguard.annotations.DoNotStrip class *
95
+ -keep @com.facebook.common.internal.DoNotStrip class *
96
+ -keep @com.facebook.jni.annotations.DoNotStrip class *
97
+
98
+ -keepclassmembers class * {
99
+ @com.facebook.proguard.annotations.DoNotStrip *;
100
+ @com.facebook.common.internal.DoNotStrip *;
101
+ @com.facebook.jni.annotations.DoNotStrip *;
102
+ }
103
+
104
+ -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
105
+ void set*(***);
106
+ *** get*();
107
+ }
108
+
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,20 @@
1
+ package com.pulsereactnativeotel
2
+
3
+ object PulseOtelConstants {
4
+ const val INSTRUMENTATION_SCOPE = "com.pulsereactnativeotel"
5
+
6
+ const val ATTR_ERROR_FATAL = "error.fatal"
7
+ const val ATTR_ERROR_TYPE = "exception.type"
8
+ const val ATTR_ERROR_MESSAGE = "exception.message"
9
+ const val ATTR_ERROR_STACK = "exception.stacktrace"
10
+ const val ATTR_ERROR_SOURCE = "error.source"
11
+
12
+ const val ATTR_THREAD_ID = "thread.id"
13
+ const val ATTR_THREAD_NAME = "thread.name"
14
+
15
+ const val PLATFORM_REACT_NATIVE = "react-native"
16
+ const val DEFAULT_ERROR_TYPE = "javascript"
17
+ const val ERROR_SOURCE_JS = "js"
18
+ }
19
+
20
+
@@ -0,0 +1,73 @@
1
+ package com.pulsereactnativeotel
2
+
3
+ import com.facebook.react.bridge.ReadableArray
4
+ import com.facebook.react.bridge.ReadableMap
5
+ import com.facebook.react.bridge.ReadableType
6
+ import com.pulse.android.sdk.PulseSDK
7
+
8
+ object PulseReactNativeOtelLogger {
9
+
10
+ fun trackEvent(
11
+ event: String,
12
+ observedTimeMs: Long,
13
+ properties: ReadableMap?
14
+ ) {
15
+ val params = buildMap {
16
+ properties?.let { putAll(it.toMap()) }
17
+ }
18
+
19
+ PulseSDK.INSTANCE.trackEvent(event, observedTimeMs, params)
20
+ }
21
+
22
+ fun reportException(
23
+ errorMessage: String,
24
+ observedTimeMs: Long,
25
+ stackTrace: String,
26
+ isFatal: Boolean,
27
+ errorType: String,
28
+ attributes: ReadableMap?
29
+ ) {
30
+ val params = buildMap {
31
+ put(PulseOtelConstants.ATTR_ERROR_TYPE, errorType.ifEmpty { PulseOtelConstants.DEFAULT_ERROR_TYPE })
32
+ put(PulseOtelConstants.ATTR_ERROR_FATAL, isFatal)
33
+ put(PulseOtelConstants.ATTR_ERROR_MESSAGE, errorMessage)
34
+ put(PulseOtelConstants.ATTR_ERROR_STACK, stackTrace)
35
+ put(PulseOtelConstants.ATTR_THREAD_ID, getCurrentThreadId())
36
+ put(PulseOtelConstants.ATTR_THREAD_NAME, Thread.currentThread().name)
37
+ put(PulseOtelConstants.ATTR_ERROR_SOURCE, PulseOtelConstants.ERROR_SOURCE_JS)
38
+ attributes?.let { putAll(it.toMap()) }
39
+ }
40
+
41
+ PulseSDK.INSTANCE.trackNonFatal(errorMessage, observedTimeMs, params)
42
+ }
43
+
44
+ private fun getCurrentThreadId(): String {
45
+ @Suppress("DEPRECATION")
46
+ return Thread.currentThread().id.toString()
47
+ }
48
+
49
+ private fun ReadableMap.toMap(): Map<String, Any?> = buildMap {
50
+ entryIterator.forEach { (key, value) ->
51
+ put(key, value?.toAny())
52
+ }
53
+ }
54
+
55
+ private fun Any.toAny(): Any = when (this) {
56
+ is String -> this
57
+ is Number -> this
58
+ is Boolean -> this
59
+ is ReadableArray -> this.toList()
60
+ else -> this.toString()
61
+ }
62
+
63
+ private fun ReadableArray.toList(): List<Any?> {
64
+ if (size() == 0) return emptyList()
65
+
66
+ return when (getType(0)) {
67
+ ReadableType.String -> toArrayList()
68
+ ReadableType.Number -> toArrayList()
69
+ ReadableType.Boolean -> toArrayList()
70
+ else -> emptyList()
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,85 @@
1
+ package com.pulsereactnativeotel
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext
4
+ import com.facebook.react.bridge.ReadableMap
5
+ import com.facebook.react.module.annotations.ReactModule
6
+ import com.pulse.android.sdk.PulseSDK
7
+ import android.os.Looper
8
+ import android.util.Log
9
+ import android.os.Handler
10
+
11
+ @ReactModule(name = PulseReactNativeOtelModule.NAME)
12
+ class PulseReactNativeOtelModule(reactContext: ReactApplicationContext) :
13
+ NativePulseReactNativeOtelSpec(reactContext) {
14
+
15
+ override fun getName(): String {
16
+ return NAME
17
+ }
18
+
19
+ override fun isInitialized(): Boolean {
20
+ return PulseSDK.INSTANCE.isInitialized()
21
+ }
22
+
23
+ override fun trackEvent(event: String, observedTimeMs: Double, properties: ReadableMap?): Boolean {
24
+ PulseReactNativeOtelLogger.trackEvent(event, observedTimeMs.toLong(), properties)
25
+ return true
26
+ }
27
+
28
+ override fun reportException(errorMessage: String, observedTimeMs: Double, stackTrace: String, isFatal: Boolean, errorType: String, attributes: ReadableMap?): Boolean {
29
+ PulseReactNativeOtelLogger.reportException(errorMessage, observedTimeMs.toLong(), stackTrace, isFatal, errorType, attributes)
30
+ return true
31
+ }
32
+
33
+ override fun startSpan(name: String, attributes: ReadableMap?): String {
34
+ return PulseReactNativeOtelTracer.startSpan(name, attributes)
35
+ }
36
+
37
+ override fun endSpan(spanId: String, statusCode: String?): Boolean {
38
+ PulseReactNativeOtelTracer.endSpan(spanId, statusCode)
39
+ return true
40
+ }
41
+
42
+ override fun addSpanEvent(spanId: String, name: String, attributes: ReadableMap?): Boolean {
43
+ PulseReactNativeOtelTracer.addEvent(spanId, name, attributes)
44
+ return true
45
+ }
46
+
47
+ override fun setSpanAttributes(spanId: String, attributes: ReadableMap?): Boolean {
48
+ PulseReactNativeOtelTracer.setAttributes(spanId, attributes)
49
+ return true
50
+ }
51
+
52
+ override fun recordSpanException(spanId: String, errorMessage: String, stackTrace: String?): Boolean {
53
+ PulseReactNativeOtelTracer.recordException(spanId, errorMessage, stackTrace)
54
+ return true
55
+ }
56
+
57
+ override fun setUserId(id: String?) {
58
+ PulseSDK.INSTANCE.setUserId(id)
59
+ }
60
+
61
+ override fun setUserProperty(name: String, value: String?) {
62
+ PulseSDK.INSTANCE.setUserProperty(name, value)
63
+ }
64
+
65
+ override fun setUserProperties(properties: ReadableMap?) {
66
+ properties?.let { props ->
67
+ PulseSDK.INSTANCE.setUserProperties {
68
+ props.entryIterator.forEach { (key, value) ->
69
+ put(key, value)
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ override fun triggerAnr() {
76
+ Handler(Looper.getMainLooper()).postAtFrontOfQueue {
77
+ Log.d("[Pulse]", "Now running PostAtFrontQueue: ${Thread.currentThread().name}")
78
+ Thread.sleep(10_000)
79
+ }
80
+ }
81
+
82
+ companion object {
83
+ const val NAME = "PulseReactNativeOtel"
84
+ }
85
+ }
@@ -0,0 +1,33 @@
1
+ package com.pulsereactnativeotel
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+ import java.util.HashMap
9
+
10
+ class PulseReactNativeOtelPackage : BaseReactPackage() {
11
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
+ return if (name == PulseReactNativeOtelModule.NAME) {
13
+ PulseReactNativeOtelModule(reactContext)
14
+ } else {
15
+ null
16
+ }
17
+ }
18
+
19
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
20
+ return ReactModuleInfoProvider {
21
+ val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
22
+ moduleInfos[PulseReactNativeOtelModule.NAME] = ReactModuleInfo(
23
+ PulseReactNativeOtelModule.NAME,
24
+ PulseReactNativeOtelModule.NAME,
25
+ false, // canOverrideExistingModule
26
+ false, // needsEagerInit
27
+ false, // isCxxModule
28
+ true // isTurboModule
29
+ )
30
+ moduleInfos
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,127 @@
1
+ package com.pulsereactnativeotel
2
+
3
+ import com.facebook.react.bridge.ReadableMap
4
+ import com.facebook.react.bridge.ReadableArray
5
+ import com.facebook.react.bridge.ReadableType
6
+ import com.pulse.android.sdk.PulseSDK
7
+ import io.opentelemetry.api.common.AttributeKey
8
+ import io.opentelemetry.api.trace.Span
9
+ import io.opentelemetry.api.trace.SpanKind
10
+ import io.opentelemetry.api.trace.StatusCode
11
+ import io.opentelemetry.api.trace.Tracer
12
+ import io.opentelemetry.context.Scope
13
+ import java.util.UUID
14
+ import java.util.concurrent.ConcurrentHashMap
15
+
16
+ object PulseReactNativeOtelTracer {
17
+
18
+ private val tracer: Tracer by lazy {
19
+ PulseSDK.INSTANCE.getOtelOrThrow()
20
+ .getOpenTelemetry()
21
+ .tracerProvider
22
+ .tracerBuilder(PulseOtelConstants.INSTRUMENTATION_SCOPE)
23
+ .build()
24
+ }
25
+
26
+ private val idToSpan = ConcurrentHashMap<String, Span>()
27
+ private val idToScope = ConcurrentHashMap<String, Scope>()
28
+
29
+ fun startSpan(name: String, attributes: ReadableMap?): String {
30
+ val span = tracer.spanBuilder(name)
31
+ .setSpanKind(SpanKind.INTERNAL)
32
+ .startSpan()
33
+
34
+ val scope = span.makeCurrent()
35
+
36
+ attributes?.applyTo(span)
37
+
38
+ val id = UUID.randomUUID().toString()
39
+ idToSpan[id] = span
40
+ idToScope[id] = scope
41
+
42
+ return id
43
+ }
44
+
45
+ fun addEvent(spanId: String, name: String, attributes: ReadableMap?) {
46
+ idToSpan[spanId]?.let { span ->
47
+ attributes?.applyTo(span)
48
+ span.addEvent(name)
49
+ }
50
+ }
51
+
52
+ fun setAttributes(spanId: String, attributes: ReadableMap?) {
53
+ idToSpan[spanId]?.let { span ->
54
+ attributes?.applyTo(span)
55
+ }
56
+ }
57
+
58
+ fun recordException(spanId: String, errorMessage: String, stackTrace: String?) {
59
+ idToSpan[spanId]?.let { span ->
60
+ val throwable = RuntimeException(errorMessage)
61
+ span.recordException(throwable)
62
+ span.setAttribute(
63
+ AttributeKey.stringKey(PulseOtelConstants.ATTR_ERROR_MESSAGE),
64
+ errorMessage
65
+ )
66
+
67
+ stackTrace?.takeIf { it.isNotEmpty() }?.let {
68
+ span.setAttribute(
69
+ AttributeKey.stringKey(PulseOtelConstants.ATTR_ERROR_STACK),
70
+ it
71
+ )
72
+ }
73
+ }
74
+ }
75
+
76
+ fun endSpan(spanId: String, statusCode: String?) {
77
+ idToSpan.remove(spanId)?.let { span ->
78
+ when (statusCode?.uppercase()) {
79
+ "OK" -> span.setStatus(StatusCode.OK)
80
+ "ERROR" -> span.setStatus(StatusCode.ERROR)
81
+ else -> span.setStatus(StatusCode.UNSET)
82
+ }
83
+ span.end()
84
+ }
85
+ idToScope.remove(spanId)?.close()
86
+ }
87
+
88
+ private fun ReadableMap.applyTo(span: Span) {
89
+ entryIterator.forEach { (key, value) ->
90
+ when (value) {
91
+ is String -> span.setAttribute(AttributeKey.stringKey(key), value)
92
+ is Boolean -> span.setAttribute(AttributeKey.booleanKey(key), value)
93
+ is Number -> span.setAttribute(AttributeKey.doubleKey(key), value.toDouble())
94
+ is ReadableArray -> {
95
+ applyArrayAttribute(span, key, value)
96
+ }
97
+ else -> {
98
+ span.setAttribute(AttributeKey.stringKey(key), value.toString())
99
+ }
100
+ }
101
+ }
102
+ }
103
+
104
+ private fun applyArrayAttribute(span: Span, key: String, array: ReadableArray) {
105
+ if (array.size() == 0) {
106
+ span.setAttribute(AttributeKey.stringArrayKey(key), emptyList())
107
+ return
108
+ }
109
+
110
+ when (array.getType(0)) {
111
+ ReadableType.String -> {
112
+ span.setAttribute(AttributeKey.stringArrayKey(key), array.toArrayList() as List<String>)
113
+ }
114
+ ReadableType.Number -> {
115
+ span.setAttribute(AttributeKey.doubleArrayKey(key), array.toArrayList() as List<Double>)
116
+ }
117
+ ReadableType.Boolean -> {
118
+ span.setAttribute(AttributeKey.booleanArrayKey(key), array.toArrayList() as List<Boolean>)
119
+ }
120
+ else -> {
121
+ span.setAttribute(AttributeKey.stringKey(key), array.toString())
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+
@@ -0,0 +1,5 @@
1
+ #import <PulseReactNativeOtelSpec/PulseReactNativeOtelSpec.h>
2
+
3
+ @interface PulseReactNativeOtel : NSObject <NativePulseReactNativeOtelSpec>
4
+
5
+ @end
@@ -0,0 +1,100 @@
1
+ #import "PulseReactNativeOtel.h"
2
+
3
+ @implementation PulseReactNativeOtel
4
+
5
+ RCT_EXPORT_MODULE()
6
+
7
+ + (BOOL)requiresMainQueueSetup
8
+ {
9
+ return NO;
10
+ }
11
+
12
+ - (instancetype)init
13
+ {
14
+ self = [super init];
15
+ if (self) {
16
+ static dispatch_once_t onceToken;
17
+ dispatch_once(&onceToken, ^{
18
+ NSLog(@"[Pulse] iOS support coming soon. All operations will be no-ops.");
19
+ });
20
+ }
21
+ return self;
22
+ }
23
+
24
+ // Check if native SDK is initialized (always false on iOS)
25
+ - (NSNumber *)isInitialized
26
+ {
27
+ return @NO;
28
+ }
29
+
30
+ // Track event (no-op)
31
+ - (NSNumber *)trackEvent:(NSString *)event
32
+ observedTimeMs:(double)observedTimeMs
33
+ properties:(NSDictionary *)properties
34
+ {
35
+ return @NO;
36
+ }
37
+
38
+ // Start span (returns no-op span ID)
39
+ - (NSString *)startSpan:(NSString *)name
40
+ attributes:(NSDictionary *)attributes
41
+ {
42
+ return [NSString stringWithFormat:@"noop-ios-%@", [[NSUUID UUID] UUIDString]];
43
+ }
44
+
45
+ // End span (no-op)
46
+ - (NSNumber *)endSpan:(NSString *)spanId
47
+ {
48
+ return @NO;
49
+ }
50
+
51
+ // Add span event (no-op)
52
+ - (NSNumber *)addSpanEvent:(NSString *)spanId
53
+ name:(NSString *)name
54
+ attributes:(NSDictionary *)attributes
55
+ {
56
+ return @NO;
57
+ }
58
+
59
+ // Set span attributes (no-op)
60
+ - (NSNumber *)setSpanAttributes:(NSString *)spanId
61
+ attributes:(NSDictionary *)attributes
62
+ {
63
+ return @NO;
64
+ }
65
+
66
+ // Record span exception (no-op)
67
+ - (NSNumber *)recordSpanException:(NSString *)spanId
68
+ errorMessage:(NSString *)errorMessage
69
+ stackTrace:(NSString *)stackTrace
70
+ {
71
+ return @NO;
72
+ }
73
+
74
+ - (nonnull NSNumber *)reportException:(nonnull NSString *)errorMessage observedTimeMs:(double)observedTimeMs stackTrace:(nonnull NSString *)stackTrace isFatal:(BOOL)isFatal errorType:(nonnull NSString *)errorType attributes:(nonnull NSDictionary *)attributes {
75
+ return @NO;
76
+ }
77
+
78
+
79
+ - (void)setUserId:(NSString * _Nullable)id {
80
+ // No op
81
+ }
82
+
83
+
84
+ - (void)setUserProperties:(nonnull NSDictionary *)properties {
85
+ // No op
86
+ }
87
+
88
+
89
+ - (void)setUserProperty:(nonnull NSString *)name value:(NSString * _Nullable)value {
90
+ // No op
91
+ }
92
+
93
+
94
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
95
+ (const facebook::react::ObjCTurboModule::InitParams &)params
96
+ {
97
+ return std::make_shared<facebook::react::NativePulseReactNativeOtelSpecJSI>(params);
98
+ }
99
+
100
+ @end
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ import { TurboModuleRegistry } from 'react-native';
4
+ export default TurboModuleRegistry.getEnforcing('PulseReactNativeOtel');
5
+ //# sourceMappingURL=NativePulseReactNativeOtel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativePulseReactNativeOtel.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAwDpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,sBAAsB,CAAC","ignoreList":[]}