@posthog/react-native-plugin 2.2.4 → 2.3.0

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.
@@ -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.54.0"
88
+ implementation "com.posthog:posthog-android:3.58.0"
89
89
  }
90
90
 
@@ -1,6 +1,8 @@
1
1
  package com.posthogreactnativeplugin
2
2
 
3
+ import android.content.Intent
3
4
  import android.util.Log
5
+ import com.facebook.react.bridge.Arguments
4
6
  import com.facebook.react.bridge.Promise
5
7
  import com.facebook.react.bridge.ReactApplicationContext
6
8
  import com.facebook.react.bridge.ReactContextBaseJavaModule
@@ -8,6 +10,7 @@ import com.facebook.react.bridge.ReactMethod
8
10
  import com.facebook.react.bridge.ReadableMap
9
11
  import com.facebook.react.bridge.UiThreadUtil
10
12
  import com.facebook.react.common.JavascriptException
13
+ import com.facebook.react.modules.core.DeviceEventManagerModule
11
14
  import com.posthog.PostHog
12
15
  import com.posthog.PostHogConfig
13
16
  import com.posthog.android.PostHogAndroid
@@ -42,6 +45,7 @@ class PosthogReactNativePluginModule(
42
45
  decideReplayConfig = getMap(sessionReplayConfig, "decideReplayConfig"),
43
46
  nativeErrorTrackingAutocapture = getBoolean(errorTrackingConfig, "nativeAutocapture", false),
44
47
  exceptionStepsConfig = getMap(errorTrackingConfig, "exceptionSteps"),
48
+ pushConfig = getMap(pluginConfig, "push"),
45
49
  promise = promise,
46
50
  )
47
51
  }
@@ -63,6 +67,7 @@ class PosthogReactNativePluginModule(
63
67
  decideReplayConfig = decideReplayConfig,
64
68
  nativeErrorTrackingAutocapture = false,
65
69
  exceptionStepsConfig = null,
70
+ pushConfig = null,
66
71
  promise = promise,
67
72
  )
68
73
  }
