@cxpinsight/react-native 0.18.0 → 0.18.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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.18.1
4
+
5
+ Needs one native rebuild after upgrading: the package now ships native code.
6
+
7
+ ### Added
8
+
9
+ - **Screenshots without host code.** The SDK carries its own native module
10
+ (`CXPScreenshot`, iOS and Android) and captures replay screenshots itself.
11
+ Before, screenshots existed only when the app passed `captureScreen`; an app
12
+ that did not recorded timelines with no pictures while the dashboard said
13
+ screenshots were on. Order: your `captureScreen` → the SDK's module →
14
+ `react-native-view-shot` if installed → none. Frames are JPEG, 720 px wide,
15
+ with text inputs masked while `recordMaskInputs` is on. Android windows with
16
+ `FLAG_SECURE` are skipped. The dashboard's Screenshots switch turns all of it off.
17
+ - **`session_start` says which build and wiring produced it**: `sdk_version`,
18
+ `screenshot_source`, `provider_mounted`, `navigation_source`, `tap_capture`.
19
+
20
+ ### Fixed
21
+
22
+ - **A session that starts by rollover knows its screen.** Coming back after the
23
+ inactivity timeout started a new session with no `page_view`, so every event
24
+ in it carried the previous session's screen and funnels never saw an entry.
25
+ The current screen is now re-announced (`session_resumed: true`) without
26
+ firing screen-view survey triggers. That `session_start` also carries
27
+ `is_first_visit` and the diagnostics above, like the one at launch.
28
+ - **Connectivity probes are not api_call events.** NetInfo's reachability check
29
+ (`generate_204`) and captive-portal checks are dropped from network capture.
30
+
3
31
  ## 0.18.0
4
32
 
5
33
  Requires `@cxpinsight/survey-spec@^0.9.0` — it calls resolveBranding,
@@ -0,0 +1,24 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ # The SDK's own native code: one module, CXPScreenshot, so replay screenshots
6
+ # work from installing the package alone — no captureScreen callback and no
7
+ # second native dependency in the host app.
8
+ Pod::Spec.new do |s|
9
+ s.name = 'CXPinsightReactNative'
10
+ s.version = package['version']
11
+ s.summary = package['description']
12
+ s.license = package['license']
13
+ s.authors = 'CXPinsight'
14
+ s.homepage = package['homepage']
15
+ s.platforms = { :ios => '13.4' }
16
+ s.source = { :git => 'https://github.com/SaifDalli2/CX_Platform.git', :tag => "react-native-v#{s.version}" }
17
+ s.source_files = 'ios/**/*.{h,m,mm}'
18
+
19
+ if respond_to?(:install_modules_dependencies, true)
20
+ install_modules_dependencies(s)
21
+ else
22
+ s.dependency 'React-Core'
23
+ end
24
+ end
package/README.md CHANGED
@@ -34,7 +34,6 @@ Taps on everything else — `TouchableHighlight`, `Button`, and any touchable in
34
34
 
35
35
  ```js
36
36
  import { CXPinsight } from '@cxpinsight/react-native';
37
- import { captureScreen } from 'react-native-view-shot';
38
37
 
39
38
  CXPinsight.init({
40
39
  applicationId: 'YOUR_APPLICATION_ID',
@@ -42,12 +41,18 @@ CXPinsight.init({
42
41
  apiBase: 'https://your-gateway.example.com',
43
42
 
44
43
  record: true, // session replay: screens, taps, scroll, inputs
45
- recordMaskInputs: true, // never store typed text
46
- captureScreen: () => // optional screenshots in the replay timeline
47
- captureScreen({ format: 'jpg', quality: 0.3, result: 'base64' }),
44
+ recordMaskInputs: true, // never store typed text; also masks inputs in screenshots
48
45
  });
49
46
  ```
50
47
 
