noibu-react-native 0.2.7 → 0.2.8
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/android/build.gradle +1 -1
- package/dist/api/ClientConfig.d.ts +99 -0
- package/dist/api/ClientConfig.js +25 -23
- package/dist/api/ClientConfig.test.d.ts +1 -0
- package/dist/api/HelpCode.d.ts +16 -0
- package/dist/api/HelpCode.js +3 -2
- package/dist/api/InputManager.d.ts +39 -0
- package/dist/api/MetroplexSocket.d.ts +132 -0
- package/dist/api/MetroplexSocket.js +4 -3
- package/dist/api/MetroplexSocket.test.d.ts +1 -0
- package/dist/api/StoredMetrics.d.ts +63 -0
- package/dist/api/StoredPageVisit.d.ts +43 -0
- package/dist/api/StoredPageVisit.js +4 -3
- package/dist/const_matchers.d.ts +1 -0
- package/dist/constants.d.ts +48 -0
- package/dist/constants.js +2 -9
- package/dist/entry/index.d.ts +13 -0
- package/dist/entry/init.d.ts +8 -0
- package/dist/entry/init.js +4 -3
- package/dist/monitors/AppNavigationMonitor.d.ts +10 -0
- package/dist/monitors/AppNavigationMonitor.js +2 -2
- package/dist/monitors/AppNavigationMonitor.test.d.ts +1 -0
- package/dist/monitors/BaseMonitor.d.ts +13 -0
- package/dist/monitors/BaseMonitor.test.d.ts +1 -0
- package/dist/monitors/ClickMonitor.d.ts +28 -0
- package/dist/monitors/ClickMonitor.test.d.ts +1 -0
- package/dist/monitors/ErrorMonitor.d.ts +39 -0
- package/dist/monitors/KeyboardInputMonitor.d.ts +18 -0
- package/dist/monitors/PageMonitor.d.ts +20 -0
- package/dist/monitors/RequestMonitor.d.ts +74 -0
- package/dist/monitors/RequestMonitor.js +8 -8
- package/dist/monitors/http-tools/GqlErrorValidator.d.ts +35 -0
- package/dist/monitors/http-tools/GqlErrorValidator.js +5 -3
- package/dist/monitors/http-tools/HTTPDataBundler.d.ts +106 -0
- package/dist/monitors/http-tools/HTTPDataBundler.js +7 -5
- package/dist/monitors/integrations/ReactNativeNavigationIntegration.d.ts +17 -0
- package/dist/pageVisit/EventDebouncer.d.ts +23 -0
- package/dist/pageVisit/HttpEventManager.d.ts +14 -0
- package/dist/pageVisit/PageVisitManager.d.ts +31 -0
- package/dist/pageVisit/pageVisitEventError.d.ts +12 -0
- package/dist/pageVisit/pageVisitEventError.js +2 -2
- package/dist/react/ErrorBoundary.d.ts +67 -0
- package/dist/sessionRecorder/SessionRecorder.d.ts +50 -0
- package/dist/sessionRecorder/SessionRecorder.js +7 -6
- package/dist/sessionRecorder/nativeSessionRecorderSubscription.d.ts +77 -0
- package/dist/sessionRecorder/types.d.ts +91 -0
- package/dist/storage/RNStorageProvider.d.ts +19 -0
- package/dist/storage/Storage.d.ts +29 -0
- package/dist/storage/StorageProvider.d.ts +23 -0
- package/dist/types/NavigationIntegration.d.ts +6 -0
- package/dist/utils/date.d.ts +7 -0
- package/dist/utils/date.js +3 -2
- package/dist/utils/eventlistener.d.ts +8 -0
- package/dist/utils/eventlistener.js +2 -3
- package/dist/utils/function.d.ts +72 -0
- package/dist/utils/log.d.ts +4 -0
- package/dist/utils/log.js +1 -3
- package/dist/utils/object.d.ts +46 -0
- package/dist/utils/performance.d.ts +6 -0
- package/dist/utils/piiRedactor.d.ts +11 -0
- package/dist/utils/polyfills.d.ts +4 -0
- package/dist/utils/stacktrace-parser.d.ts +8 -0
- package/dist/utils/stacktrace-parser.test.d.ts +1 -0
- package/package.json +2 -2
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { __awaiter } from 'tslib';
|
|
2
|
-
import {
|
|
2
|
+
import { CONTENT_TYPE, PII_REDACTION_REPLACEMENT_STRING } from '../../constants.js';
|
|
3
3
|
import ClientConfig from '../../api/ClientConfig.js';
|
|
4
4
|
import StoredMetrics from '../../api/StoredMetrics.js';
|
|
5
5
|
import { safeFromEntries, safeEntries } from '../../utils/object.js';
|
|
6
6
|
import { safeTrim, stringifyJSON } from '../../utils/function.js';
|
|
7
7
|
import { removePII } from '../../utils/piiRedactor.js';
|
|
8
8
|
import { Singleton } from '../BaseMonitor.js';
|
|
9
|
+
import '../../node_modules/@noibu/metroplex-ts-bindings/dist/index.js';
|
|
10
|
+
import { Severity } from '../../node_modules/@noibu/metroplex-ts-bindings/dist/Severity.js';
|
|
9
11
|
|
|
10
12
|
const DEFAULT_WEBSITE_SUBDOMAIN_PATTERN = /^www\d{0,2}$/;
|
|
11
13
|
const CONTENT_LENGTH = 'content-length';
|
|
@@ -60,7 +62,7 @@ class HTTPDataBundler extends Singleton {
|
|
|
60
62
|
this.initialURLPartsReversed.reverse();
|
|
61
63
|
}
|
|
62
64
|
catch (e) {
|
|
63
|
-
ClientConfig.getInstance().postInternalError({ msg: `Unable to determine hostname for initial URL`, error: e }, false,
|
|
65
|
+
ClientConfig.getInstance().postInternalError({ msg: `Unable to determine hostname for initial URL`, error: e }, false, Severity.WARN);
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
this.httpDataCollectionEnabled = !!ClientConfig.getInstance().enableHttpDataCollection;
|
|
@@ -165,7 +167,7 @@ class HTTPDataBundler extends Singleton {
|
|
|
165
167
|
return text;
|
|
166
168
|
}
|
|
167
169
|
catch (e) {
|
|
168
|
-
ClientConfig.getInstance().postInternalError({ msg: `Unable to stringify JSON response`, error: e }, false,
|
|
170
|
+
ClientConfig.getInstance().postInternalError({ msg: `Unable to stringify JSON response`, error: e }, false, Severity.WARN);
|
|
169
171
|
return null;
|
|
170
172
|
}
|
|
171
173
|
}
|
|
@@ -284,7 +286,7 @@ class HTTPDataBundler extends Singleton {
|
|
|
284
286
|
ClientConfig.getInstance().postInternalError({
|
|
285
287
|
msg: `restrictPayload received non string payload`,
|
|
286
288
|
payloadType: typeof payload,
|
|
287
|
-
}, false,
|
|
289
|
+
}, false, Severity.ERROR);
|
|
288
290
|
return HTTP_BODY_NULL_STRING;
|
|
289
291
|
}
|
|
290
292
|
if (payload === HTTP_BODY_NULL_STRING ||
|
|
@@ -428,7 +430,7 @@ class HTTPDataBundler extends Singleton {
|
|
|
428
430
|
return stringifyJSON(value);
|
|
429
431
|
}
|
|
430
432
|
catch (e) {
|
|
431
|
-
ClientConfig.getInstance().postInternalError({ msg: `Unable to stringify request body`, error: e }, false,
|
|
433
|
+
ClientConfig.getInstance().postInternalError({ msg: `Unable to stringify request body`, error: e }, false, Severity.WARN);
|
|
432
434
|
}
|
|
433
435
|
return null;
|
|
434
436
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NavigationDelegate } from 'react-native-navigation/lib/dist/src/NavigationDelegate';
|
|
2
|
+
import { Singleton } from '../BaseMonitor';
|
|
3
|
+
/** react-native-navigation adapter */
|
|
4
|
+
export declare class ReactNativeNavigationIntegration extends Singleton implements NavigationIntegration {
|
|
5
|
+
private stack;
|
|
6
|
+
private stackPointers;
|
|
7
|
+
private registration;
|
|
8
|
+
/** Attaches provided listeners to the integration */
|
|
9
|
+
register(navigation: NavigationDelegate, onNavigation: (breadcrumbs: string[]) => void): void;
|
|
10
|
+
/**
|
|
11
|
+
* Listens to ComponentWillAppear events, keeps track of visited screens and
|
|
12
|
+
* pops them if the same page is visited to prevent cycles
|
|
13
|
+
*/
|
|
14
|
+
private getListener;
|
|
15
|
+
/** Destructor */
|
|
16
|
+
protected destroy(): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Singleton } from '../monitors/BaseMonitor';
|
|
2
|
+
import { EventType, PageVisitEvent, UserStepType } from '@noibu/metroplex-ts-bindings';
|
|
3
|
+
/**
|
|
4
|
+
* Singleton class responsible for debouncing all events
|
|
5
|
+
* that are registered
|
|
6
|
+
*/
|
|
7
|
+
export declare class EventDebouncer extends Singleton {
|
|
8
|
+
private readonly debouncePeriods;
|
|
9
|
+
private readonly timeouts;
|
|
10
|
+
private readonly events;
|
|
11
|
+
/** Creates an instance of EventDebouncer */
|
|
12
|
+
constructor();
|
|
13
|
+
/**
|
|
14
|
+
* Creates an event object with the event and the time it was added then pushes
|
|
15
|
+
* that event object to the queue of events waiting to be debounced.
|
|
16
|
+
*/
|
|
17
|
+
debounce(event: Omit<PageVisitEvent, 'occ_at'>): void;
|
|
18
|
+
/** Debounce function to be executed once the debounce period is completed */
|
|
19
|
+
sendEvents: (type: EventType | UserStepType) => void;
|
|
20
|
+
/** Sets up the page hide handler to try to push remaining events in the queues */
|
|
21
|
+
_setupUnloadHandler(): void;
|
|
22
|
+
protected destroy(): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NoSeq } from '../types/Metroplex';
|
|
2
|
+
import { PageVisitEventHTTP, RawHttpData } from '@noibu/metroplex-ts-bindings';
|
|
3
|
+
/** Saves the HTTP event to the pageVisit Queue */
|
|
4
|
+
export declare function saveHTTPEvent(httpEvent: Partial<PageVisitEventHTTP>, httpData?: NoSeq<RawHttpData> | null, isGqlError?: boolean): number | undefined;
|
|
5
|
+
/** Determines if a response is a failure */
|
|
6
|
+
export declare function isHttpCodeFailure(code: unknown): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Checks if sending data is allowed based on the HTTP status code and count.
|
|
9
|
+
* status - The HTTP status code to evaluate.
|
|
10
|
+
* count - The count of events to consider.
|
|
11
|
+
* isGqlError - Whether the context is considered as a GQL error.
|
|
12
|
+
* Returns `true` if sending data is allowed, `false` otherwise.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isSendAllowed(status: number, count: number, isGqlError?: boolean): boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Singleton } from '../monitors/BaseMonitor';
|
|
2
|
+
import { PageVisitEvent } from '@noibu/metroplex-ts-bindings';
|
|
3
|
+
/**
|
|
4
|
+
* Singleton class to hold all the information
|
|
5
|
+
* about the gathered errors throught the session
|
|
6
|
+
*/
|
|
7
|
+
export declare class PageVisitManager extends Singleton {
|
|
8
|
+
partCounter: number;
|
|
9
|
+
pvEvents: PageVisitEvent[];
|
|
10
|
+
pvEventLength: number;
|
|
11
|
+
visibilityChangedCounter: number;
|
|
12
|
+
totalPvEventLength: number;
|
|
13
|
+
/** adds page visit events into the current page visit map and then sends a page visit message
|
|
14
|
+
*/
|
|
15
|
+
addPageVisitEvents(eventObjects: PageVisitEvent[]): void;
|
|
16
|
+
/**
|
|
17
|
+
* adds the page visit event into the current page visit map and then sends a page visit message
|
|
18
|
+
* returns the key to access this event in the map
|
|
19
|
+
*/
|
|
20
|
+
addPageVisitEvent(eventObj: Omit<PageVisitEvent, 'occ_at'>): void;
|
|
21
|
+
/**
|
|
22
|
+
* adds a new page visit event into the current page visit map and returns the
|
|
23
|
+
* the key to access this event in the map
|
|
24
|
+
*/
|
|
25
|
+
_addPageVisitEvent(pvEvent: PageVisitEvent): void;
|
|
26
|
+
/**
|
|
27
|
+
* _sendPageVisitMessage will reset the buffer and post the current
|
|
28
|
+
* content to metroplex
|
|
29
|
+
*/
|
|
30
|
+
_sendPageVisitMessage(): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Option, PageVisitEventError } from '@noibu/metroplex-ts-bindings';
|
|
2
|
+
import { EventInput, PageVisitErrorInput } from '../types/PageVisitErrors';
|
|
3
|
+
/**
|
|
4
|
+
* gets the onURL of a string, defaulting to the location of the webpage
|
|
5
|
+
*/
|
|
6
|
+
export declare function getOnURL(realOnURL: unknown): string;
|
|
7
|
+
/**
|
|
8
|
+
* determines if an error is a collect error
|
|
9
|
+
*/
|
|
10
|
+
export declare function isErrorCollectedByNoibu(pvError: PageVisitEventError): Promise<false | void>;
|
|
11
|
+
/** Saves the error to the Error queue */
|
|
12
|
+
export declare function saveErrorToPagevisit<T extends PageVisitErrorInput | EventInput>(payload: T, httpDataSeqNum?: Option<number>): void;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { __awaiter } from 'tslib';
|
|
2
2
|
import { isValidURL, getJSStack, stringifyJSON, getMaxSubstringAllowed, asString } from '../utils/function.js';
|
|
3
|
-
import { SEVERITY } from '../constants.js';
|
|
4
3
|
import ClientConfig from '../api/ClientConfig.js';
|
|
5
4
|
import StoredMetrics from '../api/StoredMetrics.js';
|
|
6
5
|
import { EventDebouncer } from './EventDebouncer.js';
|
|
7
6
|
import '../node_modules/@noibu/metroplex-ts-bindings/dist/index.js';
|
|
8
7
|
import { PageVisitErrorSource } from '../node_modules/@noibu/metroplex-ts-bindings/dist/PageVisitErrorSource.js';
|
|
9
8
|
import { EventType } from '../node_modules/@noibu/metroplex-ts-bindings/dist/EventType.js';
|
|
9
|
+
import { Severity } from '../node_modules/@noibu/metroplex-ts-bindings/dist/Severity.js';
|
|
10
10
|
import { ErrorType } from '../node_modules/@noibu/metroplex-ts-bindings/dist/ErrorType.js';
|
|
11
11
|
|
|
12
12
|
const BLOCKLISTED_DOMAINS = {
|
|
@@ -170,7 +170,7 @@ function isErrorCollectedByNoibu(pvError) {
|
|
|
170
170
|
// checking if this error originated from the Noibu script
|
|
171
171
|
const msg = getCollectErrorMsg(pvError);
|
|
172
172
|
if (msg) {
|
|
173
|
-
return ClientConfig.getInstance().postInternalError({ msg, pvError }, false,
|
|
173
|
+
return ClientConfig.getInstance().postInternalError({ msg, pvError }, false, Severity.ERROR);
|
|
174
174
|
}
|
|
175
175
|
return false;
|
|
176
176
|
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export type FallbackRender = (errorData: {
|
|
3
|
+
error: Error;
|
|
4
|
+
componentStack: string | null;
|
|
5
|
+
eventId: string | null;
|
|
6
|
+
resetError(): void;
|
|
7
|
+
}) => React.ReactElement;
|
|
8
|
+
export type ErrorBoundaryProps = {
|
|
9
|
+
children?: React.ReactNode | (() => React.ReactNode);
|
|
10
|
+
/**
|
|
11
|
+
* Target fallback component that gets rendered when the error boundary encounters an error.
|
|
12
|
+
*
|
|
13
|
+
* Can either provide a React Component, or a function that returns React Component as
|
|
14
|
+
* a valid fallback prop. If a function is provided, the function will be called with
|
|
15
|
+
* the error, the component stack, and a function that resets the error boundary on error.
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
fallback?: React.ReactElement | FallbackRender;
|
|
19
|
+
/** Called when the error boundary encounters an error */
|
|
20
|
+
onError?(error: Error, componentStack: string, eventId: string): void;
|
|
21
|
+
/** Called on componentDidMount() */
|
|
22
|
+
onMount?(): void;
|
|
23
|
+
/** Called if resetError() is called from the fallback render props function */
|
|
24
|
+
onReset?(error: Error | null, componentStack: string | null, eventId: string | null): void;
|
|
25
|
+
/** Called on componentWillUnmount() */
|
|
26
|
+
onUnmount?(error: Error | null, componentStack: string | null, eventId: string | null): void;
|
|
27
|
+
};
|
|
28
|
+
export type ErrorBoundaryState = {
|
|
29
|
+
componentStack: React.ErrorInfo['componentStack'] | null;
|
|
30
|
+
error: Error | null;
|
|
31
|
+
eventId: string | null;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* @description Target ErrorBoundary component that logs errors to Noibu. Requires React >= 16.
|
|
35
|
+
* @extends {Component<ErrorBoundaryProps, ErrorBoundaryState>}
|
|
36
|
+
*/
|
|
37
|
+
export declare class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
38
|
+
state: ErrorBoundaryState;
|
|
39
|
+
/**
|
|
40
|
+
* Lifecycle hook on mount
|
|
41
|
+
* @returns void
|
|
42
|
+
*/
|
|
43
|
+
componentDidMount(): void;
|
|
44
|
+
/**
|
|
45
|
+
*
|
|
46
|
+
* Handler for all errors that happen in a wrapped component
|
|
47
|
+
* @param {Error&{cause?:Error}} error
|
|
48
|
+
* @param {React.ErrorInfo} {componentStack}
|
|
49
|
+
* @returns void
|
|
50
|
+
*/
|
|
51
|
+
componentDidCatch(error: Error & {
|
|
52
|
+
cause?: Error;
|
|
53
|
+
}, { componentStack }: React.ErrorInfo): void;
|
|
54
|
+
/**
|
|
55
|
+
* * Lifecycle hook on unmount
|
|
56
|
+
* @returns void
|
|
57
|
+
*/
|
|
58
|
+
componentWillUnmount(): void;
|
|
59
|
+
/** Callback from fallback to reset the error boundary */
|
|
60
|
+
resetErrorBoundary: () => void;
|
|
61
|
+
/**
|
|
62
|
+
*
|
|
63
|
+
* Renders the fallback ui
|
|
64
|
+
* @returns {React.ReactNode}
|
|
65
|
+
*/
|
|
66
|
+
render(): React.ReactNode;
|
|
67
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { RecorderEvent } from './nativeSessionRecorderSubscription';
|
|
2
|
+
import { Singleton } from '../monitors/BaseMonitor';
|
|
3
|
+
/** Singleton class to record user sessions */
|
|
4
|
+
export default class SessionRecorder extends Singleton {
|
|
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
|
+
/** Setups the SessionRecorder instance for usage */
|
|
16
|
+
constructor();
|
|
17
|
+
/** Sets up the page hide handler to try to push remaining video events */
|
|
18
|
+
setupUnloadHandler(): void;
|
|
19
|
+
/** Sets up the post metrics handler to potentially log a debug message */
|
|
20
|
+
setupPostMetricsHandler(): void;
|
|
21
|
+
/** Starts recording the user session */
|
|
22
|
+
recordUserSession(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* handleNewRRwebEvent will process each upcoming.
|
|
25
|
+
* rrweb event. It will make sure that the current buffer
|
|
26
|
+
* is updated with the latest events and post the contents
|
|
27
|
+
* of the buffer if it exceeds max size
|
|
28
|
+
*/
|
|
29
|
+
handleRecorderEvent(recorderEvent: RecorderEvent): Promise<void>;
|
|
30
|
+
/** Compress event */
|
|
31
|
+
private pack;
|
|
32
|
+
/** Compresses the snapshot */
|
|
33
|
+
private static compress;
|
|
34
|
+
/** builds a log message with debug info */
|
|
35
|
+
buildDebugMessage(eventName: string, totalVideoTime: number, sessionLength: number): string;
|
|
36
|
+
/**
|
|
37
|
+
* handleFragPost communicates with the Metroplex socket
|
|
38
|
+
* to post video fragments when needed. It also handles
|
|
39
|
+
* necessary management of the buffer and it's related
|
|
40
|
+
* variables
|
|
41
|
+
*/
|
|
42
|
+
handleFragPost(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* unfreeze forcefully resumes recording events in case it was frozen
|
|
45
|
+
* waiting for user events
|
|
46
|
+
*/
|
|
47
|
+
unfreeze(): Promise<void>;
|
|
48
|
+
/** stops recording */
|
|
49
|
+
private freeze;
|
|
50
|
+
}
|
|
@@ -4,13 +4,14 @@ import { LogLevel, initialize, subscribeToNativeEvent } from './nativeSessionRec
|
|
|
4
4
|
import StoredMetrics from '../api/StoredMetrics.js';
|
|
5
5
|
import ClientConfig from '../api/ClientConfig.js';
|
|
6
6
|
import { stringifyJSON } from '../utils/function.js';
|
|
7
|
-
import {
|
|
7
|
+
import { MAX_TIME_FOR_UNSENT_DATA_MILLIS } from '../constants.js';
|
|
8
8
|
import MetroplexSocket from '../api/MetroplexSocket.js';
|
|
9
9
|
import { addSafeEventListener } from '../utils/eventlistener.js';
|
|
10
10
|
import { noibuLog } from '../utils/log.js';
|
|
11
11
|
import { Platform } from 'react-native';
|
|
12
12
|
import { Singleton } from '../monitors/BaseMonitor.js';
|
|
13
13
|
import '../node_modules/@noibu/metroplex-ts-bindings/dist/index.js';
|
|
14
|
+
import { Severity } from '../node_modules/@noibu/metroplex-ts-bindings/dist/Severity.js';
|
|
14
15
|
import { WebsocketMessageType } from '../node_modules/@noibu/metroplex-ts-bindings/dist/WebsocketMessageType.js';
|
|
15
16
|
|
|
16
17
|
// custom event name for posting metrics
|
|
@@ -78,7 +79,7 @@ class SessionRecorder extends Singleton {
|
|
|
78
79
|
eventName,
|
|
79
80
|
totalVideoTime,
|
|
80
81
|
sessionLength,
|
|
81
|
-
}, clientDisabled,
|
|
82
|
+
}, clientDisabled, Severity.WARN);
|
|
82
83
|
}
|
|
83
84
|
});
|
|
84
85
|
}
|
|
@@ -147,7 +148,7 @@ class SessionRecorder extends Singleton {
|
|
|
147
148
|
// If we don't adjust for time, we assume that the expected video length is
|
|
148
149
|
// the difference between the first recorded timestamp and the last recorded timestamp.
|
|
149
150
|
if (this.firstRecordedTimestamp && timestamp < this.firstRecordedTimestamp) {
|
|
150
|
-
ClientConfig.getInstance().postInternalError({ msg: `Detected time rewind. Client has been disabled.` }, true,
|
|
151
|
+
ClientConfig.getInstance().postInternalError({ msg: `Detected time rewind. Client has been disabled.` }, true, Severity.ERROR, true);
|
|
151
152
|
return;
|
|
152
153
|
}
|
|
153
154
|
const packedEvent = yield this.pack(recorderEvent.message);
|
|
@@ -216,7 +217,7 @@ class SessionRecorder extends Singleton {
|
|
|
216
217
|
totalVideoTime,
|
|
217
218
|
'start time': this.firstRecordedTimestamp,
|
|
218
219
|
'end time': this.lastRecordedTimestamp,
|
|
219
|
-
}, false,
|
|
220
|
+
}, false, Severity.ERROR);
|
|
220
221
|
this.isVideoLengthNegativeInvalid = true;
|
|
221
222
|
totalVideoTime = 0;
|
|
222
223
|
}
|
|
@@ -243,7 +244,7 @@ class SessionRecorder extends Singleton {
|
|
|
243
244
|
}
|
|
244
245
|
catch (err) {
|
|
245
246
|
// letting collect know we are closing the rrweb listener
|
|
246
|
-
ClientConfig.getInstance().postInternalError({ msg: `video frag socket closed with err: ${err.message}` }, false,
|
|
247
|
+
ClientConfig.getInstance().postInternalError({ msg: `video frag socket closed with err: ${err.message}` }, false, Severity.ERROR);
|
|
247
248
|
// if we detect an error in the frag posting, we stop recording
|
|
248
249
|
// the video
|
|
249
250
|
this.freeze();
|
|
@@ -270,7 +271,7 @@ class SessionRecorder extends Singleton {
|
|
|
270
271
|
this.recordStopper();
|
|
271
272
|
}
|
|
272
273
|
catch (e) {
|
|
273
|
-
ClientConfig.getInstance().postInternalError({ msg: `Error during handleFragPost in recordStopper: ${e}` }, false,
|
|
274
|
+
ClientConfig.getInstance().postInternalError({ msg: `Error during handleFragPost in recordStopper: ${e}` }, false, Severity.ERROR);
|
|
274
275
|
}
|
|
275
276
|
}
|
|
276
277
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** The level of logging to show in the device logcat stream. */
|
|
2
|
+
export declare enum LogLevel {
|
|
3
|
+
Verbose = "Verbose",
|
|
4
|
+
Debug = "Debug",
|
|
5
|
+
Info = "Info",
|
|
6
|
+
Warning = "Warning",
|
|
7
|
+
Error = "Error",
|
|
8
|
+
None = "None"
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The configuration that will be used to customize the session recording behaviour.
|
|
12
|
+
*
|
|
13
|
+
* @param userId [OPTIONAL default = null] Target custom identifier for the current user. If passed as null, the user id
|
|
14
|
+
* will be auto generated. The user id in general is sticky across sessions.
|
|
15
|
+
* The provided user id must follow these conditions:
|
|
16
|
+
* 1. Cannot be an empty string.
|
|
17
|
+
* 2. Should be base36 and smaller than "1Z141Z4".
|
|
18
|
+
* @param logLevel [OPTIONAL default = LogLevel.None] The level of logging to show in the device logcat stream.
|
|
19
|
+
* @param allowMeteredNetworkUsage [OPTIONAL default = false] Allows uploading session data to the servers on device metered network.
|
|
20
|
+
* @param enableWebViewCapture [OPTIONAL default = true] Allows Noibu - Session recorder to capture the web views DOM content.
|
|
21
|
+
* @param allowedDomains [OPTIONAL default = ["*"]] The whitelisted domains to allow Noibu - Session recorder to capture their DOM content.
|
|
22
|
+
* If it contains "*" as an element, all domains will be captured.
|
|
23
|
+
* @param disableOnLowEndDevices [OPTIONAL default = false] Disable Noibu - Session recorder on low-end devices.
|
|
24
|
+
* @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.
|
|
25
|
+
*/
|
|
26
|
+
export interface SessionRecorderConfig {
|
|
27
|
+
userId?: string | null;
|
|
28
|
+
logLevel?: LogLevel;
|
|
29
|
+
allowMeteredNetworkUsage?: boolean;
|
|
30
|
+
enableWebViewCapture?: boolean;
|
|
31
|
+
allowedDomains?: string[];
|
|
32
|
+
disableOnLowEndDevices?: boolean;
|
|
33
|
+
maximumDailyNetworkUsageInMB?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Initializes the Noibu - Session recording SDK if the API level is supported.
|
|
37
|
+
* param projectId [REQUIRED] The session recording project id to send data to.
|
|
38
|
+
* param config [OPTIONAL] The sessionreplay config, if not provided default values are used.
|
|
39
|
+
*/
|
|
40
|
+
export declare function initialize(projectId: string, config?: SessionRecorderConfig): void;
|
|
41
|
+
/**
|
|
42
|
+
* Sets a custom user id that can be used to identify the user. It has less
|
|
43
|
+
* restrictions than the userId parameter. You can pass any string and
|
|
44
|
+
* you can filter on it on the dashboard side. If you need the most efficient
|
|
45
|
+
* filtering on the dashboard, use the userId parameter if possible.
|
|
46
|
+
* <p>
|
|
47
|
+
* Note: custom user id cannot be null or empty, or consists only of whitespaces.
|
|
48
|
+
* </p>
|
|
49
|
+
* @param customUserId The custom user id to set.
|
|
50
|
+
*/
|
|
51
|
+
export declare function setCustomUserId(customUserId: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* Sets a custom session id that can be used to identify the session.
|
|
54
|
+
* <p>
|
|
55
|
+
* Note: custom session id cannot be null or empty, or consists only of whitespaces.
|
|
56
|
+
* </p>
|
|
57
|
+
* @param customSessionId The custom session id to set.
|
|
58
|
+
*/
|
|
59
|
+
export declare function setCustomSessionId(customSessionId: string): void;
|
|
60
|
+
export type RecorderEvent = import('./types').RecorderEvent;
|
|
61
|
+
export type UnsubscribeFn = import('./types').UnsubscribeFn;
|
|
62
|
+
/**
|
|
63
|
+
* Subscribes to a native event emitted by the Noibu Session Recorder.
|
|
64
|
+
*
|
|
65
|
+
* This function listens for the `noibuRecordingEvent` emitted from the native layer (only on Android)
|
|
66
|
+
* and invokes the provided callback whenever the event occurs. If the platform is not Android,
|
|
67
|
+
* the function will do nothing and return a no-op unsubscribe function.
|
|
68
|
+
*
|
|
69
|
+
* @param {function(RecorderEvent): void} callback - Target callback function that will be invoked with
|
|
70
|
+
* the event data whenever the `noibuRecordingEvent` is emitted.
|
|
71
|
+
*
|
|
72
|
+
* @returns {UnsubscribeFn} Target function to unsubscribe from the event. On Android, this will remove
|
|
73
|
+
* the event listener. On other platforms, it will be a no-op.
|
|
74
|
+
*
|
|
75
|
+
* @throws {Error} If the Noibu Session Recorder is not initialized before calling this function.
|
|
76
|
+
*/
|
|
77
|
+
export declare function subscribeToNativeEvent(callback: (event: RecorderEvent) => void): UnsubscribeFn;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
type Color = {
|
|
2
|
+
a: number;
|
|
3
|
+
b: number;
|
|
4
|
+
r: number;
|
|
5
|
+
g: number;
|
|
6
|
+
};
|
|
7
|
+
type Paint = {
|
|
8
|
+
strokeJoin: number;
|
|
9
|
+
strokeWidth: number;
|
|
10
|
+
strokeCap: number;
|
|
11
|
+
color: Color;
|
|
12
|
+
dither: boolean;
|
|
13
|
+
blendMode: number;
|
|
14
|
+
style: number;
|
|
15
|
+
antiAlias: boolean;
|
|
16
|
+
strokeMiter: number;
|
|
17
|
+
};
|
|
18
|
+
type Rect = {
|
|
19
|
+
top: number;
|
|
20
|
+
left: number;
|
|
21
|
+
bottom: number;
|
|
22
|
+
right: number;
|
|
23
|
+
};
|
|
24
|
+
type Command = {
|
|
25
|
+
name?: string;
|
|
26
|
+
id?: number;
|
|
27
|
+
type: string;
|
|
28
|
+
isClipRectSource?: boolean;
|
|
29
|
+
rect?: Rect;
|
|
30
|
+
paintIndex?: number;
|
|
31
|
+
op?: number;
|
|
32
|
+
antiAlias?: boolean;
|
|
33
|
+
matrix?: number[];
|
|
34
|
+
};
|
|
35
|
+
type ViewNode = {
|
|
36
|
+
viewX: number;
|
|
37
|
+
visible: boolean;
|
|
38
|
+
processedText$delegate: {
|
|
39
|
+
_value: any;
|
|
40
|
+
initializer: any;
|
|
41
|
+
};
|
|
42
|
+
viewY: number;
|
|
43
|
+
viewWidth: number;
|
|
44
|
+
clickable: boolean;
|
|
45
|
+
viewHeight: number;
|
|
46
|
+
isMasked: boolean;
|
|
47
|
+
type: string;
|
|
48
|
+
renderNodeId: number;
|
|
49
|
+
isWebView: boolean;
|
|
50
|
+
ignoreClicks: boolean;
|
|
51
|
+
children: ViewNode[];
|
|
52
|
+
width: number;
|
|
53
|
+
x: number;
|
|
54
|
+
y: number;
|
|
55
|
+
id: number;
|
|
56
|
+
text: string;
|
|
57
|
+
height: number;
|
|
58
|
+
backgroundColor?: number;
|
|
59
|
+
};
|
|
60
|
+
type ViewHierarchy = {
|
|
61
|
+
root: ViewNode;
|
|
62
|
+
visibleFragments: any[];
|
|
63
|
+
timestamp: number;
|
|
64
|
+
};
|
|
65
|
+
type SubPicture = {
|
|
66
|
+
subPictures: any[];
|
|
67
|
+
images: any[];
|
|
68
|
+
screenWidth: number;
|
|
69
|
+
textBlobs: any[];
|
|
70
|
+
density: number;
|
|
71
|
+
vertices: any[];
|
|
72
|
+
screenHeight: number;
|
|
73
|
+
activityName: string;
|
|
74
|
+
paints: Paint[];
|
|
75
|
+
typefaces: any[];
|
|
76
|
+
viewHierarchy: ViewHierarchy;
|
|
77
|
+
paths: any[];
|
|
78
|
+
activityHashCode: number;
|
|
79
|
+
commands: Command[];
|
|
80
|
+
timestamp: number;
|
|
81
|
+
};
|
|
82
|
+
export type RecorderEvent = {
|
|
83
|
+
message: NativeFrames;
|
|
84
|
+
};
|
|
85
|
+
export type NativeFrames = {
|
|
86
|
+
p: (number | boolean | SubPicture)[][];
|
|
87
|
+
a: (number[] | (number | string | string[])[])[];
|
|
88
|
+
e: (string | number)[];
|
|
89
|
+
};
|
|
90
|
+
export type UnsubscribeFn = () => void;
|
|
91
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import StorageProvider from './StorageProvider';
|
|
2
|
+
/** React native storage provider implementation */
|
|
3
|
+
export default class RNStorageProvider extends StorageProvider {
|
|
4
|
+
/** Creates new instance */
|
|
5
|
+
constructor();
|
|
6
|
+
/**
|
|
7
|
+
* Checks if storage is available
|
|
8
|
+
* @returns {Object}
|
|
9
|
+
*/
|
|
10
|
+
static isAvailable(): Promise<{
|
|
11
|
+
result: boolean;
|
|
12
|
+
error: Error | null;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Calculates used scape
|
|
16
|
+
* @returns {Number}
|
|
17
|
+
*/
|
|
18
|
+
calculateUsedSize(): Promise<number>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Singleton } from '../monitors/BaseMonitor';
|
|
2
|
+
/** Encapsulates storage api */
|
|
3
|
+
export default class Storage extends Singleton implements IStorage {
|
|
4
|
+
private readonly isRNStorageAvailable;
|
|
5
|
+
private readonly rnStorageError;
|
|
6
|
+
private readonly provider;
|
|
7
|
+
private readonly type;
|
|
8
|
+
/** Creates new instance assessing available options */
|
|
9
|
+
constructor();
|
|
10
|
+
/** Checks if storage is available */
|
|
11
|
+
isAvailable(): Promise<boolean>;
|
|
12
|
+
/** Loads value from storage */
|
|
13
|
+
load(key: string): Promise<string | null>;
|
|
14
|
+
/** Saves value to storage */
|
|
15
|
+
save(key: string, value: StorageValue): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Removes value from storage
|
|
18
|
+
* @param {String} key
|
|
19
|
+
*/
|
|
20
|
+
remove(key: string): Promise<void>;
|
|
21
|
+
/** Calculates used scape */
|
|
22
|
+
calculateUsedSize(): Promise<number>;
|
|
23
|
+
/**
|
|
24
|
+
* Returns string indicating current provider type
|
|
25
|
+
* and availability for other storage types
|
|
26
|
+
* @returns {String}
|
|
27
|
+
*/
|
|
28
|
+
getDiagnoseInfo(): Promise<string>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base implementation for LocalStorage and SessionStorage
|
|
3
|
+
*/
|
|
4
|
+
export default abstract class StorageProvider {
|
|
5
|
+
provider: Provider;
|
|
6
|
+
/** Creates new instance based on provided provider type */
|
|
7
|
+
protected constructor(provider: Provider);
|
|
8
|
+
/** Checks if provider is available */
|
|
9
|
+
static isAvailable<T extends Provider>(resolver: () => T): Promise<{
|
|
10
|
+
result: boolean;
|
|
11
|
+
error: Error | null;
|
|
12
|
+
}>;
|
|
13
|
+
/** Loads value from storage */
|
|
14
|
+
load<R = StorageValue>(key: string): Promise<R | null>;
|
|
15
|
+
/** Saves value to storage */
|
|
16
|
+
save(key: string, value: StorageValue): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Removes value from storage
|
|
19
|
+
* @param {String} key
|
|
20
|
+
*/
|
|
21
|
+
remove(key: string): Promise<void>;
|
|
22
|
+
abstract calculateUsedSize(): Promise<number>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Checks to see if the necessary Date functions that we use have been overwritten */
|
|
2
|
+
export declare function isDateOverwritten(): boolean;
|
|
3
|
+
/** Timestamp wrapper to properly handle timestamps
|
|
4
|
+
*/
|
|
5
|
+
export declare function timestampWrapper<T>(timestamp: T): number | T;
|
|
6
|
+
/** Get the current time in ISO format */
|
|
7
|
+
export declare function getOccurredNow(): string;
|
package/dist/utils/date.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import ClientConfig from '../api/ClientConfig.js';
|
|
2
|
-
import
|
|
2
|
+
import '../node_modules/@noibu/metroplex-ts-bindings/dist/index.js';
|
|
3
|
+
import { Severity } from '../node_modules/@noibu/metroplex-ts-bindings/dist/Severity.js';
|
|
3
4
|
|
|
4
5
|
/** @module Date */
|
|
5
6
|
/** Checks to see if the necessary Date functions that we use have been overwritten */
|
|
@@ -39,7 +40,7 @@ function timestampWrapper(timestamp) {
|
|
|
39
40
|
ClientConfig.getInstance().postInternalError({
|
|
40
41
|
msg: `The date object has been overwritten and can't be processed properly.
|
|
41
42
|
Client has been disabled.`,
|
|
42
|
-
}, true,
|
|
43
|
+
}, true, Severity.ERROR, true);
|
|
43
44
|
}
|
|
44
45
|
return timestamp;
|
|
45
46
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** addSafeEventListener will add an event listener for the specified event
|
|
2
|
+
* but will catch and log any errors encountered
|
|
3
|
+
* @param {} object to attach the listener to
|
|
4
|
+
* @param {} event to listen to
|
|
5
|
+
* @param {} callback function to call
|
|
6
|
+
* @param {} [capture] additional arguments
|
|
7
|
+
*/
|
|
8
|
+
export function addSafeEventListener(object: any, event: any, callback: any, capture?: any): void;
|