noibu-react-native 0.1.2 → 0.2.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.
Files changed (52) hide show
  1. package/android/.gitignore +13 -0
  2. package/android/build.gradle +79 -0
  3. package/android/gradle.properties +7 -0
  4. package/android/src/main/AndroidManifest.xml +5 -0
  5. package/android/src/main/java/com/noibu/sessionreplay/reactnative/NoibuSessionReplayModule.kt +107 -0
  6. package/android/src/main/java/com/noibu/sessionreplay/reactnative/NoibuSessionReplayPackage.kt +17 -0
  7. package/dist/api/clientConfig.d.ts +6 -7
  8. package/dist/api/clientConfig.js +14 -16
  9. package/dist/api/helpCode.d.ts +29 -0
  10. package/dist/api/inputManager.d.ts +87 -0
  11. package/dist/api/metroplexSocket.d.ts +156 -0
  12. package/dist/api/metroplexSocket.js +662 -815
  13. package/dist/api/storedMetrics.d.ts +73 -0
  14. package/dist/api/storedPageVisit.d.ts +49 -0
  15. package/dist/const_matchers.d.ts +1 -0
  16. package/dist/constants.d.ts +10 -1
  17. package/dist/constants.js +19 -2
  18. package/dist/entry/index.d.ts +1 -1
  19. package/dist/entry/init.d.ts +1 -1
  20. package/dist/entry/init.js +10 -6
  21. package/dist/monitors/appNavigationMonitor.js +3 -2
  22. package/dist/monitors/clickMonitor.d.ts +44 -0
  23. package/dist/monitors/gqlErrorValidator.d.ts +82 -0
  24. package/dist/monitors/httpDataBundler.d.ts +161 -0
  25. package/dist/monitors/inputMonitor.d.ts +34 -0
  26. package/dist/monitors/inputMonitor.js +5 -0
  27. package/dist/monitors/integrations/react-native-navigation-integration.d.ts +1 -2
  28. package/dist/monitors/keyboardInputMonitor.d.ts +17 -0
  29. package/dist/monitors/pageMonitor.d.ts +22 -0
  30. package/dist/monitors/requestMonitor.d.ts +10 -0
  31. package/dist/pageVisit/pageVisit.d.ts +52 -0
  32. package/dist/pageVisit/pageVisit.js +9 -2
  33. package/dist/pageVisit/pageVisitEventError.d.ts +15 -0
  34. package/dist/pageVisit/pageVisitEventHTTP.d.ts +18 -0
  35. package/dist/pageVisit/userStep.d.ts +5 -0
  36. package/dist/react/ErrorBoundary.js +17 -9
  37. package/dist/sessionRecorder/nativeSessionRecorderSubscription.d.ts +64 -0
  38. package/dist/sessionRecorder/nativeSessionRecorderSubscription.js +58 -0
  39. package/dist/sessionRecorder/sessionRecorder.d.ts +60 -0
  40. package/dist/sessionRecorder/sessionRecorder.js +287 -0
  41. package/dist/sessionRecorder/types.d.ts +91 -0
  42. package/dist/types/NavigationIntegration.d.ts +1 -2
  43. package/dist/types/StoredPageVisit.types.d.ts +54 -0
  44. package/dist/types/globals.d.ts +2 -1
  45. package/dist/utils/date.d.ts +6 -0
  46. package/dist/utils/eventlistener.d.ts +8 -0
  47. package/dist/utils/eventlistener.js +2 -2
  48. package/dist/utils/function.d.ts +6 -3
  49. package/dist/utils/function.js +23 -10
  50. package/dist/utils/log.d.ts +0 -1
  51. package/dist/utils/object.d.ts +2 -2
  52. package/package.json +15 -6