48
+ Screenshots in the replay are taken by the SDK's own native module — no extra
49
+ package and no capture code. It links automatically; rebuild the app once after
50
+ installing (`npx expo prebuild` / `pod install`). Text inputs are covered in
51
+ every frame while `recordMaskInputs` is on, and an Android screen with
52
+ `FLAG_SECURE` is never captured. Turn screenshots off from the dashboard
53
+ (Recording → Screenshots). To use your own capturer instead, pass
54
+ `captureScreen: () => Promise<base64 jpeg>`; it always takes precedence.
55
+
51
56
  On a physical device `apiBase` must be reachable from the device — use your machine's LAN IP, not `localhost`. The SDK checks this at startup and logs a specific message if it looks wrong.
52
57
 
53
58
  ### 3. Report screen changes
@@ -215,7 +220,7 @@ as partial rather than absent.
215
220
  | `apiBase` | — | Your CXPinsight gateway URL. |
216
221
  | `record` | dashboard | Session replay. Omit to follow the dashboard toggle. |
217
222
  | `recordMaskInputs` | `true` | Never persist typed text. |
218
- | `captureScreen` | — | Screenshot provider; omit for a timeline-only replay. |
223
+ | `captureScreen` | built in | Your own screenshot provider. Omit it: the SDK captures screenshots itself. |
219
224
  | `captureNetwork` | `true` | Sanitised `api_call` events — method, host, path, status. Never bodies or headers. |
220
225
  | `requireConsent` | `false` | Hold all capture until `optIn()`. |
221
226
  | `autoCaptureTaps` | `true` | Capture taps on touch components the babel plugin cannot reach, including inside UI libraries. |
