@posthog/react-native-plugin 0.0.1 → 2.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.
@@ -0,0 +1,317 @@
1
+ package com.posthogreactnativeplugin
2
+
3
+ import android.util.Log
4
+ import com.facebook.react.bridge.Promise
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
7
+ import com.facebook.react.bridge.ReactMethod
8
+ import com.facebook.react.bridge.ReadableMap
9
+ import com.facebook.react.bridge.UiThreadUtil
10
+ import com.posthog.PostHog
11
+ import com.posthog.PostHogConfig
12
+ import com.posthog.PostHogEvent
13
+ import com.posthog.android.PostHogAndroid
14
+ import com.posthog.android.PostHogAndroidConfig
15
+ import com.posthog.internal.PostHogPreferences
16
+ import com.posthog.internal.PostHogPreferences.Companion.ANONYMOUS_ID
17
+ import com.posthog.internal.PostHogPreferences.Companion.DISTINCT_ID
18
+ import com.posthog.internal.PostHogSessionManager
19
+ import java.util.UUID
20
+
21
+ class PosthogReactNativePluginModule(
22
+ reactContext: ReactApplicationContext,
23
+ ) : ReactContextBaseJavaModule(reactContext) {
24
+ override fun getName(): String = NAME
25
+
26
+ @ReactMethod
27
+ fun setup(
28
+ sessionId: String,
29
+ sdkOptions: ReadableMap,
30
+ pluginConfig: ReadableMap,
31
+ promise: Promise,
32
+ ) {
33
+ val sessionReplayConfig = getMap(pluginConfig, "sessionReplay")
34
+ val errorTrackingConfig = getMap(pluginConfig, "errorTracking")
35
+
36
+ setupNativeSdk(
37
+ method = "setup",
38
+ sessionId = sessionId,
39
+ sdkOptions = sdkOptions,
40
+ sessionReplayEnabled = getBoolean(sessionReplayConfig, "enabled", false),
41
+ sdkReplayConfig = getMap(sessionReplayConfig, "sdkReplayConfig"),
42
+ decideReplayConfig = getMap(sessionReplayConfig, "decideReplayConfig"),
43
+ nativeErrorTrackingAutocapture = getBoolean(errorTrackingConfig, "nativeAutocapture", false),
44
+ promise = promise,
45
+ )
46
+ }
47
+
48
+ @ReactMethod
49
+ fun start(
50
+ sessionId: String,
51
+ sdkOptions: ReadableMap,
52
+ sdkReplayConfig: ReadableMap,
53
+ decideReplayConfig: ReadableMap,
54
+ promise: Promise,
55
+ ) {
56
+ setupNativeSdk(
57
+ method = "start",
58
+ sessionId = sessionId,
59
+ sdkOptions = sdkOptions,
60
+ sessionReplayEnabled = true,
61
+ sdkReplayConfig = sdkReplayConfig,
62
+ decideReplayConfig = decideReplayConfig,
63
+ nativeErrorTrackingAutocapture = false,
64
+ promise = promise,
65
+ )
66
+ }
67
+
68
+ private fun setupNativeSdk(
69
+ method: String,
70
+ sessionId: String,
71
+ sdkOptions: ReadableMap,
72
+ sessionReplayEnabled: Boolean,
73
+ sdkReplayConfig: ReadableMap?,
74
+ decideReplayConfig: ReadableMap?,
75
+ nativeErrorTrackingAutocapture: Boolean,
76
+ promise: Promise,
77
+ ) {
78
+ val initRunnable =
79
+ Runnable {
80
+ try {
81
+ val uuid = UUID.fromString(sessionId)
82
+ PostHogSessionManager.setSessionId(uuid)
83
+
84
+ val context = this.reactApplicationContext
85
+ val apiKey = getString(sdkOptions, "apiKey", "")
86
+ val host = getString(sdkOptions, "host", PostHogConfig.DEFAULT_HOST)
87
+ val debugValue = getBoolean(sdkOptions, "debug", false)
88
+ val distinctId = getString(sdkOptions, "distinctId", "")
89
+ val anonymousId = getString(sdkOptions, "anonymousId", "")
90
+ val theSdkVersion = getString(sdkOptions, "sdkVersion", "")
91
+ val theFlushAt = getInt(sdkOptions, "flushAt", DEFAULT_FLUSH_AT)
92
+
93
+ val config =
94
+ PostHogAndroidConfig(apiKey, host).apply {
95
+ debug = debugValue
96
+ captureDeepLinks = false
97
+ captureApplicationLifecycleEvents = false
98
+ captureScreenViews = false
99
+ flushAt = theFlushAt
100
+ errorTrackingConfig.autoCapture = nativeErrorTrackingAutocapture
101
+
102
+ // React Native rethrows fatal JS errors natively as JavascriptException.
103
+ // The JS layer already captured them, so drop the native duplicate.
104
+ addBeforeSend { event -> if (isReactNativeFatalJsError(event)) null else event }
105
+
106
+ // Always apply the session replay configuration so that recording started later
107
+ // (e.g. startRecording or a linked feature flag) uses the right mode and masking;
108
+ // sessionReplayEnabled only controls whether recording starts at setup.
109
+ val maskAllTextInputs = getBoolean(sdkReplayConfig, "maskAllTextInputs", DEFAULT_MASK_ALL_TEXT_INPUTS)
110
+ val maskAllImages = getBoolean(sdkReplayConfig, "maskAllImages", DEFAULT_MASK_ALL_IMAGES)
111
+ val captureLog = getBoolean(sdkReplayConfig, "captureLog", DEFAULT_CAPTURE_LOG)
112
+
113
+ // read throttleDelayMs and use androidDebouncerDelayMs as a fallback for back compatibility
114
+ val throttleDelayMs =
115
+ when {
116
+ hasKey(sdkReplayConfig, "throttleDelayMs") -> getInt(sdkReplayConfig, "throttleDelayMs", DEFAULT_THROTTLE_DELAY_MS)
117
+ hasKey(sdkReplayConfig, "androidDebouncerDelayMs") -> getInt(sdkReplayConfig, "androidDebouncerDelayMs", DEFAULT_THROTTLE_DELAY_MS)
118
+ else -> DEFAULT_THROTTLE_DELAY_MS
119
+ }
120
+
121
+ sessionReplay = sessionReplayEnabled
122
+ sessionReplayConfig.screenshot = true
123
+ sessionReplayConfig.captureLogcat = captureLog
124
+ sessionReplayConfig.throttleDelayMs = throttleDelayMs.toLong()
125
+ sessionReplayConfig.maskAllImages = maskAllImages
126
+ sessionReplayConfig.maskAllTextInputs = maskAllTextInputs
127
+ sessionReplayConfig.sampleRate = getDoubleOrNull(sdkReplayConfig, "sampleRate")
128
+
129
+ val endpoint = getString(decideReplayConfig, "endpoint", "")
130
+ if (endpoint.isNotEmpty()) {
131
+ snapshotEndpoint = endpoint
132
+ }
133
+
134
+ if (theSdkVersion.isNotEmpty()) {
135
+ sdkName = "posthog-react-native"
136
+ sdkVersion = theSdkVersion
137
+ }
138
+ }
139
+ PostHogAndroid.setup(context, config)
140
+
141
+ setIdentify(config.cachePreferences, distinctId, anonymousId)
142
+ } catch (e: Throwable) {
143
+ logError(method, e)
144
+ } finally {
145
+ promise.resolve(null)
146
+ }
147
+ }
148
+
149
+ // forces the SDK to be initialized on the main thread
150
+ if (UiThreadUtil.isOnUiThread()) {
151
+ initRunnable.run()
152
+ } else {
153
+ UiThreadUtil.runOnUiThread(initRunnable)
154
+ }
155
+ }
156
+
157
+ @ReactMethod
158
+ fun startSession(
159
+ sessionId: String,
160
+ promise: Promise,
161
+ ) {
162
+ try {
163
+ val uuid = UUID.fromString(sessionId)
164
+ PostHogSessionManager.setSessionId(uuid)
165
+ PostHog.startSession()
166
+ } catch (e: Throwable) {
167
+ logError("startSession", e)
168
+ } finally {
169
+ promise.resolve(null)
170
+ }
171
+ }
172
+
173
+ @ReactMethod
174
+ fun isEnabled(promise: Promise) {
175
+ try {
176
+ promise.resolve(PostHog.isSessionReplayActive())
177
+ } catch (e: Throwable) {
178
+ logError("isEnabled", e)
179
+ promise.resolve(false)
180
+ }
181
+ }
182
+
183
+ @ReactMethod
184
+ fun endSession(promise: Promise) {
185
+ try {
186
+ PostHog.endSession()
187
+ } catch (e: Throwable) {
188
+ logError("endSession", e)
189
+ } finally {
190
+ promise.resolve(null)
191
+ }
192
+ }
193
+
194
+ @ReactMethod
195
+ fun identify(
196
+ distinctId: String,
197
+ anonymousId: String,
198
+ promise: Promise,
199
+ ) {
200
+ try {
201
+ setIdentify(PostHog.getConfig<PostHogConfig>()?.cachePreferences, distinctId, anonymousId)
202
+ } catch (e: Throwable) {
203
+ logError("identify", e)
204
+ } finally {
205
+ promise.resolve(null)
206
+ }
207
+ }
208
+
209
+ private fun setIdentify(
210
+ cachePreferences: PostHogPreferences?,
211
+ distinctId: String,
212
+ anonymousId: String,
213
+ ) {
214
+ cachePreferences?.let { preferences ->
215
+ if (anonymousId.isNotEmpty()) {
216
+ preferences.setValue(ANONYMOUS_ID, anonymousId)
217
+ }
218
+ if (distinctId.isNotEmpty()) {
219
+ preferences.setValue(DISTINCT_ID, distinctId)
220
+ }
221
+ }
222
+ }
223
+
224
+ @ReactMethod
225
+ fun startRecording(
226
+ resumeCurrent: Boolean,
227
+ promise: Promise,
228
+ ) {
229
+ try {
230
+ PostHog.startSessionReplay(resumeCurrent)
231
+ } catch (e: Throwable) {
232
+ logError("startRecording", e)
233
+ } finally {
234
+ promise.resolve(null)
235
+ }
236
+ }
237
+
238
+ @ReactMethod
239
+ fun stopRecording(promise: Promise) {
240
+ try {
241
+ PostHog.stopSessionReplay()
242
+ } catch (e: Throwable) {
243
+ logError("stopRecording", e)
244
+ } finally {
245
+ promise.resolve(null)
246
+ }
247
+ }
248
+
249
+ private fun getMap(
250
+ map: ReadableMap?,
251
+ key: String,
252
+ ): ReadableMap? =
253
+ runCatching {
254
+ if (map != null && map.hasKey(key) && !map.isNull(key)) {
255
+ map.getMap(key)
256
+ } else {
257
+ null
258
+ }
259
+ }.getOrNull()
260
+
261
+ private fun hasKey(
262
+ map: ReadableMap?,
263
+ key: String,
264
+ ): Boolean = runCatching { map != null && map.hasKey(key) && !map.isNull(key) }.getOrDefault(false)
265
+
266
+ private fun getBoolean(
267
+ map: ReadableMap?,
268
+ key: String,
269
+ default: Boolean,
270
+ ): Boolean = runCatching { if (hasKey(map, key)) map?.getBoolean(key) ?: default else default }.getOrDefault(default)
271
+
272
+ private fun getString(
273
+ map: ReadableMap?,
274
+ key: String,
275
+ default: String,
276
+ ): String = runCatching { if (hasKey(map, key)) map?.getString(key) ?: default else default }.getOrDefault(default)
277
+
278
+ private fun getInt(
279
+ map: ReadableMap?,
280
+ key: String,
281
+ default: Int,
282
+ ): Int = runCatching { if (hasKey(map, key)) map?.getInt(key) ?: default else default }.getOrDefault(default)
283
+
284
+ private fun getDoubleOrNull(
285
+ map: ReadableMap?,
286
+ key: String,
287
+ ): Double? = runCatching { if (hasKey(map, key)) map?.getDouble(key) else null }.getOrNull()
288
+
289
+ private fun isReactNativeFatalJsError(event: PostHogEvent): Boolean {
290
+ if (event.event != "\$exception") return false
291
+ val exceptionList = event.properties?.get("\$exception_list") as? List<*> ?: return false
292
+ return exceptionList.any { item ->
293
+ val exception = item as? Map<*, *> ?: return@any false
294
+ exception["type"] == "JavascriptException" &&
295
+ (exception["module"] as? String)?.startsWith("com.facebook.react") == true
296
+ }
297
+ }
298
+
299
+ private fun logError(
300
+ method: String,
301
+ error: Throwable,
302
+ ) {
303
+ Log.println(Log.ERROR, POSTHOG_TAG, "Method $method, error: $error")
304
+ }
305
+
306
+ companion object {
307
+ const val NAME = "PosthogReactNativePlugin"
308
+ const val POSTHOG_TAG = "PostHog"
309
+
310
+ // Default session replay configuration values
311
+ const val DEFAULT_MASK_ALL_TEXT_INPUTS = true
312
+ const val DEFAULT_MASK_ALL_IMAGES = true
313
+ const val DEFAULT_CAPTURE_LOG = true
314
+ const val DEFAULT_FLUSH_AT = 20
315
+ const val DEFAULT_THROTTLE_DELAY_MS = 1000
316
+ }
317
+ }
@@ -0,0 +1,13 @@
1
+ package com.posthogreactnativeplugin
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class PosthogReactNativePluginPackage : ReactPackage {
9
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
10
+ listOf(PosthogReactNativePluginModule(reactContext))
11
+
12
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> = emptyList()
13
+ }
@@ -0,0 +1,2 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTViewManager.h>
@@ -0,0 +1,45 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface RCT_EXTERN_MODULE(PosthogReactNativePlugin, NSObject)
4
+
5
+ RCT_EXTERN_METHOD(setup:(NSString)sessionId
6
+ withSdkOptions:(NSDictionary)sdkOptions
7
+ withPluginConfig:(NSDictionary)pluginConfig
8
+ withResolver:(RCTPromiseResolveBlock)resolve
9
+ withRejecter:(RCTPromiseRejectBlock)reject)
10
+
11
+ RCT_EXTERN_METHOD(start:(NSString)sessionId
12
+ withSdkOptions:(NSDictionary)sdkOptions
13
+ withSdkReplayConfig:(NSDictionary)sdkReplayConfig
14
+ withDecideReplayConfig:(NSDictionary)decideReplayConfig
15
+ withResolver:(RCTPromiseResolveBlock)resolve
16
+ withRejecter:(RCTPromiseRejectBlock)reject)
17
+
18
+ RCT_EXTERN_METHOD(startSession:(NSString)sessionId
19
+ withResolver:(RCTPromiseResolveBlock)resolve
20
+ withRejecter:(RCTPromiseRejectBlock)reject)
21
+
22
+ RCT_EXTERN_METHOD(isEnabled:(RCTPromiseResolveBlock)resolve
23
+ withRejecter:(RCTPromiseRejectBlock)reject)
24
+
25
+ RCT_EXTERN_METHOD(endSession:(RCTPromiseResolveBlock)resolve
26
+ withRejecter:(RCTPromiseRejectBlock)reject)
27
+
28
+ RCT_EXTERN_METHOD(identify:(NSString)distinctId
29
+ withAnonymousId:(NSString)anonymousId
30
+ withResolver:(RCTPromiseResolveBlock)resolve
31
+ withRejecter:(RCTPromiseRejectBlock)reject)
32
+
33
+ RCT_EXTERN_METHOD(startRecording:(BOOL)resumeCurrent
34
+ withResolver:(RCTPromiseResolveBlock)resolve
35
+ withRejecter:(RCTPromiseRejectBlock)reject)
36
+
37
+ RCT_EXTERN_METHOD(stopRecording:(RCTPromiseResolveBlock)resolve
38
+ withRejecter:(RCTPromiseRejectBlock)reject)
39
+
40
+ + (BOOL)requiresMainQueueSetup
41
+ {
42
+ return NO;
43
+ }
44
+
45
+ @end
@@ -0,0 +1,254 @@
1
+ import PostHog
2
+
3
+ /// Meant for internally logging PostHog related things
4
+ private func hedgeLog(_ message: String) {
5
+ print("[PostHog] \(message)")
6
+ }
7
+
8
+ // Deduplication works on Android (both architectures) and iOS (old architecture only).
9
+ // On the iOS new architecture, fatal JS exception events surface as a generic SIGABRT
10
+ // crash event with no JS-error text in any field, so they currently cannot be filtered.
11
+ private let fatalJsErrorMarkers = ["Unhandled JS Exception", "ExceptionsManager.reportException", "facebook::jsi::JSError"]
12
+
13
+ private func containsFatalJsErrorMarker(_ text: String?) -> Bool {
14
+ guard let text else { return false }
15
+ return fatalJsErrorMarkers.contains { text.contains($0) }
16
+ }
17
+
18
+ private func isReactNativeFatalJsError(_ event: PostHogEvent) -> Bool {
19
+ guard event.event == "$exception",
20
+ let exceptionList = event.properties["$exception_list"] as? [[String: Any]]
21
+ else { return false }
22
+ return exceptionList.contains { exception in
23
+ if containsFatalJsErrorMarker(exception["type"] as? String) {
24
+ return true
25
+ }
26
+ if containsFatalJsErrorMarker(exception["value"] as? String) {
27
+ return true
28
+ }
29
+ // New-architecture RN rethrows fatal JS errors as a C++ jsi::JSError (SIGABRT);
30
+ // the JS-error text only survives in the signal's crash-info message.
31
+ let mechanism = exception["mechanism"] as? [String: Any]
32
+ let meta = mechanism?["meta"] as? [String: Any]
33
+ let signal = meta?["signal"] as? [String: Any]
34
+ return containsFatalJsErrorMarker(signal?["crash_info_message"] as? String)
35
+ }
36
+ }
37
+
38
+ @objc(PosthogReactNativePlugin)
39
+ class PosthogReactNativePlugin: NSObject {
40
+ private var config: PostHogConfig?
41
+
42
+ @objc(setup:withSdkOptions:withPluginConfig:withResolver:withRejecter:)
43
+ func setup(
44
+ sessionId: String, sdkOptions: [String: Any], pluginConfig: [String: Any],
45
+ resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock
46
+ ) {
47
+ let sessionReplayConfig = pluginConfig["sessionReplay"] as? [String: Any] ?? [:]
48
+ let errorTrackingConfig = pluginConfig["errorTracking"] as? [String: Any] ?? [:]
49
+
50
+ setupNativeSdk(
51
+ method: "setup",
52
+ sessionId: sessionId,
53
+ sdkOptions: sdkOptions,
54
+ sessionReplayEnabled: sessionReplayConfig["enabled"] as? Bool ?? false,
55
+ sdkReplayConfig: sessionReplayConfig["sdkReplayConfig"] as? [String: Any] ?? [:],
56
+ decideReplayConfig: sessionReplayConfig["decideReplayConfig"] as? [String: Any] ?? [:],
57
+ nativeErrorTrackingAutocapture: errorTrackingConfig["nativeAutocapture"] as? Bool ?? false,
58
+ resolve: resolve
59
+ )
60
+ }
61
+
62
+ @objc(start:withSdkOptions:withSdkReplayConfig:withDecideReplayConfig:withResolver:withRejecter:)
63
+ func start(
64
+ sessionId: String, sdkOptions: [String: Any], sdkReplayConfig: [String: Any],
65
+ decideReplayConfig: [String: Any], resolve: RCTPromiseResolveBlock,
66
+ reject _: RCTPromiseRejectBlock
67
+ ) {
68
+ setupNativeSdk(
69
+ method: "start",
70
+ sessionId: sessionId,
71
+ sdkOptions: sdkOptions,
72
+ sessionReplayEnabled: true,
73
+ sdkReplayConfig: sdkReplayConfig,
74
+ decideReplayConfig: decideReplayConfig,
75
+ nativeErrorTrackingAutocapture: false,
76
+ resolve: resolve
77
+ )
78
+ }
79
+
80
+ private func setupNativeSdk(
81
+ method _: String,
82
+ sessionId: String,
83
+ sdkOptions: [String: Any],
84
+ sessionReplayEnabled: Bool,
85
+ sdkReplayConfig: [String: Any],
86
+ decideReplayConfig: [String: Any],
87
+ nativeErrorTrackingAutocapture: Bool,
88
+ resolve: RCTPromiseResolveBlock
89
+ ) {
90
+ if sessionId.isEmpty {
91
+ hedgeLog("Invalid empty sessionId provided.")
92
+ resolve(nil)
93
+ return
94
+ }
95
+
96
+ let projectToken =
97
+ (sdkOptions["projectToken"] as? String)
98
+ ?? (sdkOptions["apiKey"] as? String)
99
+ ?? ""
100
+ let host = sdkOptions["host"] as? String ?? PostHogConfig.defaultHost
101
+ let debug = sdkOptions["debug"] as? Bool ?? false
102
+
103
+ PostHogSessionManager.shared.setSessionId(sessionId)
104
+
105
+ let config = PostHogConfig(projectToken: projectToken, host: host)
106
+ config.captureApplicationLifecycleEvents = false
107
+ config.captureScreenViews = false
108
+ config.debug = debug
109
+ config.errorTrackingConfig.autoCapture = nativeErrorTrackingAutocapture
110
+
111
+ // React Native rethrows fatal JS errors natively (RCTFatalException / ExceptionsManager).
112
+ // The JS layer already captured them, so drop the native duplicate.
113
+ config.setBeforeSend { event in
114
+ isReactNativeFatalJsError(event) ? nil : event
115
+ }
116
+
117
+ if #available(iOS 15.0, *) {
118
+ config.surveys = false
119
+ }
120
+
121
+ // Always apply the session replay configuration so that recording started later
122
+ // (e.g. startRecording or a linked feature flag) uses the right mode and masking;
123
+ // sessionReplayEnabled only controls whether recording starts at setup.
124
+ config.sessionReplay = sessionReplayEnabled
125
+ config.sessionReplayConfig.screenshotMode = true
126
+
127
+ let maskAllTextInputs = sdkReplayConfig["maskAllTextInputs"] as? Bool ?? true
128
+ config.sessionReplayConfig.maskAllTextInputs = maskAllTextInputs
129
+
130
+ let maskAllImages = sdkReplayConfig["maskAllImages"] as? Bool ?? true
131
+ config.sessionReplayConfig.maskAllImages = maskAllImages
132
+
133
+ let maskAllSandboxedViews = sdkReplayConfig["maskAllSandboxedViews"] as? Bool ?? true
134
+ config.sessionReplayConfig.maskAllSandboxedViews = maskAllSandboxedViews
135
+
136
+ // read throttleDelayMs and use iOSdebouncerDelayMs as a fallback for back compatibility
137
+ let throttleDelayMs =
138
+ (sdkReplayConfig["throttleDelayMs"] as? Int)
139
+ ?? (sdkReplayConfig["iOSdebouncerDelayMs"] as? Int)
140
+ ?? 1000
141
+
142
+ let timeInterval: TimeInterval = Double(throttleDelayMs) / 1000.0
143
+ config.sessionReplayConfig.throttleDelay = timeInterval
144
+
145
+ let captureNetworkTelemetry = sdkReplayConfig["captureNetworkTelemetry"] as? Bool ?? true
146
+ config.sessionReplayConfig.captureNetworkTelemetry = captureNetworkTelemetry
147
+
148
+ let captureLog = sdkReplayConfig["captureLog"] as? Bool ?? true
149
+ config.sessionReplayConfig.captureLogs = captureLog
150
+
151
+ config.sessionReplayConfig.sampleRate = sdkReplayConfig["sampleRate"] as? NSNumber
152
+
153
+ let screenshotModeBackgroundCapture = sdkReplayConfig["screenshotModeBackgroundCapture"] as? Bool ?? false
154
+ config.sessionReplayConfig.screenshotModeBackgroundCapture = screenshotModeBackgroundCapture
155
+
156
+ let endpoint = decideReplayConfig["endpoint"] as? String ?? ""
157
+ if !endpoint.isEmpty {
158
+ config.snapshotEndpoint = endpoint
159
+ }
160
+
161
+ let distinctId = sdkOptions["distinctId"] as? String ?? ""
162
+ let anonymousId = sdkOptions["anonymousId"] as? String ?? ""
163
+
164
+ let sdkVersion = sdkOptions["sdkVersion"] as? String ?? ""
165
+
166
+ let flushAt = sdkOptions["flushAt"] as? Int ?? 20
167
+ config.flushAt = flushAt
168
+
169
+ if !sdkVersion.isEmpty {
170
+ postHogSdkName = "posthog-react-native"
171
+ postHogVersion = sdkVersion
172
+ }
173
+
174
+ PostHogSDK.shared.setup(config)
175
+
176
+ self.config = config
177
+
178
+ guard let storageManager = self.config?.storageManager else {
179
+ hedgeLog("Storage manager is not available in the config.")
180
+ resolve(nil)
181
+ return
182
+ }
183
+
184
+ setIdentify(storageManager, distinctId: distinctId, anonymousId: anonymousId)
185
+
186
+ resolve(nil)
187
+ }
188
+
189
+ @objc(startSession:withResolver:withRejecter:)
190
+ func startSession(
191
+ sessionId: String, resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock
192
+ ) {
193
+ if sessionId.isEmpty {
194
+ hedgeLog("Invalid empty sessionId provided.")
195
+ resolve(nil)
196
+ return
197
+ }
198
+ PostHogSessionManager.shared.setSessionId(sessionId)
199
+ PostHogSDK.shared.startSession()
200
+ resolve(nil)
201
+ }
202
+
203
+ @objc(isEnabled:withRejecter:)
204
+ func isEnabled(resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock) {
205
+ let isEnabled = PostHogSDK.shared.isSessionReplayActive()
206
+ resolve(isEnabled)
207
+ }
208
+
209
+ @objc(endSession:withRejecter:)
210
+ func endSession(resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock) {
211
+ PostHogSDK.shared.endSession()
212
+ resolve(nil)
213
+ }
214
+
215
+ @objc(identify:withAnonymousId:withResolver:withRejecter:)
216
+ func identify(
217
+ distinctId: String, anonymousId: String, resolve: RCTPromiseResolveBlock,
218
+ reject _: RCTPromiseRejectBlock
219
+ ) {
220
+ guard let storageManager = config?.storageManager else {
221
+ hedgeLog("Storage manager is not available in the config.")
222
+ resolve(nil)
223
+ return
224
+ }
225
+ setIdentify(storageManager, distinctId: distinctId, anonymousId: anonymousId)
226
+
227
+ resolve(nil)
228
+ }
229
+
230
+ private func setIdentify(
231
+ _ storageManager: PostHogStorageManager, distinctId: String, anonymousId: String
232
+ ) {
233
+ if !anonymousId.isEmpty {
234
+ storageManager.setAnonymousId(anonymousId)
235
+ }
236
+ if !distinctId.isEmpty {
237
+ storageManager.setDistinctId(distinctId)
238
+ }
239
+ }
240
+
241
+ @objc(startRecording:withResolver:withRejecter:)
242
+ func startRecording(
243
+ resumeCurrent: Bool, resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock
244
+ ) {
245
+ PostHogSDK.shared.startSessionRecording(resumeCurrent: resumeCurrent)
246
+ resolve(nil)
247
+ }
248
+
249
+ @objc(stopRecording:withRejecter:)
250
+ func stopRecording(resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock) {
251
+ PostHogSDK.shared.stopSessionRecording()
252
+ resolve(nil)
253
+ }
254
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ exports.endSession = endSession;
8
+ exports.identify = identify;
9
+ exports.isEnabled = isEnabled;
10
+ exports.setup = setup;
11
+ exports.start = start;
12
+ exports.startRecording = startRecording;
13
+ exports.startSession = startSession;
14
+ exports.stopRecording = stopRecording;
15
+ var _reactNative = require("react-native");
16
+ const LINKING_ERROR = `The package '@posthog/react-native-plugin' doesn't seem to be linked. Make sure: \n\n` + _reactNative.Platform.select({
17
+ ios: "- You have run 'pod install'\n",
18
+ default: ''
19
+ }) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
20
+ const PosthogReactNativePlugin = _reactNative.NativeModules.PosthogReactNativePlugin ? _reactNative.NativeModules.PosthogReactNativePlugin : new Proxy({}, {
21
+ get() {
22
+ throw new Error(LINKING_ERROR);
23
+ }
24
+ });
25
+ function setup(sessionId, sdkOptions, pluginConfig = {}) {
26
+ return PosthogReactNativePlugin.setup(sessionId, sdkOptions, pluginConfig);
27
+ }
28
+ function start(sessionId, sdkOptions, sdkReplayConfig, decideReplayConfig) {
29
+ return PosthogReactNativePlugin.start(sessionId, sdkOptions, sdkReplayConfig, decideReplayConfig);
30
+ }
31
+ function startSession(sessionId) {
32
+ return PosthogReactNativePlugin.startSession(sessionId);
33
+ }
34
+ function endSession() {
35
+ return PosthogReactNativePlugin.endSession();
36
+ }
37
+ function isEnabled() {
38
+ return PosthogReactNativePlugin.isEnabled();
39
+ }
40
+ function identify(distinctId, anonymousId) {
41
+ return PosthogReactNativePlugin.identify(distinctId, anonymousId);
42
+ }
43
+ function startRecording(resumeCurrent) {
44
+ return PosthogReactNativePlugin.startRecording(resumeCurrent);
45
+ }
46
+ function stopRecording() {
47
+ return PosthogReactNativePlugin.stopRecording();
48
+ }
49
+ const PostHogReactNativePlugin = {
50
+ setup,
51
+ start,
52
+ startSession,
53
+ endSession,
54
+ isEnabled,
55
+ identify,
56
+ startRecording,
57
+ stopRecording
58
+ };
59
+ var _default = exports.default = PostHogReactNativePlugin;
60
+ //# sourceMappingURL=index.js.map