clintplay-react-native 0.2.0 → 0.2.2
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/README.md +4 -4
- package/android/build.gradle +13 -16
- package/android/src/main/java/digital/clint/clintplay/ClintplayRNModule.kt +57 -5
- package/dist/index.d.mts +35 -3
- package/dist/index.d.ts +35 -3
- package/dist/index.js +281 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +273 -37
- package/dist/index.mjs.map +1 -1
- package/ios/ClintplayRN.mm +107 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Session capture SDK for React Native / Expo apps — companion to [`clintplay-sdk`](../sdk) (web). Captures lifecycle, screen dimensions, navigation, JS errors, network requests (Fetch & XHR/Axios), console output, touches (screen-relative & target-relative), and native visual wireframe replay on iOS and Android.
|
|
4
4
|
|
|
5
|
-
> **Status:** `0.2.
|
|
5
|
+
> **Status:** `0.2.2` — Production-ready JS runtime and Native TurboModule. Supports native view hierarchy traversal on iOS (`ios/ClintplayRN.mm`) and Android (`android/.../ClintplayRNModule.kt`), visual wireframe session replay, network interception for `fetch` and `XMLHttpRequest`/Axios, automatic navigation tracking, and intelligent auto-masking for sensitive fields.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ pnpm add clintplay-react-native
|
|
|
12
12
|
npm install clintplay-react-native
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Peer deps: `react >=18`, `react-native >=0.74`. Optional peer: `@react-native-async-storage/async-storage` (enables the offline retry queue when present).
|
|
15
|
+
Peer deps: `react >=18`, `react-native >=0.74`. Optional peer: `@react-native-async-storage/async-storage` (enables the offline retry queue when present). Android verified on Expo SDK 57 / RN 0.86.3 with New Architecture (debug and release builds, and wireframe capture on an emulator); iOS not re-verified on that version.
|
|
16
16
|
|
|
17
17
|
In your `app.json`, register the config plugin:
|
|
18
18
|
|
|
@@ -81,7 +81,7 @@ export default function RootLayout() {
|
|
|
81
81
|
| `enabled` | `boolean` | `true` | Master killswitch. Set to `false` to disable all telemetry. |
|
|
82
82
|
| `sampleRate` | `number` | `1.0` | Probabilistic sampling rate between `0.0` and `1.0`. |
|
|
83
83
|
| `captureWireframe` | `boolean` | `false` | Enable native view-hierarchy visual wireframe capture for session replay. |
|
|
84
|
-
| `wireframeIntervalMs` | `number` | `1000` | Wireframe snapshot interval in milliseconds. |
|
|
84
|
+
| `wireframeIntervalMs` | `number` | `1000` | Wireframe snapshot interval in milliseconds. A snapshot identical to the previous one is not re-sent (an unchanged screen is re-sent every 30 s), and a snapshot is also taken ~400 ms after every touch end and navigation, so screens visited for less than the interval still reach the replay. |
|
|
85
85
|
| `captureNetwork` | `boolean` | `true` | Intercept `globalThis.fetch` and `XMLHttpRequest` (including Axios) requests. |
|
|
86
86
|
| `captureConsole` | `boolean` | `false` | Intercept `console.warn` and `console.error` logs. |
|
|
87
87
|
| `captureConsoleLog` | `boolean` | `false` | Intercept `console.log` logs when `captureConsole: true`. |
|
|
@@ -104,7 +104,7 @@ export default function RootLayout() {
|
|
|
104
104
|
| `network` | `fetch` & `XMLHttpRequest` / Axios | **On** | HTTP method, URL (sanitized), status code, duration, and errors. Protected against double-counting. |
|
|
105
105
|
| `navigation` | Navigation Trackers & `trackScreen` | **On (Tracker)** | Screen path changes (`to`, `from`). Automatic via `<ClintplayNavigationTracker />` or React Navigation listener. |
|
|
106
106
|
| `touch` | `<ClintplayCaptureProvider>` | **Provider** | Tap / touch gestures with both screen-relative coordinates (`screenX`, `screenY`) and target-relative coordinates (`x`, `y`). |
|
|
107
|
-
| `wireframe` | Native TurboModule | **Opt-in** | Visual UI hierarchy snapshots (view rects,
|
|
107
|
+
| `wireframe` | Native TurboModule | **Opt-in** | Visual UI hierarchy snapshots (view rects, background colors, text with font size, color and alignment — plus weight on iOS, and bold on Android —, masking) across iOS and Android. Sent on change, after interactions, and every 30 s while unchanged. |
|
|
108
108
|
| `log` | Console Interceptor | **Opt-in** | Console output (`warn`, `error`, and optional `log`). |
|
|
109
109
|
| `identify` | `Clintplay.identify()` | **Manual** | Associating sessions with authenticated users and custom traits. |
|
|
110
110
|
|
package/android/build.gradle
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
buildscript
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
1
|
+
// No `buildscript` block: this module is only built inside a host app, whose
|
|
2
|
+
// root build.gradle already puts the Android and Kotlin Gradle plugins on the
|
|
3
|
+
// classpath (bare RN and Expo templates alike). Don't read a Kotlin version from
|
|
4
|
+
// `rootProject.ext` either: Expo's template defines none, and in Groovy reading
|
|
5
|
+
// a missing `ext` property throws instead of returning null.
|
|
6
|
+
|
|
7
|
+
// On the classpath through the host's root build.gradle (resolved by the
|
|
8
|
+
// includeBuild in its settings.gradle). Runs the codegen for the
|
|
9
|
+
// `codegenConfig` in package.json (`ClintplayRNSpec`): autolinking adds that
|
|
10
|
+
// spec's `codegen/jni` folder to the app's CMake build and its C++
|
|
11
|
+
// ModuleProvider is what binds this TurboModule to JS, so without the plugin
|
|
12
|
+
// the app's CMake configure fails on a directory that was never generated.
|
|
13
|
+
apply plugin: 'com.facebook.react'
|
|
17
14
|
apply plugin: 'com.android.library'
|
|
18
15
|
apply plugin: 'org.jetbrains.kotlin.android'
|
|
19
16
|
|
|
@@ -2,13 +2,19 @@ package digital.clint.clintplay
|
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
4
|
import android.content.Context
|
|
5
|
+
import android.graphics.Typeface
|
|
5
6
|
import android.graphics.drawable.ColorDrawable
|
|
6
7
|
import android.graphics.drawable.Drawable
|
|
7
8
|
import android.os.Build
|
|
8
9
|
import android.os.Handler
|
|
9
10
|
import android.os.Looper
|
|
10
11
|
import android.text.InputType
|
|
12
|
+
import android.text.Spanned
|
|
11
13
|
import android.text.method.PasswordTransformationMethod
|
|
14
|
+
import android.text.style.AbsoluteSizeSpan
|
|
15
|
+
import android.text.style.ForegroundColorSpan
|
|
16
|
+
import android.text.style.StyleSpan
|
|
17
|
+
import android.view.Gravity
|
|
12
18
|
import android.view.View
|
|
13
19
|
import android.view.ViewGroup
|
|
14
20
|
import android.view.WindowManager
|
|
@@ -37,9 +43,11 @@ import kotlin.math.round
|
|
|
37
43
|
// coordinate conversion, background colors, extracted text, and
|
|
38
44
|
// privacy auto-masking (clintplay-mask tags, password fields, and sensitive autofill hints).
|
|
39
45
|
//
|
|
40
|
-
// React Native's codegen emits `NativeClintplayRNSpec`
|
|
41
|
-
// `src/spec/NativeClintplayRN.ts` at build time.
|
|
42
|
-
//
|
|
46
|
+
// React Native's codegen emits `NativeClintplayRNSpec` and its C++ JSI glue
|
|
47
|
+
// from `src/spec/NativeClintplayRN.ts` at build time. This class does not
|
|
48
|
+
// extend that spec: the glue calls the methods below by name and JNI signature
|
|
49
|
+
// (`()Ljava/lang/String;`), so they must match the spec by hand. The
|
|
50
|
+
// TurboReactPackage in `ClintplayRNPackage.kt` routes calls here.
|
|
43
51
|
@ReactModule(name = ClintplayRNModule.NAME)
|
|
44
52
|
class ClintplayRNModule(reactContext: ReactApplicationContext) :
|
|
45
53
|
ReactContextBaseJavaModule(reactContext), TurboModule {
|
|
@@ -112,6 +120,45 @@ class ClintplayRNModule(reactContext: ReactApplicationContext) :
|
|
|
112
120
|
return false
|
|
113
121
|
}
|
|
114
122
|
|
|
123
|
+
// React Native sets size and colour as spans on the text, so the spans at the first
|
|
124
|
+
// character win over the TextView's own defaults. Weight is only sent when bold is
|
|
125
|
+
// actually detected: RN carries it in its own CustomStyleSpan, which this does not read,
|
|
126
|
+
// and a guessed 400 would look measured. Best effort: the text alone already makes the
|
|
127
|
+
// replay readable.
|
|
128
|
+
private fun putTextStyle(node: JSONObject, view: TextView, density: Float) {
|
|
129
|
+
try {
|
|
130
|
+
val text = view.text
|
|
131
|
+
val safeDensity = if (density > 0f) density else 1f
|
|
132
|
+
var sizePx = view.textSize
|
|
133
|
+
var color = view.currentTextColor
|
|
134
|
+
var bold = view.typeface?.isBold == true
|
|
135
|
+
if (text is Spanned && text.isNotEmpty()) {
|
|
136
|
+
text.getSpans(0, 1, AbsoluteSizeSpan::class.java).lastOrNull()?.let { span ->
|
|
137
|
+
sizePx = if (span.dip) span.size * safeDensity else span.size.toFloat()
|
|
138
|
+
}
|
|
139
|
+
text.getSpans(0, 1, ForegroundColorSpan::class.java).lastOrNull()?.let { span ->
|
|
140
|
+
color = span.foregroundColor
|
|
141
|
+
}
|
|
142
|
+
if (text.getSpans(0, 1, StyleSpan::class.java).any { (it.style and Typeface.BOLD) != 0 }) {
|
|
143
|
+
bold = true
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
node.put("fontSize", round(sizePx / safeDensity * 10.0) / 10.0)
|
|
147
|
+
if (bold) node.put("fontWeight", 700)
|
|
148
|
+
node.put("color", formatColor(color))
|
|
149
|
+
val horizontal = Gravity.getAbsoluteGravity(view.gravity, view.layoutDirection) and
|
|
150
|
+
Gravity.HORIZONTAL_GRAVITY_MASK
|
|
151
|
+
node.put(
|
|
152
|
+
"textAlign",
|
|
153
|
+
when (horizontal) {
|
|
154
|
+
Gravity.CENTER_HORIZONTAL -> "center"
|
|
155
|
+
Gravity.RIGHT -> "right"
|
|
156
|
+
else -> "left"
|
|
157
|
+
},
|
|
158
|
+
)
|
|
159
|
+
} catch (_: Throwable) {}
|
|
160
|
+
}
|
|
161
|
+
|
|
115
162
|
private fun serializeView(
|
|
116
163
|
view: View?,
|
|
117
164
|
nodeCounter: AtomicInteger,
|
|
@@ -206,6 +253,9 @@ class ClintplayRNModule(reactContext: ReactApplicationContext) :
|
|
|
206
253
|
var extractedText: String? = null
|
|
207
254
|
if (view is TextView) {
|
|
208
255
|
extractedText = view.text?.toString()
|
|
256
|
+
if (!isMasked && !extractedText.isNullOrEmpty()) {
|
|
257
|
+
putTextStyle(nodeObj, view, density)
|
|
258
|
+
}
|
|
209
259
|
}
|
|
210
260
|
|
|
211
261
|
if (extractedText != null) {
|
|
@@ -278,7 +328,9 @@ class ClintplayRNModule(reactContext: ReactApplicationContext) :
|
|
|
278
328
|
|
|
279
329
|
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
280
330
|
fun captureViewHierarchy(): String {
|
|
281
|
-
|
|
331
|
+
// Not the inherited `currentActivity`: since RN 0.80 that is a deprecated
|
|
332
|
+
// protected Kotlin function, which property syntax does not resolve.
|
|
333
|
+
val activity = reactApplicationContext.currentActivity ?: return FALLBACK_JSON
|
|
282
334
|
|
|
283
335
|
if (activity.isFinishing || activity.isDestroyed) {
|
|
284
336
|
return FALLBACK_JSON
|
|
@@ -328,7 +380,7 @@ class ClintplayRNModule(reactContext: ReactApplicationContext) :
|
|
|
328
380
|
|
|
329
381
|
companion object {
|
|
330
382
|
const val NAME = "ClintplayRN"
|
|
331
|
-
private const val VERSION = "0.2.
|
|
383
|
+
private const val VERSION = "0.2.2"
|
|
332
384
|
private const val FALLBACK_JSON = "{\"version\":1,\"nodes\":[]}"
|
|
333
385
|
|
|
334
386
|
private val NO_METHOD_SENTINEL = Any()
|
package/dist/index.d.mts
CHANGED
|
@@ -7,6 +7,8 @@ interface SessionState {
|
|
|
7
7
|
startedAt: number;
|
|
8
8
|
lastActivity: number;
|
|
9
9
|
userId: string | null;
|
|
10
|
+
tags: Set<string>;
|
|
11
|
+
metadata: Record<string, unknown>;
|
|
10
12
|
}
|
|
11
13
|
declare class SessionManager {
|
|
12
14
|
private state;
|
|
@@ -15,13 +17,16 @@ declare class SessionManager {
|
|
|
15
17
|
private create;
|
|
16
18
|
current(): SessionState;
|
|
17
19
|
touch(): void;
|
|
20
|
+
addTag(tag: string): boolean;
|
|
21
|
+
hasTag(tag: string): boolean;
|
|
22
|
+
setMetadata(data: Record<string, unknown>): void;
|
|
18
23
|
setUser(userId: string | null): boolean;
|
|
19
24
|
isStale(): boolean;
|
|
20
25
|
rotateIfStale(): boolean;
|
|
21
26
|
reset(): void;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
type EventType = 'meta' | 'screen' | 'lifecycle' | 'navigation' | 'touch' | 'error' | 'log' | 'identify' | 'network' | 'wireframe';
|
|
29
|
+
type EventType = 'meta' | 'screen' | 'lifecycle' | 'navigation' | 'touch' | 'error' | 'log' | 'identify' | 'network' | 'wireframe' | 'metadata' | 'tag';
|
|
25
30
|
interface DeviceInfo {
|
|
26
31
|
platform: 'ios' | 'android' | 'web';
|
|
27
32
|
osVersion?: string;
|
|
@@ -92,6 +97,18 @@ interface NetworkEvent {
|
|
|
92
97
|
durationMs?: number;
|
|
93
98
|
error?: string;
|
|
94
99
|
}
|
|
100
|
+
interface MetadataEvent {
|
|
101
|
+
type: 'metadata';
|
|
102
|
+
ts: number;
|
|
103
|
+
data: Record<string, unknown>;
|
|
104
|
+
}
|
|
105
|
+
interface TagEvent {
|
|
106
|
+
type: 'tag';
|
|
107
|
+
ts: number;
|
|
108
|
+
data: {
|
|
109
|
+
tag: string;
|
|
110
|
+
};
|
|
111
|
+
}
|
|
95
112
|
interface WireframeNode {
|
|
96
113
|
id: number;
|
|
97
114
|
type: string;
|
|
@@ -117,7 +134,7 @@ interface WireframeEvent {
|
|
|
117
134
|
version: number;
|
|
118
135
|
nodes: WireframeNode[];
|
|
119
136
|
}
|
|
120
|
-
type ReplayEvent = MetaEvent | ScreenEvent | LifecycleEvent | NavigationEvent | TouchEvent | ErrorEvent | LogEvent | IdentifyEvent | NetworkEvent | WireframeEvent;
|
|
137
|
+
type ReplayEvent = MetaEvent | ScreenEvent | LifecycleEvent | NavigationEvent | TouchEvent | ErrorEvent | LogEvent | IdentifyEvent | NetworkEvent | WireframeEvent | MetadataEvent | TagEvent;
|
|
121
138
|
interface BackendEvent {
|
|
122
139
|
type: string;
|
|
123
140
|
timestamp: number;
|
|
@@ -186,6 +203,10 @@ interface Recorder {
|
|
|
186
203
|
start(): void;
|
|
187
204
|
stop(): void;
|
|
188
205
|
takeWireframeSnapshot(): void;
|
|
206
|
+
/** Captura depois que a interação assenta (toque, navegação). Chamadas próximas viram uma. */
|
|
207
|
+
scheduleWireframeSnapshot(): void;
|
|
208
|
+
/** A próxima captura é enviada mesmo igual à anterior (sessão nova precisa da tela). */
|
|
209
|
+
resetWireframeBaseline(): void;
|
|
189
210
|
}
|
|
190
211
|
|
|
191
212
|
interface NetworkInterceptorDeps {
|
|
@@ -386,6 +407,13 @@ interface InternalConfig {
|
|
|
386
407
|
debug: boolean;
|
|
387
408
|
captureWireframe: boolean;
|
|
388
409
|
}
|
|
410
|
+
declare const METADATA_MAX_KEYS = 50;
|
|
411
|
+
declare const METADATA_MAX_DEPTH = 3;
|
|
412
|
+
declare const METADATA_MAX_STRING_LENGTH = 1024;
|
|
413
|
+
declare const METADATA_MAX_BYTES = 8192;
|
|
414
|
+
declare const TAG_MAX_LENGTH = 128;
|
|
415
|
+
declare function sanitizeTag(rawTag: unknown): string | null;
|
|
416
|
+
declare function sanitizeMetadata(data: unknown): Record<string, unknown> | null;
|
|
389
417
|
declare function formatEventForBatch(event: ReplayEvent | BackendEvent, defaultSessionId: string): BackendEvent;
|
|
390
418
|
declare function init(rawConfig: ClintplayReactNativeConfig): typeof Clintplay;
|
|
391
419
|
declare function identify(userId: string | number | null, traits?: Record<string, unknown>): void;
|
|
@@ -394,6 +422,8 @@ declare function reset(options?: {
|
|
|
394
422
|
}): void;
|
|
395
423
|
declare function trackScreen(path: string, from?: string): void;
|
|
396
424
|
declare function reportError(err: unknown): void;
|
|
425
|
+
declare function setMetadata(data: Record<string, unknown>): void;
|
|
426
|
+
declare function tag(tagName: string): void;
|
|
397
427
|
declare function flush(): Promise<void>;
|
|
398
428
|
declare function stop(): Promise<void>;
|
|
399
429
|
declare function current(): {
|
|
@@ -419,10 +449,12 @@ declare const Clintplay: {
|
|
|
419
449
|
reset: typeof reset;
|
|
420
450
|
trackScreen: typeof trackScreen;
|
|
421
451
|
reportError: typeof reportError;
|
|
452
|
+
setMetadata: typeof setMetadata;
|
|
453
|
+
tag: typeof tag;
|
|
422
454
|
flush: typeof flush;
|
|
423
455
|
stop: typeof stop;
|
|
424
456
|
current: typeof current;
|
|
425
457
|
_getState: typeof _getState;
|
|
426
458
|
};
|
|
427
459
|
|
|
428
|
-
export { type BackendEvent, CLINTPLAY_MASK_ID, Clintplay, ClintplayCaptureProvider, type ClintplayCaptureProviderProps, ClintplayMaskImage, type ClintplayMaskImageProps, ClintplayMaskTextInput, type ClintplayMaskTextInputProps, ClintplayMaskView, type ClintplayMaskViewProps, ClintplayNavigationTracker, type ClintplayNavigationTrackerProps, type ClintplayReactNativeConfig, type DeviceInfo, type NavigationRouteLike, type NavigationStateLike, type NavigationStateListenerCallback, type NavigationStateListenerOptions, type ReplayBatch, type ReplayEvent, SDK_VERSION, type UseClintplayNavigationOptions, type UseClintplayNavigationResult, type WireframeEvent, type WireframeNode, _resetLastTrackedRoute, createClintplayNavigationStateListener, formatEventForBatch, getActiveRouteName, init, sanitizeRoute, useClintplayNavigationTracking };
|
|
460
|
+
export { type BackendEvent, CLINTPLAY_MASK_ID, Clintplay, ClintplayCaptureProvider, type ClintplayCaptureProviderProps, ClintplayMaskImage, type ClintplayMaskImageProps, ClintplayMaskTextInput, type ClintplayMaskTextInputProps, ClintplayMaskView, type ClintplayMaskViewProps, ClintplayNavigationTracker, type ClintplayNavigationTrackerProps, type ClintplayReactNativeConfig, type DeviceInfo, METADATA_MAX_BYTES, METADATA_MAX_DEPTH, METADATA_MAX_KEYS, METADATA_MAX_STRING_LENGTH, type MetadataEvent, type NavigationRouteLike, type NavigationStateLike, type NavigationStateListenerCallback, type NavigationStateListenerOptions, type ReplayBatch, type ReplayEvent, SDK_VERSION, TAG_MAX_LENGTH, type TagEvent, type UseClintplayNavigationOptions, type UseClintplayNavigationResult, type WireframeEvent, type WireframeNode, _resetLastTrackedRoute, createClintplayNavigationStateListener, formatEventForBatch, getActiveRouteName, init, sanitizeMetadata, sanitizeRoute, sanitizeTag, setMetadata, tag, useClintplayNavigationTracking };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ interface SessionState {
|
|
|
7
7
|
startedAt: number;
|
|
8
8
|
lastActivity: number;
|
|
9
9
|
userId: string | null;
|
|
10
|
+
tags: Set<string>;
|
|
11
|
+
metadata: Record<string, unknown>;
|
|
10
12
|
}
|
|
11
13
|
declare class SessionManager {
|
|
12
14
|
private state;
|
|
@@ -15,13 +17,16 @@ declare class SessionManager {
|
|
|
15
17
|
private create;
|
|
16
18
|
current(): SessionState;
|
|
17
19
|
touch(): void;
|
|
20
|
+
addTag(tag: string): boolean;
|
|
21
|
+
hasTag(tag: string): boolean;
|
|
22
|
+
setMetadata(data: Record<string, unknown>): void;
|
|
18
23
|
setUser(userId: string | null): boolean;
|
|
19
24
|
isStale(): boolean;
|
|
20
25
|
rotateIfStale(): boolean;
|
|
21
26
|
reset(): void;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
type EventType = 'meta' | 'screen' | 'lifecycle' | 'navigation' | 'touch' | 'error' | 'log' | 'identify' | 'network' | 'wireframe';
|
|
29
|
+
type EventType = 'meta' | 'screen' | 'lifecycle' | 'navigation' | 'touch' | 'error' | 'log' | 'identify' | 'network' | 'wireframe' | 'metadata' | 'tag';
|
|
25
30
|
interface DeviceInfo {
|
|
26
31
|
platform: 'ios' | 'android' | 'web';
|
|
27
32
|
osVersion?: string;
|
|
@@ -92,6 +97,18 @@ interface NetworkEvent {
|
|
|
92
97
|
durationMs?: number;
|
|
93
98
|
error?: string;
|
|
94
99
|
}
|
|
100
|
+
interface MetadataEvent {
|
|
101
|
+
type: 'metadata';
|
|
102
|
+
ts: number;
|
|
103
|
+
data: Record<string, unknown>;
|
|
104
|
+
}
|
|
105
|
+
interface TagEvent {
|
|
106
|
+
type: 'tag';
|
|
107
|
+
ts: number;
|
|
108
|
+
data: {
|
|
109
|
+
tag: string;
|
|
110
|
+
};
|
|
111
|
+
}
|
|
95
112
|
interface WireframeNode {
|
|
96
113
|
id: number;
|
|
97
114
|
type: string;
|
|
@@ -117,7 +134,7 @@ interface WireframeEvent {
|
|
|
117
134
|
version: number;
|
|
118
135
|
nodes: WireframeNode[];
|
|
119
136
|
}
|
|
120
|
-
type ReplayEvent = MetaEvent | ScreenEvent | LifecycleEvent | NavigationEvent | TouchEvent | ErrorEvent | LogEvent | IdentifyEvent | NetworkEvent | WireframeEvent;
|
|
137
|
+
type ReplayEvent = MetaEvent | ScreenEvent | LifecycleEvent | NavigationEvent | TouchEvent | ErrorEvent | LogEvent | IdentifyEvent | NetworkEvent | WireframeEvent | MetadataEvent | TagEvent;
|
|
121
138
|
interface BackendEvent {
|
|
122
139
|
type: string;
|
|
123
140
|
timestamp: number;
|
|
@@ -186,6 +203,10 @@ interface Recorder {
|
|
|
186
203
|
start(): void;
|
|
187
204
|
stop(): void;
|
|
188
205
|
takeWireframeSnapshot(): void;
|
|
206
|
+
/** Captura depois que a interação assenta (toque, navegação). Chamadas próximas viram uma. */
|
|
207
|
+
scheduleWireframeSnapshot(): void;
|
|
208
|
+
/** A próxima captura é enviada mesmo igual à anterior (sessão nova precisa da tela). */
|
|
209
|
+
resetWireframeBaseline(): void;
|
|
189
210
|
}
|
|
190
211
|
|
|
191
212
|
interface NetworkInterceptorDeps {
|
|
@@ -386,6 +407,13 @@ interface InternalConfig {
|
|
|
386
407
|
debug: boolean;
|
|
387
408
|
captureWireframe: boolean;
|
|
388
409
|
}
|
|
410
|
+
declare const METADATA_MAX_KEYS = 50;
|
|
411
|
+
declare const METADATA_MAX_DEPTH = 3;
|
|
412
|
+
declare const METADATA_MAX_STRING_LENGTH = 1024;
|
|
413
|
+
declare const METADATA_MAX_BYTES = 8192;
|
|
414
|
+
declare const TAG_MAX_LENGTH = 128;
|
|
415
|
+
declare function sanitizeTag(rawTag: unknown): string | null;
|
|
416
|
+
declare function sanitizeMetadata(data: unknown): Record<string, unknown> | null;
|
|
389
417
|
declare function formatEventForBatch(event: ReplayEvent | BackendEvent, defaultSessionId: string): BackendEvent;
|
|
390
418
|
declare function init(rawConfig: ClintplayReactNativeConfig): typeof Clintplay;
|
|
391
419
|
declare function identify(userId: string | number | null, traits?: Record<string, unknown>): void;
|
|
@@ -394,6 +422,8 @@ declare function reset(options?: {
|
|
|
394
422
|
}): void;
|
|
395
423
|
declare function trackScreen(path: string, from?: string): void;
|
|
396
424
|
declare function reportError(err: unknown): void;
|
|
425
|
+
declare function setMetadata(data: Record<string, unknown>): void;
|
|
426
|
+
declare function tag(tagName: string): void;
|
|
397
427
|
declare function flush(): Promise<void>;
|
|
398
428
|
declare function stop(): Promise<void>;
|
|
399
429
|
declare function current(): {
|
|
@@ -419,10 +449,12 @@ declare const Clintplay: {
|
|
|
419
449
|
reset: typeof reset;
|
|
420
450
|
trackScreen: typeof trackScreen;
|
|
421
451
|
reportError: typeof reportError;
|
|
452
|
+
setMetadata: typeof setMetadata;
|
|
453
|
+
tag: typeof tag;
|
|
422
454
|
flush: typeof flush;
|
|
423
455
|
stop: typeof stop;
|
|
424
456
|
current: typeof current;
|
|
425
457
|
_getState: typeof _getState;
|
|
426
458
|
};
|
|
427
459
|
|
|
428
|
-
export { type BackendEvent, CLINTPLAY_MASK_ID, Clintplay, ClintplayCaptureProvider, type ClintplayCaptureProviderProps, ClintplayMaskImage, type ClintplayMaskImageProps, ClintplayMaskTextInput, type ClintplayMaskTextInputProps, ClintplayMaskView, type ClintplayMaskViewProps, ClintplayNavigationTracker, type ClintplayNavigationTrackerProps, type ClintplayReactNativeConfig, type DeviceInfo, type NavigationRouteLike, type NavigationStateLike, type NavigationStateListenerCallback, type NavigationStateListenerOptions, type ReplayBatch, type ReplayEvent, SDK_VERSION, type UseClintplayNavigationOptions, type UseClintplayNavigationResult, type WireframeEvent, type WireframeNode, _resetLastTrackedRoute, createClintplayNavigationStateListener, formatEventForBatch, getActiveRouteName, init, sanitizeRoute, useClintplayNavigationTracking };
|
|
460
|
+
export { type BackendEvent, CLINTPLAY_MASK_ID, Clintplay, ClintplayCaptureProvider, type ClintplayCaptureProviderProps, ClintplayMaskImage, type ClintplayMaskImageProps, ClintplayMaskTextInput, type ClintplayMaskTextInputProps, ClintplayMaskView, type ClintplayMaskViewProps, ClintplayNavigationTracker, type ClintplayNavigationTrackerProps, type ClintplayReactNativeConfig, type DeviceInfo, METADATA_MAX_BYTES, METADATA_MAX_DEPTH, METADATA_MAX_KEYS, METADATA_MAX_STRING_LENGTH, type MetadataEvent, type NavigationRouteLike, type NavigationStateLike, type NavigationStateListenerCallback, type NavigationStateListenerOptions, type ReplayBatch, type ReplayEvent, SDK_VERSION, TAG_MAX_LENGTH, type TagEvent, type UseClintplayNavigationOptions, type UseClintplayNavigationResult, type WireframeEvent, type WireframeNode, _resetLastTrackedRoute, createClintplayNavigationStateListener, formatEventForBatch, getActiveRouteName, init, sanitizeMetadata, sanitizeRoute, sanitizeTag, setMetadata, tag, useClintplayNavigationTracking };
|