@posthog/react-native-plugin 2.1.2 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -327,12 +327,12 @@ SOFTWARE.
327
327
 
328
328
  ---
329
329
 
330
- Some files in this codebase contain code from MCPCat/mcpcat-typescript-sdk.
330
+ Some files in this codebase contain code from agentcathq/agentcat-typescript-sdk (formerly MCPCat/mcpcat-typescript-sdk).
331
331
  In such cases it is explicitly stated in the file header. This license only applies to the relevant code in such cases.
332
332
 
333
333
  MIT License
334
334
 
335
- Copyright (c) 2025 MCPcat
335
+ Copyright (c) 2025 AgentCat, Inc. (formerly MCPcat)
336
336
 
337
337
  Permission is hereby granted, free of charge, to any person obtaining a copy
338
338
  of this software and associated documentation files (the "Software"), to deal
@@ -85,6 +85,6 @@ dependencies {
85
85
  //noinspection GradleDynamicVersion
86
86
  implementation "com.facebook.react:react-native:+"
87
87
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
88
- implementation "com.posthog:posthog-android:3.52.0"
88
+ implementation "com.posthog:posthog-android:3.53.6"
89
89
  }
90
90
 
@@ -7,9 +7,9 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule
7
7
  import com.facebook.react.bridge.ReactMethod
8
8
  import com.facebook.react.bridge.ReadableMap
9
9
  import com.facebook.react.bridge.UiThreadUtil
10
+ import com.facebook.react.common.JavascriptException
10
11
  import com.posthog.PostHog
11
12
  import com.posthog.PostHogConfig
12
- import com.posthog.PostHogEvent
13
13
  import com.posthog.android.PostHogAndroid
14
14
  import com.posthog.android.PostHogAndroidConfig
15
15
  import com.posthog.internal.PostHogPreferences
@@ -117,7 +117,7 @@ class PosthogReactNativePluginModule(
117
117
 
118
118
  // React Native rethrows fatal JS errors natively as JavascriptException.
119
119
  // The JS layer already captured them, so drop the native duplicate.
120
- addBeforeSend { event -> if (isReactNativeFatalJsError(event)) null else event }
120
+ errorTrackingConfig.ignoredExceptionTypes.add(JavascriptException::class.java)
121
121
 
122
122
  // Always apply the session replay configuration so that recording started later
123
123
  // (e.g. startRecording or a linked feature flag) uses the right mode and masking;
@@ -320,16 +320,6 @@ class PosthogReactNativePluginModule(
320
320
  key: String,
321
321
  ): Double? = runCatching { if (hasKey(map, key)) map?.getDouble(key) else null }.getOrNull()
322
322
 
323
- private fun isReactNativeFatalJsError(event: PostHogEvent): Boolean {
324
- if (event.event != "\$exception") return false
325
- val exceptionList = event.properties?.get("\$exception_list") as? List<*> ?: return false
326
- return exceptionList.any { item ->
327
- val exception = item as? Map<*, *> ?: return@any false
328
- exception["type"] == "JavascriptException" &&
329
- (exception["module"] as? String)?.startsWith("com.facebook.react") == true
330
- }
331
- }
332
-
333
323
  private fun logError(
334
324
  method: String,
335
325
  error: Throwable,
@@ -5,7 +5,18 @@ private func hedgeLog(_ message: String) {
5
5
  print("[PostHog] \(message)")
6
6
  }
7
7
 
8
- // Deduplication works on Android (both architectures) and iOS (old architecture only).
8
+ #if !os(iOS)
9
+ // Session replay is part of posthog-ios's iOS-only surface, so recording is a no-op on macOS.
10
+ // Log once so a caller isn't left wondering why recording "started" but nothing arrives.
11
+ private var didLogSessionReplayUnsupported = false
12
+ private func logSessionReplayUnsupportedOnMacOS() {
13
+ guard !didLogSessionReplayUnsupported else { return }
14
+ didLogSessionReplayUnsupported = true
15
+ hedgeLog("Session replay is not supported on macOS")
16
+ }
17
+ #endif
18
+
19
+ // Deduplication works on Android (both architectures), iOS (old architecture only), and macOS.
9
20
  // On the iOS new architecture, fatal JS exception events surface as a generic SIGABRT
10
21
  // crash event with no JS-error text in any field, so they currently cannot be filtered.
11
22
  private let fatalJsErrorMarkers = ["Unhandled JS Exception", "ExceptionsManager.reportException", "facebook::jsi::JSError"]
@@ -126,49 +137,53 @@ class PosthogReactNativePlugin: NSObject {
126
137
  isReactNativeFatalJsError(event) ? nil : event
127
138
  }
128
139
 
129
- if #available(iOS 15.0, *) {
130
- config.surveys = false
131
- }
140
+ // Surveys and session replay are iOS-only in posthog-ios, so the APIs below
141
+ // don't exist on macOS. macOS gets error tracking only.
142
+ #if os(iOS)
143
+ if #available(iOS 15.0, *) {
144
+ config.surveys = false
145
+ }
132
146
 