@@ -0,0 +1,17 @@
1
+ /**
2
+ * KeyboardInputMonitor is a listener class that attaches a
3
+ * keyboard input listener of the document object.
4
+ */
5
+ export class KeyboardInputMonitor {
6
+ /**
7
+ * Begins the monitoring process
8
+ * we currently only monitor two input locations: textarea and input
9
+ */
10
+ monitor(): void;
11
+ /**
12
+ * Validates an event
13
+ * @param uiViewClassName
14
+ * @param props
15
+ */
16
+ _handle(uiViewClassName: any, props: any): void;
17
+ }
@@ -0,0 +1,22 @@
1
+ /** Monitors the page events which we capture and later process */
2
+ export class PageMonitor {
3
+ /**
4
+ * gets the singleton instance
5
+ * @returns {PageMonitor}
6
+ */
7
+ static getInstance(): PageMonitor;
8
+ /** Starts monitoring page events on the document */
9
+ monitor(): void;
10
+ /**
11
+ * Handles a single page event
12
+ * @param {} event
13
+ */
14
+ _onPageEventHandle(event: any): void;
15
+ /** Returns document state */
16
+ getDocumentState(): "hidden" | "active" | "passive";
17
+ /**
18
+ * Returns object size in bytes
19
+ * @param {} obj
20
+ */
21
+ getSizeInBytes(obj: any): number;
22
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Handles fetch failure
3
+ * @param {} err
4
+ * @param {} url
5
+ */
6
+ export function handleFetchFailure(err: any, url: any): void;
7
+ /**
8
+ * Monitors all requests
9
+ */
10
+ export function monitorRequests(): void;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Singleton class to hold all the information
3
+ * about the gathered errors throught the session
4
+ */
5
+ export class PageVisit {
6
+ /**
7
+ * creates a PV object
8
+ */
9
+ static configureInstance(): void;
10
+ /** gets the singleton instance
11
+ * @returns {PageVisit}
12
+ */
13
+ static getInstance(): PageVisit;
14
+ /**
15
+ * Creates a page visit frag from an events array and part counter. Appends
16
+ * the end time field and returns the frag
17
+ * @param {} pvEvents
18
+ * @param {} partCounter
19
+ */
20
+ static makePageVisitFrag(pvEvents: any, partCounter: any): any;
21
+ partCounter: number;
22
+ pvMap: {};
23
+ pvEventLength: number;
24
+ visibilityChangedCounter: number;
25
+ totalPvEventLength: number;
26
+ inDebounceHandle: number;
27
+ isInAcceleratedPvPostMode: boolean;
28
+ /** adds page visit events into the current page visit map and then sends a page visit message
29
+ * @param {} eventObjects
30
+ * @param {} type
31
+ */
32
+ addPageVisitEvents(eventObjects: any, type: any): void;
33
+ /**
34
+ * adds the page visit event into the current page visit map and then sends a page visit message
35
+ * returns the the key to access this event in the map
36
+ * @param {} eventObj
37
+ * @param {} type
38
+ */
39
+ addPageVisitEvent(eventObj: any, type: any): string | number[];
40
+ /**
41
+ * adds a new page visit event into the current page visit map and returns the
42
+ * the key to access this event in the map
43
+ * @param {} eventObj
44
+ * @param {} type
45
+ */
46
+ _addPageVisitEvent(eventObj: any, type: any): string | number[];
47
+ /**
48
+ * _sendPageVisitMessage will reset the buffer and post the current
49
+ * content to metroplex
50
+ */
51
+ _sendPageVisitMessage(): void;
52
+ }
@@ -3,6 +3,7 @@ import { TYPE_ATT_NAME, OCCURRED_AT_ATT_NAME, MAX_PAGEVISIT_EVENTS, MAX_PAGEVISI
3
3
  import ClientConfig from '../api/clientConfig.js';
4
4
  import MetroplexSocket from '../api/metroplexSocket.js';
5
5
  import StoredMetrics from '../api/storedMetrics.js';
6
+ import { noibuLog } from '../utils/log.js';
6
7
 
7
8
  /** @module Pagevisit */
8
9
 
@@ -64,6 +65,10 @@ class PageVisit {
64
65
  * @param {} type
65
66
  */
66
67
  addPageVisitEvent(eventObj, type) {
68
+ noibuLog('addPageVisitEvent', {
69
+ eventObj,
70
+ type,
71
+ });
67
72
  const id = this._addPageVisitEvent(eventObj, type);
68
73
  this._sendPageVisitMessage();
69
74
  return id;
@@ -112,8 +117,10 @@ class PageVisit {
112
117
  const pagevistFrag = {};
113
118
  pagevistFrag[PV_EVENTS_ATT_NAME] = pvEvents;
114
119
  pagevistFrag[PV_PART_COUNTER_ATT_NAME] = partCounter;
115
- MetroplexSocket.getInstance().addEndTimeToPayload(pagevistFrag, true);
116
- return pagevistFrag;
120
+ return MetroplexSocket.getInstance().addEndTimeToPayload(
121
+ pagevistFrag,
122
+ true,
123
+ );
117
124
  }
118
125
 
119
126
  /**
@@ -0,0 +1,15 @@
1
+ /**
2
+ * gets the onURL of a string, defaulting to the location of the webpage
3
+ */
4
+ export function getOnURL(realOnURL: any): string;
5
+ /**
6
+ * determines if an error is a collect error
7
+ * @param {} pvError
8
+ */
9
+ export function isErrorCollectedByNoibu(pvError: any): boolean;
10
+ /** Saves the error to the ErrorQueue
11
+ * @param {} type
12
+ * @param {} payload
13
+ * @param {} [httpDataSeqNum]
14
+ */
15
+ export function saveErrorToPagevisit(type: any, payload: any, httpDataSeqNum?: any): void;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Determines if a response is a failure
3
+ * @param {number} code
4
+ */
5
+ export function isHttpCodeFailure(code: number): boolean;
6
+ /** Class representing a PageVisitEventHTTP */
7
+ export class PageVisitEventHTTP {
8
+ /**
9
+ * Creates an instance of the http event for the pv
10
+ * @param {} httpEvent
11
+ * @param {} httpData
12
+ */
13
+ constructor(httpEvent: any, httpData: any);
14
+ httpEvent: any;
15
+ httpData: any;
16
+ /** Saves the HTTP event to the pageVisit Queue */
17
+ saveHTTPEvent(): void;
18
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * updates the payload of a user step in order to stringify the css class
3
+ * @param {} payload
4
+ */
5
+ export function updatePayload(payload: any): any;
@@ -36,7 +36,9 @@ class ErrorBoundary extends React.Component {
36
36
  InputManager.getInstance()._addErrorFromJSSdk(error, 'ReactError');
37
37
  const eventId = uuid.v4();
38
38
  if (onError) {
39
- onError(error, componentStack, eventId);
39
+ if (typeof componentStack === 'string') {
40
+ onError(error, componentStack, eventId);
41
+ }
40
42
  }
41
43
  // componentDidCatch is used over getDerivedStateFromError
42
44
  // so that componentStack is accessible through state.
@@ -50,7 +52,9 @@ class ErrorBoundary extends React.Component {
50
52
  const { error, componentStack, eventId } = this.state;
51
53
  const { onUnmount } = this.props;
52
54
  if (onUnmount) {
53
- onUnmount(error, componentStack, eventId);
55
+ if (typeof componentStack === 'string') {
56
+ onUnmount(error, componentStack, eventId);
57
+ }
54
58
  }
55
59
  }
56
60
  /**
@@ -61,7 +65,9 @@ class ErrorBoundary extends React.Component {
61
65
  const { onReset } = this.props;
62
66
  const { error, componentStack, eventId } = this.state;
63
67
  if (onReset) {
64
- onReset(error, componentStack, eventId);
68
+ if (typeof componentStack === 'string') {
69
+ onReset(error, componentStack, eventId);
70
+ }
65
71
  }
66
72
  this.setState(INITIAL_STATE);
67
73
  };
@@ -76,12 +82,14 @@ class ErrorBoundary extends React.Component {
76
82
  if (error) {
77
83
  let element;
78
84
  if (typeof fallback === 'function') {
79
- element = fallback({
80
- error,
81
- componentStack,
82
- resetError: this.resetErrorBoundary.bind(this),
83
- eventId,
84
- });
85
+ if (typeof componentStack === 'string') {
86
+ element = fallback({
87
+ error,
88
+ componentStack,
89
+ resetError: this.resetErrorBoundary.bind(this),
90
+ eventId,
91
+ });
92
+ }
85
93
  }
86
94
  else {
87
95
  element = fallback;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The level of logging to show in the device logcat stream.
3
+ */
4
+ export declare enum LogLevel {
5
+ Verbose = "Verbose",
6
+ Debug = "Debug",
7
+ Info = "Info",
8
+ Warning = "Warning",
9
+ Error = "Error",
10
+ None = "None"
11
+ }
12
+ /**
13
+ * The configuration that will be used to customize the session recording behaviour.
14
+ *
15
+ * @param userId [OPTIONAL default = null] A custom identifier for the current user. If passed as null, the user id
16
+ * will be auto generated. The user id in general is sticky across sessions.
17
+ * The provided user id must follow these conditions:
18
+ * 1. Cannot be an empty string.
19
+ * 2. Should be base36 and smaller than "1Z141Z4".
20
+ * @param logLevel [OPTIONAL default = LogLevel.None] The level of logging to show in the device logcat stream.
21
+ * @param allowMeteredNetworkUsage [OPTIONAL default = false] Allows uploading session data to the servers on device metered network.
22
+ * @param enableWebViewCapture [OPTIONAL default = true] Allows Noibu - Session recorder to capture the web views DOM content.
23
+ * @param allowedDomains [OPTIONAL default = ["*"]] The whitelisted domains to allow Noibu - Session recorder to capture their DOM content.
24
+ * If it contains "*" as an element, all domains will be captured.
25
+ * @param disableOnLowEndDevices [OPTIONAL default = false] Disable Noibu - Session recorder on low-end devices.
26
+ * @param maximumDailyNetworkUsageInMB [OPTIONAL default = null] Maximum daily network usage for Noibu - Session recorder (null = No limit). When the limit is reached, Noibu - Session recorder will turn on lean mode.
27
+ */
28
+ export interface SessionRecorderConfig {
29
+ userId?: string | null;
30
+ logLevel?: LogLevel;
31
+ allowMeteredNetworkUsage?: boolean;
32
+ enableWebViewCapture?: boolean;
33
+ allowedDomains?: string[];
34
+ disableOnLowEndDevices?: boolean;
35
+ maximumDailyNetworkUsageInMB?: number;
36
+ }
37
+ /**
38
+ * Initializes the Noibu - Session recording SDK if the API level is supported.
39
+ * param projectId [REQUIRED] The session recording project id to send data to.
40
+ * param config [OPTIONAL] The sessionreplay config, if not provided default values are used.
41
+ */
42
+ export declare function initialize(projectId: string, config?: SessionRecorderConfig): void;
43
+ /**
44
+ * Sets a custom user id that can be used to identify the user. It has less
45
+ * restrictions than the userId parameter. You can pass any string and
46
+ * you can filter on it on the dashboard side. If you need the most efficient
47
+ * filtering on the dashboard, use the userId parameter if possible.
48
+ * <p>
49
+ * Note: custom user id cannot be null or empty, or consists only of whitespaces.
50
+ * </p>
51
+ * @param customUserId The custom user id to set.
52
+ */
53
+ export declare function setCustomUserId(customUserId: string): void;
54
+ /**
55
+ * Sets a custom session id that can be used to identify the session.
56
+ * <p>
57
+ * Note: custom session id cannot be null or empty, or consists only of whitespaces.
58
+ * </p>
59
+ * @param customSessionId The custom session id to set.
60
+ */
61
+ export declare function setCustomSessionId(customSessionId: string): void;
62
+ export type RecorderEvent = import('./types').RecorderEvent;
63
+ export type UnsubscribeFn = import('./types').UnsubscribeFn;
64
+ export declare function subscribeToNativeEvent(callback: (event: RecorderEvent) => void): UnsubscribeFn;
@@ -0,0 +1,58 @@
1
+ import { NativeEventEmitter, Platform, NativeModules } from 'react-native';
2
+ import { noibuLog } from '../utils/log.js';
3
+
4
+ const LINKING_ERROR = `The package 'noibu-session-replay' doesn't seem to be linked. Make sure: \n\n` +
5
+ // Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) + TODO: add back when iOS is supported.
6
+ '- You rebuilt the app after installing the package\n' +
7
+ '- You are not using Expo Go\n';
8
+ const { NativeSessionRecorder } = NativeModules;
9
+ let nativeModuleEmitter;
10
+ const SupportedPlatforms = ['android'];
11
+ /**
12
+ * The level of logging to show in the device logcat stream.
13
+ */
14
+ // eslint-disable-next-line no-shadow
15
+ var LogLevel;
16
+ (function (LogLevel) {
17
+ LogLevel["Verbose"] = "Verbose";
18
+ LogLevel["Debug"] = "Debug";
19
+ LogLevel["Info"] = "Info";
20
+ LogLevel["Warning"] = "Warning";
21
+ LogLevel["Error"] = "Error";
22
+ LogLevel["None"] = "None";
23
+ })(LogLevel || (LogLevel = {}));
24
+ /**
25
+ * Initializes the Noibu - Session recording SDK if the API level is supported.
26
+ * param projectId [REQUIRED] The session recording project id to send data to.
27
+ * param config [OPTIONAL] The sessionreplay config, if not provided default values are used.
28
+ */
29
+ function initialize(projectId, config) {
30
+ if (!(typeof config === 'object' || typeof config === 'undefined')) {
31
+ throw Error('Invalid session recording initialization arguments. Please check the docs for assitance.');
32
+ }
33
+ nativeModuleEmitter = new NativeEventEmitter(NativeSessionRecorder);
34
+ // applying default values
35
+ const { userId = null, logLevel = LogLevel.None, allowMeteredNetworkUsage = false, enableWebViewCapture = true, allowedDomains = ['*'], disableOnLowEndDevices = false, maximumDailyNetworkUsageInMB = null, } = config ?? {};
36
+ if (!SupportedPlatforms.includes(Platform.OS)) {
37
+ noibuLog(`Noibu - Session recording supports ${SupportedPlatforms.join(', ')} only for now.`);
38
+ return;
39
+ }
40
+ if (NativeSessionRecorder === null) {
41
+ noibuLog('Noibu - Session recording did not initialize properly.', LINKING_ERROR);
42
+ return;
43
+ }
44
+ // We use two parameters because the react method parameters do not accept nullable primitive types.
45
+ const enableDailyNetworkUsageLimit = maximumDailyNetworkUsageInMB != null;
46
+ const refinedMaximumDailyNetworkUsageInMB = maximumDailyNetworkUsageInMB ?? 0;
47
+ NativeSessionRecorder.initialize(projectId, userId, logLevel, allowMeteredNetworkUsage, enableWebViewCapture, allowedDomains, disableOnLowEndDevices, enableDailyNetworkUsageLimit, refinedMaximumDailyNetworkUsageInMB);
48
+ }
49
+ function subscribeToNativeEvent(callback) {
50
+ if (!nativeModuleEmitter) {
51
+ throw new Error('You have to initialize Noibu Session Recorder first');
52
+ }
53
+ nativeModuleEmitter.addListener('noibuRecordingEvent', callback);
54
+ // return () => subscription.remove();
55
+ return () => { };
56
+ }
57
+
58
+ export { LogLevel, initialize, subscribeToNativeEvent };
@@ -0,0 +1,60 @@
1
+ import { RecorderEvent } from './nativeSessionRecorderSubscription';
2
+ /** Singleton class to record user sessions */
3
+ export default class SessionRecorder {
4
+ private static instance;
5
+ private eventBuffer;
6
+ private vfCounter;
7
+ private didSetupRecorder;
8
+ private isVideoLengthNegativeInvalid;
9
+ private lastFragPostTimestamp;
10
+ private pauseTimeout;
11
+ private lastRecordedTimestamp;
12
+ private firstRecordedTimestamp;
13
+ private recordStopper;
14
+ private freezingEvents;
15
+ /**
16
+ * Creates an instance of the session recorder
17
+ */
18
+ constructor();
19
+ /**
20
+ * Setups the SessionRecorder instance for usage
21
+ */
22
+ static getInstance(): SessionRecorder;
23
+ /** Sets up the page hide handler to try to push remaining video events */
24
+ setupUnloadHandler(): void;
25
+ /** Sets up the post metrics handler to potentially log a debug message */
26
+ setupPostMetricsHandler(): void;
27
+ /**
28
+ * Starts recording the user session
29
+ */
30
+ recordUserSession(): Promise<void>;
31
+ /**
32
+ * handleNewRRwebEvent will process each upcoming.
33
+ * rrweb event. It will make sure that the current buffer
34
+ * is updated with the latest events and post the contents
35
+ * of the buffer if it exceeds max size
36
+ */
37
+ handleRecorderEvent(recorderEvent: RecorderEvent): Promise<void>;
38
+ /**
39
+ * Compress event
40
+ */
41
+ private pack;
42
+ private static compress;
43
+ /** builds a log message with debug info
44
+ */
45
+ buildDebugMessage(eventName: string, totalVideoTime: number, sessionLength: number): string;
46
+ /**
47
+ * handleFragPost communicates with the Metroplex socket
48
+ * to post video fragments when needed. It also handles
49
+ * necessary management of the buffer and it's related
50
+ * variables
51
+ */
52
+ handleFragPost(): Promise<void>;
53
+ /**
54
+ * unfreeze forcefully resumes recording events in case it was frozen
55
+ * waiting for user events
56
+ */
57
+ unfreeze(): Promise<void>;
58
+ /** stops recording */
59
+ private freeze;
60
+ }