noibu-react-native 0.2.1 → 0.2.3
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 +1 -1
- package/android/src/main/java/com/noibu/sessionreplay/reactnative/NoibuSessionReplayModule.kt +10 -0
- package/dist/api/clientConfig.js +225 -217
- package/dist/api/metroplexSocket.js +406 -416
- package/dist/constants.js +14 -2
- package/dist/entry/init.js +58 -56
- package/dist/monitors/appNavigationMonitor.js +2 -3
- package/dist/monitors/clickMonitor.js +16 -9
- package/dist/monitors/errorMonitor.js +30 -8
- package/dist/monitors/gqlErrorValidator.js +4 -4
- package/dist/monitors/httpDataBundler.js +525 -713
- package/dist/monitors/integrations/react-native-navigation-integration.js +4 -2
- package/dist/monitors/requestMonitor.js +350 -365
- package/dist/pageVisit/eventDebouncer.js +110 -0
- package/dist/pageVisit/pageVisitEventError.js +1 -1
- package/dist/pageVisit/pageVisitEventHTTP.js +78 -93
- package/dist/react/ErrorBoundary.js +18 -15
- package/dist/sessionRecorder/nativeSessionRecorderSubscription.js +3 -2
- package/dist/sessionRecorder/sessionRecorder.js +151 -150
- package/dist/{api → src/api}/clientConfig.d.ts +1 -1
- package/dist/{api → src/api}/metroplexSocket.d.ts +25 -25
- package/dist/{constants.d.ts → src/constants.d.ts} +44 -0
- package/dist/{entry → src/entry}/init.d.ts +1 -1
- package/dist/{monitors → src/monitors}/clickMonitor.d.ts +1 -1
- package/dist/{monitors → src/monitors}/gqlErrorValidator.d.ts +6 -6
- package/dist/src/monitors/httpDataBundler.d.ts +127 -0
- package/dist/src/monitors/requestMonitor.d.ts +10 -0
- package/dist/src/pageVisit/eventDebouncer.d.ts +31 -0
- package/dist/src/pageVisit/pageVisitEventHTTP.d.ts +25 -0
- package/dist/{sessionRecorder → src/sessionRecorder}/types.d.ts +1 -1
- package/dist/{storage → src/storage}/rnStorageProvider.d.ts +1 -1
- package/dist/{storage → src/storage}/storage.d.ts +1 -1
- package/dist/{storage → src/storage}/storageProvider.d.ts +3 -3
- package/dist/{utils → src/utils}/function.d.ts +25 -4
- package/dist/{utils → src/utils}/object.d.ts +9 -4
- package/dist/src/utils/piiRedactor.d.ts +11 -0
- package/dist/src/utils/polyfills.d.ts +7 -0
- package/dist/storage/rnStorageProvider.js +7 -5
- package/dist/storage/storage.js +43 -35
- package/dist/storage/storageProvider.js +23 -19
- package/dist/types/Config.d.ts +24 -20
- package/dist/types/PageVisit.types.d.ts +151 -0
- package/dist/types/PageVisitMetrics.types.d.ts +27 -0
- package/dist/types/RRWeb.d.ts +48 -0
- package/dist/types/StoredPageVisit.types.d.ts +2 -4
- package/dist/types/WrappedObjects.d.ts +6 -0
- package/dist/utils/function.js +110 -76
- package/dist/utils/object.js +58 -6
- package/dist/utils/piiRedactor.js +98 -0
- package/dist/utils/polyfills.js +24 -0
- package/package.json +24 -9
- package/dist/monitors/httpDataBundler.d.ts +0 -161
- package/dist/monitors/requestMonitor.d.ts +0 -10
- package/dist/pageVisit/pageVisitEventHTTP.d.ts +0 -18
- package/dist/types/PageVisit.d.ts +0 -22
- package/dist/types/globals.d.ts +0 -45
- /package/dist/{api → src/api}/helpCode.d.ts +0 -0
- /package/dist/{api → src/api}/inputManager.d.ts +0 -0
- /package/dist/{api → src/api}/storedMetrics.d.ts +0 -0
- /package/dist/{api → src/api}/storedPageVisit.d.ts +0 -0
- /package/dist/{const_matchers.d.ts → src/const_matchers.d.ts} +0 -0
- /package/dist/{entry → src/entry}/index.d.ts +0 -0
- /package/dist/{monitors → src/monitors}/appNavigationMonitor.d.ts +0 -0
- /package/dist/{monitors → src/monitors}/errorMonitor.d.ts +0 -0
- /package/dist/{monitors → src/monitors}/inputMonitor.d.ts +0 -0
- /package/dist/{monitors → src/monitors}/integrations/react-native-navigation-integration.d.ts +0 -0
- /package/dist/{monitors → src/monitors}/keyboardInputMonitor.d.ts +0 -0
- /package/dist/{monitors → src/monitors}/pageMonitor.d.ts +0 -0
- /package/dist/{pageVisit → src/pageVisit}/pageVisit.d.ts +0 -0
- /package/dist/{pageVisit → src/pageVisit}/pageVisitEventError.d.ts +0 -0
- /package/dist/{pageVisit → src/pageVisit}/userStep.d.ts +0 -0
- /package/dist/{react → src/react}/ErrorBoundary.d.ts +0 -0
- /package/dist/{sessionRecorder → src/sessionRecorder}/nativeSessionRecorderSubscription.d.ts +0 -0
- /package/dist/{sessionRecorder → src/sessionRecorder}/sessionRecorder.d.ts +0 -0
- /package/dist/{utils → src/utils}/date.d.ts +0 -0
- /package/dist/{utils → src/utils}/eventlistener.d.ts +0 -0
- /package/dist/{utils → src/utils}/log.d.ts +0 -0
- /package/dist/{utils → src/utils}/performance.d.ts +0 -0
- /package/dist/{utils → src/utils}/stacktrace-parser.d.ts +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { CLICK_EVENT_TYPE, CSS_CLASS_ATT_NAME, CUSTOM_ERROR_EVENT_TYPE, ERROR_EVENT_ERROR_TYPE, ERROR_EVENT_UNHANDLED_REJECTION_TYPE, ERROR_LOG_EVENT_ERROR_TYPE, ERROR_SOURCE_ATT_NAME, FETCH_EXCEPTION_ERROR_TYPE, GLOBAL_REASSIGN_ERROR_TYPE, GQL_COLUMN_ATT_NAME, GQL_ERROR_ATT_NAME, GQL_ERROR_TYPE, GQL_EXTENSIONS_ATT_NAME, GQL_LINE_ATT_NAME, GQL_LOCATIONS_ATT_NAME, GQL_MESSAGE_ATT_NAME, GQL_PATH_ATT_NAME, GQL_SRC_ATT_NAME, HTMLID_ATT_NAME, HTTP_CODE_ATT_NAME, HTTP_DATA_PAYLOAD_ATT_NAME, HTTP_DATA_REQ_HEADERS_ATT_NAME, HTTP_DATA_RESP_HEADERS_ATT_NAME, HTTP_DATA_RESP_PAYLOAD_ATT_NAME, HTTP_METHOD_ATT_NAME, HTTP_RESP_CODE_ATT_NAME, HTTP_RESP_LENGTH_ATT_NAME, HTTP_RESP_TIME_ATT_NAME, IMAGE_ERROR_TYPE, JS_ERROR_ATT_NAME, JS_STACK_CAUSE_ATT_NAME, JS_STACK_COL_ATT_NAME, JS_STACK_FILE_ATT_NAME, JS_STACK_FRAMES_ATT_NAME, JS_STACK_LINE_ATT_NAME, JS_STACK_MESSAGE_ATT_NAME, JS_STACK_METHOD_ATT_NAME, KEYBOARD_EVENT_TYPE, PAGE_CHECK_ERROR_ATT_NAME, PAGE_CHECK_ERROR_TYPE, PV_SEQ_ATT_NAME, RESOURCE_ERROR_ATT_NAME, RESPONSE_ERROR_TYPE, SOURCE_ATT_NAME, TAGNAME_ATT_NAME, TEXT_ATT_NAME, TYPE_ATT_NAME, URL_ATT_NAME, WRAPPED_EXCEPTION_ERROR_TYPE } from '../src/constants';
|
|
2
|
+
export type PVEventHTTP = {
|
|
3
|
+
[HTTP_METHOD_ATT_NAME]: string;
|
|
4
|
+
[HTTP_RESP_CODE_ATT_NAME]: number;
|
|
5
|
+
[URL_ATT_NAME]: string;
|
|
6
|
+
[HTTP_RESP_TIME_ATT_NAME]: number;
|
|
7
|
+
[HTTP_RESP_LENGTH_ATT_NAME]?: number;
|
|
8
|
+
[PV_SEQ_ATT_NAME]?: number;
|
|
9
|
+
};
|
|
10
|
+
export type HTTPDataBundle = {
|
|
11
|
+
[HTTP_DATA_REQ_HEADERS_ATT_NAME]: Record<string, string>;
|
|
12
|
+
[HTTP_DATA_PAYLOAD_ATT_NAME]: string;
|
|
13
|
+
[HTTP_DATA_RESP_HEADERS_ATT_NAME]: Record<string, string>;
|
|
14
|
+
[HTTP_DATA_RESP_PAYLOAD_ATT_NAME]: string;
|
|
15
|
+
};
|
|
16
|
+
export interface UserInputMonitorPayload {
|
|
17
|
+
[SOURCE_ATT_NAME]: string;
|
|
18
|
+
[TEXT_ATT_NAME]: string;
|
|
19
|
+
[TAGNAME_ATT_NAME]: string;
|
|
20
|
+
[HTMLID_ATT_NAME]: string;
|
|
21
|
+
[TYPE_ATT_NAME]: string;
|
|
22
|
+
[CSS_CLASS_ATT_NAME]: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ClickMonitorPayload extends UserInputMonitorPayload {
|
|
25
|
+
[SOURCE_ATT_NAME]: string;
|
|
26
|
+
[TEXT_ATT_NAME]: string;
|
|
27
|
+
[TAGNAME_ATT_NAME]: string;
|
|
28
|
+
[HTMLID_ATT_NAME]: string;
|
|
29
|
+
[TYPE_ATT_NAME]: typeof CLICK_EVENT_TYPE;
|
|
30
|
+
[CSS_CLASS_ATT_NAME]: string;
|
|
31
|
+
}
|
|
32
|
+
export interface KeyboardMonitorPayload extends UserInputMonitorPayload {
|
|
33
|
+
[SOURCE_ATT_NAME]: string;
|
|
34
|
+
[TEXT_ATT_NAME]: string;
|
|
35
|
+
[TAGNAME_ATT_NAME]: string;
|
|
36
|
+
[HTMLID_ATT_NAME]: string;
|
|
37
|
+
[TYPE_ATT_NAME]: typeof KEYBOARD_EVENT_TYPE;
|
|
38
|
+
[CSS_CLASS_ATT_NAME]: string;
|
|
39
|
+
}
|
|
40
|
+
export type JSErrorOutputMessage = {
|
|
41
|
+
[JS_STACK_MESSAGE_ATT_NAME]: string;
|
|
42
|
+
[JS_STACK_FRAMES_ATT_NAME]: {
|
|
43
|
+
[JS_STACK_FILE_ATT_NAME]: string;
|
|
44
|
+
[JS_STACK_LINE_ATT_NAME]: string;
|
|
45
|
+
[JS_STACK_METHOD_ATT_NAME]: string;
|
|
46
|
+
[JS_STACK_COL_ATT_NAME]?: number;
|
|
47
|
+
}[];
|
|
48
|
+
[JS_STACK_CAUSE_ATT_NAME]?: UserInputMonitorPayload[];
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Helper class to improve typing in methods that use anonymous classes: Keyboard and Click monitors
|
|
52
|
+
*/
|
|
53
|
+
export interface UserInputEventMonitor {
|
|
54
|
+
isValid(event: Event): boolean;
|
|
55
|
+
getPayload(event: Event): UserInputMonitorPayload;
|
|
56
|
+
}
|
|
57
|
+
export declare namespace PVError {
|
|
58
|
+
export interface Input {
|
|
59
|
+
[ERROR_EVENT_ERROR_TYPE]: EventInput;
|
|
60
|
+
[ERROR_EVENT_UNHANDLED_REJECTION_TYPE]: EventInput;
|
|
61
|
+
[RESPONSE_ERROR_TYPE]: ResponseInput;
|
|
62
|
+
[GQL_ERROR_TYPE]: GqlInput;
|
|
63
|
+
[IMAGE_ERROR_TYPE]: ResourceInput;
|
|
64
|
+
[PAGE_CHECK_ERROR_TYPE]: PageCheckInput;
|
|
65
|
+
[WRAPPED_EXCEPTION_ERROR_TYPE]: EventInput;
|
|
66
|
+
[FETCH_EXCEPTION_ERROR_TYPE]: EventInput;
|
|
67
|
+
[ERROR_LOG_EVENT_ERROR_TYPE]: JErrorObject;
|
|
68
|
+
[CUSTOM_ERROR_EVENT_TYPE]: EventInput;
|
|
69
|
+
[GLOBAL_REASSIGN_ERROR_TYPE]: GlobalReassignInput;
|
|
70
|
+
}
|
|
71
|
+
export interface Output {
|
|
72
|
+
[ERROR_EVENT_ERROR_TYPE]: Event;
|
|
73
|
+
[ERROR_EVENT_UNHANDLED_REJECTION_TYPE]: Event;
|
|
74
|
+
[RESPONSE_ERROR_TYPE]: Http;
|
|
75
|
+
[GQL_ERROR_TYPE]: Gql;
|
|
76
|
+
[IMAGE_ERROR_TYPE]: Image;
|
|
77
|
+
[PAGE_CHECK_ERROR_TYPE]: PageCheck;
|
|
78
|
+
[WRAPPED_EXCEPTION_ERROR_TYPE]: Event;
|
|
79
|
+
[FETCH_EXCEPTION_ERROR_TYPE]: Event;
|
|
80
|
+
[ERROR_LOG_EVENT_ERROR_TYPE]: Log;
|
|
81
|
+
[CUSTOM_ERROR_EVENT_TYPE]: Event;
|
|
82
|
+
[GLOBAL_REASSIGN_ERROR_TYPE]: Event;
|
|
83
|
+
}
|
|
84
|
+
export type ResourceInput = {
|
|
85
|
+
tag_name: string;
|
|
86
|
+
id?: string;
|
|
87
|
+
src: string;
|
|
88
|
+
detail: string;
|
|
89
|
+
};
|
|
90
|
+
export type PageCheckInput = {
|
|
91
|
+
checkId: string;
|
|
92
|
+
version?: string;
|
|
93
|
+
triggerId?: string;
|
|
94
|
+
assertionId?: string;
|
|
95
|
+
log: string[];
|
|
96
|
+
};
|
|
97
|
+
export type GqlInput = {
|
|
98
|
+
[GQL_SRC_ATT_NAME]?: string;
|
|
99
|
+
[GQL_MESSAGE_ATT_NAME]: string;
|
|
100
|
+
[GQL_LOCATIONS_ATT_NAME]?: {
|
|
101
|
+
[GQL_LINE_ATT_NAME]: number;
|
|
102
|
+
[GQL_COLUMN_ATT_NAME]: number;
|
|
103
|
+
}[];
|
|
104
|
+
[GQL_PATH_ATT_NAME]?: string[];
|
|
105
|
+
[GQL_EXTENSIONS_ATT_NAME]?: string;
|
|
106
|
+
};
|
|
107
|
+
export type ResponseInput = Pick<Response, 'status' | 'url'>;
|
|
108
|
+
export type JErrWithCause = Partial<{
|
|
109
|
+
cause: UserInputMonitorPayload[];
|
|
110
|
+
}>;
|
|
111
|
+
export type JErrWithStack = Error & JErrWithCause;
|
|
112
|
+
export type JErrWithFilename = {
|
|
113
|
+
fileName: string;
|
|
114
|
+
lineNumber: string;
|
|
115
|
+
columnNumber: number;
|
|
116
|
+
} & JErrWithCause;
|
|
117
|
+
export type JErrorObject = JErrWithFilename | JErrWithStack;
|
|
118
|
+
export type EventInput = {
|
|
119
|
+
filename?: string;
|
|
120
|
+
error: JErrorObject;
|
|
121
|
+
};
|
|
122
|
+
export type GlobalReassignInput = {
|
|
123
|
+
error: Error;
|
|
124
|
+
};
|
|
125
|
+
interface Base {
|
|
126
|
+
[TYPE_ATT_NAME]: string;
|
|
127
|
+
[URL_ATT_NAME]: string;
|
|
128
|
+
[ERROR_SOURCE_ATT_NAME]?: keyof Input;
|
|
129
|
+
}
|
|
130
|
+
export interface Http extends Base {
|
|
131
|
+
[HTTP_CODE_ATT_NAME]: number;
|
|
132
|
+
[PV_SEQ_ATT_NAME]?: number;
|
|
133
|
+
}
|
|
134
|
+
export interface Event extends Base {
|
|
135
|
+
[JS_ERROR_ATT_NAME]: JSErrorOutputMessage;
|
|
136
|
+
}
|
|
137
|
+
export interface Gql extends Base {
|
|
138
|
+
[GQL_ERROR_ATT_NAME]: GqlInput;
|
|
139
|
+
[PV_SEQ_ATT_NAME]?: number;
|
|
140
|
+
}
|
|
141
|
+
export interface Log extends Base {
|
|
142
|
+
[JS_ERROR_ATT_NAME]: JSErrorOutputMessage;
|
|
143
|
+
}
|
|
144
|
+
export interface PageCheck extends Base {
|
|
145
|
+
[PAGE_CHECK_ERROR_ATT_NAME]: PageCheckInput;
|
|
146
|
+
}
|
|
147
|
+
export interface Image extends Base {
|
|
148
|
+
[RESOURCE_ERROR_ATT_NAME]: ResourceInput;
|
|
149
|
+
}
|
|
150
|
+
export {};
|
|
151
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BROWSER_ID_ATT_NAME, COLLECT_VER_ATT_NAME, CURRENT_METRICS_VERSION, CURRENT_NOIBUJS_VERSION, CUSTOM_ATTRIBUTE_FAIL_COUNT_ATT_NAME, CUSTOM_ATTRIBUTE_KEY_COUNT_ATT_NAME, CUSTOM_ATTRIBUTE_TOTAL_COUNT_ATT_NAME, DID_CUT_PV_ATT_NAME, DID_CUT_VID_ATT_NAME, DID_START_VID_ATT_NAME, ERR_COUNT_EXPECTED_ATT_NAME, OTHER_METRICS_ATT_NAME, EXP_VIDEO_LENGTH_ATT_NAME, HTTP_COUNT_EXPECTED_ATT_NAME, ON_URL_ATT_NAME, PV_CLICKS_ATT_NAME, PV_EXP_HTTP_DATA_SEQ_ATT_NAME, PV_EXP_PART_COUNTER_ATT_NAME, PV_EXP_VF_SEQ_ATT_NAME, PV_HTTP_PAYLOADS_COLLECTED_ATT_NAME, PV_HTTP_PAYLOADS_DROPPED_OVERSIZE_ATT_NAME, PV_HTTP_PAYLOADS_DROPPED_TYPE_ATT_NAME, PV_HTTP_REQUESTS_DROPPED_OVER_LIMIT, PV_ID_ATT_NAME, VER_ATT_NAME, VIDEO_CLICKS_ATT_NAME, OTHER_METRICS_KEYS } from '../src/constants';
|
|
2
|
+
export interface PageVisitMetrics {
|
|
3
|
+
[BROWSER_ID_ATT_NAME]: null | string;
|
|
4
|
+
[PV_ID_ATT_NAME]: string;
|
|
5
|
+
[COLLECT_VER_ATT_NAME]: typeof CURRENT_NOIBUJS_VERSION;
|
|
6
|
+
[VER_ATT_NAME]: typeof CURRENT_METRICS_VERSION;
|
|
7
|
+
[EXP_VIDEO_LENGTH_ATT_NAME]: number;
|
|
8
|
+
[PV_EXP_VF_SEQ_ATT_NAME]: number;
|
|
9
|
+
[PV_EXP_PART_COUNTER_ATT_NAME]: number;
|
|
10
|
+
[PV_EXP_HTTP_DATA_SEQ_ATT_NAME]: number;
|
|
11
|
+
[PV_HTTP_PAYLOADS_COLLECTED_ATT_NAME]: number;
|
|
12
|
+
[PV_HTTP_PAYLOADS_DROPPED_OVERSIZE_ATT_NAME]: number;
|
|
13
|
+
[PV_HTTP_PAYLOADS_DROPPED_TYPE_ATT_NAME]: number;
|
|
14
|
+
[PV_HTTP_REQUESTS_DROPPED_OVER_LIMIT]: number;
|
|
15
|
+
[VIDEO_CLICKS_ATT_NAME]: number;
|
|
16
|
+
[PV_CLICKS_ATT_NAME]: number;
|
|
17
|
+
[DID_CUT_PV_ATT_NAME]: boolean;
|
|
18
|
+
[DID_CUT_VID_ATT_NAME]: boolean;
|
|
19
|
+
[DID_START_VID_ATT_NAME]: boolean;
|
|
20
|
+
[HTTP_COUNT_EXPECTED_ATT_NAME]: number;
|
|
21
|
+
[ERR_COUNT_EXPECTED_ATT_NAME]: number;
|
|
22
|
+
[ON_URL_ATT_NAME]: string;
|
|
23
|
+
[CUSTOM_ATTRIBUTE_TOTAL_COUNT_ATT_NAME]: number;
|
|
24
|
+
[CUSTOM_ATTRIBUTE_FAIL_COUNT_ATT_NAME]: number;
|
|
25
|
+
[CUSTOM_ATTRIBUTE_KEY_COUNT_ATT_NAME]: number;
|
|
26
|
+
[OTHER_METRICS_ATT_NAME]?: Partial<Record<ValueOf<typeof OTHER_METRICS_KEYS>, number>>;
|
|
27
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export declare const enum EventType {
|
|
2
|
+
DomContentLoaded = 0,
|
|
3
|
+
Load = 1,
|
|
4
|
+
FullSnapshot = 2,
|
|
5
|
+
IncrementalSnapshot = 3,
|
|
6
|
+
Meta = 4,
|
|
7
|
+
Custom = 5,
|
|
8
|
+
Plugin = 6
|
|
9
|
+
}
|
|
10
|
+
export declare const enum NodeType {
|
|
11
|
+
Document = 0,
|
|
12
|
+
DocumentType = 1,
|
|
13
|
+
Element = 2,
|
|
14
|
+
Text = 3,
|
|
15
|
+
CDATA = 4,
|
|
16
|
+
Comment = 5
|
|
17
|
+
}
|
|
18
|
+
export declare const enum IncrementalSource {
|
|
19
|
+
Mutation = 0,
|
|
20
|
+
MouseMove = 1,
|
|
21
|
+
MouseInteraction = 2,
|
|
22
|
+
Scroll = 3,
|
|
23
|
+
ViewportResize = 4,
|
|
24
|
+
Input = 5,
|
|
25
|
+
TouchMove = 6,
|
|
26
|
+
MediaInteraction = 7,
|
|
27
|
+
StyleSheetRule = 8,
|
|
28
|
+
CanvasMutation = 9,
|
|
29
|
+
Font = 10,
|
|
30
|
+
Log = 11,
|
|
31
|
+
Drag = 12,
|
|
32
|
+
StyleDeclaration = 13,
|
|
33
|
+
Selection = 14,
|
|
34
|
+
AdoptedStyleSheet = 15
|
|
35
|
+
}
|
|
36
|
+
export declare const enum MouseInteractions {
|
|
37
|
+
MouseUp = 0,
|
|
38
|
+
MouseDown = 1,
|
|
39
|
+
Click = 2,
|
|
40
|
+
ContextMenu = 3,
|
|
41
|
+
DblClick = 4,
|
|
42
|
+
Focus = 5,
|
|
43
|
+
Blur = 6,
|
|
44
|
+
TouchStart = 7,
|
|
45
|
+
TouchMove_Departed = 8,
|
|
46
|
+
TouchEnd = 9,
|
|
47
|
+
TouchCancel = 10
|
|
48
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BROWSER_ID_ATT_NAME, COLLECT_VER_ATT_NAME, CONN_COUNT_ATT_NAME, CSS_URLS_ATT_NAME, END_AT_ATT_NAME, IS_LAST_ATT_NAME, LANG_ATT_NAME, LENGTH_ATT_NAME, METROPLEX_SOCKET_INSTANCE_ID_ATT_NAME, ON_URL_ATT_NAME, PAGE_GROUPS_ATT_NAME, PAGE_TITLE_ATT_NAME, PAGE_VISIT_INFORMATION_ATT_NAME, PAGE_VISIT_PART_ATT_NAME, PAGE_VISIT_VID_FRAG_ATT_NAME, PV_EVENTS_ATT_NAME, PV_EXP_PART_COUNTER_ATT_NAME, PV_EXP_VF_SEQ_ATT_NAME, PV_ID_ATT_NAME, PV_PART_COUNTER_ATT_NAME, PV_SEQ_ATT_NAME, REF_URL_ATT_NAME, SCRIPT_ID_ATT_NAME, SCRIPT_INSTANCE_ID_ATT_NAME, SEQ_NUM_ATT_NAME, SOCKET_INSTANCE_ID_ATT_NAME, STARTED_AT_ATT_NAME, VIDEO_FRAG_ATT_NAME, VIDEO_METROPLEX_TYPE } from '../constants';
|
|
1
|
+
import { BROWSER_ID_ATT_NAME, COLLECT_VER_ATT_NAME, CONN_COUNT_ATT_NAME, CSS_URLS_ATT_NAME, END_AT_ATT_NAME, IS_LAST_ATT_NAME, LANG_ATT_NAME, LENGTH_ATT_NAME, METROPLEX_SOCKET_INSTANCE_ID_ATT_NAME, ON_URL_ATT_NAME, PAGE_GROUPS_ATT_NAME, PAGE_TITLE_ATT_NAME, PAGE_VISIT_INFORMATION_ATT_NAME, PAGE_VISIT_PART_ATT_NAME, PAGE_VISIT_VID_FRAG_ATT_NAME, PV_EVENTS_ATT_NAME, PV_EXP_PART_COUNTER_ATT_NAME, PV_EXP_VF_SEQ_ATT_NAME, PV_ID_ATT_NAME, PV_PART_COUNTER_ATT_NAME, PV_SEQ_ATT_NAME, REF_URL_ATT_NAME, SCRIPT_ID_ATT_NAME, SCRIPT_INSTANCE_ID_ATT_NAME, SEQ_NUM_ATT_NAME, SOCKET_INSTANCE_ID_ATT_NAME, STARTED_AT_ATT_NAME, VIDEO_FRAG_ATT_NAME, VIDEO_METROPLEX_TYPE, VIDEO_RECORDER_ATT_NAME } from '../src/constants';
|
|
2
2
|
export interface PageVisitPart {
|
|
3
3
|
[PV_PART_COUNTER_ATT_NAME]: number;
|
|
4
4
|
[END_AT_ATT_NAME]: number;
|
|
@@ -25,6 +25,7 @@ export interface PageVisitInfo {
|
|
|
25
25
|
[STARTED_AT_ATT_NAME]: string;
|
|
26
26
|
[COLLECT_VER_ATT_NAME]: number;
|
|
27
27
|
[LANG_ATT_NAME]?: string;
|
|
28
|
+
[VIDEO_RECORDER_ATT_NAME]: string;
|
|
28
29
|
}
|
|
29
30
|
export interface PageVisitPartsBase {
|
|
30
31
|
pageVisitInfo: PageVisitInfo;
|
|
@@ -44,9 +45,6 @@ export interface VideoFrag {
|
|
|
44
45
|
[SEQ_NUM_ATT_NAME]: number;
|
|
45
46
|
[CSS_URLS_ATT_NAME]: string[];
|
|
46
47
|
}
|
|
47
|
-
export interface VideoMetroplexMessage {
|
|
48
|
-
[PAGE_VISIT_VID_FRAG_ATT_NAME]: VideoFrag;
|
|
49
|
-
}
|
|
50
48
|
export interface CompletePageVisitParts {
|
|
51
49
|
[PAGE_VISIT_VID_FRAG_ATT_NAME]: Array<VideoFrag>;
|
|
52
50
|
[PAGE_VISIT_INFORMATION_ATT_NAME]: PageVisitInfo;
|
package/dist/utils/function.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { __awaiter } from 'tslib';
|
|
1
2
|
import { Platform } from 'react-native';
|
|
2
3
|
import { parseStack } from './stacktrace-parser.js';
|
|
3
|
-
import { MAX_STRING_LENGTH, MAX_BEACON_PAYLOAD_SIZE, REQUIRED_DATA_PROCESSING_URLS, PII_EMAIL_PATTERN, PII_REDACTION_REPLACEMENT_STRING, PII_DIGIT_PATTERN, DEFAULT_STACK_FRAME_FIELD_VALUE } from '../constants.js';
|
|
4
|
+
import { MAX_STRING_LENGTH, MAX_BEACON_PAYLOAD_SIZE, REQUIRED_DATA_PROCESSING_URLS, PII_EMAIL_PATTERN, PII_REDACTION_REPLACEMENT_STRING, PII_DIGIT_PATTERN, JS_STACK_LINE_ATT_NAME, DEFAULT_STACK_FRAME_FIELD_VALUE, JS_STACK_METHOD_ATT_NAME, JS_STACK_FILE_ATT_NAME } from '../constants.js';
|
|
4
5
|
import { noibuLog } from './log.js';
|
|
5
6
|
import { unwrapNoibuWrapped } from './object.js';
|
|
6
7
|
|
|
@@ -8,9 +9,9 @@ import { unwrapNoibuWrapped } from './object.js';
|
|
|
8
9
|
* Returns a stack trace frame with default filed values
|
|
9
10
|
*/
|
|
10
11
|
const getDefaultFrame = () => ({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
[JS_STACK_LINE_ATT_NAME]: DEFAULT_STACK_FRAME_FIELD_VALUE,
|
|
13
|
+
[JS_STACK_METHOD_ATT_NAME]: DEFAULT_STACK_FRAME_FIELD_VALUE,
|
|
14
|
+
[JS_STACK_FILE_ATT_NAME]: DEFAULT_STACK_FRAME_FIELD_VALUE,
|
|
14
15
|
});
|
|
15
16
|
/**
|
|
16
17
|
* returns a string that satisfies a max length
|
|
@@ -60,13 +61,7 @@ function processFrames(rawFrames) {
|
|
|
60
61
|
* @param errObj error to extract stack from
|
|
61
62
|
*/
|
|
62
63
|
function getJSStack(errObj) {
|
|
63
|
-
let frames = [
|
|
64
|
-
{
|
|
65
|
-
line: DEFAULT_STACK_FRAME_FIELD_VALUE,
|
|
66
|
-
mname: DEFAULT_STACK_FRAME_FIELD_VALUE,
|
|
67
|
-
file: DEFAULT_STACK_FRAME_FIELD_VALUE,
|
|
68
|
-
},
|
|
69
|
-
];
|
|
64
|
+
let frames = [getDefaultFrame()];
|
|
70
65
|
// if the errObj type is not an object or null
|
|
71
66
|
// return a default frame
|
|
72
67
|
if (typeof errObj !== 'object' || !errObj || !errObj.stack) {
|
|
@@ -85,10 +80,9 @@ function getJSStack(errObj) {
|
|
|
85
80
|
/**
|
|
86
81
|
* Checks if possiblyStacktrace has any stack frames present
|
|
87
82
|
*/
|
|
88
|
-
function isStackTrace(
|
|
83
|
+
function isStackTrace(_possiblyStacktrace) {
|
|
89
84
|
try {
|
|
90
|
-
// todo
|
|
91
|
-
// return parseStack(possiblyStacktrace).length > 0;
|
|
85
|
+
// todo implement for react native
|
|
92
86
|
return false;
|
|
93
87
|
}
|
|
94
88
|
catch (e) {
|
|
@@ -126,52 +120,54 @@ function stringifyJSON(jsonObject) {
|
|
|
126
120
|
* @param timeout
|
|
127
121
|
* @param sendAndForget
|
|
128
122
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
// a send-and-forget request is made by using the beacon API (fetch + keepalive)
|
|
136
|
-
if (sendAndForget) {
|
|
137
|
-
const stringData = stringifyJSON(data);
|
|
138
|
-
const currentPayloadSize = new Blob([stringData]).size;
|
|
139
|
-
// if we have a large object or fetch is not available, we skip sending the message
|
|
140
|
-
if (currentPayloadSize > MAX_BEACON_PAYLOAD_SIZE) {
|
|
141
|
-
return Promise.resolve();
|
|
123
|
+
function makeRequest(method, url, data, headers, timeout, sendAndForget) {
|
|
124
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
const ua = Object.keys(headers).findLast(k => k.toLowerCase() === 'user-agent');
|
|
126
|
+
const headersWithUa = Object.assign({}, headers);
|
|
127
|
+
if (!headers[ua]) {
|
|
128
|
+
headersWithUa['User-Agent'] = yield getUserAgent();
|
|
142
129
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
return new Promise((resolve, reject) => {
|
|
152
|
-
const xhr = new XMLHttpRequest();
|
|
153
|
-
xhr.open(method, url);
|
|
154
|
-
xhr.timeout = timeout;
|
|
155
|
-
Object.keys(headersWithUa).forEach(header => {
|
|
156
|
-
xhr.setRequestHeader(header, headers[header]);
|
|
157
|
-
});
|
|
158
|
-
xhr.onload = () => {
|
|
159
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
160
|
-
resolve(xhr.response);
|
|
130
|
+
// a send-and-forget request is made by using the beacon API (fetch + keepalive)
|
|
131
|
+
if (sendAndForget) {
|
|
132
|
+
const stringData = stringifyJSON(data);
|
|
133
|
+
const currentPayloadSize = new Blob([stringData]).size;
|
|
134
|
+
// if we have a large object or fetch is not available, we skip sending the message
|
|
135
|
+
if (currentPayloadSize > MAX_BEACON_PAYLOAD_SIZE) {
|
|
136
|
+
return Promise.resolve();
|
|
161
137
|
}
|
|
162
|
-
|
|
138
|
+
return unwrapNoibuWrapped(fetch)(url, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: headersWithUa,
|
|
141
|
+
body: stringifyJSON(data),
|
|
142
|
+
// keep alive outlives the current page, its the same as beacon
|
|
143
|
+
keepalive: true,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return new Promise((resolve, reject) => {
|
|
147
|
+
const xhr = new XMLHttpRequest();
|
|
148
|
+
xhr.open(method, url);
|
|
149
|
+
xhr.timeout = timeout;
|
|
150
|
+
Object.keys(headersWithUa).forEach(header => {
|
|
151
|
+
xhr.setRequestHeader(header, headers[header]);
|
|
152
|
+
});
|
|
153
|
+
xhr.onload = () => {
|
|
154
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
155
|
+
resolve(xhr.response);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
reject(new Error(`Custom Request failed: ${xhr.statusText}`));
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
xhr.onerror = () => {
|
|
163
162
|
reject(new Error(`Custom Request failed: ${xhr.statusText}`));
|
|
163
|
+
};
|
|
164
|
+
if (data) {
|
|
165
|
+
xhr.send(stringifyJSON(data));
|
|
164
166
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
};
|
|
169
|
-
if (data) {
|
|
170
|
-
xhr.send(stringifyJSON(data));
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
xhr.send();
|
|
174
|
-
}
|
|
167
|
+
else {
|
|
168
|
+
xhr.send();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
175
171
|
});
|
|
176
172
|
}
|
|
177
173
|
/**
|
|
@@ -190,20 +186,22 @@ let userAgentCache = '';
|
|
|
190
186
|
* Fakes the user agent retrieval, since there are no good libraries that support both expo and plain RN
|
|
191
187
|
* caches the result for the session
|
|
192
188
|
*/
|
|
193
|
-
|
|
194
|
-
|
|
189
|
+
function getUserAgent() {
|
|
190
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
191
|
+
if (userAgentCache) {
|
|
192
|
+
return userAgentCache;
|
|
193
|
+
}
|
|
194
|
+
noibuLog('getUserAgent start');
|
|
195
|
+
if (Platform.OS === 'android') {
|
|
196
|
+
const { Brand, Model, Release } = Platform.constants;
|
|
197
|
+
userAgentCache = `Mozilla/5.0 (Linux; Android ${Release}; ${Brand} ${Model}; React Native ${Platform.Version}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36`;
|
|
198
|
+
}
|
|
199
|
+
else if (Platform.OS === 'ios') {
|
|
200
|
+
userAgentCache = `Mozilla/5.0 (iPhone; CPU iPhone OS ${Platform.constants.osVersion} like Mac OS X; React Native ${Platform.Version}) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1`;
|
|
201
|
+
}
|
|
202
|
+
noibuLog('getUserAgent end', { userAgentCache });
|
|
195
203
|
return userAgentCache;
|
|
196
|
-
}
|
|
197
|
-
noibuLog('getUserAgent start');
|
|
198
|
-
if (Platform.OS === 'android') {
|
|
199
|
-
const { Brand, Model, Release } = Platform.constants;
|
|
200
|
-
userAgentCache = `Mozilla/5.0 (Linux; Android ${Release}; ${Brand} ${Model}; React Native ${Platform.Version}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36`;
|
|
201
|
-
}
|
|
202
|
-
else if (Platform.OS === 'ios') {
|
|
203
|
-
userAgentCache = `Mozilla/5.0 (iPhone; CPU iPhone OS ${Platform.constants.osVersion} like Mac OS X; React Native ${Platform.Version}) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1`;
|
|
204
|
-
}
|
|
205
|
-
noibuLog('getUserAgent end', { userAgentCache });
|
|
206
|
-
return userAgentCache;
|
|
204
|
+
});
|
|
207
205
|
}
|
|
208
206
|
/**
|
|
209
207
|
* isInvalidURLConfig will verify that Collect is being initializes with
|
|
@@ -272,14 +270,50 @@ function isInstanceOf(instance, type) {
|
|
|
272
270
|
/**
|
|
273
271
|
* To grab the video recorder type based on the device we run the app on.
|
|
274
272
|
*/
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
273
|
+
function getVideoRecorderType() {
|
|
274
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
275
|
+
if (Platform.OS === 'android') {
|
|
276
|
+
return 'AndroidNative';
|
|
277
|
+
}
|
|
278
|
+
if (Platform.OS === 'ios') {
|
|
279
|
+
return 'IOSNative';
|
|
280
|
+
}
|
|
281
|
+
return '';
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
/** String.trim, but safe */
|
|
285
|
+
function safeTrim(text) {
|
|
286
|
+
if (typeof text !== 'string') {
|
|
287
|
+
return '';
|
|
278
288
|
}
|
|
279
|
-
|
|
280
|
-
|
|
289
|
+
return text.trim();
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Tries to get the stack trace from the given error object and returns it.
|
|
293
|
+
* If the error object does not have a stack trace, an empty string is returned.
|
|
294
|
+
*
|
|
295
|
+
* @param {Error} error - The error object from which to retrieve the stack trace.
|
|
296
|
+
* @returns {string} The stack trace of the error, if available. Otherwise, an empty string is returned.
|
|
297
|
+
*/
|
|
298
|
+
function tryGetStackTrace(error) {
|
|
299
|
+
let result = '';
|
|
300
|
+
try {
|
|
301
|
+
if (error && error.stack) {
|
|
302
|
+
result = `(stack: ${error.stack})`;
|
|
303
|
+
}
|
|
281
304
|
}
|
|
282
|
-
|
|
305
|
+
catch (_a) {
|
|
306
|
+
// non-standard property - that's fine
|
|
307
|
+
}
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Checks whether the given value is a string or an instance of String.
|
|
312
|
+
* @param {*} value - The value to be checked.
|
|
313
|
+
* @returns {boolean} Returns true if the value is a string or an instance of String, otherwise returns false.
|
|
314
|
+
*/
|
|
315
|
+
function isString(value) {
|
|
316
|
+
return typeof value === 'string' || value instanceof String;
|
|
283
317
|
}
|
|
284
318
|
|
|
285
|
-
export { asString, getJSStack, getMaxSubstringAllowed, getUserAgent, getVideoRecorderType, isInstanceOf, isInvalidURLConfig, isNoibuJSAlreadyLoaded, isStackTrace, isValidURL, makeRequest, maskTextInput, processFrames, stringifyJSON };
|
|
319
|
+
export { asString, getJSStack, getMaxSubstringAllowed, getUserAgent, getVideoRecorderType, isInstanceOf, isInvalidURLConfig, isNoibuJSAlreadyLoaded, isStackTrace, isString, isValidURL, makeRequest, maskTextInput, processFrames, safeTrim, stringifyJSON, tryGetStackTrace };
|
package/dist/utils/object.js
CHANGED
|
@@ -51,9 +51,7 @@ function unwrapNoibuWrapped(anything) {
|
|
|
51
51
|
* Checks whether the prototype's property is writeable. If it is not,
|
|
52
52
|
* checks whether the property can be made writeable. If it can, it is
|
|
53
53
|
* set to writeable.
|
|
54
|
-
*
|
|
55
|
-
* @param {string} property
|
|
56
|
-
* @returns {boolean} Whether the property on the prototype is (or is now) writeable
|
|
54
|
+
* returns Whether the property on the prototype is (or is now) writeable
|
|
57
55
|
*/
|
|
58
56
|
const propWriteableOrMadeWriteable = (proto, property) => {
|
|
59
57
|
if (!proto ||
|
|
@@ -65,9 +63,9 @@ const propWriteableOrMadeWriteable = (proto, property) => {
|
|
|
65
63
|
// has under the open property
|
|
66
64
|
const propDescriptor = Object.getOwnPropertyDescriptor(proto, property);
|
|
67
65
|
// Checking if the open property is read-only
|
|
68
|
-
if (!propDescriptor
|
|
66
|
+
if (!(propDescriptor === null || propDescriptor === void 0 ? void 0 : propDescriptor.writable)) {
|
|
69
67
|
// Checking if we can write to it
|
|
70
|
-
if (propDescriptor
|
|
68
|
+
if (propDescriptor === null || propDescriptor === void 0 ? void 0 : propDescriptor.configurable) {
|
|
71
69
|
// Making it writable to wrap it
|
|
72
70
|
Object.defineProperty(proto, property, {
|
|
73
71
|
writable: true,
|
|
@@ -117,5 +115,59 @@ const iterateObjectRecursively = (instance, visit, limit = { depth: 5 }) => {
|
|
|
117
115
|
};
|
|
118
116
|
iterate(instance, 1);
|
|
119
117
|
};
|
|
118
|
+
const safeEntries = (obj) => {
|
|
119
|
+
const collection = [];
|
|
120
|
+
if (!obj) {
|
|
121
|
+
return collection;
|
|
122
|
+
}
|
|
123
|
+
if (obj instanceof Headers) {
|
|
124
|
+
obj.forEach((value, key) => {
|
|
125
|
+
collection.push([key, value]);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
else if (typeof obj === 'object') {
|
|
129
|
+
collection.push(...Object.entries(obj));
|
|
130
|
+
}
|
|
131
|
+
return collection;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Determines if the given instance is iterable.
|
|
135
|
+
* An object is considered iterable if it has a method whose key is `Symbol.iterator`.
|
|
136
|
+
* This method checks if `instance[Symbol.iterator]` is a function, and if so, returns `true`.
|
|
137
|
+
* If `instance` is `null` or `undefined`, or if accessing `instance[Symbol.iterator]` throws an error,
|
|
138
|
+
* this method returns `false`.
|
|
139
|
+
*/
|
|
140
|
+
function isIterable(instance) {
|
|
141
|
+
if (!instance) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
return typeof instance[Symbol.iterator] === 'function';
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Replaces the behaviour of Object.fromEntries() as it is not supported on all browsers
|
|
153
|
+
* @param {Iterable} entries The iterable to parse into an object
|
|
154
|
+
* @returns An object containing the same key/values as the iterable passed
|
|
155
|
+
*/
|
|
156
|
+
const safeFromEntries = (entries) => {
|
|
157
|
+
if (!isIterable(entries)) {
|
|
158
|
+
return {};
|
|
159
|
+
}
|
|
160
|
+
const obj = {};
|
|
161
|
+
try {
|
|
162
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
163
|
+
for (const [key, value] of entries) {
|
|
164
|
+
obj[key] = value;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
// noting we can do - give up
|
|
169
|
+
}
|
|
170
|
+
return obj;
|
|
171
|
+
};
|
|
120
172
|
|
|
121
|
-
export { iterateObjectRecursively, propWriteableOrMadeWriteable, replace, unwrapNoibuWrapped };
|
|
173
|
+
export { iterateObjectRecursively, propWriteableOrMadeWriteable, replace, safeEntries, safeFromEntries, unwrapNoibuWrapped };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { iterateObjectRecursively } from './object.js';
|
|
2
|
+
import { HTTP_PII_BLOCKING_PATTERNS, PII_REDACTION_REPLACEMENT_STRING } from '../constants.js';
|
|
3
|
+
import { stringifyJSON } from './function.js';
|
|
4
|
+
|
|
5
|
+
const fuzzyFieldsToRedact = [
|
|
6
|
+
'password',
|
|
7
|
+
'address',
|
|
8
|
+
'credit',
|
|
9
|
+
'postal',
|
|
10
|
+
'token',
|
|
11
|
+
'phone',
|
|
12
|
+
'mobile',
|
|
13
|
+
'expiry',
|
|
14
|
+
'account',
|
|
15
|
+
'email',
|
|
16
|
+
];
|
|
17
|
+
const exactFieldsToRedact = [
|
|
18
|
+
'firstname',
|
|
19
|
+
'lastname',
|
|
20
|
+
'street',
|
|
21
|
+
'fullname',
|
|
22
|
+
'creditcard',
|
|
23
|
+
'postcode',
|
|
24
|
+
'zipcode',
|
|
25
|
+
'city',
|
|
26
|
+
'town',
|
|
27
|
+
'county',
|
|
28
|
+
'cc',
|
|
29
|
+
'cardtype',
|
|
30
|
+
'cardnumber',
|
|
31
|
+
'email',
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Redacts one string, returns redacted value or false if nothing to redact
|
|
35
|
+
*/
|
|
36
|
+
function shouldRedact(field) {
|
|
37
|
+
// check for exact and fuzzy matches
|
|
38
|
+
return (exactFieldsToRedact.some(v => field === v) ||
|
|
39
|
+
fuzzyFieldsToRedact.some(v => field.indexOf(v) >= 0));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Try to parse content as a JSON object and
|
|
43
|
+
* iterate it recursively removing PII.
|
|
44
|
+
* Returns original content if nothing was changes or error is thrown.
|
|
45
|
+
* @param {String} content
|
|
46
|
+
*/
|
|
47
|
+
function tryParseObjectAndRemovePII(content) {
|
|
48
|
+
const first = content[0];
|
|
49
|
+
const isParsable = first === '{' || first === '[';
|
|
50
|
+
if (!isParsable) {
|
|
51
|
+
return content;
|
|
52
|
+
}
|
|
53
|
+
let redacted = false;
|
|
54
|
+
try {
|
|
55
|
+
const instance = JSON.parse(content);
|
|
56
|
+
iterateObjectRecursively(instance, (current, property) => {
|
|
57
|
+
if (typeof property !== 'string') {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const propertyLowerCase = property.toLowerCase();
|
|
61
|
+
const shouldRedactField = shouldRedact(propertyLowerCase);
|
|
62
|
+
if (shouldRedactField) {
|
|
63
|
+
redacted = true;
|
|
64
|
+
return PII_REDACTION_REPLACEMENT_STRING;
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
});
|
|
68
|
+
return redacted ? stringifyJSON(instance) : content;
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
return content;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Takes a string and redacts any PII we're able to detect
|
|
76
|
+
*
|
|
77
|
+
* content - the string from which we want to redact PII
|
|
78
|
+
* returns the string with any PII we're able to detect redacted.
|
|
79
|
+
*/
|
|
80
|
+
function removePII(content) {
|
|
81
|
+
/* eslint-disable no-param-reassign */
|
|
82
|
+
// In order to be fail-safe, let's return null if we won't be able to remove PII.
|
|
83
|
+
if (typeof content !== 'string') {
|
|
84
|
+
return '';
|
|
85
|
+
}
|
|
86
|
+
if (!content.length) {
|
|
87
|
+
return content;
|
|
88
|
+
}
|
|
89
|
+
content = tryParseObjectAndRemovePII(content);
|
|
90
|
+
// Go through each of the PII matching patterns
|
|
91
|
+
HTTP_PII_BLOCKING_PATTERNS.forEach(pattern => {
|
|
92
|
+
// Replace all matches with the appropriate number of asterisks
|
|
93
|
+
content = content.replace(pattern, PII_REDACTION_REPLACEMENT_STRING);
|
|
94
|
+
});
|
|
95
|
+
return content;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { removePII, shouldRedact };
|