133
- // Always apply the session replay configuration so that recording started later
134
- // (e.g. startRecording or a linked feature flag) uses the right mode and masking;
135
- // sessionReplayEnabled only controls whether recording starts at setup.
136
- config.sessionReplay = sessionReplayEnabled
137
- config.sessionReplayConfig.screenshotMode = true
147
+ // Always apply the session replay configuration so that recording started later
148
+ // (e.g. startRecording or a linked feature flag) uses the right mode and masking;
149
+ // sessionReplayEnabled only controls whether recording starts at setup.
150
+ config.sessionReplay = sessionReplayEnabled
151
+ config.sessionReplayConfig.screenshotMode = true
138
152
 
139
- let maskAllTextInputs = sdkReplayConfig["maskAllTextInputs"] as? Bool ?? true
140
- config.sessionReplayConfig.maskAllTextInputs = maskAllTextInputs
153
+ let maskAllTextInputs = sdkReplayConfig["maskAllTextInputs"] as? Bool ?? true
154
+ config.sessionReplayConfig.maskAllTextInputs = maskAllTextInputs
141
155
 
142
- let maskAllImages = sdkReplayConfig["maskAllImages"] as? Bool ?? true
143
- config.sessionReplayConfig.maskAllImages = maskAllImages
156
+ let maskAllImages = sdkReplayConfig["maskAllImages"] as? Bool ?? true
157
+ config.sessionReplayConfig.maskAllImages = maskAllImages
144
158
 
145
- let maskAllSandboxedViews = sdkReplayConfig["maskAllSandboxedViews"] as? Bool ?? true
146
- config.sessionReplayConfig.maskAllSandboxedViews = maskAllSandboxedViews
159
+ let maskAllSandboxedViews = sdkReplayConfig["maskAllSandboxedViews"] as? Bool ?? true
160
+ config.sessionReplayConfig.maskAllSandboxedViews = maskAllSandboxedViews
147
161
 
148
- // read throttleDelayMs and use iOSdebouncerDelayMs as a fallback for back compatibility
149
- let throttleDelayMs =
150
- (sdkReplayConfig["throttleDelayMs"] as? Int)
151
- ?? (sdkReplayConfig["iOSdebouncerDelayMs"] as? Int)
152
- ?? 1000
162
+ // read throttleDelayMs and use iOSdebouncerDelayMs as a fallback for back compatibility
163
+ let throttleDelayMs =
164
+ (sdkReplayConfig["throttleDelayMs"] as? Int)
165
+ ?? (sdkReplayConfig["iOSdebouncerDelayMs"] as? Int)
166
+ ?? 1000
153
167
 
154
- let timeInterval: TimeInterval = Double(throttleDelayMs) / 1000.0
155
- config.sessionReplayConfig.throttleDelay = timeInterval
168
+ let timeInterval: TimeInterval = Double(throttleDelayMs) / 1000.0
169
+ config.sessionReplayConfig.throttleDelay = timeInterval
156
170
 
157
- let captureNetworkTelemetry = sdkReplayConfig["captureNetworkTelemetry"] as? Bool ?? true
158
- config.sessionReplayConfig.captureNetworkTelemetry = captureNetworkTelemetry
171
+ let captureNetworkTelemetry = sdkReplayConfig["captureNetworkTelemetry"] as? Bool ?? true
172
+ config.sessionReplayConfig.captureNetworkTelemetry = captureNetworkTelemetry
159
173
 
160
- let captureLog = sdkReplayConfig["captureLog"] as? Bool ?? true
161
- config.sessionReplayConfig.captureLogs = captureLog
174
+ let captureLog = sdkReplayConfig["captureLog"] as? Bool ?? true
175
+ config.sessionReplayConfig.captureLogs = captureLog
162
176
 