@@ -0,0 +1,33 @@
1
+ // The SDK's own native code: one module, CXPScreenshot (see
2
+ // src/main/java/com/cxpinsight/reactnative/CXPScreenshotModule.java).
3
+
4
+ def safeExtGet(prop, fallback) {
5
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
6
+ }
7
+
8
+ apply plugin: 'com.android.library'
9
+
10
+ android {
11
+ namespace "com.cxpinsight.reactnative"
12
+ compileSdkVersion safeExtGet('compileSdkVersion', 35)
13
+
14
+ defaultConfig {
15
+ minSdkVersion safeExtGet('minSdkVersion', 24)
16
+ targetSdkVersion safeExtGet('targetSdkVersion', 35)
17
+ }
18
+
19
+ compileOptions {
20
+ sourceCompatibility JavaVersion.VERSION_17
21
+ targetCompatibility JavaVersion.VERSION_17
22
+ }
23
+ }
24
+
25
+ repositories {
26
+ google()
27
+ mavenCentral()
28
+ }
29
+
30
+ dependencies {
31
+ // Resolved to the app's React Native version by the React Native Gradle plugin.
32
+ implementation "com.facebook.react:react-android"
33
+ }
@@ -0,0 +1 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" />
@@ -0,0 +1,134 @@
1
+ package com.cxpinsight.reactnative;
2
+
3
+ import android.app.Activity;
4
+ import android.graphics.Bitmap;
5
+ import android.graphics.Canvas;
6
+ import android.graphics.Paint;
7
+ import android.util.Base64;
8
+ import android.view.View;
9
+ import android.view.ViewGroup;
10
+ import android.view.WindowManager;
11
+ import android.widget.EditText;
12
+
13
+ import androidx.annotation.NonNull;
14
+
15
+ import com.facebook.react.bridge.Promise;
16
+ import com.facebook.react.bridge.ReactApplicationContext;
17
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
18
+ import com.facebook.react.bridge.ReactMethod;
19
+
20
+ import java.io.ByteArrayOutputStream;
21
+ import java.util.concurrent.ExecutorService;
22
+ import java.util.concurrent.Executors;
23
+
24
+ /**
25
+ * Replay screenshots taken by the SDK itself — the Android half of
26
+ * ios/CXPScreenshot.m. capture(quality, maxWidth, maskInputs) resolves a base64
27
+ * JPEG of the current activity's window, or null. Null means "no frame this
28
+ * time", never an error: a recording must not fail over a frame.
29
+ */
30
+ public class CXPScreenshotModule extends ReactContextBaseJavaModule {
31
+ static final String NAME = "CXPScreenshot";
32
+
33
+ private final ExecutorService encoder = Executors.newSingleThreadExecutor();
34
+
35
+ CXPScreenshotModule(ReactApplicationContext context) {
36
+ super(context);
37
+ }
38
+
39
+ @NonNull
40
+ @Override
41
+ public String getName() {
42
+ return NAME;
43
+ }
44
+
45
+ @ReactMethod
46
+ public void capture(final double quality, final double maxWidth, final boolean maskInputs, final Promise promise) {
47
+ final Activity activity = getReactApplicationContext().getCurrentActivity();
48
+ if (activity == null) {
49
+ promise.resolve(null);
50
+ return;
51
+ }
52
+ // FLAG_SECURE is the app saying "never capture this screen". The OS already
53
+ // blanks it from screenshots and recents; the SDK does the same.
54
+ if ((activity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
55
+ promise.resolve(null);
56
+ return;
57
+ }
58
+ activity.runOnUiThread(new Runnable() {
59
+ @Override
60
+ public void run() {
61
+ final Bitmap bitmap;
62
+ try {
63
+ bitmap = draw(activity.getWindow().getDecorView().getRootView(), maxWidth, maskInputs);
64
+ } catch (Throwable t) {
65
+ promise.resolve(null);
66
+ return;
67
+ }
68
+ if (bitmap == null) {
69
+ promise.resolve(null);
70
+ return;
71
+ }
72
+ encoder.execute(new Runnable() {
73
+ @Override
74
+ public void run() {
75
+ try {
76
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
77
+ int q = (int) Math.round((quality > 0 && quality <= 1 ? quality : 0.5) * 100);
78
+ bitmap.compress(Bitmap.CompressFormat.JPEG, q, out);
79
+ promise.resolve(Base64.encodeToString(out.toByteArray(), Base64.NO_WRAP));
80
+ } catch (Throwable t) {
81
+ promise.resolve(null);
82
+ } finally {
83
+ bitmap.recycle();
84
+ }
85
+ }
86
+ });
87
+ }
88
+ });
89
+ }
90
+
91
+ // Draws straight at the output width rather than full resolution and scaling
92
+ // after: fewer pixels, drawn once.
93
+ private static Bitmap draw(View root, double maxWidth, boolean maskInputs) {
94
+ int w = root.getWidth();
95
+ int h = root.getHeight();
96
+ if (w <= 0 || h <= 0) return null;
97
+ float scale = maxWidth > 0 ? Math.min(1f, (float) (maxWidth / w)) : 1f;
98
+ int ow = Math.max(1, Math.round(w * scale));
99
+ int oh = Math.max(1, Math.round(h * scale));
100
+
101
+ Bitmap bitmap = Bitmap.createBitmap(ow, oh, Bitmap.Config.ARGB_8888);
102
+ Canvas canvas = new Canvas(bitmap);
103
+ canvas.scale(scale, scale);
104
+ root.draw(canvas);
105
+
106
+ // Text inputs are covered, not photographed: what someone types is theirs.
107
+ if (maskInputs) {
108
+ Paint paint = new Paint();
109
+ paint.setColor(0xFF8C8C8C);
110
+ int[] rootAt = new int[2];
111
+ root.getLocationInWindow(rootAt);
112
+ maskInputs(root, canvas, paint, rootAt);
113
+ }
114
+ return bitmap;
115
+ }
116
+
117
+ private static void maskInputs(View view, Canvas canvas, Paint paint, int[] rootAt) {
118
+ if (view.getVisibility() != View.VISIBLE) return;
119
+ if (view instanceof EditText) {
120
+ int[] at = new int[2];
121
+ view.getLocationInWindow(at);
122
+ float x = at[0] - rootAt[0];
123
+ float y = at[1] - rootAt[1];
124
+ canvas.drawRect(x, y, x + view.getWidth(), y + view.getHeight(), paint);
125
+ return;
126
+ }
127
+ if (view instanceof ViewGroup) {
128
+ ViewGroup group = (ViewGroup) view;
129
+ for (int i = 0; i < group.getChildCount(); i++) {
130
+ maskInputs(group.getChildAt(i), canvas, paint, rootAt);
131
+ }
132
+ }
133
+ }
134
+ }
@@ -0,0 +1,25 @@
1
+ package com.cxpinsight.reactnative;
2
+
3
+ import androidx.annotation.NonNull;
4
+
5
+ import com.facebook.react.ReactPackage;
6
+ import com.facebook.react.bridge.NativeModule;
7
+ import com.facebook.react.bridge.ReactApplicationContext;
8
+ import com.facebook.react.uimanager.ViewManager;
9
+
10
+ import java.util.Collections;
11
+ import java.util.List;
12
+
13
+ public class CXPinsightPackage implements ReactPackage {
14
+ @NonNull
15
+ @Override
16
+ public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
17
+ return Collections.<NativeModule>singletonList(new CXPScreenshotModule(reactContext));
18
+ }
19
+
20
+ @NonNull
21
+ @Override
22
+ public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
23
+ return Collections.emptyList();
24
+ }
25
+ }
@@ -0,0 +1,99 @@
1
+ // Replay screenshots taken by the SDK itself.
2
+ //
3
+ // Screenshots used to exist only when the host app passed a captureScreen
4
+ // callback backed by a second native package. An app that followed the setup
5
+ // guide without it — or with it installed but not wired — recorded timelines
6
+ // with no pictures, while the dashboard said screenshots were on.
7
+ //
8
+ // So the SDK carries its own. One method, capture(quality, maxWidth,
9
+ // maskInputs), resolving a base64 JPEG of the key window or null. Null means
10
+ // "no frame this time", never an error: a recording must not fail over a frame.
11
+
12
+ #import <React/RCTBridgeModule.h>
13
+ #import <UIKit/UIKit.h>
14
+
15
+ @interface CXPScreenshot : NSObject <RCTBridgeModule>
16
+ @end
17
+
18
+ @implementation CXPScreenshot
19
+
20
+ RCT_EXPORT_MODULE(CXPScreenshot)
21
+
22
+ + (BOOL)requiresMainQueueSetup { return NO; }
23
+
24
+ // UIKit drawing belongs on the main queue. Encoding does not, and is moved off it below.
25
+ - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); }
26
+
27
+ static UIWindow *CXPKeyWindow(void)
28
+ {
29
+ for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
30
+ if (scene.activationState != UISceneActivationStateForegroundActive) continue;
31
+ if (![scene isKindOfClass:[UIWindowScene class]]) continue;
32
+ for (UIWindow *w in ((UIWindowScene *)scene).windows) {
33
+ if (w.isKeyWindow) return w;
34
+ }
35
+ }
36
+ return nil;
37
+ }
38
+
39
+ // Text inputs are covered, not photographed: what someone types is theirs, and
40
+ // a masked recording is the SDK's default everywhere else.
41
+ static void CXPCollectInputs(UIView *view, UIWindow *window, NSMutableArray<NSValue *> *out)
42
+ {
43
+ if (view.hidden || view.alpha < 0.01) return;
44
+ BOOL isInput = [view isKindOfClass:[UITextField class]] ||
45
+ ([view isKindOfClass:[UITextView class]] && ((UITextView *)view).isEditable);
46
+ if (isInput) {
47
+ [out addObject:[NSValue valueWithCGRect:[view convertRect:view.bounds toView:window]]];
48
+ return;
49
+ }
50
+ for (UIView *child in view.subviews) CXPCollectInputs(child, window, out);
51
+ }
52
+
53
+ RCT_EXPORT_METHOD(capture:(double)quality
54
+ maxWidth:(double)maxWidth
55
+ maskInputs:(BOOL)maskInputs
56
+ resolve:(RCTPromiseResolveBlock)resolve
57
+ reject:(__unused RCTPromiseRejectBlock)reject)
58
+ {
59
+ // Backgrounded: iOS shows a snapshot, not the app, and drawing now is wasted.
60
+ if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
61
+ resolve([NSNull null]);
62
+ return;
63
+ }
64
+ UIWindow *window = CXPKeyWindow();
65
+ CGSize size = window.bounds.size;
66
+ if (!window || size.width < 1 || size.height < 1) {
67
+ resolve([NSNull null]);
68
+ return;
69
+ }
70
+
71
+ // Render straight at the output width rather than at the screen's 3x and
72
+ // scaling down after: a third of the pixels, drawn once.
73
+ CGFloat scale = window.screen.scale;
74
+ if (maxWidth > 0) scale = MIN(scale, maxWidth / size.width);
75
+ UIGraphicsImageRendererFormat *format = [UIGraphicsImageRendererFormat preferredFormat];
76
+ format.scale = scale;
77
+ format.opaque = YES;
78
+
79
+ NSMutableArray<NSValue *> *masks = [NSMutableArray new];
80
+ if (maskInputs) CXPCollectInputs(window, window, masks);
81
+
82
+ UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size format:format];
83
+ UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext *ctx) {
84
+ [window drawViewHierarchyInRect:window.bounds afterScreenUpdates:NO];
85
+ if (masks.count) {
86
+ [[UIColor colorWithWhite:0.55 alpha:1] setFill];
87
+ for (NSValue *r in masks) UIRectFill(r.CGRectValue);
88
+ }
89
+ }];
90
+
91
+ double q = quality > 0 && quality <= 1 ? quality : 0.5;
92
+ dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
93
+ NSData *jpeg = UIImageJPEGRepresentation(image, q);
94
+ if (!jpeg) { resolve([NSNull null]); return; }
95
+ resolve([jpeg base64EncodedStringWithOptions:0]);
96
+ });
97
+ }
98
+
99
+ @end
@@ -16,6 +16,9 @@ declare class CXPinsightSDK {
16
16
  /** The field currently focused, so blur can report dwell + changed. */
17
17
  private fieldVisit;
18
18
  private currentScreen;
19
+ private screenshotsOff;
20
+ private screenshotSource;
21
+ private integration;
19
22
  private previousScreen;
20
23
  private engagementStartTs;
21
24
  private engagementAccumMs;
@@ -62,6 +65,13 @@ declare class CXPinsightSDK {
62
65
  private applyServerSdkConfig;
63
66
  /** Roll the recording + engagement clock onto a freshly-started session. */
64
67
  private onSessionRollover;
68
+ /** Which build, and how it is wired — so a gap in a session can be read, not guessed. */
69
+ private sessionDiagnostics;
70
+ /** @internal Called by CXPinsightProvider so session_start can report how screens are tracked. */
71
+ _noteIntegration(i: Partial<{
72
+ provider: boolean;
73
+ navigation: 'ref' | 'screen_prop' | 'none';
74
+ }>): void;
65
75
  /** Emit everything buffered before init() resolved, oldest first. */
66
76
  private flushPending;
67
77
  /** Wire native signals → typed analytics events (shared taxonomy). */
@@ -76,6 +86,12 @@ declare class CXPinsightSDK {
76
86
  track(eventName: string, data?: Record<string, unknown>, eventType?: string, teeToRecording?: boolean,
77
87
  /** Internal: preserves the original time of a call buffered before init(). */
78
88
  eventTimestamp?: string): void;
89
+ /**
90
+ * The screenshot capturer for a recording that is starting now: the host's
91
+ * captureScreen, else the SDK's own (nativeScreenshot.ts), unless the
92
+ * dashboard turned screenshots off.
93
+ */
94
+ private screenshotProvider;
79
95
  /** Track a screen view (triggers remote page_view rules) */
80
96
  trackScreen(screenName: string, data?: Record<string, unknown>): void;
81
97
  /** Identify the current user */
@@ -1 +1 @@
1
- {"version":3,"file":"CXPinsight.d.ts","sourceRoot":"","sources":["../src/CXPinsight.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CXPinsight.d.ts","sourceRoot":"","sources":["../src/CXPinsight.ts"],"names":[],"mappings":"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"}
package/lib/CXPinsight.js CHANGED
@@ -53,6 +53,8 @@ const interactionObserver_1 = require("./instrumented/interactionObserver");
53
53
  const lifecycle_1 = require("./core/lifecycle");
54
54
  const networkCapture_1 = require("./core/networkCapture");
55
55
  const scrollCapture_1 = require("./core/scrollCapture");
56
+ const version_1 = require("./version");
57
+ const nativeScreenshot_1 = require("./core/nativeScreenshot");
56
58
  const autoTapCapture_1 = require("./core/autoTapCapture");
57
59
  const consent_1 = require("./core/consent");
58
60
  // Canonical eventType per event name — keeps mobile events aligned with the shared
@@ -90,6 +92,12 @@ class CXPinsightSDK {
90
92
  this.fieldVisit = null;
91
93
  // Current + previous screen, so click/scroll/screen_view events carry screenName.
92
94
  this.currentScreen = null;
95
+ // Set by the dashboard's recording.screenshots === false; read by screenshotProvider().
96
+ this.screenshotsOff = false;
97
+ // Where replay screenshots come from this run — reported on session_start.
98
+ this.screenshotSource = 'none';
99
+ // How the host wired the SDK, as far as the SDK can see — reported on session_start.
100
+ this.integration = { provider: false, navigation: 'none' };
93
101
  this.previousScreen = null;
94
102
  // Engagement clock — active foreground time this session (paused while backgrounded),
95
103
  // attached to every event as engagementTimeMs (parity with web's time-on-page).
@@ -130,9 +138,10 @@ class CXPinsightSDK {
130
138
  if (!config.applicationId) {
131
139
  throw new Error('[CXPinsight] init requires config.applicationId');
132
140
  }
133
- // Screenshots are deliberately NOT forced on: they need the host's own
134
- // captureScreen callback (react-native-view-shot), which a flag cannot
135
- // synthesise. If it was passed, recording uses it either way.
141
+ this.screenshotsOff = false;
142
+ // Screenshots need no flag: the SDK takes them itself (nativeScreenshot.ts)
143
+ // unless the host passed its own captureScreen or the dashboard turned
144
+ // them off.
136
145
  //
137
146
  // captureEverything says WHAT to capture within a session. It deliberately
138
147
  // does not touch recording.sampleRate, which says HOW MANY sessions to
@@ -273,7 +282,7 @@ class CXPinsightSDK {
273
282
  applicationId: config.applicationId,
274
283
  sampleRate: config.recordingSampleRate,
275
284
  maskInputs: config.recordMaskInputs,
276
- captureScreen: config.captureScreen,
285
+ captureScreen: this.screenshotProvider(),
277
286
  // The screen the user is ON when this recording begins. Without it a
278
287
  // recording that starts mid-session opens with screen null and stays
279
288
  // nameless until the next navigation — which may never come.
@@ -333,6 +342,7 @@ class CXPinsightSDK {
333
342
  this.track('session_start', {
334
343
  sessionId: (0, session_1.getSessionId)(),
335
344
  is_first_visit: isFirstVisit,
345
+ ...this.sessionDiagnostics(),
336
346
  }, 'navigation', false);
337
347
  }
338
348
  // Replay anything the app tracked while init() was still resolving. After
@@ -495,8 +505,12 @@ class CXPinsightSDK {
495
505
  // controlled nothing, because screenshots depended solely on whether the
496
506
  // app passed a captureScreen callback. Turning it off now actually stops
497
507
  // them, without the app having to ship a new build.
508
+ //
509
+ // Off also has to stop the SDK's OWN capture (nativeScreenshot.ts), which
510
+ // needs no callback — clearing captureScreen alone would leave it running.
498
511
  if (s.recording?.screenshots === false) {
499
512
  config.captureScreen = undefined;
513
+ this.screenshotsOff = true;
500
514
  }
501
515
  // Bodies ride WITH network capture rather than needing a second switch: an
502
516
  // api_call that says a request happened but not what it carried answers
@@ -536,7 +550,7 @@ class CXPinsightSDK {
536
550
  applicationId: this.config.applicationId,
537
551
  sampleRate: this.config.recordingSampleRate,
538
552
  maskInputs: this.config.recordMaskInputs,
539
- captureScreen: this.config.captureScreen,
553
+ captureScreen: this.screenshotProvider(),
540
554
  screen: this.currentScreen,
541
555
  });
542
556
  }
@@ -545,7 +559,44 @@ class CXPinsightSDK {
545
559
  }
546
560
  }
547
561
  this.startEngagementClock(); // reset engagement time for the new session
548
- this.track('session_start', { sessionId: newSessionId }, 'navigation', false);
562
+ this.track('session_start', {
563
+ sessionId: newSessionId,
564
+ is_first_visit: false,
565
+ ...this.sessionDiagnostics(),
566
+ }, 'navigation', false);
567
+ // THE SCREEN THE USER IS STILL ON BELONGS TO THIS SESSION TOO.
568
+ //
569
+ // A rollover happens when the app comes back after the inactivity timeout,
570
+ // usually on the screen it was left on. trackScreen() is idempotent for the
571
+ // current screen, so nothing re-announced it: the new session had no
572
+ // page_view at all, every later event inherited a screen name from the
573
+ // previous session, and funnels starting there never saw an entry. Seen in
574
+ // production as sessions with pageViews 0 and every event on one screen.
575
+ //
576
+ // Only the event, not the screen-view trigger hook: re-entering a screen by waiting is
577
+ // not a navigation, and a screen-view survey trigger must not fire on it.
578
+ this.previousScreen = null;
579
+ if (this.currentScreen) {
580
+ this.track('page_view', {
581
+ page: this.currentScreen,
582
+ screen_name: this.currentScreen,
583
+ session_resumed: true,
584
+ }, 'page_view', false);
585
+ }
586
+ }
587
+ /** Which build, and how it is wired — so a gap in a session can be read, not guessed. */
588
+ sessionDiagnostics() {
589
+ return {
590
+ sdk_version: version_1.SDK_VERSION,
591
+ screenshot_source: this.screenshotSource,
592
+ provider_mounted: this.integration.provider,
593
+ navigation_source: this.integration.navigation,
594
+ tap_capture: (0, autoTapCapture_1.isAutoTapCaptureActive)(),
595
+ };
596
+ }
597
+ /** @internal Called by CXPinsightProvider so session_start can report how screens are tracked. */
598
+ _noteIntegration(i) {
599
+ Object.assign(this.integration, i);
549
600
  }
550
601
  /** Emit everything buffered before init() resolved, oldest first. */
551
602
  flushPending() {
@@ -888,6 +939,20 @@ class CXPinsightSDK {
888
939
  };
889
940
  (0, triggerEngine_1.onEventTracked)(eventName, targetingContext);
890
941
  }
942
+ /**
943
+ * The screenshot capturer for a recording that is starting now: the host's
944
+ * captureScreen, else the SDK's own (nativeScreenshot.ts), unless the
945
+ * dashboard turned screenshots off.
946
+ */
947
+ screenshotProvider() {
948
+ if (!this.config || this.screenshotsOff) {
949
+ this.screenshotSource = 'none';
950
+ return undefined;
951
+ }
952
+ const r = (0, nativeScreenshot_1.resolveScreenshotProvider)(this.config.captureScreen, { maskInputs: this.config.recordMaskInputs });
953
+ this.screenshotSource = r.source;
954
+ return r.provider;
955
+ }
891
956
  /** Track a screen view (triggers remote page_view rules) */
892
957
  trackScreen(screenName, data) {
893
958
  // IDEMPOTENT FOR THE SCREEN ALREADY SHOWING.
@@ -1152,7 +1217,7 @@ class CXPinsightSDK {
1152
1217
  applicationId: this.config.applicationId,
1153
1218
  sampleRate: this.config.recordingSampleRate,
1154
1219
  maskInputs: this.config.recordMaskInputs,
1155
- captureScreen: this.config.captureScreen,
1220
+ captureScreen: this.screenshotProvider(),
1156
1221
  // The screen the user is ON when this recording begins. Without it a
1157
1222
  // recording that starts mid-session opens with screen null and stays
1158
1223
  // nameless until the next navigation — which may never come.
@@ -1216,7 +1281,7 @@ class CXPinsightSDK {
1216
1281
  applicationId: this.config.applicationId,
1217
1282
  sampleRate: this.config.recordingSampleRate,
1218
1283
  maskInputs: this.config.recordMaskInputs,
1219
- captureScreen: this.config.captureScreen,
1284
+ captureScreen: this.screenshotProvider(),
1220
1285
  // The screen the user is ON when this recording begins. Without it a
1221
1286
  // recording that starts mid-session opens with screen null and stays
1222
1287
  // nameless until the next navigation — which may never come.