@@ -76,6 +81,7 @@ class PosthogReactNativePluginModule(
76
81
  decideReplayConfig: ReadableMap?,
77
82
  nativeErrorTrackingAutocapture: Boolean,
78
83
  exceptionStepsConfig: ReadableMap?,
84
+ pushConfig: ReadableMap?,
79
85
  promise: Promise,
80
86
  ) {
81
87
  val initRunnable =
@@ -92,6 +98,9 @@ class PosthogReactNativePluginModule(
92
98
  val anonymousId = getString(sdkOptions, "anonymousId", "")
93
99
  val theSdkVersion = getString(sdkOptions, "sdkVersion", "")
94
100
  val theFlushAt = getInt(sdkOptions, "flushAt", DEFAULT_FLUSH_AT)
101
+ val theOptOut = getBoolean(sdkOptions, "optOut", false)
102
+ // Default true: an older JS layer that never sends this keeps native's own fetch.
103
+ val thePreloadFeatureFlags = getBoolean(sdkOptions, "preloadFeatureFlags", true)
95
104
 
96
105
  // Forward custom headers (e.g. Authorization for a reverse proxy) so the native SDK
97
106
  // attaches them to the requests it sends directly (session replay, crash uploads).
@@ -104,6 +113,8 @@ class PosthogReactNativePluginModule(
104
113
  val config =
105
114
  PostHogAndroidConfig(apiKey, host).apply {
106
115
  debug = debugValue
116
+ optOut = theOptOut
117
+ preloadFeatureFlags = thePreloadFeatureFlags
107
118
  captureDeepLinks = false
108
119
  captureApplicationLifecycleEvents = false
109
120
  captureScreenViews = false
@@ -153,6 +164,25 @@ class PosthogReactNativePluginModule(
153
164
  snapshotEndpoint = endpoint
154
165
  }
155
166
 
167
+ // Only set when present: the legacy start() path predates push, and there the
168
+ // native defaults (both true) must win, matching posthog-android on its own.
169
+ if (hasKey(pushConfig, "capturePushNotificationSubscriptions")) {
170
+ capturePushNotificationSubscriptions =
171
+ getBoolean(pushConfig, "capturePushNotificationSubscriptions", true)
172
+ }
173
+ if (hasKey(pushConfig, "capturePushNotificationOpened")) {
174
+ capturePushNotificationOpened = getBoolean(pushConfig, "capturePushNotificationOpened", true)
175
+ }
176
+
177
+ // Installed only when JS asked for it: an uninvited bridging provider would
178
+ // change how the native SDK handles a 401 on the subscription call.
179
+ if (getBoolean(pushConfig, "pushIdentityProviderEnabled", false)) {
180
+ pushModule = this@PosthogReactNativePluginModule
181
+ pushIdentityProvider = { distinctId, appId, completion ->
182
+ requestPushIdentityToken(distinctId, appId, completion)
183
+ }
184
+ }
185
+
156
186
  if (theSdkVersion.isNotEmpty()) {
157
187
  sdkName = "posthog-react-native"
158
188
  sdkVersion = theSdkVersion
@@ -161,6 +191,8 @@ class PosthogReactNativePluginModule(
161
191
  PostHogAndroid.setup(context, config)
162
192
 
163
193
  setIdentify(config.cachePreferences, distinctId, anonymousId)
194
+
195
+ captureColdStartPushOpenIfNeeded(config)
164
196
  } catch (e: Throwable) {
165
197
  logError(method, e)
166
198
  } finally {
@@ -228,6 +260,50 @@ class PosthogReactNativePluginModule(
228
260
  }
229
261
  }
230
262
 
263
+ // Calls the native SDK rather than writing preferences like identify() does: reset() is what
264
+ // unregisters the logged-out user's push subscription and re-registers under the new identity.
265
+ @ReactMethod
266
+ fun reset(
267
+ distinctId: String,
268
+ anonymousId: String,
269
+ promise: Promise,
270
+ ) {
271
+ try {
272
+ PostHog.reset()
273
+ // Native reset() mints its own anonymous id; overwrite it with the JS one so the two
274
+ // SDKs stay on the same identity. Must run after reset(), which needs the pre-reset
275
+ // distinctId to know which subscription to unregister. Known gap (as on iOS): native's
276
+ // async push re-registration can read the identity before this write lands and register
277
+ // under a throwaway id; retryPending() converges it on the next flush.
278
+ setIdentify(PostHog.getConfig<PostHogConfig>()?.cachePreferences, distinctId, anonymousId)
279
+ } catch (e: Throwable) {
280
+ logError("reset", e)
281
+ } finally {
282
+ promise.resolve(null)
283
+ }
284
+ }
285
+
286
+ // Runtime consent changes must reach native: it persists its own opt-out flag and only reads
287
+ // the JS value at setup(), so a refreshed FCM token could otherwise auto-register after the
288
+ // user opted out. optIn() also resumes deferred push work on the next flush.
289
+ @ReactMethod
290
+ fun setOptOut(
291
+ optOut: Boolean,
292
+ promise: Promise,
293
+ ) {
294
+ try {
295
+ if (optOut) {
296
+ PostHog.optOut()
297
+ } else {
298
+ PostHog.optIn()
299
+ }
300
+ } catch (e: Throwable) {
301
+ logError("setOptOut", e)
302
+ } finally {
303
+ promise.resolve(null)
304
+ }
305
+ }
306
+
231
307
  private fun setIdentify(
232
308
  cachePreferences: PostHogPreferences?,
233
309
  distinctId: String,
@@ -333,15 +409,228 @@ class PosthogReactNativePluginModule(
333
409
  Log.println(Log.ERROR, POSTHOG_TAG, "Method $method, error: $error")
334
410
  }
335
411
 
412
+ // These reject instead of this module's usual swallow-and-resolve convention: a failed
413
+ // registration is a distinct signal, not a silent success. The JS layer's public methods
414
+ // never throw, so the rejection is what gives it something to log.
415
+ @ReactMethod
416
+ fun registerPushNotificationToken(
417
+ deviceToken: String?,
418
+ appId: String?,
419
+ promise: Promise,
420
+ ) {
421
+ try {
422
+ if (deviceToken.isNullOrBlank()) {
423
+ // A blank token is dropped silently by the native SDK, so surface it here
424
+ // instead of reporting false success.
425
+ promise.reject(
426
+ PUSH_ERROR_CODE,
427
+ "registerPushNotificationToken: deviceToken is blank; token not registered.",
428
+ )
429
+ return
430
+ }
431
+ val resolvedAppId = appId?.trim()?.takeIf { it.isNotEmpty() } ?: firebaseProjectId
432
+ if (resolvedAppId.isNullOrEmpty()) {
433
+ val message =
434
+ "registerPushNotificationToken: no appId provided and no Firebase project id " +
435
+ "could be resolved, skipping. Pass appId explicitly if this app does not use Firebase."
436
+ Log.w(POSTHOG_TAG, message)
437
+ promise.reject(PUSH_ERROR_CODE, message)
438
+ return
439
+ }
440
+ PostHog.registerPushNotificationToken(deviceToken, resolvedAppId)
441
+ promise.resolve(null)
442
+ } catch (e: Throwable) {
443
+ logError("registerPushNotificationToken", e)
444
+ promise.reject(PUSH_ERROR_CODE, e)
445
+ }
446
+ }
447
+
448
+ @ReactMethod
449
+ fun unregisterPushNotificationToken(promise: Promise) {
450
+ try {
451
+ PostHog.unregisterPushNotificationToken()
452
+ promise.resolve(null)
453
+ } catch (e: Throwable) {
454
+ logError("unregisterPushNotificationToken", e)
455
+ promise.reject(PUSH_ERROR_CODE, e)
456
+ }
457
+ }
458
+
459
+ @ReactMethod
460
+ fun capturePushNotificationOpened(
461
+ properties: ReadableMap,
462
+ promise: Promise,
463
+ ) {
464
+ try {
465
+ // subtitle is iOS-only; posthog-android's capturePushNotificationOpened has no
466
+ // subtitle parameter, so JS's value is dropped here.
467
+ val title = if (hasKey(properties, "title")) properties.getString("title") else null
468
+ val body = if (hasKey(properties, "body")) properties.getString("body") else null
469
+ val payload = if (hasKey(properties, "payload")) properties.getMap("payload")?.toHashMap() else null
470
+ val action = if (hasKey(properties, "action")) properties.getString("action") else null
471
+ PostHog.capturePushNotificationOpened(title, body, payload, action)
472
+ promise.resolve(null)
473
+ } catch (e: Throwable) {
474
+ logError("capturePushNotificationOpened", e)
475
+ promise.reject(PUSH_ERROR_CODE, e)
476
+ }
477
+ }
478
+
479
+ // posthog-android's open-capture integration registers ActivityLifecycleCallbacks during
480
+ // setup(), which the bridge reaches only after the launch Activity was created — so the
481
+ // cold-start tray tap it exists for is the one creation it can never observe here. Read
482
+ // the launch intent directly, then strip the marker so the integration (or a re-run)
483
+ // can't capture the same tap again from this intent object.
484
+ private fun captureColdStartPushOpenIfNeeded(config: PostHogAndroidConfig) {
485
+ if (!config.capturePushNotificationOpened) {
486
+ return
487
+ }
488
+ val intent = currentActivity?.intent ?: return
489
+ // A relaunch from recents redelivers the original tray intent to a fresh activity;
490
+ // capturing it would count a days-old tap as a new open.
491
+ if (intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY != 0) {
492
+ return
493
+ }
494
+ try {
495
+ intent.getStringExtra(GOOGLE_MESSAGE_ID) ?: return
496
+ // Unmarshalling extras throws BadParcelableException on a Parcelable class this
497
+ // classloader lacks; read before stripping the marker so a failed read leaves the
498
+ // intent as the native integration expects it.
499
+ val payload =
500
+ intent.extras?.let { bundle ->
501
+ bundle.keySet().associateWith { key ->
502
+ @Suppress("DEPRECATION")
503
+ bundle.get(key)
504
+ }
505
+ }
506
+ intent.removeExtra(GOOGLE_MESSAGE_ID)
507
+ PostHog.capturePushNotificationOpened(null, null, payload, null)
508
+ } catch (e: Throwable) {
509
+ logError("capturePushNotificationOpened", e)
510
+ }
511
+ }
512
+
513
+ @ReactMethod
514
+ fun providePushIdentityToken(
515
+ requestId: String?,
516
+ token: String?,
517
+ promise: Promise,
518
+ ) {
519
+ try {
520
+ if (requestId != null) {
521
+ pushIdentityCompletions.remove(requestId)?.invoke(token)
522
+ }
523
+ } catch (e: Throwable) {
524
+ logError("providePushIdentityToken", e)
525
+ } finally {
526
+ promise.resolve(null)
527
+ }
528
+ }
529
+
530
+ // Required by NativeEventEmitter on the old architecture; events are broadcast via
531
+ // RCTDeviceEventEmitter, so there is nothing to do here.
532
+ @ReactMethod
533
+ fun addListener(eventName: String?) = Unit
534
+
535
+ @ReactMethod
536
+ fun removeListeners(count: Int) = Unit
537
+
538
+ override fun invalidate() {
539
+ if (pushModule === this) {
540
+ // Decline mints fast after teardown instead of stalling the native 10s watchdog.
541
+ pushModule = null
542
+ }
543
+ super.invalidate()
544
+ }
545
+
336
546
  companion object {
337
547
  const val NAME = "PosthogReactNativePlugin"
338
548
  const val POSTHOG_TAG = "PostHog"
339
549
 
550
+ // FCM stamps this extra on the tray-tap launch intent; mirrors posthog-android's
551
+ // PostHogActivityLifecycleCallbackIntegration.
552
+ private const val GOOGLE_MESSAGE_ID = "google.message_id"
553
+
340
554
  // Default session replay configuration values
341
555
  const val DEFAULT_MASK_ALL_TEXT_INPUTS = true
342
556
  const val DEFAULT_MASK_ALL_IMAGES = true
343
557
  const val DEFAULT_CAPTURE_LOG = true
344
558
  const val DEFAULT_FLUSH_AT = 20
345
559
  const val DEFAULT_THROTTLE_DELAY_MS = 1000
560
+
561
+ private const val PUSH_ERROR_CODE = "PosthogReactNativePluginError"
562
+ private const val PUSH_IDENTITY_EVENT = "PostHogPushIdentityRequest"
563
+ private const val PUSH_IDENTITY_REPLY_TTL_MS = 15_000L
564
+
565
+ // Modules die on every bridge reload, so the provider closure resolves the live
566
+ // module through this static reference at call time, not a captured setup-time one.
567
+ @Volatile
568
+ private var pushModule: PosthogReactNativePluginModule? = null
569
+
570
+ private val pushIdentityCompletions = java.util.concurrent.ConcurrentHashMap<String, (String?) -> Unit>()
571
+
572
+ private fun requestPushIdentityToken(
573
+ distinctId: String,
574
+ appId: String,
575
+ completion: (String?) -> Unit,
576
+ ) {
577
+ val module = pushModule
578
+ val context = module?.reactApplicationContext
579
+ if (module == null || context == null || !context.hasActiveReactInstance()) {
580
+ declinePushIdentity(completion, "no React instance attached")
581
+ return
582
+ }
583
+ val requestId = UUID.randomUUID().toString()
584
+ pushIdentityCompletions[requestId] = completion
585
+ try {
586
+ val params =
587
+ Arguments.createMap().apply {
588
+ putString("requestId", requestId)
589
+ putString("distinctId", distinctId)
590
+ putString("appId", appId)
591
+ }
592
+ context
593
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
594
+ .emit(PUSH_IDENTITY_EVENT, params)
595
+ } catch (e: Throwable) {
596
+ pushIdentityCompletions.remove(requestId)
597
+ declinePushIdentity(completion, "failed to reach JS: ${e.message}")
598
+ return
599
+ }
600
+ // The native SDK's own 10s mint watchdog handles the fallback; this only drops the
601
+ // entry so a late JS reply is ignored and the completion doesn't leak.
602
+ UiThreadUtil.runOnUiThread({ pushIdentityCompletions.remove(requestId) }, PUSH_IDENTITY_REPLY_TTL_MS)
603
+ }
604
+
605
+ // A null identity token sends the request unauthenticated, which a project requiring
606
+ // identity verification rejects server-side. Log the reason so that failure is
607
+ // greppable and distinct from a host that deliberately returned null.
608
+ private fun declinePushIdentity(
609
+ completion: (String?) -> Unit,
610
+ reason: String,
611
+ ) {
612
+ Log.w(POSTHOG_TAG, "Push subscription will be sent unauthenticated: $reason")
613
+ completion(null)
614
+ }
615
+
616
+ @Volatile private var cachedFirebaseProjectId: String? = null
617
+
618
+ // No Firebase dependency here, so the project id is looked up reflectively and any
619
+ // failure (class missing, Firebase not initialized) is swallowed. Only a successful lookup
620
+ // is cached: getInstance() throws until Firebase initializes, so memoizing the null would
621
+ // strand every later token-refresh call with no resolvable appId for the process lifetime.
622
+ private val firebaseProjectId: String?
623
+ get() =
624
+ cachedFirebaseProjectId
625
+ ?: try {
626
+ val firebaseAppClass = Class.forName("com.google.firebase.FirebaseApp")
627
+ val firebaseApp = firebaseAppClass.getMethod("getInstance").invoke(null)
628
+ val options = firebaseAppClass.getMethod("getOptions").invoke(firebaseApp)
629
+ (options?.javaClass?.getMethod("getProjectId")?.invoke(options) as? String)?.also {
630
+ cachedFirebaseProjectId = it
631
+ }
632
+ } catch (e: Throwable) {
633
+ null
634
+ }
346
635
  }
347
636
  }
@@ -1,2 +1,3 @@
1
1
  #import <React/RCTBridgeModule.h>
2
2
  #import <React/RCTViewManager.h>
3
+ #import <React/RCTEventEmitter.h>
@@ -1,6 +1,7 @@
1
1
  #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTEventEmitter.h>
2
3
 
3
- @interface RCT_EXTERN_MODULE(PosthogReactNativePlugin, NSObject)
4
+ @interface RCT_EXTERN_MODULE(PosthogReactNativePlugin, RCTEventEmitter)
4
5
 
5
6
  RCT_EXTERN_METHOD(setup:(NSString)sessionId
6
7
  withSdkOptions:(NSDictionary)sdkOptions
@@ -42,6 +43,32 @@ RCT_EXTERN_METHOD(addExceptionStep:(NSString)message
42
43
  withResolver:(RCTPromiseResolveBlock)resolve
43
44
  withRejecter:(RCTPromiseRejectBlock)reject)
44
45
 
46
+ RCT_EXTERN_METHOD(reset:(NSString)distinctId
47
+ withAnonymousId:(NSString)anonymousId
48
+ withResolver:(RCTPromiseResolveBlock)resolve
49
+ withRejecter:(RCTPromiseRejectBlock)reject)
50
+
51
+ RCT_EXTERN_METHOD(registerPushNotificationToken:(NSString)deviceToken
52
+ withAppId:(NSString)appId
53
+ withResolver:(RCTPromiseResolveBlock)resolve
54
+ withRejecter:(RCTPromiseRejectBlock)reject)
55
+
56
+ RCT_EXTERN_METHOD(unregisterPushNotificationToken:(RCTPromiseResolveBlock)resolve
57
+ withRejecter:(RCTPromiseRejectBlock)reject)
58
+
59
+ RCT_EXTERN_METHOD(setOptOut:(BOOL)optOut
60
+ withResolver:(RCTPromiseResolveBlock)resolve
61
+ withRejecter:(RCTPromiseRejectBlock)reject)
62
+
63
+ RCT_EXTERN_METHOD(capturePushNotificationOpened:(NSDictionary)properties
64
+ withResolver:(RCTPromiseResolveBlock)resolve
65
+ withRejecter:(RCTPromiseRejectBlock)reject)
66
+
67
+ RCT_EXTERN_METHOD(providePushIdentityToken:(NSString)requestId
68
+ withToken:(NSString)token
69
+ withResolver:(RCTPromiseResolveBlock)resolve
70
+ withRejecter:(RCTPromiseRejectBlock)reject)
71
+
45
72
  + (BOOL)requiresMainQueueSetup
46
73
  {
47
74
  return NO;
@@ -46,10 +46,40 @@ private func isReactNativeFatalJsError(_ event: PostHogEvent) -> Bool {
46
46
  }
47
47
  }
48
48
 
49
+ // A nil identity token sends the request unauthenticated, which a project requiring
50
+ // identity verification rejects server-side. Log the reason so that failure is greppable
51
+ // and distinct from a host that deliberately returned nil.
52
+ private func declinePushIdentity(_ completion: (String?) -> Void, _ reason: String) {
53
+ hedgeLog("Push subscription will be sent unauthenticated: \(reason)")
54
+ completion(nil)
55
+ }
56
+
49
57
  @objc(PosthogReactNativePlugin)
50
- class PosthogReactNativePlugin: NSObject {
58
+ class PosthogReactNativePlugin: RCTEventEmitter {
51
59
  private var config: PostHogConfig?
52
60
 
61
+ private static let pushIdentityEvent = "PostHogPushIdentityRequest"
62
+
63
+ // This module dies on every bridge reload, so the provider closure resolves the live
64
+ // module through this static weak reference at call time, not a captured setup-time one.
65
+ private static weak var pushInstance: PosthogReactNativePlugin?
66
+
67
+ // Main-thread confined, like the rest of the identity-request bookkeeping below.
68
+ private var hasPushListeners = false
69
+ private var pushIdentityCompletions: [String: (String?) -> Void] = [:]
70
+
71
+ override func supportedEvents() -> [String]! {
72
+ [PosthogReactNativePlugin.pushIdentityEvent]
73
+ }
74
+
75
+ override func startObserving() {
76
+ DispatchQueue.main.async { self.hasPushListeners = true }
77
+ }
78
+
79
+ override func stopObserving() {
80
+ DispatchQueue.main.async { self.hasPushListeners = false }
81
+ }
82
+
53
83
  @objc(setup:withSdkOptions:withPluginConfig:withResolver:withRejecter:)
54
84
  func setup(
55
85
  sessionId: String, sdkOptions: [String: Any], pluginConfig: [String: Any],
@@ -68,6 +98,7 @@ class PosthogReactNativePlugin: NSObject {
68
98
  decideReplayConfig: sessionReplayConfig["decideReplayConfig"] as? [String: Any] ?? [:],
69
99
  nativeErrorTrackingAutocapture: errorTrackingConfig["nativeAutocapture"] as? Bool ?? false,
70
100
  exceptionStepsConfig: exceptionStepsConfig,
101
+ pushConfig: pluginConfig["push"] as? [String: Any] ?? [:],
71
102
  resolve: resolve
72
103
  )
73
104
  }
@@ -87,6 +118,7 @@ class PosthogReactNativePlugin: NSObject {
87
118
  decideReplayConfig: decideReplayConfig,
88
119
  nativeErrorTrackingAutocapture: false,
89
120
  exceptionStepsConfig: [:],
121
+ pushConfig: [:],
90
122
  resolve: resolve
91
123
  )
92
124
  }
@@ -100,6 +132,7 @@ class PosthogReactNativePlugin: NSObject {
100
132
  decideReplayConfig: [String: Any],
101
133
  nativeErrorTrackingAutocapture: Bool,
102
134
  exceptionStepsConfig: [String: Any],
135
+ pushConfig: [String: Any],
103
136
  resolve: RCTPromiseResolveBlock
104
137
  ) {
105
138
  if sessionId.isEmpty {
@@ -193,6 +226,11 @@ class PosthogReactNativePlugin: NSObject {
193
226
  let flushAt = sdkOptions["flushAt"] as? Int ?? 20
194
227
  config.flushAt = flushAt
195
228
 
229
+ config.optOut = sdkOptions["optOut"] as? Bool ?? false
230
+ // JS owns flags; it tells us when the native preload would be a duplicate. posthog-ios
231
+ // has no remoteConfig switch to mirror — it deprecated the option and always loads.
232
+ config.preloadFeatureFlags = sdkOptions["preloadFeatureFlags"] as? Bool ?? true
233
+
196
234
  // Forward custom headers (e.g. Authorization for a reverse proxy) so the native SDK
197
235
  // attaches them to the requests it sends directly (session replay, crash uploads).
198
236
  // Keep only string values so a stray non-string doesn't drop every header (matches Android).
@@ -205,6 +243,40 @@ class PosthogReactNativePlugin: NSObject {
205
243
  postHogVersion = sdkVersion
206
244
  }
207
245
 
246
+ // Only set when present: the legacy start() path predates push, and there the
247
+ // native defaults (both true) must win, matching posthog-ios on its own.
248
+ if let capturePushSubscriptions = pushConfig["capturePushNotificationSubscriptions"] as? Bool {
249
+ config.capturePushNotificationSubscriptions = capturePushSubscriptions
250
+ }
251
+ if let capturePushOpened = pushConfig["capturePushNotificationOpened"] as? Bool {
252
+ config.capturePushNotificationOpened = capturePushOpened
253
+ }
254
+
255
+ // Installed only when JS asked for it: an uninvited bridging provider would change
256
+ // how the native SDK handles a 401 on the subscription call.
257
+ if pushConfig["pushIdentityProviderEnabled"] as? Bool == true {
258
+ PosthogReactNativePlugin.pushInstance = self
259
+ config.pushIdentityProvider = { distinctId, appId, completion in
260
+ DispatchQueue.main.async {
261
+ guard let instance = PosthogReactNativePlugin.pushInstance, instance.hasPushListeners else {
262
+ declinePushIdentity(completion, "no JS listener attached")
263
+ return
264
+ }
265
+ let requestId = UUID().uuidString
266
+ instance.pushIdentityCompletions[requestId] = completion
267
+ instance.sendEvent(
268
+ withName: PosthogReactNativePlugin.pushIdentityEvent,
269
+ body: ["requestId": requestId, "distinctId": distinctId, "appId": appId]
270
+ )
271
+ // The native SDK's own 10s mint watchdog handles the fallback; this only
272
+ // drops the entry so a late JS reply is ignored and the closure doesn't leak.
273
+ DispatchQueue.main.asyncAfter(deadline: .now() + 15) { [weak instance] in
274
+ instance?.pushIdentityCompletions.removeValue(forKey: requestId)
275
+ }
276
+ }
277
+ }
278
+ }
279
+
208
280
  PostHogSDK.shared.setup(config)
209
281
 
210
282
  self.config = config
@@ -265,6 +337,23 @@ class PosthogReactNativePlugin: NSObject {
265
337
  resolve(nil)
266
338
  }
267
339
 
340
+ // Calls the native SDK rather than writing storage like identify() does: reset() is what
341
+ // unregisters the logged-out user's push subscription and re-registers under the new identity.
342
+ @objc(reset:withAnonymousId:withResolver:withRejecter:)
343
+ func reset(
344
+ distinctId: String, anonymousId: String, resolve: RCTPromiseResolveBlock,
345
+ reject _: RCTPromiseRejectBlock
346
+ ) {
347
+ PostHogSDK.shared.reset()
348
+ // Native reset() mints its own anonymous id; overwrite it with the JS one so the two SDKs
349
+ // stay on the same identity. Must run after reset(), which needs the pre-reset distinctId
350
+ // to know which subscription to unregister.
351
+ if let storageManager = config?.storageManager {
352
+ setIdentify(storageManager, distinctId: distinctId, anonymousId: anonymousId)
353
+ }
354
+ resolve(nil)
355
+ }
356
+
268
357
  private func setIdentify(
269
358
  _ storageManager: PostHogStorageManager, distinctId: String, anonymousId: String
270
359
  ) {
@@ -276,6 +365,21 @@ class PosthogReactNativePlugin: NSObject {
276
365
  }
277
366
  }
278
367
 
368
+ // Runtime consent changes must reach native: it persists its own opt-out flag and only
369
+ // reads the JS value at setup(), so a refreshed APNs token could otherwise auto-register
370
+ // after the user opted out. optIn() also reinstalls the integrations opt-out removed.
371
+ @objc(setOptOut:withResolver:withRejecter:)
372
+ func setOptOut(
373
+ optOut: Bool, resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock
374
+ ) {
375
+ if optOut {
376
+ PostHogSDK.shared.optOut()
377
+ } else {
378
+ PostHogSDK.shared.optIn()
379
+ }
380
+ resolve(nil)
381
+ }
382
+
279
383
  @objc(startRecording:withResolver:withRejecter:)
280
384
  func startRecording(
281
385
  resumeCurrent: Bool, resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock
@@ -306,4 +410,64 @@ class PosthogReactNativePlugin: NSObject {
306
410
  PostHogSDK.shared.addExceptionStep(message, properties: properties)
307
411
  resolve(nil)
308
412
  }
413
+
414
+ @objc(registerPushNotificationToken:withAppId:withResolver:withRejecter:)
415
+ func registerPushNotificationToken(
416
+ deviceToken: String, appId: String?, resolve: RCTPromiseResolveBlock,
417
+ reject: RCTPromiseRejectBlock
418
+ ) {
419
+ #if os(iOS)
420
+ // A blank token is dropped silently by the native SDK, so surface it here
421
+ // instead of reporting false success.
422
+ if deviceToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
423
+ reject("PosthogReactNativePluginError", "registerPushNotificationToken: deviceToken is blank; token not registered.", nil)
424
+ return
425
+ }
426
+ PostHogSDK.shared.registerPushNotificationToken(deviceToken, appId: appId)
427
+ resolve(nil)
428
+ #else
429
+ // posthog-ios push registration is iOS-only (the backend rejects the macos platform).
430
+ _ = reject
431
+ hedgeLog("registerPushNotificationToken is not supported on macOS; token not registered.")
432
+ resolve(nil)
433
+ #endif
434
+ }
435
+
436
+ @objc(unregisterPushNotificationToken:withRejecter:)
437
+ func unregisterPushNotificationToken(resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock) {
438
+ #if os(iOS)
439
+ PostHogSDK.shared.unregisterPushNotificationToken()
440
+ #else
441
+ hedgeLog("unregisterPushNotificationToken is not supported on macOS; nothing to unregister.")
442
+ #endif
443
+ resolve(nil)
444
+ }
445
+
446
+ @objc(capturePushNotificationOpened:withResolver:withRejecter:)
447
+ func capturePushNotificationOpened(
448
+ properties: [String: Any], resolve: RCTPromiseResolveBlock, reject _: RCTPromiseRejectBlock
449
+ ) {
450
+ PostHogSDK.shared.capturePushNotificationOpened(
451
+ title: properties["title"] as? String,
452
+ subtitle: properties["subtitle"] as? String,
453
+ body: properties["body"] as? String,
454
+ payload: properties["payload"] as? [String: Any],
455
+ action: properties["action"] as? String
456
+ )
457
+ resolve(nil)
458
+ }
459
+
460
+ @objc(providePushIdentityToken:withToken:withResolver:withRejecter:)
461
+ func providePushIdentityToken(
462
+ requestId: String, token: String?, resolve: RCTPromiseResolveBlock,
463
+ reject _: RCTPromiseRejectBlock
464
+ ) {
465
+ DispatchQueue.main.async { [weak self] in
466
+ guard let completion = self?.pushIdentityCompletions.removeValue(forKey: requestId) else {
467
+ return
468
+ }
469
+ completion(token)
470
+ }
471
+ resolve(nil)
472
+ }
309
473
  }
@@ -4,15 +4,21 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.addExceptionStep = addExceptionStep;
7
+ exports.capturePushNotificationOpened = capturePushNotificationOpened;
7
8
  exports.default = void 0;
8
9
  exports.endSession = endSession;
9
10
  exports.identify = identify;
10
11
  exports.isEnabled = isEnabled;
12
+ exports.registerPushNotificationToken = registerPushNotificationToken;
13
+ exports.reset = reset;
14
+ exports.setOptOut = setOptOut;
15
+ exports.setPushIdentityProvider = setPushIdentityProvider;
11
16
  exports.setup = setup;
12
17
  exports.start = start;
13
18
  exports.startRecording = startRecording;
14
19
  exports.startSession = startSession;
15
20
  exports.stopRecording = stopRecording;
21
+ exports.unregisterPushNotificationToken = unregisterPushNotificationToken;
16
22
  var _reactNative = require("react-native");
17
23
  const LINKING_ERROR = `The package '@posthog/react-native-plugin' doesn't seem to be linked. Make sure: \n\n` + _reactNative.Platform.select({
18
24
  ios: "- You have run 'pod install'\n",
@@ -41,6 +47,15 @@ function isEnabled() {
41
47
  function identify(distinctId, anonymousId) {
42
48
  return PosthogReactNativePlugin.identify(distinctId, anonymousId);
43
49
  }
50
+
51
+ /**
52
+ * Resets the native SDK's identity on logout. Unlike {@link identify}, this calls the native
53
+ * SDK's own `reset()`, which unregisters the logged-out user's push subscription and
54
+ * re-registers it under the new anonymous id.
55
+ */
56
+ function reset(distinctId, anonymousId) {
57
+ return PosthogReactNativePlugin.reset(distinctId, anonymousId);
58
+ }
44
59
  function startRecording(resumeCurrent) {
45
60
  return PosthogReactNativePlugin.startRecording(resumeCurrent);
46
61
  }
@@ -50,6 +65,63 @@ function stopRecording() {
50
65
  function addExceptionStep(message, properties) {
51
66
  return PosthogReactNativePlugin.addExceptionStep(message, properties ?? {});
52
67
  }
68
+ function registerPushNotificationToken(deviceToken, appId) {
69
+ return PosthogReactNativePlugin.registerPushNotificationToken(deviceToken, appId);
70
+ }
71
+ function unregisterPushNotificationToken() {
72
+ return PosthogReactNativePlugin.unregisterPushNotificationToken();
73
+ }
74
+
75
+ /**
76
+ * Propagates a runtime consent change to the native SDK, which otherwise only reads the JS
77
+ * opt-out flag at setup() and could auto-register a refreshed push token after optOut().
78
+ */
79
+ function setOptOut(optOut) {
80
+ return PosthogReactNativePlugin.setOptOut(optOut);
81
+ }
82
+ function capturePushNotificationOpened(properties) {
83
+ return PosthogReactNativePlugin.capturePushNotificationOpened(properties);
84
+ }
85
+
86
+ /**
87
+ * Mints a signed identity-verification token for a push subscription request.
88
+ * Return null to send the request without an identity token.
89
+ */
90
+
91
+ const PUSH_IDENTITY_EVENT = 'PostHogPushIdentityRequest';
92
+ let pushIdentitySubscription;
93
+
94
+ /**
95
+ * Installs the JS side of the push identity-provider bridge. The native SDK asks for a
96
+ * token via a `PostHogPushIdentityRequest` event; the reply is routed back with
97
+ * `providePushIdentityToken`, keyed by the request id so late replies are ignored.
98
+ *
99
+ * Install before setup() with `push.pushIdentityProviderEnabled` set, so the native
100
+ * config gets its bridging provider at SDK initialization. Any provider failure
101
+ * degrades to a null token — an unauthenticated request — never a stalled mint.
102
+ */
103
+ function setPushIdentityProvider(provider) {
104
+ pushIdentitySubscription?.remove();
105
+ // Via the proxy, not raw NativeModules: an unlinked module would build an emitter over
106
+ // undefined and throw a generic RN error inside the SDK's init try, taking replay down with it.
107
+ const emitter = new _reactNative.NativeEventEmitter(PosthogReactNativePlugin);
108
+ pushIdentitySubscription = emitter.addListener(PUSH_IDENTITY_EVENT, async request => {
109
+ let token = null;
110
+ try {
111
+ const minted = await provider(request.distinctId, request.appId);
112
+ token = typeof minted === 'string' ? minted : null;
113
+ } catch (e) {
114
+ // eslint-disable-next-line no-console
115
+ console.warn(`[PostHog] pushIdentityProvider threw: ${e}. Push subscription will be sent unauthenticated.`);
116
+ }
117
+ try {
118
+ await PosthogReactNativePlugin.providePushIdentityToken(request.requestId, token);
119
+ } catch (e) {
120
+ // eslint-disable-next-line no-console
121
+ console.warn(`[PostHog] Failed to deliver push identity token to native: ${e}`);
122
+ }
123
+ });
124
+ }
53
125
  const PostHogReactNativePlugin = {
54
126
  setup,
55
127
  start,
@@ -57,9 +129,15 @@ const PostHogReactNativePlugin = {
57
129
  endSession,
58
130
  isEnabled,
59
131
  identify,
132
+ reset,
60
133
  startRecording,
61
134
  stopRecording,
62
- addExceptionStep
135
+ addExceptionStep,
136
+ registerPushNotificationToken,
137
+ unregisterPushNotificationToken,
138
+ setOptOut,
139
+ capturePushNotificationOpened,
140
+ setPushIdentityProvider
63
141
  };
64
142
  var _default = exports.default = PostHogReactNativePlugin;
65
143
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["_reactNative","require","LINKING_ERROR","Platform","select","ios","default","PosthogReactNativePlugin","NativeModules","Proxy","get","Error","setup","sessionId","sdkOptions","pluginConfig","start","sdkReplayConfig","decideReplayConfig","startSession","endSession","isEnabled","identify","distinctId","anonymousId","startRecording","resumeCurrent","stopRecording","addExceptionStep","message","properties","PostHogReactNativePlugin","_default","exports"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;;;;;;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,MAAMC,aAAa,GACjB,uFAAuF,GACvFC,qBAAQ,CAACC,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,wBAAwB,GAAGC,0BAAa,CAACD,wBAAwB,GACnEC,0BAAa,CAACD,wBAAwB,GACtC,IAAIE,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACT,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAyBE,SAASU,KAAKA,CACnBC,SAAiB,EACjBC,UAAuC,EACvCC,YAA4C,GAAG,CAAC,CAAC,EAClC;EACf,OAAOR,wBAAwB,CAACK,KAAK,CAACC,SAAS,EAAEC,UAAU,EAAEC,YAAY,CAAC;AAC5E;AAEO,SAASC,KAAKA,CACnBH,SAAiB,EACjBC,UAAuC,EACvCG,eAA4C,EAC5CC,kBAA+C,EAChC;EACf,OAAOX,wBAAwB,CAACS,KAAK,CAACH,SAAS,EAAEC,UAAU,EAAEG,eAAe,EAAEC,kBAAkB,CAAC;AACnG;AAEO,SAASC,YAAYA,CAACN,SAAiB,EAAiB;EAC7D,OAAON,wBAAwB,CAACY,YAAY,CAACN,SAAS,CAAC;AACzD;AAEO,SAASO,UAAUA,CAAA,EAAkB;EAC1C,OAAOb,wBAAwB,CAACa,UAAU,CAAC,CAAC;AAC9C;AAEO,SAASC,SAASA,CAAA,EAAqB;EAC5C,OAAOd,wBAAwB,CAACc,SAAS,CAAC,CAAC;AAC7C;AAEO,SAASC,QAAQA,CAACC,UAAkB,EAAEC,WAAmB,EAAiB;EAC/E,OAAOjB,wBAAwB,CAACe,QAAQ,CAACC,UAAU,EAAEC,WAAW,CAAC;AACnE;AAEO,SAASC,cAAcA,CAACC,aAAsB,EAAiB;EACpE,OAAOnB,wBAAwB,CAACkB,cAAc,CAACC,aAAa,CAAC;AAC/D;AAEO,SAASC,aAAaA,CAAA,EAAkB;EAC7C,OAAOpB,wBAAwB,CAACoB,aAAa,CAAC,CAAC;AACjD;AAEO,SAASC,gBAAgBA,CAACC,OAAe,EAAEC,UAAwC,EAAiB;EACzG,OAAOvB,wBAAwB,CAACqB,gBAAgB,CAACC,OAAO,EAAEC,UAAU,IAAI,CAAC,CAAC,CAAC;AAC7E;AAkCA,MAAMC,wBAAwD,GAAG;EAC/DnB,KAAK;EACLI,KAAK;EACLG,YAAY;EACZC,UAAU;EACVC,SAAS;EACTC,QAAQ;EACRG,cAAc;EACdE,aAAa;EACbC;AACF,CAAC;AAAA,IAAAI,QAAA,GAAAC,OAAA,CAAA3B,OAAA,GAEcyB,wBAAwB","ignoreList":[]}
1
+ {"version":3,"names":["_reactNative","require","LINKING_ERROR","Platform","select","ios","default","PosthogReactNativePlugin","NativeModules","Proxy","get","Error","setup","sessionId","sdkOptions","pluginConfig","start","sdkReplayConfig","decideReplayConfig","startSession","endSession","isEnabled","identify","distinctId","anonymousId","reset","startRecording","resumeCurrent","stopRecording","addExceptionStep","message","properties","registerPushNotificationToken","deviceToken","appId","unregisterPushNotificationToken","setOptOut","optOut","capturePushNotificationOpened","PUSH_IDENTITY_EVENT","pushIdentitySubscription","setPushIdentityProvider","provider","remove","emitter","NativeEventEmitter","addListener","request","token","minted","e","console","warn","providePushIdentityToken","requestId","PostHogReactNativePlugin","_default","exports"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- import { NativeModules, Platform } from 'react-native';
3
+ import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
4
4
  const LINKING_ERROR = `The package '@posthog/react-native-plugin' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
5
5
  ios: "- You have run 'pod install'\n",
6
6
  default: ''
@@ -28,6 +28,15 @@ export function isEnabled() {
28
28
  export function identify(distinctId, anonymousId) {
29
29
  return PosthogReactNativePlugin.identify(distinctId, anonymousId);
30
30
  }
31
+
32
+ /**
33
+ * Resets the native SDK's identity on logout. Unlike {@link identify}, this calls the native
34
+ * SDK's own `reset()`, which unregisters the logged-out user's push subscription and
35
+ * re-registers it under the new anonymous id.
36
+ */
37
+ export function reset(distinctId, anonymousId) {
38
+ return PosthogReactNativePlugin.reset(distinctId, anonymousId);
39
+ }
31
40
  export function startRecording(resumeCurrent) {
32
41
  return PosthogReactNativePlugin.startRecording(resumeCurrent);
33
42
  }
@@ -37,6 +46,63 @@ export function stopRecording() {
37
46
  export function addExceptionStep(message, properties) {
38
47
  return PosthogReactNativePlugin.addExceptionStep(message, properties ?? {});
39
48
  }
49
+ export function registerPushNotificationToken(deviceToken, appId) {
50
+ return PosthogReactNativePlugin.registerPushNotificationToken(deviceToken, appId);
51
+ }
52
+ export function unregisterPushNotificationToken() {
53
+ return PosthogReactNativePlugin.unregisterPushNotificationToken();
54
+ }
55
+
56
+ /**
57
+ * Propagates a runtime consent change to the native SDK, which otherwise only reads the JS
58
+ * opt-out flag at setup() and could auto-register a refreshed push token after optOut().
59
+ */
60
+ export function setOptOut(optOut) {
61
+ return PosthogReactNativePlugin.setOptOut(optOut);
62
+ }
63
+ export function capturePushNotificationOpened(properties) {
64
+ return PosthogReactNativePlugin.capturePushNotificationOpened(properties);
65
+ }
66
+
67
+ /**
68
+ * Mints a signed identity-verification token for a push subscription request.
69
+ * Return null to send the request without an identity token.
70
+ */
71
+
72
+ const PUSH_IDENTITY_EVENT = 'PostHogPushIdentityRequest';
73
+ let pushIdentitySubscription;
74
+
75
+ /**
76
+ * Installs the JS side of the push identity-provider bridge. The native SDK asks for a
77
+ * token via a `PostHogPushIdentityRequest` event; the reply is routed back with
78
+ * `providePushIdentityToken`, keyed by the request id so late replies are ignored.
79
+ *
80
+ * Install before setup() with `push.pushIdentityProviderEnabled` set, so the native
81
+ * config gets its bridging provider at SDK initialization. Any provider failure
82
+ * degrades to a null token — an unauthenticated request — never a stalled mint.
83
+ */
84
+ export function setPushIdentityProvider(provider) {
85
+ pushIdentitySubscription?.remove();
86
+ // Via the proxy, not raw NativeModules: an unlinked module would build an emitter over
87
+ // undefined and throw a generic RN error inside the SDK's init try, taking replay down with it.
88
+ const emitter = new NativeEventEmitter(PosthogReactNativePlugin);
89
+ pushIdentitySubscription = emitter.addListener(PUSH_IDENTITY_EVENT, async request => {
90
+ let token = null;
91
+ try {
92
+ const minted = await provider(request.distinctId, request.appId);
93
+ token = typeof minted === 'string' ? minted : null;
94
+ } catch (e) {
95
+ // eslint-disable-next-line no-console
96
+ console.warn(`[PostHog] pushIdentityProvider threw: ${e}. Push subscription will be sent unauthenticated.`);
97
+ }
98
+ try {
99
+ await PosthogReactNativePlugin.providePushIdentityToken(request.requestId, token);
100
+ } catch (e) {
101
+ // eslint-disable-next-line no-console
102
+ console.warn(`[PostHog] Failed to deliver push identity token to native: ${e}`);
103
+ }
104
+ });
105
+ }
40
106
  const PostHogReactNativePlugin = {
41
107
  setup,
42
108
  start,
@@ -44,9 +110,15 @@ const PostHogReactNativePlugin = {
44
110
  endSession,
45
111
  isEnabled,
46
112
  identify,
113
+ reset,
47
114
  startRecording,
48
115
  stopRecording,
49
- addExceptionStep
116
+ addExceptionStep,
117
+ registerPushNotificationToken,
118
+ unregisterPushNotificationToken,
119
+ setOptOut,
120
+ capturePushNotificationOpened,
121
+ setPushIdentityProvider
50
122
  };
51
123
  export default PostHogReactNativePlugin;
52
124
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["NativeModules","Platform","LINKING_ERROR","select","ios","default","PosthogReactNativePlugin","Proxy","get","Error","setup","sessionId","sdkOptions","pluginConfig","start","sdkReplayConfig","decideReplayConfig","startSession","endSession","isEnabled","identify","distinctId","anonymousId","startRecording","resumeCurrent","stopRecording","addExceptionStep","message","properties","PostHogReactNativePlugin"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AAEtD,MAAMC,aAAa,GACjB,uFAAuF,GACvFD,QAAQ,CAACE,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,wBAAwB,GAAGN,aAAa,CAACM,wBAAwB,GACnEN,aAAa,CAACM,wBAAwB,GACtC,IAAIC,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACP,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAyBL,OAAO,SAASQ,KAAKA,CACnBC,SAAiB,EACjBC,UAAuC,EACvCC,YAA4C,GAAG,CAAC,CAAC,EAClC;EACf,OAAOP,wBAAwB,CAACI,KAAK,CAACC,SAAS,EAAEC,UAAU,EAAEC,YAAY,CAAC;AAC5E;AAEA,OAAO,SAASC,KAAKA,CACnBH,SAAiB,EACjBC,UAAuC,EACvCG,eAA4C,EAC5CC,kBAA+C,EAChC;EACf,OAAOV,wBAAwB,CAACQ,KAAK,CAACH,SAAS,EAAEC,UAAU,EAAEG,eAAe,EAAEC,kBAAkB,CAAC;AACnG;AAEA,OAAO,SAASC,YAAYA,CAACN,SAAiB,EAAiB;EAC7D,OAAOL,wBAAwB,CAACW,YAAY,CAACN,SAAS,CAAC;AACzD;AAEA,OAAO,SAASO,UAAUA,CAAA,EAAkB;EAC1C,OAAOZ,wBAAwB,CAACY,UAAU,CAAC,CAAC;AAC9C;AAEA,OAAO,SAASC,SAASA,CAAA,EAAqB;EAC5C,OAAOb,wBAAwB,CAACa,SAAS,CAAC,CAAC;AAC7C;AAEA,OAAO,SAASC,QAAQA,CAACC,UAAkB,EAAEC,WAAmB,EAAiB;EAC/E,OAAOhB,wBAAwB,CAACc,QAAQ,CAACC,UAAU,EAAEC,WAAW,CAAC;AACnE;AAEA,OAAO,SAASC,cAAcA,CAACC,aAAsB,EAAiB;EACpE,OAAOlB,wBAAwB,CAACiB,cAAc,CAACC,aAAa,CAAC;AAC/D;AAEA,OAAO,SAASC,aAAaA,CAAA,EAAkB;EAC7C,OAAOnB,wBAAwB,CAACmB,aAAa,CAAC,CAAC;AACjD;AAEA,OAAO,SAASC,gBAAgBA,CAACC,OAAe,EAAEC,UAAwC,EAAiB;EACzG,OAAOtB,wBAAwB,CAACoB,gBAAgB,CAACC,OAAO,EAAEC,UAAU,IAAI,CAAC,CAAC,CAAC;AAC7E;AAkCA,MAAMC,wBAAwD,GAAG;EAC/DnB,KAAK;EACLI,KAAK;EACLG,YAAY;EACZC,UAAU;EACVC,SAAS;EACTC,QAAQ;EACRG,cAAc;EACdE,aAAa;EACbC;AACF,CAAC;AAED,eAAeG,wBAAwB","ignoreList":[]}
1
+ {"version":3,"names":["NativeEventEmitter","NativeModules","Platform","LINKING_ERROR","select","ios","default","PosthogReactNativePlugin","Proxy","get","Error","setup","sessionId","sdkOptions","pluginConfig","start","sdkReplayConfig","decideReplayConfig","startSession","endSession","isEnabled","identify","distinctId","anonymousId","reset","startRecording","resumeCurrent","stopRecording","addExceptionStep","message","properties","registerPushNotificationToken","deviceToken","appId","unregisterPushNotificationToken","setOptOut","optOut","capturePushNotificationOpened","PUSH_IDENTITY_EVENT","pushIdentitySubscription","setPushIdentityProvider","provider","remove","emitter","addListener","request","token","minted","e","console","warn","providePushIdentityToken","requestId","PostHogReactNativePlugin"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -14,9 +14,21 @@ export interface PostHogReactNativePluginErrorTrackingConfig {
14
14
  nativeAutocapture?: boolean;
15
15
  exceptionSteps?: PostHogReactNativePluginExceptionStepsConfig;
16
16
  }
17
+ export interface PostHogReactNativePluginPushConfig {
18
+ capturePushNotificationSubscriptions?: boolean;
19
+ capturePushNotificationOpened?: boolean;
20
+ /**
21
+ * A provider callback can't cross the bridge, so this only tells native whether to
22
+ * install the bridging provider at all — installing one the host didn't ask for
23
+ * would change how the native SDK handles a 401 on the subscription call.
24
+ * Pair with {@link setPushIdentityProvider} before calling setup().
25
+ */
26
+ pushIdentityProviderEnabled?: boolean;
27
+ }
17
28
  export interface PostHogReactNativePluginConfig {
18
29
  sessionReplay?: PostHogReactNativePluginSessionReplayConfig;
19
30
  errorTracking?: PostHogReactNativePluginErrorTrackingConfig;
31
+ push?: PostHogReactNativePluginPushConfig;
20
32
  }
21
33
  export declare function setup(sessionId: string, sdkOptions: PostHogReactNativePluginMap, pluginConfig?: PostHogReactNativePluginConfig): Promise<void>;
22
34
  export declare function start(sessionId: string, sdkOptions: PostHogReactNativePluginMap, sdkReplayConfig: PostHogReactNativePluginMap, decideReplayConfig: PostHogReactNativePluginMap): Promise<void>;
@@ -24,9 +36,38 @@ export declare function startSession(sessionId: string): Promise<void>;
24
36
  export declare function endSession(): Promise<void>;
25
37
  export declare function isEnabled(): Promise<boolean>;
26
38
  export declare function identify(distinctId: string, anonymousId: string): Promise<void>;
39
+ /**
40
+ * Resets the native SDK's identity on logout. Unlike {@link identify}, this calls the native
41
+ * SDK's own `reset()`, which unregisters the logged-out user's push subscription and
42
+ * re-registers it under the new anonymous id.
43
+ */
44
+ export declare function reset(distinctId: string, anonymousId: string): Promise<void>;
27
45
  export declare function startRecording(resumeCurrent: boolean): Promise<void>;
28
46
  export declare function stopRecording(): Promise<void>;
29
47
  export declare function addExceptionStep(message: string, properties?: PostHogReactNativePluginMap): Promise<void>;
48
+ export declare function registerPushNotificationToken(deviceToken: string, appId: string | null): Promise<void>;
49
+ export declare function unregisterPushNotificationToken(): Promise<void>;
50
+ /**
51
+ * Propagates a runtime consent change to the native SDK, which otherwise only reads the JS
52
+ * opt-out flag at setup() and could auto-register a refreshed push token after optOut().
53
+ */
54
+ export declare function setOptOut(optOut: boolean): Promise<void>;
55
+ export declare function capturePushNotificationOpened(properties: PostHogReactNativePluginMap): Promise<void>;
56
+ /**
57
+ * Mints a signed identity-verification token for a push subscription request.
58
+ * Return null to send the request without an identity token.
59
+ */
60
+ export type PostHogPushIdentityProvider = (distinctId: string, appId: string) => Promise<string | null>;
61
+ /**
62
+ * Installs the JS side of the push identity-provider bridge. The native SDK asks for a
63
+ * token via a `PostHogPushIdentityRequest` event; the reply is routed back with
64
+ * `providePushIdentityToken`, keyed by the request id so late replies are ignored.
65
+ *
66
+ * Install before setup() with `push.pushIdentityProviderEnabled` set, so the native
67
+ * config gets its bridging provider at SDK initialization. Any provider failure
68
+ * degrades to a null token — an unauthenticated request — never a stalled mint.
69
+ */
70
+ export declare function setPushIdentityProvider(provider: PostHogPushIdentityProvider): void;
30
71
  export interface PostHogReactNativePluginModule {
31
72
  setup: (sessionId: string, sdkOptions: PostHogReactNativePluginMap, pluginConfig?: PostHogReactNativePluginConfig) => Promise<void>;
32
73
  /**
@@ -37,9 +78,15 @@ export interface PostHogReactNativePluginModule {
37
78
  endSession: () => Promise<void>;
38
79
  isEnabled: () => Promise<boolean>;
39
80
  identify: (distinctId: string, anonymousId: string) => Promise<void>;
81
+ reset: (distinctId: string, anonymousId: string) => Promise<void>;
40
82
  startRecording: (resumeCurrent: boolean) => Promise<void>;
41
83
  stopRecording: () => Promise<void>;
42
84
  addExceptionStep: (message: string, properties?: PostHogReactNativePluginMap) => Promise<void>;
85
+ registerPushNotificationToken: (deviceToken: string, appId: string | null) => Promise<void>;
86
+ unregisterPushNotificationToken: () => Promise<void>;
87
+ setOptOut: (optOut: boolean) => Promise<void>;
88
+ capturePushNotificationOpened: (properties: PostHogReactNativePluginMap) => Promise<void>;
89
+ setPushIdentityProvider: (provider: PostHogPushIdentityProvider) => void;
43
90
  }
44
91
  declare const PostHogReactNativePlugin: PostHogReactNativePluginModule;
45
92
  export default PostHogReactNativePlugin;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAoBA,MAAM,MAAM,2BAA2B,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,CAAA;AAEhE,MAAM,WAAW,2CAA2C;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,eAAe,CAAC,EAAE,2BAA2B,CAAA;IAC7C,kBAAkB,CAAC,EAAE,2BAA2B,CAAA;CACjD;AAED,MAAM,WAAW,4CAA4C;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,2CAA2C;IAC1D,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,cAAc,CAAC,EAAE,4CAA4C,CAAA;CAC9D;AAED,MAAM,WAAW,kCAAkC;IACjD,oCAAoC,CAAC,EAAE,OAAO,CAAA;IAC9C,6BAA6B,CAAC,EAAE,OAAO,CAAA;IACvC;;;;;OAKG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAA;CACtC;AAED,MAAM,WAAW,8BAA8B;IAC7C,aAAa,CAAC,EAAE,2CAA2C,CAAA;IAC3D,aAAa,CAAC,EAAE,2CAA2C,CAAA;IAC3D,IAAI,CAAC,EAAE,kCAAkC,CAAA;CAC1C;AAED,wBAAgB,KAAK,CACnB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,2BAA2B,EACvC,YAAY,GAAE,8BAAmC,GAChD,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,KAAK,CACnB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,2BAA2B,EACvC,eAAe,EAAE,2BAA2B,EAC5C,kBAAkB,EAAE,2BAA2B,GAC9C,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7D;AAED,wBAAgB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1C;AAED,wBAAgB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAE5C;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/E;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;AAED,wBAAgB,cAAc,CAAC,aAAa,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpE;AAED,wBAAgB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAE7C;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzG;AAED,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtG;AAED,wBAAgB,+BAA+B,IAAI,OAAO,CAAC,IAAI,CAAC,CAE/D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAExD;AAED,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpG;AAED;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;AAMvG;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,IAAI,CAwBnF;AAED,MAAM,WAAW,8BAA8B;IAC7C,KAAK,EAAE,CACL,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,2BAA2B,EACvC,YAAY,CAAC,EAAE,8BAA8B,KAC1C,OAAO,CAAC,IAAI,CAAC,CAAA;IAElB;;OAEG;IACH,KAAK,EAAE,CACL,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,2BAA2B,EACvC,eAAe,EAAE,2BAA2B,EAC5C,kBAAkB,EAAE,2BAA2B,KAC5C,OAAO,CAAC,IAAI,CAAC,CAAA;IAElB,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAElD,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAE/B,SAAS,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAEjC,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAEpE,KAAK,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAEjE,cAAc,EAAE,CAAC,aAAa,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAEzD,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAElC,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,2BAA2B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAE9F,6BAA6B,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAE3F,+BAA+B,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAEpD,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAE7C,6BAA6B,EAAE,CAAC,UAAU,EAAE,2BAA2B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAEzF,uBAAuB,EAAE,CAAC,QAAQ,EAAE,2BAA2B,KAAK,IAAI,CAAA;CACzE;AAED,QAAA,MAAM,wBAAwB,EAAE,8BAgB/B,CAAA;AAED,eAAe,wBAAwB,CAAA"}
@@ -14,9 +14,21 @@ export interface PostHogReactNativePluginErrorTrackingConfig {
14
14
  nativeAutocapture?: boolean;
15
15
  exceptionSteps?: PostHogReactNativePluginExceptionStepsConfig;
16
16
  }
17
+ export interface PostHogReactNativePluginPushConfig {
18
+ capturePushNotificationSubscriptions?: boolean;
19
+ capturePushNotificationOpened?: boolean;
20
+ /**
21
+ * A provider callback can't cross the bridge, so this only tells native whether to
22
+ * install the bridging provider at all — installing one the host didn't ask for
23
+ * would change how the native SDK handles a 401 on the subscription call.
24
+ * Pair with {@link setPushIdentityProvider} before calling setup().
25
+ */
26
+ pushIdentityProviderEnabled?: boolean;
27
+ }
17
28
  export interface PostHogReactNativePluginConfig {
18
29
  sessionReplay?: PostHogReactNativePluginSessionReplayConfig;
19
30
  errorTracking?: PostHogReactNativePluginErrorTrackingConfig;
31
+ push?: PostHogReactNativePluginPushConfig;
20
32
  }
21
33
  export declare function setup(sessionId: string, sdkOptions: PostHogReactNativePluginMap, pluginConfig?: PostHogReactNativePluginConfig): Promise<void>;
22
34
  export declare function start(sessionId: string, sdkOptions: PostHogReactNativePluginMap, sdkReplayConfig: PostHogReactNativePluginMap, decideReplayConfig: PostHogReactNativePluginMap): Promise<void>;
@@ -24,9 +36,38 @@ export declare function startSession(sessionId: string): Promise<void>;
24
36
  export declare function endSession(): Promise<void>;
25
37
  export declare function isEnabled(): Promise<boolean>;
26
38
  export declare function identify(distinctId: string, anonymousId: string): Promise<void>;
39
+ /**
40
+ * Resets the native SDK's identity on logout. Unlike {@link identify}, this calls the native
41
+ * SDK's own `reset()`, which unregisters the logged-out user's push subscription and
42
+ * re-registers it under the new anonymous id.
43
+ */
44
+ export declare function reset(distinctId: string, anonymousId: string): Promise<void>;
27
45
  export declare function startRecording(resumeCurrent: boolean): Promise<void>;
28
46
  export declare function stopRecording(): Promise<void>;
29
47
  export declare function addExceptionStep(message: string, properties?: PostHogReactNativePluginMap): Promise<void>;
48
+ export declare function registerPushNotificationToken(deviceToken: string, appId: string | null): Promise<void>;
49
+ export declare function unregisterPushNotificationToken(): Promise<void>;
50
+ /**
51
+ * Propagates a runtime consent change to the native SDK, which otherwise only reads the JS
52
+ * opt-out flag at setup() and could auto-register a refreshed push token after optOut().
53
+ */
54
+ export declare function setOptOut(optOut: boolean): Promise<void>;
55
+ export declare function capturePushNotificationOpened(properties: PostHogReactNativePluginMap): Promise<void>;
56
+ /**
57
+ * Mints a signed identity-verification token for a push subscription request.
58
+ * Return null to send the request without an identity token.
59
+ */
60
+ export type PostHogPushIdentityProvider = (distinctId: string, appId: string) => Promise<string | null>;
61
+ /**
62
+ * Installs the JS side of the push identity-provider bridge. The native SDK asks for a
63
+ * token via a `PostHogPushIdentityRequest` event; the reply is routed back with
64
+ * `providePushIdentityToken`, keyed by the request id so late replies are ignored.
65
+ *
66
+ * Install before setup() with `push.pushIdentityProviderEnabled` set, so the native
67
+ * config gets its bridging provider at SDK initialization. Any provider failure
68
+ * degrades to a null token — an unauthenticated request — never a stalled mint.
69
+ */
70
+ export declare function setPushIdentityProvider(provider: PostHogPushIdentityProvider): void;
30
71
  export interface PostHogReactNativePluginModule {
31
72
  setup: (sessionId: string, sdkOptions: PostHogReactNativePluginMap, pluginConfig?: PostHogReactNativePluginConfig) => Promise<void>;
32
73
  /**
@@ -37,9 +78,15 @@ export interface PostHogReactNativePluginModule {
37
78
  endSession: () => Promise<void>;
38
79
  isEnabled: () => Promise<boolean>;
39
80
  identify: (distinctId: string, anonymousId: string) => Promise<void>;
81
+ reset: (distinctId: string, anonymousId: string) => Promise<void>;
40
82
  startRecording: (resumeCurrent: boolean) => Promise<void>;
41
83
  stopRecording: () => Promise<void>;
42
84
  addExceptionStep: (message: string, properties?: PostHogReactNativePluginMap) => Promise<void>;
85
+ registerPushNotificationToken: (deviceToken: string, appId: string | null) => Promise<void>;
86
+ unregisterPushNotificationToken: () => Promise<void>;
87
+ setOptOut: (optOut: boolean) => Promise<void>;
88
+ capturePushNotificationOpened: (properties: PostHogReactNativePluginMap) => Promise<void>;
89
+ setPushIdentityProvider: (provider: PostHogPushIdentityProvider) => void;
43
90
  }
44
91
  declare const PostHogReactNativePlugin: PostHogReactNativePluginModule;
45
92
  export default PostHogReactNativePlugin;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAmBA,MAAM,MAAM,2BAA2B,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,CAAA;AAEhE,MAAM,WAAW,2CAA2C;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,eAAe,CAAC,EAAE,2BAA2B,CAAA;IAC7C,kBAAkB,CAAC,EAAE,2BAA2B,CAAA;CACjD;AAED,MAAM,WAAW,4CAA4C;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,2CAA2C;IAC1D,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,cAAc,CAAC,EAAE,4CAA4C,CAAA;CAC9D;AAED,MAAM,WAAW,8BAA8B;IAC7C,aAAa,CAAC,EAAE,2CAA2C,CAAA;IAC3D,aAAa,CAAC,EAAE,2CAA2C,CAAA;CAC5D;AAED,wBAAgB,KAAK,CACnB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,2BAA2B,EACvC,YAAY,GAAE,8BAAmC,GAChD,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,KAAK,CACnB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,2BAA2B,EACvC,eAAe,EAAE,2BAA2B,EAC5C,kBAAkB,EAAE,2BAA2B,GAC9C,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7D;AAED,wBAAgB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1C;AAED,wBAAgB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAE5C;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/E;AAED,wBAAgB,cAAc,CAAC,aAAa,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpE;AAED,wBAAgB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAE7C;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzG;AAED,MAAM,WAAW,8BAA8B;IAC7C,KAAK,EAAE,CACL,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,2BAA2B,EACvC,YAAY,CAAC,EAAE,8BAA8B,KAC1C,OAAO,CAAC,IAAI,CAAC,CAAA;IAElB;;OAEG;IACH,KAAK,EAAE,CACL,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,2BAA2B,EACvC,eAAe,EAAE,2BAA2B,EAC5C,kBAAkB,EAAE,2BAA2B,KAC5C,OAAO,CAAC,IAAI,CAAC,CAAA;IAElB,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAElD,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAE/B,SAAS,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAEjC,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAEpE,cAAc,EAAE,CAAC,aAAa,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAEzD,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAElC,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,2BAA2B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/F;AAED,QAAA,MAAM,wBAAwB,EAAE,8BAU/B,CAAA;AAED,eAAe,wBAAwB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAoBA,MAAM,MAAM,2BAA2B,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,CAAA;AAEhE,MAAM,WAAW,2CAA2C;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,eAAe,CAAC,EAAE,2BAA2B,CAAA;IAC7C,kBAAkB,CAAC,EAAE,2BAA2B,CAAA;CACjD;AAED,MAAM,WAAW,4CAA4C;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,2CAA2C;IAC1D,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,cAAc,CAAC,EAAE,4CAA4C,CAAA;CAC9D;AAED,MAAM,WAAW,kCAAkC;IACjD,oCAAoC,CAAC,EAAE,OAAO,CAAA;IAC9C,6BAA6B,CAAC,EAAE,OAAO,CAAA;IACvC;;;;;OAKG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAA;CACtC;AAED,MAAM,WAAW,8BAA8B;IAC7C,aAAa,CAAC,EAAE,2CAA2C,CAAA;IAC3D,aAAa,CAAC,EAAE,2CAA2C,CAAA;IAC3D,IAAI,CAAC,EAAE,kCAAkC,CAAA;CAC1C;AAED,wBAAgB,KAAK,CACnB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,2BAA2B,EACvC,YAAY,GAAE,8BAAmC,GAChD,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,KAAK,CACnB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,2BAA2B,EACvC,eAAe,EAAE,2BAA2B,EAC5C,kBAAkB,EAAE,2BAA2B,GAC9C,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7D;AAED,wBAAgB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1C;AAED,wBAAgB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAE5C;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/E;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;AAED,wBAAgB,cAAc,CAAC,aAAa,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpE;AAED,wBAAgB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAE7C;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzG;AAED,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtG;AAED,wBAAgB,+BAA+B,IAAI,OAAO,CAAC,IAAI,CAAC,CAE/D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAExD;AAED,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpG;AAED;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;AAMvG;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,IAAI,CAwBnF;AAED,MAAM,WAAW,8BAA8B;IAC7C,KAAK,EAAE,CACL,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,2BAA2B,EACvC,YAAY,CAAC,EAAE,8BAA8B,KAC1C,OAAO,CAAC,IAAI,CAAC,CAAA;IAElB;;OAEG;IACH,KAAK,EAAE,CACL,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,2BAA2B,EACvC,eAAe,EAAE,2BAA2B,EAC5C,kBAAkB,EAAE,2BAA2B,KAC5C,OAAO,CAAC,IAAI,CAAC,CAAA;IAElB,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAElD,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAE/B,SAAS,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAEjC,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAEpE,KAAK,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAEjE,cAAc,EAAE,CAAC,aAAa,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAEzD,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAElC,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,2BAA2B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAE9F,6BAA6B,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAE3F,+BAA+B,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAEpD,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAE7C,6BAA6B,EAAE,CAAC,UAAU,EAAE,2BAA2B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAEzF,uBAAuB,EAAE,CAAC,QAAQ,EAAE,2BAA2B,KAAK,IAAI,CAAA;CACzE;AAED,QAAA,MAAM,wBAAwB,EAAE,8BAgB/B,CAAA;AAED,eAAe,wBAAwB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/react-native-plugin",
3
- "version": "2.2.4",
3
+ "version": "2.3.0",
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",
@@ -69,7 +69,6 @@
69
69
  "react-native": "*"
70
70
  },
71
71
  "jest": {
72
- "preset": "react-native",
73
72
  "modulePathIgnorePatterns": [
74
73
  "<rootDir>/lib/"
75
74
  ],
@@ -6,7 +6,7 @@ folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1
6
6
  # Single source of truth for the posthog-ios native dependency version.
7
7
  # Used by both the SPM and CocoaPods resolution paths below; bump this
8
8
  # line when picking up a new posthog-ios release.
9
- posthog_ios_version = '3.69.0'
9
+ posthog_ios_version = '3.69.2'
10
10
 
11
11
  Pod::Spec.new do |s|
12
12
  s.name = "posthog-react-native-plugin"
package/src/index.tsx CHANGED
@@ -1,4 +1,5 @@
1
- import { NativeModules, Platform } from 'react-native'
1
+ import { NativeEventEmitter, NativeModules, Platform } from 'react-native'
2
+ import type { EmitterSubscription } from 'react-native'
2
3
 
3
4
  const LINKING_ERROR =
4
5
  `The package '@posthog/react-native-plugin' doesn't seem to be linked. Make sure: \n\n` +
@@ -35,9 +36,22 @@ export interface PostHogReactNativePluginErrorTrackingConfig {
35
36
  exceptionSteps?: PostHogReactNativePluginExceptionStepsConfig
36
37
  }
37
38
 
39
+ export interface PostHogReactNativePluginPushConfig {
40
+ capturePushNotificationSubscriptions?: boolean
41
+ capturePushNotificationOpened?: boolean
42
+ /**
43
+ * A provider callback can't cross the bridge, so this only tells native whether to
44
+ * install the bridging provider at all — installing one the host didn't ask for
45
+ * would change how the native SDK handles a 401 on the subscription call.
46
+ * Pair with {@link setPushIdentityProvider} before calling setup().
47
+ */
48
+ pushIdentityProviderEnabled?: boolean
49
+ }
50
+
38
51
  export interface PostHogReactNativePluginConfig {
39
52
  sessionReplay?: PostHogReactNativePluginSessionReplayConfig
40
53
  errorTracking?: PostHogReactNativePluginErrorTrackingConfig
54
+ push?: PostHogReactNativePluginPushConfig
41
55
  }
42
56
 
43
57
  export function setup(
@@ -73,6 +87,15 @@ export function identify(distinctId: string, anonymousId: string): Promise<void>
73
87
  return PosthogReactNativePlugin.identify(distinctId, anonymousId)
74
88
  }
75
89
 
90
+ /**
91
+ * Resets the native SDK's identity on logout. Unlike {@link identify}, this calls the native
92
+ * SDK's own `reset()`, which unregisters the logged-out user's push subscription and
93
+ * re-registers it under the new anonymous id.
94
+ */
95
+ export function reset(distinctId: string, anonymousId: string): Promise<void> {
96
+ return PosthogReactNativePlugin.reset(distinctId, anonymousId)
97
+ }
98
+
76
99
  export function startRecording(resumeCurrent: boolean): Promise<void> {
77
100
  return PosthogReactNativePlugin.startRecording(resumeCurrent)
78
101
  }
@@ -85,6 +108,71 @@ export function addExceptionStep(message: string, properties?: PostHogReactNativ
85
108
  return PosthogReactNativePlugin.addExceptionStep(message, properties ?? {})
86
109
  }
87
110
 
111
+ export function registerPushNotificationToken(deviceToken: string, appId: string | null): Promise<void> {
112
+ return PosthogReactNativePlugin.registerPushNotificationToken(deviceToken, appId)
113
+ }
114
+
115
+ export function unregisterPushNotificationToken(): Promise<void> {
116
+ return PosthogReactNativePlugin.unregisterPushNotificationToken()
117
+ }
118
+
119
+ /**
120
+ * Propagates a runtime consent change to the native SDK, which otherwise only reads the JS
121
+ * opt-out flag at setup() and could auto-register a refreshed push token after optOut().
122
+ */
123
+ export function setOptOut(optOut: boolean): Promise<void> {
124
+ return PosthogReactNativePlugin.setOptOut(optOut)
125
+ }
126
+
127
+ export function capturePushNotificationOpened(properties: PostHogReactNativePluginMap): Promise<void> {
128
+ return PosthogReactNativePlugin.capturePushNotificationOpened(properties)
129
+ }
130
+
131
+ /**
132
+ * Mints a signed identity-verification token for a push subscription request.
133
+ * Return null to send the request without an identity token.
134
+ */
135
+ export type PostHogPushIdentityProvider = (distinctId: string, appId: string) => Promise<string | null>
136
+
137
+ const PUSH_IDENTITY_EVENT = 'PostHogPushIdentityRequest'
138
+
139
+ let pushIdentitySubscription: EmitterSubscription | undefined
140
+
141
+ /**
142
+ * Installs the JS side of the push identity-provider bridge. The native SDK asks for a
143
+ * token via a `PostHogPushIdentityRequest` event; the reply is routed back with
144
+ * `providePushIdentityToken`, keyed by the request id so late replies are ignored.
145
+ *
146
+ * Install before setup() with `push.pushIdentityProviderEnabled` set, so the native
147
+ * config gets its bridging provider at SDK initialization. Any provider failure
148
+ * degrades to a null token — an unauthenticated request — never a stalled mint.
149
+ */
150
+ export function setPushIdentityProvider(provider: PostHogPushIdentityProvider): void {
151
+ pushIdentitySubscription?.remove()
152
+ // Via the proxy, not raw NativeModules: an unlinked module would build an emitter over
153
+ // undefined and throw a generic RN error inside the SDK's init try, taking replay down with it.
154
+ const emitter = new NativeEventEmitter(PosthogReactNativePlugin)
155
+ pushIdentitySubscription = emitter.addListener(
156
+ PUSH_IDENTITY_EVENT,
157
+ async (request: { requestId: string; distinctId: string; appId: string }) => {
158
+ let token: string | null = null
159
+ try {
160
+ const minted = await provider(request.distinctId, request.appId)
161
+ token = typeof minted === 'string' ? minted : null
162
+ } catch (e) {
163
+ // eslint-disable-next-line no-console
164
+ console.warn(`[PostHog] pushIdentityProvider threw: ${e}. Push subscription will be sent unauthenticated.`)
165
+ }
166
+ try {
167
+ await PosthogReactNativePlugin.providePushIdentityToken(request.requestId, token)
168
+ } catch (e) {
169
+ // eslint-disable-next-line no-console
170
+ console.warn(`[PostHog] Failed to deliver push identity token to native: ${e}`)
171
+ }
172
+ }
173
+ )
174
+ }
175
+
88
176
  export interface PostHogReactNativePluginModule {
89
177
  setup: (
90
178
  sessionId: string,
@@ -110,11 +198,23 @@ export interface PostHogReactNativePluginModule {
110
198
 
111
199
  identify: (distinctId: string, anonymousId: string) => Promise<void>
112
200
 
201
+ reset: (distinctId: string, anonymousId: string) => Promise<void>
202
+
113
203
  startRecording: (resumeCurrent: boolean) => Promise<void>
114
204
 
115
205
  stopRecording: () => Promise<void>
116
206
 
117
207
  addExceptionStep: (message: string, properties?: PostHogReactNativePluginMap) => Promise<void>
208
+
209
+ registerPushNotificationToken: (deviceToken: string, appId: string | null) => Promise<void>
210
+
211
+ unregisterPushNotificationToken: () => Promise<void>
212
+
213
+ setOptOut: (optOut: boolean) => Promise<void>
214
+
215
+ capturePushNotificationOpened: (properties: PostHogReactNativePluginMap) => Promise<void>
216
+
217
+ setPushIdentityProvider: (provider: PostHogPushIdentityProvider) => void
118
218
  }
119
219
 
120
220
  const PostHogReactNativePlugin: PostHogReactNativePluginModule = {
@@ -124,9 +224,15 @@ const PostHogReactNativePlugin: PostHogReactNativePluginModule = {
124
224
  endSession,
125
225
  isEnabled,
126
226
  identify,
227
+ reset,
127
228
  startRecording,
128
229
  stopRecording,
129
230
  addExceptionStep,
231
+ registerPushNotificationToken,
232
+ unregisterPushNotificationToken,
233
+ setOptOut,
234
+ capturePushNotificationOpened,
235
+ setPushIdentityProvider,
130
236
  }
131
237
 
132
238
  export default PostHogReactNativePlugin