163
- config.sessionReplayConfig.sampleRate = sdkReplayConfig["sampleRate"] as? NSNumber
177
+ config.sessionReplayConfig.sampleRate = sdkReplayConfig["sampleRate"] as? NSNumber
164
178
 
165
- let screenshotModeBackgroundCapture = sdkReplayConfig["screenshotModeBackgroundCapture"] as? Bool ?? false
166
- config.sessionReplayConfig.screenshotModeBackgroundCapture = screenshotModeBackgroundCapture
179
+ let screenshotModeBackgroundCapture = sdkReplayConfig["screenshotModeBackgroundCapture"] as? Bool ?? false
180
+ config.sessionReplayConfig.screenshotModeBackgroundCapture = screenshotModeBackgroundCapture
167
181
 
168
- let endpoint = decideReplayConfig["endpoint"] as? String ?? ""
169
- if !endpoint.isEmpty {
170
- config.snapshotEndpoint = endpoint
171
- }
182
+ let endpoint = decideReplayConfig["endpoint"] as? String ?? ""
183
+ if !endpoint.isEmpty {
184
+ config.snapshotEndpoint = endpoint
185
+ }
186
+ #endif
172
187
 
173
188
  let distinctId = sdkOptions["distinctId"] as? String ?? ""
174
189
  let anonymousId = sdkOptions["anonymousId"] as? String ?? ""
@@ -221,8 +236,12 @@ class PosthogReactNativePlugin: NSObject {
221
236
 
222
237
  @objc(isEnabled:withRejecter:)
223
238
  func isEnabled(resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock) {
224
- let isEnabled = PostHogSDK.shared.isSessionReplayActive()
225
- resolve(isEnabled)
239
+ #if os(iOS)
240
+ resolve(PostHogSDK.shared.isSessionReplayActive())
241
+ #else
242
+ // Session replay is unsupported on macOS.
243
+ resolve(false)
244
+ #endif
226
245
  }
227
246
 
228
247
  @objc(endSession:withRejecter:)
@@ -261,13 +280,21 @@ class PosthogReactNativePlugin: NSObject {
261
280
  func startRecording(
262
281
  resumeCurrent: Bool, resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock
263
282
  ) {
264
- PostHogSDK.shared.startSessionRecording(resumeCurrent: resumeCurrent)
283
+ #if os(iOS)
284
+ PostHogSDK.shared.startSessionRecording(resumeCurrent: resumeCurrent)
285
+ #else
286
+ logSessionReplayUnsupportedOnMacOS()
287
+ #endif
265
288
  resolve(nil)
266
289
  }
267
290
 
268
291
  @objc(stopRecording:withRejecter:)
269
292
  func stopRecording(resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock) {
270
- PostHogSDK.shared.stopSessionRecording()
293
+ #if os(iOS)
294
+ PostHogSDK.shared.stopSessionRecording()
295
+ #else
296
+ logSessionReplayUnsupportedOnMacOS()
297
+ #endif
271
298
  resolve(nil)
272
299
  }
273
300
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/react-native-plugin",
3
- "version": "2.1.2",
3
+ "version": "2.2.1",
4
4
  "description": "PostHog React Native plugin for iOS and Android integrations",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/commonjs/index.js",
@@ -39,7 +39,8 @@
39
39
  "keywords": [
40
40
  "react-native",
41
41
  "ios",
42
- "android"
42
+ "android",
43
+ "macos"
43
44
  ],
44
45
  "repository": {
45
46
  "type": "git",
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
16
16
  s.license = package["license"]
17
17
  s.authors = package["author"]
18
18
 
19
- s.platforms = { :ios => min_ios_version_supported }
19
+ s.platforms = { :ios => min_ios_version_supported, :osx => '10.15' }
20
20
  s.source = { :git => "https://github.com/PostHog/posthog-js.git", :tag => "@posthog/react-native-plugin@#{s.version}" }
21
21
 
22
22
  s.source_files = "ios/**/*.{swift,h,hpp,m,mm,c,cpp}"
@@ -40,6 +40,7 @@ Pod::Spec.new do |s|
40
40
  s.dependency 'PostHog', "~> #{posthog_ios_version}"
41
41
  end
42
42
  s.ios.deployment_target = '13.0'
43
+ s.osx.deployment_target = '10.15'
43
44
  s.swift_versions = "5.3"
44
45
 
45
46