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.
- package/android/.gitignore +13 -0
- package/android/build.gradle +79 -0
- package/android/gradle.properties +7 -0
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/java/com/noibu/sessionreplay/reactnative/NoibuSessionReplayModule.kt +107 -0
- package/android/src/main/java/com/noibu/sessionreplay/reactnative/NoibuSessionReplayPackage.kt +17 -0
- package/dist/api/clientConfig.d.ts +6 -7
- package/dist/api/clientConfig.js +14 -16
- package/dist/api/helpCode.d.ts +29 -0
- package/dist/api/inputManager.d.ts +87 -0
- package/dist/api/metroplexSocket.d.ts +156 -0
- package/dist/api/metroplexSocket.js +662 -815
- package/dist/api/storedMetrics.d.ts +73 -0
- package/dist/api/storedPageVisit.d.ts +49 -0
- package/dist/const_matchers.d.ts +1 -0
- package/dist/constants.d.ts +10 -1
- package/dist/constants.js +19 -2
- package/dist/entry/index.d.ts +1 -1
- package/dist/entry/init.d.ts +1 -1
- package/dist/entry/init.js +10 -6
- package/dist/monitors/appNavigationMonitor.js +3 -2
- package/dist/monitors/clickMonitor.d.ts +44 -0
- package/dist/monitors/gqlErrorValidator.d.ts +82 -0
- package/dist/monitors/httpDataBundler.d.ts +161 -0
- package/dist/monitors/inputMonitor.d.ts +34 -0
- package/dist/monitors/inputMonitor.js +5 -0
- package/dist/monitors/integrations/react-native-navigation-integration.d.ts +1 -2
- package/dist/monitors/keyboardInputMonitor.d.ts +17 -0
- package/dist/monitors/pageMonitor.d.ts +22 -0
- package/dist/monitors/requestMonitor.d.ts +10 -0
- package/dist/pageVisit/pageVisit.d.ts +52 -0
- package/dist/pageVisit/pageVisit.js +9 -2
- package/dist/pageVisit/pageVisitEventError.d.ts +15 -0
- package/dist/pageVisit/pageVisitEventHTTP.d.ts +18 -0
- package/dist/pageVisit/userStep.d.ts +5 -0
- package/dist/react/ErrorBoundary.js +17 -9
- package/dist/sessionRecorder/nativeSessionRecorderSubscription.d.ts +64 -0
- package/dist/sessionRecorder/nativeSessionRecorderSubscription.js +58 -0
- package/dist/sessionRecorder/sessionRecorder.d.ts +60 -0
- package/dist/sessionRecorder/sessionRecorder.js +287 -0
- package/dist/sessionRecorder/types.d.ts +91 -0
- package/dist/types/NavigationIntegration.d.ts +1 -2
- package/dist/types/StoredPageVisit.types.d.ts +54 -0
- package/dist/types/globals.d.ts +2 -1
- package/dist/utils/date.d.ts +6 -0
- package/dist/utils/eventlistener.d.ts +8 -0
- package/dist/utils/eventlistener.js +2 -2
- package/dist/utils/function.d.ts +6 -3
- package/dist/utils/function.js +23 -10
- package/dist/utils/log.d.ts +0 -1
- package/dist/utils/object.d.ts +2 -2
- package/package.json +15 -6
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
|
|
3
|
+
def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["Noibu_kotlinVersion"]
|
|
4
|
+
|
|
5
|
+
repositories {
|
|
6
|
+
google()
|
|
7
|
+
mavenCentral()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
dependencies {
|
|
11
|
+
classpath "com.android.tools.build:gradle:8.5.1"
|
|
12
|
+
// noinspection DifferentKotlinGradleVersion
|
|
13
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def isNewArchitectureEnabled() {
|
|
18
|
+
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
apply plugin: "com.android.library"
|
|
22
|
+
apply plugin: "kotlin-android"
|
|
23
|
+
|
|
24
|
+
if (isNewArchitectureEnabled()) {
|
|
25
|
+
apply plugin: "com.facebook.react"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def getExtOrDefault(name) {
|
|
29
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["Noibu_" + name]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
def getExtOrIntegerDefault(name) {
|
|
33
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Noibu_" + name]).toInteger()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
android {
|
|
37
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
38
|
+
|
|
39
|
+
defaultConfig {
|
|
40
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
41
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
42
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
43
|
+
}
|
|
44
|
+
buildTypes {
|
|
45
|
+
release {
|
|
46
|
+
minifyEnabled false
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
lintOptions {
|
|
51
|
+
disable "GradleCompatible"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
compileOptions {
|
|
55
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
56
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
repositories {
|
|
61
|
+
mavenCentral()
|
|
62
|
+
google()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
66
|
+
|
|
67
|
+
dependencies {
|
|
68
|
+
implementation "com.facebook.react:react-native:+"
|
|
69
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
70
|
+
implementation "com.noibu:sessionreplay-recorder:0.1.0"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (isNewArchitectureEnabled()) {
|
|
74
|
+
react {
|
|
75
|
+
jsRootDir = file("../src/")
|
|
76
|
+
libraryName = "noibusessionreplay"
|
|
77
|
+
codegenJavaPackageName = "com.noibu.sessionreplay.reactnative"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
package com.noibu.sessionreplay.reactnative
|
|
2
|
+
|
|
3
|
+
import android.os.Handler
|
|
4
|
+
import android.os.Looper
|
|
5
|
+
import android.util.Log
|
|
6
|
+
import com.facebook.react.bridge.*
|
|
7
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
8
|
+
|
|
9
|
+
import com.noibu.sessionreplay.SessionReplayConfig
|
|
10
|
+
import com.noibu.sessionreplay.models.ApplicationFramework
|
|
11
|
+
import com.noibu.sessionreplay.models.LogLevel
|
|
12
|
+
|
|
13
|
+
import com.noibu.sessionreplay.SessionReplay
|
|
14
|
+
|
|
15
|
+
class NoibuSessionReplayModule(reactContext: ReactApplicationContext) :
|
|
16
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
17
|
+
|
|
18
|
+
init {
|
|
19
|
+
NoibuSessionReplayModule.reactContext = reactContext
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
override fun getName(): String {
|
|
23
|
+
return NAME
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private fun sendEvent(eventName: String, params: WritableMap?) {
|
|
27
|
+
Log.d("SessionReplayModule", "will send event: ${params}")
|
|
28
|
+
reactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)?.emit(eventName, params)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@ReactMethod
|
|
32
|
+
fun initialize(
|
|
33
|
+
projectId: String,
|
|
34
|
+
userId: String?,
|
|
35
|
+
logLevel: String,
|
|
36
|
+
allowMeteredNetworkUsage: Boolean,
|
|
37
|
+
enableWebViewCapture: Boolean,
|
|
38
|
+
allowedDomains: ReadableArray,
|
|
39
|
+
disableOnLowEndDevices: Boolean,
|
|
40
|
+
enableDailyNetworkUsageLimit: Boolean,
|
|
41
|
+
maximumDailyNetworkUsageInMB: Double,
|
|
42
|
+
promise: Promise
|
|
43
|
+
) {
|
|
44
|
+
val allowedActivities = listOf<String>(); // not supported
|
|
45
|
+
val disallowedActivities = listOf<String>(); // not supported
|
|
46
|
+
|
|
47
|
+
// We use two parameters because the react method parameters do not accept nullable primitive types.
|
|
48
|
+
// Moreover, the Long data type is not supported. Js numbers are translated into doubles.
|
|
49
|
+
val maximumDailyNetworkUsageInMBLong =
|
|
50
|
+
if (enableDailyNetworkUsageLimit) maximumDailyNetworkUsageInMB.toLong() else null
|
|
51
|
+
|
|
52
|
+
val config = SessionReplayConfig(
|
|
53
|
+
projectId,
|
|
54
|
+
userId,
|
|
55
|
+
LogLevel.valueOf(logLevel),
|
|
56
|
+
allowMeteredNetworkUsage,
|
|
57
|
+
enableWebViewCapture,
|
|
58
|
+
readableArrayToList(allowedDomains),
|
|
59
|
+
ApplicationFramework.ReactNative,
|
|
60
|
+
allowedActivities,
|
|
61
|
+
disallowedActivities,
|
|
62
|
+
disableOnLowEndDevices,
|
|
63
|
+
maximumDailyNetworkUsageInMBLong
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
Handler(Looper.getMainLooper()).post {
|
|
67
|
+
promise.resolve(
|
|
68
|
+
SessionReplay.initialize(
|
|
69
|
+
currentActivity!!.getApplicationContext(),
|
|
70
|
+
config,
|
|
71
|
+
currentActivity
|
|
72
|
+
) { param ->
|
|
73
|
+
Handler(Looper.getMainLooper()).post {
|
|
74
|
+
val params = Arguments.createMap()
|
|
75
|
+
params.putString("message", param)
|
|
76
|
+
sendEvent("noibuRecordingEvent", params)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@ReactMethod
|
|
84
|
+
fun setCustomUserId(customUserId: String, promise: Promise) {
|
|
85
|
+
promise.resolve(SessionReplay.setCustomUserId(customUserId))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@ReactMethod
|
|
89
|
+
fun setCustomSessionId(customSessionId: String, promise: Promise) {
|
|
90
|
+
promise.resolve(SessionReplay.setCustomSessionId(customSessionId))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private fun readableArrayToList(arr: ReadableArray): List<String> {
|
|
94
|
+
val ret = mutableListOf<String>()
|
|
95
|
+
|
|
96
|
+
for (i in 0 until arr.size()) {
|
|
97
|
+
ret.add(arr.getString(i))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return ret
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
companion object {
|
|
104
|
+
const val NAME = "NativeSessionRecorder"
|
|
105
|
+
private var reactContext: ReactApplicationContext? = null
|
|
106
|
+
}
|
|
107
|
+
}
|
package/android/src/main/java/com/noibu/sessionreplay/reactnative/NoibuSessionReplayPackage.kt
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
package com.noibu.sessionreplay.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
|
+
import com.facebook.react.bridge.JavaScriptModule
|
|
5
|
+
import com.facebook.react.bridge.NativeModule
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
|
+
import com.facebook.react.uimanager.ViewManager
|
|
8
|
+
|
|
9
|
+
class NoibuSessionReplayPackage : ReactPackage {
|
|
10
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
11
|
+
return listOf(NoibuSessionReplayModule(reactContext))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
15
|
+
return emptyList()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -7,10 +7,10 @@ import { CustomerConfig, StoredConfig } from '../types/Config';
|
|
|
7
7
|
* storing and retrieval.
|
|
8
8
|
*/
|
|
9
9
|
export default class ClientConfig {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
readonly pageVisitId: string;
|
|
11
|
+
browserId: StoredConfig['BrowserId'];
|
|
12
12
|
private pageVisitSeq;
|
|
13
|
-
|
|
13
|
+
lastActiveTime: Date;
|
|
14
14
|
private readonly noibuErrorURL;
|
|
15
15
|
private cltErrorPostCounter;
|
|
16
16
|
private readonly maxSocketInactiveTime;
|
|
@@ -30,17 +30,16 @@ export default class ClientConfig {
|
|
|
30
30
|
static configureInstance({ noibuErrorURL, customerConfig, }: {
|
|
31
31
|
noibuErrorURL: string;
|
|
32
32
|
customerConfig: CustomerConfig;
|
|
33
|
-
}): void
|
|
33
|
+
}): Promise<void>;
|
|
34
34
|
/**
|
|
35
35
|
* gets the singleton instance
|
|
36
|
-
* @returns {ClientConfig}
|
|
37
36
|
*/
|
|
38
37
|
static getInstance(): ClientConfig;
|
|
39
38
|
/** lockClient will disable the client script for a single pagevisit for
|
|
40
39
|
* duration given in minuntes */
|
|
41
40
|
lockClient(duration: number, msg: string): Promise<void>;
|
|
42
41
|
/** Locks the client until the next page loads */
|
|
43
|
-
lockClientUntilNextPage(msg: string): void
|
|
42
|
+
lockClientUntilNextPage(msg: string): Promise<void>;
|
|
44
43
|
/** Updates the config object to store the given last active time */
|
|
45
44
|
updateLastActiveTime(lastActiveTime: Date): Promise<void>;
|
|
46
45
|
/** Gets the current page visit sequence number that should be used */
|
|
@@ -98,7 +97,7 @@ export default class ClientConfig {
|
|
|
98
97
|
* and disable the client if required
|
|
99
98
|
* severity expects one of the SEVERITY_x level constants, or else error will be used
|
|
100
99
|
*/
|
|
101
|
-
postNoibuErrorAndOptionallyDisableClient(errorMsg:
|
|
100
|
+
postNoibuErrorAndOptionallyDisableClient(errorMsg: any, disableClient: boolean, severity: (typeof SEVERITY)[keyof typeof SEVERITY], keepAlive?: boolean): Promise<void>;
|
|
102
101
|
/**
|
|
103
102
|
* Returns true if the page visit is considered to be inactive
|
|
104
103
|
*/
|
package/dist/api/clientConfig.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import uuid from 'react-native-uuid';
|
|
2
|
-
import { MAX_METROPLEX_SOCKET_INNACTIVE_TIME, SEVERITY, NOIBU_BROWSER_ID_KYWRD, PV_SEQ_NUM_RESET_TIME_MINUTES, JS_ENV, MAX_PAGEVISIT_VISITED, CLIENT_LOCK_TIME_MINUTES, GET_SCRIPT_ID, MAX_COLLECT_ERROR_LOG } from '../constants.js';
|
|
2
|
+
import { MAX_METROPLEX_SOCKET_INNACTIVE_TIME, SEVERITY, NOIBU_BROWSER_ID_KYWRD, PV_SEQ_NUM_RESET_TIME_MINUTES, JS_ENV, MAX_PAGEVISIT_VISITED, CLIENT_LOCK_TIME_MINUTES, GET_SCRIPT_ID, GET_DEVICE_ENV, MAX_COLLECT_ERROR_LOG } from '../constants.js';
|
|
3
3
|
import { stringifyJSON, getMaxSubstringAllowed, getUserAgent, makeRequest } from '../utils/function.js';
|
|
4
4
|
import Storage from '../storage/storage.js';
|
|
5
5
|
import { noibuLog } from '../utils/log.js';
|
|
@@ -44,8 +44,6 @@ class ClientConfig {
|
|
|
44
44
|
this.lastActiveTime = new Date();
|
|
45
45
|
// error URL to send Noibu errors to
|
|
46
46
|
this.noibuErrorURL = noibuErrorURL;
|
|
47
|
-
// sets up this.browserId, this.isClientDisabled, this.pageVisitSeq
|
|
48
|
-
this._setupStorageVars();
|
|
49
47
|
// error sent to backend counter
|
|
50
48
|
this.cltErrorPostCounter = 0;
|
|
51
49
|
// variables for checking if the socket is inactive
|
|
@@ -58,18 +56,16 @@ class ClientConfig {
|
|
|
58
56
|
this.blockedElements = customerConfig.blockedElements;
|
|
59
57
|
}
|
|
60
58
|
/** Configures the singleton instance */
|
|
61
|
-
static configureInstance({ noibuErrorURL, customerConfig, }) {
|
|
59
|
+
static async configureInstance({ noibuErrorURL, customerConfig, }) {
|
|
62
60
|
if (!this.instance) {
|
|
63
|
-
// Set this.noibuErrorURL preemptively in case ClientConfig isn't able to be
|
|
64
|
-
// configured properly and throws an error.
|
|
65
|
-
// This will ensure we get the expected error POST request at the correct URL.
|
|
66
61
|
ClientConfig.noibuErrorURL = noibuErrorURL;
|
|
67
62
|
this.instance = new ClientConfig(noibuErrorURL, customerConfig);
|
|
63
|
+
// sets up this.browserId, this.isClientDisabled, this.pageVisitSeq
|
|
64
|
+
await this.instance._setupStorageVars();
|
|
68
65
|
}
|
|
69
66
|
}
|
|
70
67
|
/**
|
|
71
68
|
* gets the singleton instance
|
|
72
|
-
* @returns {ClientConfig}
|
|
73
69
|
*/
|
|
74
70
|
static getInstance() {
|
|
75
71
|
if (!this.instance) {
|
|
@@ -86,11 +82,11 @@ class ClientConfig {
|
|
|
86
82
|
noibuLSObject.DisabledStatus = true;
|
|
87
83
|
noibuLSObject.ClientUnlockTime = expiryTime;
|
|
88
84
|
await this._storeBrowserData(noibuLSObject);
|
|
89
|
-
this.postNoibuErrorAndOptionallyDisableClient(msg, true, SEVERITY.warn);
|
|
85
|
+
await this.postNoibuErrorAndOptionallyDisableClient(msg, true, SEVERITY.warn);
|
|
90
86
|
}
|
|
91
87
|
/** Locks the client until the next page loads */
|
|
92
88
|
lockClientUntilNextPage(msg) {
|
|
93
|
-
this.postNoibuErrorAndOptionallyDisableClient(msg, true, SEVERITY.warn);
|
|
89
|
+
return this.postNoibuErrorAndOptionallyDisableClient(msg, true, SEVERITY.warn);
|
|
94
90
|
}
|
|
95
91
|
/** Updates the config object to store the given last active time */
|
|
96
92
|
async updateLastActiveTime(lastActiveTime) {
|
|
@@ -167,7 +163,7 @@ class ClientConfig {
|
|
|
167
163
|
async _setupStorageVars() {
|
|
168
164
|
const storage = Storage.getInstance();
|
|
169
165
|
if (!(await storage.isAvailable())) {
|
|
170
|
-
this.postNoibuErrorAndOptionallyDisableClient(`Storage is unavailable, disabling client. ${await storage.getDiagnoseInfo()}`, true, SEVERITY.error);
|
|
166
|
+
void this.postNoibuErrorAndOptionallyDisableClient(`Storage is unavailable, disabling client. ${await storage.getDiagnoseInfo()}`, true, SEVERITY.error);
|
|
171
167
|
return;
|
|
172
168
|
}
|
|
173
169
|
// getting the current content of the storage
|
|
@@ -177,6 +173,7 @@ class ClientConfig {
|
|
|
177
173
|
noibuLSObject.CurrentPageVisitCount = 0;
|
|
178
174
|
}
|
|
179
175
|
this.browserId = noibuLSObject.BrowserId;
|
|
176
|
+
noibuLog('ClientConfig - _setupStorageVars', { noibuLSObject });
|
|
180
177
|
this.pageVisitSeq = noibuLSObject.CurrentPageVisitCount;
|
|
181
178
|
this.isClientDisabled = noibuLSObject.DisabledStatus;
|
|
182
179
|
// If the client has been disabled just return.
|
|
@@ -201,7 +198,7 @@ class ClientConfig {
|
|
|
201
198
|
// setting the lock time
|
|
202
199
|
noibuLSObject.ClientUnlockTime = expiryTime;
|
|
203
200
|
noibuLSObject.DisabledStatus = true;
|
|
204
|
-
this.postNoibuErrorAndOptionallyDisableClient(`Hit max page visits, disabling client for ${CLIENT_LOCK_TIME_MINUTES}mins`, true, SEVERITY.error);
|
|
201
|
+
await this.postNoibuErrorAndOptionallyDisableClient(`Hit max page visits, disabling client for ${CLIENT_LOCK_TIME_MINUTES}mins`, true, SEVERITY.error);
|
|
205
202
|
}
|
|
206
203
|
// we now check if we successfully saved the data
|
|
207
204
|
const savedData = await this._storeBrowserData(noibuLSObject);
|
|
@@ -209,7 +206,7 @@ class ClientConfig {
|
|
|
209
206
|
// error happened, thus we disable collect.
|
|
210
207
|
if (!savedData.BrowserId) {
|
|
211
208
|
// we do not set a lock expiry date here since we cannot store to storage
|
|
212
|
-
this.postNoibuErrorAndOptionallyDisableClient(`Null browser in storage, disabling client`, true, SEVERITY.error);
|
|
209
|
+
void this.postNoibuErrorAndOptionallyDisableClient(`Null browser in storage, disabling client`, true, SEVERITY.error);
|
|
213
210
|
this.browserId = '';
|
|
214
211
|
}
|
|
215
212
|
}
|
|
@@ -267,7 +264,7 @@ class ClientConfig {
|
|
|
267
264
|
return data;
|
|
268
265
|
}
|
|
269
266
|
catch (e) {
|
|
270
|
-
this.postNoibuErrorAndOptionallyDisableClient(`Error writing browser data to storage, disabling client: ${e.message}, ${await storage.getDiagnoseInfo()}`, true, SEVERITY.error);
|
|
267
|
+
await this.postNoibuErrorAndOptionallyDisableClient(`Error writing browser data to storage, disabling client: ${e.message}, ${await storage.getDiagnoseInfo()}`, true, SEVERITY.error);
|
|
271
268
|
// sending empty fields if we encountered errors while storing in the LS
|
|
272
269
|
return this._generateNewBrowserData();
|
|
273
270
|
}
|
|
@@ -315,7 +312,8 @@ class ClientConfig {
|
|
|
315
312
|
pageVisitId: this.pageVisitId,
|
|
316
313
|
scriptId: GET_SCRIPT_ID(),
|
|
317
314
|
ua: await getUserAgent(),
|
|
318
|
-
|
|
315
|
+
deviceEnv: GET_DEVICE_ENV(),
|
|
316
|
+
error: stringifyJSON(errorMsg),
|
|
319
317
|
};
|
|
320
318
|
// if the page visits sends more errors than the
|
|
321
319
|
// allowed threshold we disable the client
|
|
@@ -347,7 +345,7 @@ class ClientConfig {
|
|
|
347
345
|
});
|
|
348
346
|
}
|
|
349
347
|
else {
|
|
350
|
-
unwrapNoibuWrapped(fetch)(this.noibuErrorURL, {
|
|
348
|
+
void unwrapNoibuWrapped(fetch)(this.noibuErrorURL, {
|
|
351
349
|
method: 'POST',
|
|
352
350
|
headers,
|
|
353
351
|
body: stringifyJSON(errorContent),
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HelpCode class is responsible for help code feature related functionality
|
|
3
|
+
*/
|
|
4
|
+
export default class HelpCode {
|
|
5
|
+
/**
|
|
6
|
+
* Singleton instance
|
|
7
|
+
* @returns {HelpCode}
|
|
8
|
+
*/
|
|
9
|
+
static getInstance(): HelpCode;
|
|
10
|
+
requestContext: {
|
|
11
|
+
resolve: null;
|
|
12
|
+
reject: null;
|
|
13
|
+
promise: null;
|
|
14
|
+
} | null;
|
|
15
|
+
/**
|
|
16
|
+
* Handles the received help code event.
|
|
17
|
+
* @param {CustomEvent<string>} event - The event object with string detail property.
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
20
|
+
receiveHelpCode(event: CustomEvent<string>): void;
|
|
21
|
+
/**
|
|
22
|
+
* Requests a help code and returns a Promise that resolves when the help code is obtained
|
|
23
|
+
* or rejects if the noibu connection is unavailable.
|
|
24
|
+
|
|
25
|
+
* @returns {Promise<string>} Promise object representing the help code request.
|
|
26
|
+
* @throws {string} Throws an error if the noibu connection is unavailable.
|
|
27
|
+
*/
|
|
28
|
+
requestHelpCode(): Promise<string>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** this class controls the input that customers can inject into
|
|
2
|
+
* our script via the NoibuJS SDK
|
|
3
|
+
*/
|
|
4
|
+
export default class InputManager {
|
|
5
|
+
/** gets the singleton instance */
|
|
6
|
+
static getInstance(): InputManager;
|
|
7
|
+
customIDs: {};
|
|
8
|
+
customErrorsCount: number;
|
|
9
|
+
TOO_MANY_IDS_ADDED_MSG: string;
|
|
10
|
+
ID_NAME_ALREADY_ADDED_MSG: string;
|
|
11
|
+
NAME_TOO_LONG_MSG: string;
|
|
12
|
+
VALUE_TOO_LONG_MSG: string;
|
|
13
|
+
INVALID_NAME_TYPE_MSG: string;
|
|
14
|
+
INVALID_VALUE_TYPE_MSG: string;
|
|
15
|
+
NAME_HAS_NO_LENGTH_MSG: string;
|
|
16
|
+
VALUE_HAS_NO_LENGTH_MSG: string;
|
|
17
|
+
SUCCESS_MSG: string;
|
|
18
|
+
ERROR_HAS_NO_MSG_MSG: string;
|
|
19
|
+
ERROR_HAS_NO_STACK_MSG: string;
|
|
20
|
+
NULL_CUSTOM_ERR_MSG: string;
|
|
21
|
+
ERROR_ALREADY_RECEIVED_MSG: string;
|
|
22
|
+
INVALID_ERROR_SOURCE_MSG: string;
|
|
23
|
+
TOO_MANY_ERRORS_RECEIVED_PER_PAGEVISIT_MSG: string;
|
|
24
|
+
/** exposes functions to the window of the browser for the clients
|
|
25
|
+
* to interact with on their end
|
|
26
|
+
*/
|
|
27
|
+
exposeFunctions(): {
|
|
28
|
+
requestHelpCode: (alert?: boolean) => Promise<string>;
|
|
29
|
+
addCustomAttribute: (name: string, value: string) => Promise<string>;
|
|
30
|
+
addError: (customError: Error) => string;
|
|
31
|
+
addJsSdkError: (customError: string, errorSource: string) => string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* gets the sdk object that will be assigned to a window variable
|
|
35
|
+
* @returns {{
|
|
36
|
+
* requestHelpCode: (alert?: boolean) => Promise<string>,
|
|
37
|
+
* addCustomAttribute: (name: string, value: string) => Promise<string>,
|
|
38
|
+
* addError: (customError: Error) => string,
|
|
39
|
+
* addJsSdkError: (customError: string, errorSource: string) => string
|
|
40
|
+
* }}
|
|
41
|
+
*/
|
|
42
|
+
_getSDKWindowObject(): {
|
|
43
|
+
requestHelpCode: (alert?: boolean) => Promise<string>;
|
|
44
|
+
addCustomAttribute: (name: string, value: string) => Promise<string>;
|
|
45
|
+
addError: (customError: Error) => string;
|
|
46
|
+
addJsSdkError: (customError: string, errorSource: string) => string;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* validates the custom error that was passed
|
|
50
|
+
* @param {} customError
|
|
51
|
+
*/
|
|
52
|
+
_validateCustomError(customError: any): string;
|
|
53
|
+
/**
|
|
54
|
+
* Validates and sets the custom error to our internal trackers
|
|
55
|
+
* @param {} customError
|
|
56
|
+
*/
|
|
57
|
+
_validateAndSetCustomError(customError: any): string;
|
|
58
|
+
/**
|
|
59
|
+
* adds an error from a JS Sdk to the session
|
|
60
|
+
* @param {} customError
|
|
61
|
+
* @param {string} errorSource
|
|
62
|
+
*/
|
|
63
|
+
_addErrorFromJSSdk(customError: any, errorSource: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* adds a custom Error to the session
|
|
66
|
+
* @param {} customError
|
|
67
|
+
*/
|
|
68
|
+
_addCustomError(customError: any): string;
|
|
69
|
+
/**
|
|
70
|
+
* adds a custom id to the session
|
|
71
|
+
* @param {} name
|
|
72
|
+
* @param {} value
|
|
73
|
+
*/
|
|
74
|
+
_addCustomAttribute(name: any, value: any): Promise<string>;
|
|
75
|
+
/**
|
|
76
|
+
* validation function for customer input
|
|
77
|
+
* @param {} name
|
|
78
|
+
* @param {} value
|
|
79
|
+
*/
|
|
80
|
+
_validateCustomIDInput(name: any, value: any): string;
|
|
81
|
+
/**
|
|
82
|
+
* Requests a help code from the HelpCode instance.
|
|
83
|
+
* @param {boolean} [alertUser=true] - Whether to alert the user about the help code request.
|
|
84
|
+
* @returns {Promise<string>} A promise that resolves with the requested help code.
|
|
85
|
+
*/
|
|
86
|
+
_requestHelpCode(): Promise<string>;
|
|
87
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { END_AT_ATT_NAME } from '../constants';
|
|
2
|
+
/** Manages the socket to Metroplex */
|
|
3
|
+
export default class MetroplexSocket {
|
|
4
|
+
_initialURL: string;
|
|
5
|
+
ackedOnce: boolean;
|
|
6
|
+
connectionCount: number;
|
|
7
|
+
connectionPromise: Promise<any>;
|
|
8
|
+
connectionURL: string;
|
|
9
|
+
currentConnectionAttempts: number;
|
|
10
|
+
forceClosed: boolean;
|
|
11
|
+
helpCodeCb: null | ((...args: any[]) => any);
|
|
12
|
+
initialReferingURL: string;
|
|
13
|
+
static instance: MetroplexSocket;
|
|
14
|
+
instanceId: string | number[];
|
|
15
|
+
isRetryLoopDisabled: boolean;
|
|
16
|
+
latestReceivedSeqNumStoredTime: Date;
|
|
17
|
+
latestReceivedSeqNumber: number;
|
|
18
|
+
messageSequenceNum: number;
|
|
19
|
+
metroRetryFrequencyMS: 30000;
|
|
20
|
+
metroplexTypeLock: Record<any, any>;
|
|
21
|
+
pageVisitInfoSent: boolean;
|
|
22
|
+
postURL: string;
|
|
23
|
+
previousMessageType: string;
|
|
24
|
+
retryMessageQueue: any[];
|
|
25
|
+
retryMetroplexInterval: null | number;
|
|
26
|
+
scriptInstanceId: string | number[] | undefined;
|
|
27
|
+
sessionLength: number;
|
|
28
|
+
sessionStartTime: any;
|
|
29
|
+
sessionTimestamp: Date;
|
|
30
|
+
socket: WebSocket | null;
|
|
31
|
+
socketInstanceId: string | number[] | null;
|
|
32
|
+
socketCloseCodes: string[];
|
|
33
|
+
socketOpens: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Creates an instance of metroplex
|
|
36
|
+
* id of script, to make sure only a single socket is open
|
|
37
|
+
*/
|
|
38
|
+
constructor(scriptInstanceId?: string | number[]);
|
|
39
|
+
/**
|
|
40
|
+
* gets the singleton instance
|
|
41
|
+
* @returns {MetroplexSocket}
|
|
42
|
+
*/
|
|
43
|
+
static getInstance(scriptInstanceId?: string | number[]): MetroplexSocket;
|
|
44
|
+
/**
|
|
45
|
+
* Adds the seq num field to the given payload depending on whether its
|
|
46
|
+
* a page visit part or video frag
|
|
47
|
+
*/
|
|
48
|
+
_addSeqNumToPayload(type: string, payload: any): void;
|
|
49
|
+
/**
|
|
50
|
+
* sets the seq num in the payload for the given key and increments the
|
|
51
|
+
* global seq number
|
|
52
|
+
*/
|
|
53
|
+
_setSeqNumInPayloadAndIncrementSeqNum(payloadKey: string, payload: any): void;
|
|
54
|
+
/** requests help code and saves a callback to be called on response */
|
|
55
|
+
requestHelpCode(cb: (typeof this)['helpCodeCb']): Promise<boolean>;
|
|
56
|
+
/**
|
|
57
|
+
* Immediately sends a message to Metroplex over the web socket
|
|
58
|
+
* Queues the message if the connection isn't open yet.
|
|
59
|
+
* returns true if message was sent succefully, false otherwise
|
|
60
|
+
*/
|
|
61
|
+
sendMessage(type: string, payload: any): Promise<boolean>;
|
|
62
|
+
/** Updates the latest pv message sent timestamp if events contain any user steps
|
|
63
|
+
*/
|
|
64
|
+
_updateLatestPvTimestamp(events: any[]): Promise<void>;
|
|
65
|
+
/** returns true if the socket is either connecting or connected to metroplex */
|
|
66
|
+
isConnected(): boolean;
|
|
67
|
+
/** returns true if we are connecting to the socket */
|
|
68
|
+
isConnecting(): boolean;
|
|
69
|
+
/** close will close the socket opened for video frag transmission */
|
|
70
|
+
close(): void;
|
|
71
|
+
/** Connects the web socket to metroplex and calls callback upon successfully connecting
|
|
72
|
+
*/
|
|
73
|
+
handleConnect(forceOpen: boolean): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* connectSocket will establish a websocket connection to the metroplex
|
|
76
|
+
* service
|
|
77
|
+
*/
|
|
78
|
+
connectSocket(): Promise<any>;
|
|
79
|
+
/** Calculates and sets the end_at field of the payload
|
|
80
|
+
* @param {} payload
|
|
81
|
+
* @param {} isPageVisit
|
|
82
|
+
*/
|
|
83
|
+
addEndTimeToPayload(payload: any, isPageVisit: boolean): typeof payload & {
|
|
84
|
+
[END_AT_ATT_NAME]: string;
|
|
85
|
+
};
|
|
86
|
+
/** open handler for socket */
|
|
87
|
+
_onSocketOpen(): Promise<void>;
|
|
88
|
+
/** message handler for socket
|
|
89
|
+
* @param {} event
|
|
90
|
+
*/
|
|
91
|
+
_onSocketMessage(event: any): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Returns true if the message's payload has the payload type given and has a sequence
|
|
94
|
+
* number higher than seqNum
|
|
95
|
+
*/
|
|
96
|
+
_messagePayloadHasLargerSeqNum(message: any, payloadType: string, seqNum: number): any;
|
|
97
|
+
/**
|
|
98
|
+
* removes messages from the retry queue that are smaller than the
|
|
99
|
+
* latest stored message in metroplex
|
|
100
|
+
*/
|
|
101
|
+
_clearRetryQueue(seqNum: number): void;
|
|
102
|
+
/** will resend everything that is in the retry queue */
|
|
103
|
+
_sendUnconfirmedMessages(socketWasAlreadyOpen: boolean): Promise<void>;
|
|
104
|
+
/** sets up the interval to empty the queue as we receive confirmation messages from metroplex */
|
|
105
|
+
setupRetryMechanism(): void;
|
|
106
|
+
/** sets up events that will trigger the event queue to be emptied */
|
|
107
|
+
_setupOffloadEvents(): void;
|
|
108
|
+
/**
|
|
109
|
+
* will handle the final moments of a page being active. It
|
|
110
|
+
* will try to empty both the queues with beacons.
|
|
111
|
+
*/
|
|
112
|
+
_handleUnload(): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* will post full page visit to metroplex. It
|
|
115
|
+
* will try to empty both the queues with beacons.
|
|
116
|
+
*/
|
|
117
|
+
postFullPageVisit(maxMessageSize: number): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* will send a message to metroplex via a post request that will outlive the current page
|
|
120
|
+
*/
|
|
121
|
+
postMessage(msg: any): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* Stringifies the payload into JSON, sends it to the back end if the session
|
|
124
|
+
* is active and returns true. If inactive the session and socket are closed
|
|
125
|
+
* and this method returns false.
|
|
126
|
+
*/
|
|
127
|
+
_sendSocketMessage(payload: any): Promise<void>;
|
|
128
|
+
/**
|
|
129
|
+
* Closes the socket connection if the session is inactive. Returns true if the
|
|
130
|
+
* session is inactive
|
|
131
|
+
*/
|
|
132
|
+
closeIfInactive(): Promise<boolean>;
|
|
133
|
+
/** will get page information, calling this will increase the connection count */
|
|
134
|
+
getPageInformation(): Promise<{
|
|
135
|
+
br_id: string;
|
|
136
|
+
pv_id: string;
|
|
137
|
+
v: 5;
|
|
138
|
+
seq: number | null;
|
|
139
|
+
on_url: string;
|
|
140
|
+
ref_url: string;
|
|
141
|
+
start_at: string;
|
|
142
|
+
conc: number;
|
|
143
|
+
cv: 2;
|
|
144
|
+
last: boolean;
|
|
145
|
+
script_id: string;
|
|
146
|
+
script_inst_id: string | number[] | undefined;
|
|
147
|
+
mp_sock_inst_id: string | number[];
|
|
148
|
+
sock_inst_id: string | number[] | null;
|
|
149
|
+
video_recorder: string;
|
|
150
|
+
}>;
|
|
151
|
+
/**
|
|
152
|
+
* Try to parse help code response and fire custom event
|
|
153
|
+
* @param {String} response
|
|
154
|
+
*/
|
|
155
|
+
_tryProcessHelpCodeResponse(response: any): boolean;
|
|
156
|
+
}
|