noibu-react-native 0.2.11 → 0.2.13
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/CHANGELOG.md +21 -0
- package/README.md +69 -0
- package/dist/api/ClientConfig.d.ts +3 -0
- package/dist/api/ClientConfig.js +3 -0
- package/dist/api/MetroplexSocket.d.ts +1 -1
- package/dist/api/MetroplexSocket.js +15 -8
- package/dist/constants.d.ts +0 -6
- package/dist/constants.js +1 -1
- package/dist/mobileTransformer/mobile-replay/index.d.ts +10 -0
- package/dist/mobileTransformer/mobile-replay/index.js +57 -0
- package/dist/mobileTransformer/mobile-replay/mobile.types.d.ts +312 -0
- package/dist/mobileTransformer/mobile-replay/mobile.types.js +11 -0
- package/dist/mobileTransformer/mobile-replay/rrweb.d.ts +573 -0
- package/dist/mobileTransformer/mobile-replay/rrweb.js +39 -0
- package/dist/mobileTransformer/mobile-replay/schema/mobile/rr-mobile-schema.json.js +1559 -0
- package/dist/mobileTransformer/mobile-replay/schema/web/rr-web-schema.json.js +1183 -0
- package/dist/mobileTransformer/mobile-replay/transformer/colors.d.ts +1 -0
- package/dist/mobileTransformer/mobile-replay/transformer/colors.js +43 -0
- package/dist/mobileTransformer/mobile-replay/transformer/screen-chrome.d.ts +13 -0
- package/dist/mobileTransformer/mobile-replay/transformer/screen-chrome.js +142 -0
- package/dist/mobileTransformer/mobile-replay/transformer/transformers.d.ts +59 -0
- package/dist/mobileTransformer/mobile-replay/transformer/transformers.js +1160 -0
- package/dist/mobileTransformer/mobile-replay/transformer/types.d.ts +40 -0
- package/dist/mobileTransformer/mobile-replay/transformer/wireframeStyle.d.ts +16 -0
- package/dist/mobileTransformer/mobile-replay/transformer/wireframeStyle.js +197 -0
- package/dist/mobileTransformer/utils.d.ts +1 -0
- package/dist/mobileTransformer/utils.js +5 -0
- package/dist/monitors/http-tools/GqlErrorValidator.js +4 -3
- package/dist/sessionRecorder/SessionRecorder.js +7 -4
- package/dist/sessionRecorder/nativeSessionRecorderSubscription.js +23 -3
- package/dist/sessionRecorder/types.d.ts +1 -1
- package/dist/utils/piiRedactor.js +15 -2
- package/dist/utils/piiRedactor.test.d.ts +1 -0
- package/ios/IOSPocEmitter.h +7 -0
- package/ios/IOSPocEmitter.m +33 -0
- package/noibu-react-native.podspec +50 -0
- package/package.json +17 -4
- package/android/.gitignore +0 -13
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes of the noibu-react-native SDK release series are documented in this file using
|
|
4
|
+
the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
|
|
5
|
+
|
|
6
|
+
## 0.2.13
|
|
7
|
+
|
|
8
|
+
### Alpha support for iOS Session Replay in React Native
|
|
9
|
+
|
|
10
|
+
Implemented functionality that enables session replay support for iOS in React Native. Session videos are essential for
|
|
11
|
+
diagnosing errors. A video of an error occurring gives unique insight into the customer experience, and may help you
|
|
12
|
+
identify the step, field, button, or process that triggers the error.
|
|
13
|
+
|
|
14
|
+
### Add support for customizable PII redaction fields
|
|
15
|
+
|
|
16
|
+
Implemented functionality to allow custom exact and fuzzy fields for PII redaction, configurable via ClientConfig. Added
|
|
17
|
+
corresponding tests to validate redaction logic and ensure compatibility with default and custom field configurations.
|
|
18
|
+
Refactored redaction logic to use dynamically retrieved configurations for better flexibility.
|
|
19
|
+
|
|
20
|
+
For more details on how to configure the SDK, please refer to
|
|
21
|
+
the [README.md - Configuration](./README.md#configuration).
|
package/README.md
CHANGED
|
@@ -72,6 +72,9 @@ That's it! First time the module is set up, it runs an init and starts listening
|
|
|
72
72
|
- `@property [blockedElements] {string[]}` - lets you specify component ids to be ignored by SDK when collecting error information
|
|
73
73
|
- `@property [enableHttpDataCollection] {boolean}` - indicates whether SDK should collect HTTP information like headers or body from requests
|
|
74
74
|
- `@property [listOfUrlsToCollectHttpDataFrom] {string[]}` - is an allow list of URLs to allow HTTP data collection from, works best with `enableHttpDataCollection` enabled
|
|
75
|
+
- `@property [httpPiiBlockingPatterns] {RegExp[]}` - allows you to specify RegEx patterns for what PII information should be removed from JSON request and response data for the value in a key value pair
|
|
76
|
+
- `@property [fuzzyFieldsToRedact] {string[]}` - allows you to specify fuzzy strings for what PII information should be removed from JSON request and response data based on the key in a key value pair
|
|
77
|
+
- `@property [exactFieldsToRedact] {string[]}` - allows you to specify strict strings for what PII information should be removed from JSON request and response data based on the key in a key value pair
|
|
75
78
|
|
|
76
79
|
Example:
|
|
77
80
|
|
|
@@ -81,6 +84,72 @@ setupNoibu({
|
|
|
81
84
|
enableHttpDataCollection: true,
|
|
82
85
|
listOfUrlsToCollectHttpDataFrom: ['https://react-native-app.myshop.com/backend', 'https://example.com/some-path/'],
|
|
83
86
|
blockedElements: ['sensitive-info'],
|
|
87
|
+
httpPiiBlockingPatterns: [
|
|
88
|
+
// Match credit cards [https://www.regular-expressions.info/creditcard.html]
|
|
89
|
+
// Visa
|
|
90
|
+
/\b4\d{12}(?:\d{3})?\b/g,
|
|
91
|
+
|
|
92
|
+
// MasterCard
|
|
93
|
+
/\b(?:5[1-5]\d{2}|222[1-9]|22[3-9]\d|2[3-6]\d{2}|27[01]\d|2720)\d{12}\b/g,
|
|
94
|
+
|
|
95
|
+
// Amex
|
|
96
|
+
/\b3[47]\d{13}\b/g,
|
|
97
|
+
|
|
98
|
+
// Diners Club
|
|
99
|
+
/\b3(?:0[0-5]|[68]\d)\d{11}\b/g,
|
|
100
|
+
|
|
101
|
+
// Discover
|
|
102
|
+
/\b6(?:011|5\d{2})\d{12}\b/g,
|
|
103
|
+
|
|
104
|
+
// JCB
|
|
105
|
+
/\b(?:2131|1800|35\d{3})\d{11}\b/g,
|
|
106
|
+
|
|
107
|
+
// Emails [https://www.regular-expressions.info/email.html]
|
|
108
|
+
/\b[a-z0-9!#$%&'*+/=?^_‘{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_‘{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\b/g,
|
|
109
|
+
|
|
110
|
+
// US SSN with or without dashes
|
|
111
|
+
// [https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch04s12.html]
|
|
112
|
+
/\b(?!000|666)[0-8]\d{2}[-.● ]?(?!00)\d{2}[-.● ]?(?!0000)\d{4}\b/g,
|
|
113
|
+
|
|
114
|
+
// Canadian SIN with or without dashes [https://regexpattern.com/social-insurance-number-ca]
|
|
115
|
+
/\b(\d{3}[-.● ]?\d{3}[-.● ]?\d{3})\b/g,
|
|
116
|
+
|
|
117
|
+
// International phone numbers
|
|
118
|
+
// [https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch04s03.html]
|
|
119
|
+
/\+(?:\d●?){6,14}\d\b/g,
|
|
120
|
+
|
|
121
|
+
// US/Canada phone numbers
|
|
122
|
+
// [https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch04s02.html]
|
|
123
|
+
/(\b|\+)?(1[-.● ]?)?\(?(\d{3})\)?[-.● ]?(\d{3})[-.● ]?(\d{4})\b/g,
|
|
124
|
+
],
|
|
125
|
+
fuzzyFieldsToRedact: [
|
|
126
|
+
'password',
|
|
127
|
+
'address',
|
|
128
|
+
'credit',
|
|
129
|
+
'postal',
|
|
130
|
+
'token',
|
|
131
|
+
'phone',
|
|
132
|
+
'mobile',
|
|
133
|
+
'expiry',
|
|
134
|
+
'account',
|
|
135
|
+
'email',
|
|
136
|
+
],
|
|
137
|
+
exactFieldsToRedact: [
|
|
138
|
+
'firstname',
|
|
139
|
+
'lastname',
|
|
140
|
+
'street',
|
|
141
|
+
'fullname',
|
|
142
|
+
'creditcard',
|
|
143
|
+
'postcode',
|
|
144
|
+
'zipcode',
|
|
145
|
+
'city',
|
|
146
|
+
'town',
|
|
147
|
+
'county',
|
|
148
|
+
'cc',
|
|
149
|
+
'cardtype',
|
|
150
|
+
'cardnumber',
|
|
151
|
+
'email',
|
|
152
|
+
],
|
|
84
153
|
});
|
|
85
154
|
```
|
|
86
155
|
|
|
@@ -21,6 +21,9 @@ export default class ClientConfig extends Singleton {
|
|
|
21
21
|
readonly listOfUrlsToCollectHttpDataFrom: CustomerConfig['listOfUrlsToCollectHttpDataFrom'];
|
|
22
22
|
readonly enableHttpDataCollection: CustomerConfig['enableHttpDataCollection'];
|
|
23
23
|
readonly blockedElements: CustomerConfig['blockedElements'];
|
|
24
|
+
readonly httpPiiBlockingPatterns: CustomerConfig['httpPiiBlockingPatterns'];
|
|
25
|
+
readonly fuzzyFieldsToRedact: CustomerConfig['fuzzyFieldsToRedact'];
|
|
26
|
+
readonly exactFieldsToRedact: CustomerConfig['exactFieldsToRedact'];
|
|
24
27
|
private alreadyPostingError;
|
|
25
28
|
/** Error handling for constructor */
|
|
26
29
|
handleConstructorError(noibuErrorURL?: string, customerConfig?: CustomerConfig): void;
|
package/dist/api/ClientConfig.js
CHANGED
|
@@ -67,6 +67,9 @@ class ClientConfig extends Singleton {
|
|
|
67
67
|
this.listOfUrlsToCollectHttpDataFrom = customerConfig.listOfUrlsToCollectHttpDataFrom;
|
|
68
68
|
this.enableHttpDataCollection = customerConfig.enableHttpDataCollection;
|
|
69
69
|
this.blockedElements = customerConfig.blockedElements;
|
|
70
|
+
this.httpPiiBlockingPatterns = customerConfig.httpPiiBlockingPatterns;
|
|
71
|
+
this.fuzzyFieldsToRedact = customerConfig.fuzzyFieldsToRedact;
|
|
72
|
+
this.exactFieldsToRedact = customerConfig.exactFieldsToRedact;
|
|
70
73
|
// sets up this.browserId, this.isClientDisabled, this.pageVisitSeq
|
|
71
74
|
this.configurationPromise = this._setupStorageVars();
|
|
72
75
|
}
|
|
@@ -4,7 +4,7 @@ import { NoSeqNumSlidingMessage, RetryQueueWSMessage } from '../types/Metroplex'
|
|
|
4
4
|
/**
|
|
5
5
|
* Grab the video recorder type based on the device we run the app on.
|
|
6
6
|
*/
|
|
7
|
-
export declare function getVideoRecorderType(): Promise<VideoRecorder>;
|
|
7
|
+
export declare function getVideoRecorderType(): Promise<VideoRecorder.RRWeb | VideoRecorder.AndroidNative>;
|
|
8
8
|
/**
|
|
9
9
|
* Implements rolling window of specified size,
|
|
10
10
|
* but only makes a cut once array length exceeds 150%.
|
|
@@ -22,9 +22,6 @@ function getVideoRecorderType() {
|
|
|
22
22
|
if (Platform.OS === 'android') {
|
|
23
23
|
return 'AndroidNative';
|
|
24
24
|
}
|
|
25
|
-
if (Platform.OS === 'ios') {
|
|
26
|
-
return 'IOSNative';
|
|
27
|
-
}
|
|
28
25
|
return 'RRWeb'; // should never happen
|
|
29
26
|
});
|
|
30
27
|
}
|
|
@@ -43,6 +40,7 @@ function createSlidingArrayOfSize(size, arraySource = [], downsizeThreshold = 1.
|
|
|
43
40
|
return (...args) => {
|
|
44
41
|
target.push(...args);
|
|
45
42
|
if (target.length >= downsizeThreshold * size) {
|
|
43
|
+
StoredMetrics.getInstance().setDidCutPv();
|
|
46
44
|
target.splice(0, size / downsizeFactor);
|
|
47
45
|
}
|
|
48
46
|
};
|
|
@@ -57,7 +55,7 @@ const METROPLEX_RETRY_FREQUENCY = 30000;
|
|
|
57
55
|
const CURRENT_PV_VERSION = 5;
|
|
58
56
|
// maximum number of connection a single page visit can reach
|
|
59
57
|
const MAX_METROPLEX_CONNECTION_COUNT = 100;
|
|
60
|
-
const MAX_RETRY_MSG_Q_SIZE =
|
|
58
|
+
const MAX_RETRY_MSG_Q_SIZE = 1000;
|
|
61
59
|
/** Manages the socket to Metroplex */
|
|
62
60
|
class MetroplexSocket extends Singleton {
|
|
63
61
|
/**
|
|
@@ -249,7 +247,7 @@ class MetroplexSocket extends Singleton {
|
|
|
249
247
|
return;
|
|
250
248
|
}
|
|
251
249
|
this.currentConnectionAttempts += 1;
|
|
252
|
-
this.socket = new WebSocket(this.connectionURL,
|
|
250
|
+
this.socket = new WebSocket(this.connectionURL, [], {
|
|
253
251
|
headers: {
|
|
254
252
|
'User-Agent': yield getUserAgent(), // must pass useragent explicitly
|
|
255
253
|
},
|
|
@@ -568,7 +566,7 @@ class MetroplexSocket extends Singleton {
|
|
|
568
566
|
});
|
|
569
567
|
yield this.postMessage(currentCompletePv);
|
|
570
568
|
// debug log if large retry message queue
|
|
571
|
-
if (this.retryMessageQueue.length >
|
|
569
|
+
if (this.retryMessageQueue.length > MAX_RETRY_MSG_Q_SIZE) {
|
|
572
570
|
let postInfoMessage = `Vid: ${currentCompletePv.pvvf.length}`;
|
|
573
571
|
postInfoMessage += ` PV: ${currentCompletePv.pvp.length}`;
|
|
574
572
|
postInfoMessage += ` HTTP: ${(_a = currentCompletePv.pvh) === null || _a === void 0 ? void 0 : _a.length},`;
|
|
@@ -603,7 +601,7 @@ class MetroplexSocket extends Singleton {
|
|
|
603
601
|
// debug message gets through
|
|
604
602
|
const clientDisabled = ClientConfig.getInstance().isClientDisabled;
|
|
605
603
|
ClientConfig.getInstance().isClientDisabled = false;
|
|
606
|
-
ClientConfig.getInstance().postInternalError(message, clientDisabled, Severity.
|
|
604
|
+
ClientConfig.getInstance().postInternalError(message, clientDisabled, Severity.ERROR);
|
|
607
605
|
}
|
|
608
606
|
});
|
|
609
607
|
}
|
|
@@ -700,7 +698,16 @@ class MetroplexSocket extends Singleton {
|
|
|
700
698
|
const data = response.substring(prefix.length);
|
|
701
699
|
const success = /^\d{6}$/.test(data);
|
|
702
700
|
if (this.helpCodeCb) {
|
|
703
|
-
|
|
701
|
+
const event = {
|
|
702
|
+
detail: data,
|
|
703
|
+
bubbles: false,
|
|
704
|
+
cancelable: false,
|
|
705
|
+
cancelBubble: false,
|
|
706
|
+
};
|
|
707
|
+
Object.defineProperty(event, 'detail', {
|
|
708
|
+
get: () => ({ success, data }),
|
|
709
|
+
});
|
|
710
|
+
this.helpCodeCb(event);
|
|
704
711
|
}
|
|
705
712
|
return true;
|
|
706
713
|
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -4,12 +4,6 @@ export declare const MAX_STRING_LENGTH = 1024;
|
|
|
4
4
|
export declare const MAX_TIME_FOR_UNSENT_DATA_MILLIS = 500;
|
|
5
5
|
export declare const PII_EMAIL_PATTERN: RegExp;
|
|
6
6
|
export declare const PII_REDACTION_REPLACEMENT_STRING = "******";
|
|
7
|
-
export declare const SEVERITY: {
|
|
8
|
-
error: string;
|
|
9
|
-
warn: string;
|
|
10
|
-
info: string;
|
|
11
|
-
debug: string;
|
|
12
|
-
};
|
|
13
7
|
export declare const MAX_METROPLEX_SOCKET_INNACTIVE_TIME: number;
|
|
14
8
|
export declare const MAX_BEACON_PAYLOAD_SIZE = 59000;
|
|
15
9
|
export declare const CONTENT_TYPE = "content-type";
|
package/dist/constants.js
CHANGED
|
@@ -24,7 +24,7 @@ const CONTENT_TYPE = 'content-type';
|
|
|
24
24
|
* Gets the script id from the cookie object, returns default if cannot be found
|
|
25
25
|
*/
|
|
26
26
|
function GET_SCRIPT_ID() {
|
|
27
|
-
return "1.0.104-rn-sdk-0.2.
|
|
27
|
+
return "1.0.104-rn-sdk-0.2.13" ;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Gets the max metro recon number
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { eventWithTime } from './rrweb';
|
|
2
|
+
import { ErrorObject } from 'ajv';
|
|
3
|
+
import { mobileEventWithTime } from './mobile.types';
|
|
4
|
+
export declare function validateFromMobile(data: unknown): {
|
|
5
|
+
isValid: boolean;
|
|
6
|
+
errors: ErrorObject[] | null | undefined;
|
|
7
|
+
};
|
|
8
|
+
export declare function transformEventToWeb(event: unknown, validateTransformation?: boolean): eventWithTime;
|
|
9
|
+
export declare function transformToWeb(mobileData: (eventWithTime | mobileEventWithTime)[]): eventWithTime[];
|
|
10
|
+
export declare function validateAgainstWebSchema(data: unknown): boolean;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
import mobileSchema from './schema/mobile/rr-mobile-schema.json.js';
|
|
3
|
+
import webSchema from './schema/web/rr-web-schema.json.js';
|
|
4
|
+
import { makeFullEvent, makeIncrementalEvent, makeMetaEvent, makeCustomEvent } from './transformer/transformers.js';
|
|
5
|
+
|
|
6
|
+
//import { captureException, captureMessage } from '@sentry/react'
|
|
7
|
+
//@ts-ignore
|
|
8
|
+
const ajv = new Ajv({
|
|
9
|
+
//@ts-ignore
|
|
10
|
+
allowUnionTypes: true,
|
|
11
|
+
}); // options can be passed, e.g. {allErrors: true}
|
|
12
|
+
const transformers = {
|
|
13
|
+
2: makeFullEvent,
|
|
14
|
+
3: makeIncrementalEvent,
|
|
15
|
+
4: makeMetaEvent,
|
|
16
|
+
5: makeCustomEvent,
|
|
17
|
+
};
|
|
18
|
+
ajv.compile(mobileSchema);
|
|
19
|
+
const webSchemaValidator = ajv.compile(webSchema);
|
|
20
|
+
function couldBeEventWithTime(x) {
|
|
21
|
+
return (typeof x === 'object' && x !== null && 'type' in x && 'timestamp' in x);
|
|
22
|
+
}
|
|
23
|
+
function transformEventToWeb(event, validateTransformation) {
|
|
24
|
+
try {
|
|
25
|
+
if (couldBeEventWithTime(event)) {
|
|
26
|
+
const transformer = transformers[event.type];
|
|
27
|
+
if (transformer) {
|
|
28
|
+
const transformed = transformer(event);
|
|
29
|
+
if (validateTransformation) ;
|
|
30
|
+
return transformed;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
//captureMessage(`No type in event`, { extra: { event } })
|
|
34
|
+
console.error('No type in event: ', event);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
//captureException(e, { extra: { event } })
|
|
40
|
+
console.error('Exception captured: ', event);
|
|
41
|
+
}
|
|
42
|
+
return event;
|
|
43
|
+
}
|
|
44
|
+
function validateAgainstWebSchema(data) {
|
|
45
|
+
const validationResult = webSchemaValidator(data);
|
|
46
|
+
if (!validationResult) {
|
|
47
|
+
// we are passing all data through this validation now and don't know how safe the schema is
|
|
48
|
+
// captureMessage('transformation did not match schema', {
|
|
49
|
+
// extra: { data, errors: webSchemaValidator.errors },
|
|
50
|
+
// });
|
|
51
|
+
console.error('transformation did not match schema', data, webSchemaValidator.errors);
|
|
52
|
+
}
|
|
53
|
+
//@ts-ignore
|
|
54
|
+
return validationResult;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { transformEventToWeb, validateAgainstWebSchema };
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { customEvent, EventType, IncrementalSource, removedNodeMutation } from './rrweb';
|
|
2
|
+
export declare enum NodeType {
|
|
3
|
+
Document = 0,
|
|
4
|
+
DocumentType = 1,
|
|
5
|
+
Element = 2,
|
|
6
|
+
Text = 3,
|
|
7
|
+
CDATA = 4,
|
|
8
|
+
Comment = 5
|
|
9
|
+
}
|
|
10
|
+
export type documentNode = {
|
|
11
|
+
type: NodeType.Document;
|
|
12
|
+
childNodes: serializedNodeWithId[];
|
|
13
|
+
compatMode?: string;
|
|
14
|
+
};
|
|
15
|
+
export type documentTypeNode = {
|
|
16
|
+
type: NodeType.DocumentType;
|
|
17
|
+
name: string;
|
|
18
|
+
publicId: string;
|
|
19
|
+
systemId: string;
|
|
20
|
+
};
|
|
21
|
+
export type attributes = {
|
|
22
|
+
[key: string]: string | number | true | null;
|
|
23
|
+
};
|
|
24
|
+
export type elementNode = {
|
|
25
|
+
type: NodeType.Element;
|
|
26
|
+
tagName: string;
|
|
27
|
+
attributes: attributes;
|
|
28
|
+
childNodes: serializedNodeWithId[];
|
|
29
|
+
isSVG?: true;
|
|
30
|
+
needBlock?: boolean;
|
|
31
|
+
isCustom?: true;
|
|
32
|
+
};
|
|
33
|
+
export type textNode = {
|
|
34
|
+
type: NodeType.Text;
|
|
35
|
+
textContent: string;
|
|
36
|
+
isStyle?: true;
|
|
37
|
+
};
|
|
38
|
+
export type cdataNode = {
|
|
39
|
+
type: NodeType.CDATA;
|
|
40
|
+
textContent: '';
|
|
41
|
+
};
|
|
42
|
+
export type commentNode = {
|
|
43
|
+
type: NodeType.Comment;
|
|
44
|
+
textContent: string;
|
|
45
|
+
};
|
|
46
|
+
export type serializedNode = (documentNode | documentTypeNode | elementNode | textNode | cdataNode | commentNode) & {
|
|
47
|
+
rootId?: number;
|
|
48
|
+
isShadowHost?: boolean;
|
|
49
|
+
isShadow?: boolean;
|
|
50
|
+
};
|
|
51
|
+
export type serializedNodeWithId = serializedNode & {
|
|
52
|
+
id: number;
|
|
53
|
+
};
|
|
54
|
+
export type MobileNodeType = 'text' | 'image' | 'screenshot' | 'rectangle' | 'placeholder' | 'web_view' | 'input' | 'div' | 'radio_group' | 'status_bar' | 'navigation_bar';
|
|
55
|
+
export type MobileStyles = {
|
|
56
|
+
/**
|
|
57
|
+
* @description maps to CSS color. Accepts any valid CSS color value. Expects a #RGB value e.g. #000 or #000000
|
|
58
|
+
*/
|
|
59
|
+
color?: string;
|
|
60
|
+
/**
|
|
61
|
+
* @description maps to CSS background-color. Accepts any valid CSS color value. Expects a #RGB value e.g. #000 or #000000
|
|
62
|
+
*/
|
|
63
|
+
backgroundColor?: string;
|
|
64
|
+
/**
|
|
65
|
+
* @description if provided this will be used as a base64 encoded image source for the backgroundImage css property, with no other attributes it is assumed to be a PNG
|
|
66
|
+
*/
|
|
67
|
+
backgroundImage?: string;
|
|
68
|
+
/**
|
|
69
|
+
* @description can be used alongside the background image property to specify how the image is rendered. Accepts a subset of the valid values for CSS background-size property. If not provided (and backgroundImage is present) defaults to 'auto'
|
|
70
|
+
*/
|
|
71
|
+
backgroundSize?: 'contain' | 'cover' | 'auto';
|
|
72
|
+
/**
|
|
73
|
+
* @description if borderWidth is present, then border style is assumed to be solid
|
|
74
|
+
*/
|
|
75
|
+
borderWidth?: string | number;
|
|
76
|
+
/**
|
|
77
|
+
* @description if borderRadius is present, then border style is assumed to be solid
|
|
78
|
+
*/
|
|
79
|
+
borderRadius?: string | number;
|
|
80
|
+
/**
|
|
81
|
+
* @description if borderColor is present, then border style is assumed to be solid
|
|
82
|
+
*/
|
|
83
|
+
borderColor?: string;
|
|
84
|
+
/**
|
|
85
|
+
* @description vertical alignment with respect to its parent
|
|
86
|
+
*/
|
|
87
|
+
verticalAlign?: 'top' | 'bottom' | 'center';
|
|
88
|
+
/**
|
|
89
|
+
* @description horizontal alignment with respect to its parent
|
|
90
|
+
*/
|
|
91
|
+
horizontalAlign?: 'left' | 'right' | 'center';
|
|
92
|
+
/**
|
|
93
|
+
* @description maps to CSS font-size. Accepts any valid CSS font-size value. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px
|
|
94
|
+
*/
|
|
95
|
+
fontSize?: string | number;
|
|
96
|
+
/**
|
|
97
|
+
* @description maps to CSS font-family. Accepts any valid CSS font-family value.
|
|
98
|
+
*/
|
|
99
|
+
fontFamily?: string;
|
|
100
|
+
/**
|
|
101
|
+
* @description maps to CSS padding-left. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px
|
|
102
|
+
*/
|
|
103
|
+
paddingLeft?: string | number;
|
|
104
|
+
/**
|
|
105
|
+
* @description maps to CSS padding-right. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px
|
|
106
|
+
*/
|
|
107
|
+
paddingRight?: string | number;
|
|
108
|
+
/**
|
|
109
|
+
* @description maps to CSS padding-top. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px
|
|
110
|
+
*/
|
|
111
|
+
paddingTop?: string | number;
|
|
112
|
+
/**
|
|
113
|
+
* @description maps to CSS padding-bottom. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px
|
|
114
|
+
*/
|
|
115
|
+
paddingBottom?: string | number;
|
|
116
|
+
};
|
|
117
|
+
type wireframeBase = {
|
|
118
|
+
id: number;
|
|
119
|
+
/**
|
|
120
|
+
* @description x and y are the top left corner of the element, if they are present then the element is absolutely positioned, if they are not present this is equivalent to setting them to 0
|
|
121
|
+
*/
|
|
122
|
+
x?: number;
|
|
123
|
+
y?: number;
|
|
124
|
+
width: number | '100vw';
|
|
125
|
+
height: number;
|
|
126
|
+
childWireframes?: wireframe[];
|
|
127
|
+
type: MobileNodeType;
|
|
128
|
+
style?: MobileStyles;
|
|
129
|
+
};
|
|
130
|
+
export type wireframeInputBase = wireframeBase & {
|
|
131
|
+
type: 'input';
|
|
132
|
+
/**
|
|
133
|
+
* @description for several attributes we technically only care about true or absent as values. They are represented as bare attributes in HTML <input disabled>. When true that attribute is added to the HTML element, when absent that attribute is not added to the HTML element. When false or absent they are not added to the element.
|
|
134
|
+
*/
|
|
135
|
+
disabled: boolean;
|
|
136
|
+
};
|
|
137
|
+
export type wireframeCheckBox = wireframeInputBase & {
|
|
138
|
+
inputType: 'checkbox';
|
|
139
|
+
/**
|
|
140
|
+
* @description for several attributes we technically only care about true or absent as values. They are represented as bare attributes in HTML <input checked>. When true that attribute is added to the HTML element, when absent that attribute is not added to the HTML element. When false or absent they are not added to the element.
|
|
141
|
+
*/
|
|
142
|
+
checked: boolean;
|
|
143
|
+
label?: string;
|
|
144
|
+
};
|
|
145
|
+
export type wireframeToggle = wireframeInputBase & {
|
|
146
|
+
inputType: 'toggle';
|
|
147
|
+
checked: boolean;
|
|
148
|
+
label?: string;
|
|
149
|
+
};
|
|
150
|
+
export type wireframeRadioGroup = wireframeBase & {
|
|
151
|
+
type: 'radio_group';
|
|
152
|
+
};
|
|
153
|
+
export type wireframeRadio = wireframeInputBase & {
|
|
154
|
+
inputType: 'radio';
|
|
155
|
+
/**
|
|
156
|
+
* @description for several attributes we technically only care about true or absent as values. They are represented as bare attributes in HTML <input checked>. When true that attribute is added to the HTML element, when absent that attribute is not added to the HTML element. When false or absent they are not added to the element.
|
|
157
|
+
*/
|
|
158
|
+
checked: boolean;
|
|
159
|
+
label?: string;
|
|
160
|
+
};
|
|
161
|
+
export type wireframeInput = wireframeInputBase & {
|
|
162
|
+
inputType: 'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url';
|
|
163
|
+
value?: string;
|
|
164
|
+
};
|
|
165
|
+
export type wireframeSelect = wireframeInputBase & {
|
|
166
|
+
inputType: 'select';
|
|
167
|
+
value?: string;
|
|
168
|
+
options?: string[];
|
|
169
|
+
};
|
|
170
|
+
export type wireframeTextArea = wireframeInputBase & {
|
|
171
|
+
inputType: 'text_area';
|
|
172
|
+
value?: string;
|
|
173
|
+
};
|
|
174
|
+
export type wireframeButton = wireframeInputBase & {
|
|
175
|
+
inputType: 'button';
|
|
176
|
+
/**
|
|
177
|
+
* @description this is the text that is displayed on the button, if not sent then you must send childNodes with the button content
|
|
178
|
+
*/
|
|
179
|
+
value?: string;
|
|
180
|
+
};
|
|
181
|
+
export type wireframeProgress = wireframeInputBase & {
|
|
182
|
+
inputType: 'progress';
|
|
183
|
+
/**
|
|
184
|
+
* @description This attribute specifies how much of the task that has been completed. It must be a valid floating point number between 0 and max, or between 0 and 1 if max is omitted. If there is no value attribute, the progress bar is indeterminate; this indicates that an activity is ongoing with no indication of how long it is expected to take. When bar style is rating this is the number of filled stars.
|
|
185
|
+
*/
|
|
186
|
+
value?: number;
|
|
187
|
+
/**
|
|
188
|
+
* @description The max attribute, if present, must have a value greater than 0 and be a valid floating point number. The default value is 1. When bar style is rating this is the number of stars.
|
|
189
|
+
*/
|
|
190
|
+
max?: number;
|
|
191
|
+
style?: MobileStyles & {
|
|
192
|
+
bar: 'horizontal' | 'circular' | 'rating';
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
export type wireframeInputComponent = wireframeCheckBox | wireframeRadio | wireframeInput | wireframeSelect | wireframeTextArea | wireframeButton | wireframeProgress | wireframeToggle;
|
|
196
|
+
export type wireframeText = wireframeBase & {
|
|
197
|
+
type: 'text';
|
|
198
|
+
text: string;
|
|
199
|
+
};
|
|
200
|
+
export type wireframeImage = wireframeBase & {
|
|
201
|
+
type: 'image';
|
|
202
|
+
/**
|
|
203
|
+
* @description this will be used as base64 encoded image source, with no other attributes it is assumed to be a PNG, if omitted a placeholder is rendered
|
|
204
|
+
*/
|
|
205
|
+
base64?: string;
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* @description a screenshot behaves exactly like an image, but it is expected to be a screenshot of the screen at the time of the event, when sent as a mutation it must always attached to the root of the playback, when sent as an initial snapshot it must be sent as the first or only snapshot so that it attaches to the body of the playback
|
|
209
|
+
*/
|
|
210
|
+
export type wireframeScreenshot = wireframeImage & {
|
|
211
|
+
type: 'screenshot';
|
|
212
|
+
};
|
|
213
|
+
export type wireframeRectangle = wireframeBase & {
|
|
214
|
+
type: 'rectangle';
|
|
215
|
+
};
|
|
216
|
+
export type wireframeWebView = wireframeBase & {
|
|
217
|
+
type: 'web_view';
|
|
218
|
+
url?: string;
|
|
219
|
+
};
|
|
220
|
+
export type wireframePlaceholder = wireframeBase & {
|
|
221
|
+
type: 'placeholder';
|
|
222
|
+
label?: string;
|
|
223
|
+
};
|
|
224
|
+
export type wireframeDiv = wireframeBase & {
|
|
225
|
+
type: 'div';
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* @description the status bar respects styling and positioning, but it is expected to be at the top of the screen with limited styling and no child elements
|
|
229
|
+
*/
|
|
230
|
+
export type wireframeStatusBar = wireframeBase & {
|
|
231
|
+
type: 'status_bar';
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* @description the navigation bar respects styling and positioning, but it is expected to be at the bottom of the screen with limited styling and no child elements
|
|
235
|
+
*/
|
|
236
|
+
export type wireframeNavigationBar = wireframeBase & {
|
|
237
|
+
type: 'navigation_bar';
|
|
238
|
+
};
|
|
239
|
+
export type wireframe = wireframeText | wireframeImage | wireframeScreenshot | wireframeRectangle | wireframeDiv | wireframeInputComponent | wireframeRadioGroup | wireframeWebView | wireframePlaceholder | wireframeStatusBar | wireframeNavigationBar;
|
|
240
|
+
export type fullSnapshotEvent = {
|
|
241
|
+
type: EventType.FullSnapshot;
|
|
242
|
+
data: {
|
|
243
|
+
/**
|
|
244
|
+
* @description This mimics the RRWeb full snapshot event type, except instead of reporting a serialized DOM it reports a wireframe representation of the screen.
|
|
245
|
+
*/
|
|
246
|
+
wireframes: wireframe[];
|
|
247
|
+
initialOffset: {
|
|
248
|
+
top: number;
|
|
249
|
+
left: number;
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
export type incrementalSnapshotEvent = {
|
|
254
|
+
type: EventType.IncrementalSnapshot;
|
|
255
|
+
data: any;
|
|
256
|
+
} | MobileIncrementalSnapshotEvent;
|
|
257
|
+
export type MobileNodeMutation = {
|
|
258
|
+
parentId: number;
|
|
259
|
+
wireframe: wireframe;
|
|
260
|
+
};
|
|
261
|
+
export type MobileNodeMutationData = {
|
|
262
|
+
source: IncrementalSource.Mutation;
|
|
263
|
+
/**
|
|
264
|
+
* @description An update is implemented as a remove and then an add, so the updates array contains the ID of the removed node and the wireframe for the added node
|
|
265
|
+
*/
|
|
266
|
+
updates?: MobileNodeMutation[];
|
|
267
|
+
adds?: MobileNodeMutation[];
|
|
268
|
+
/**
|
|
269
|
+
* @description A mobile remove is identical to a web remove
|
|
270
|
+
*/
|
|
271
|
+
removes?: removedNodeMutation[];
|
|
272
|
+
};
|
|
273
|
+
export type MobileIncrementalSnapshotEvent = {
|
|
274
|
+
type: EventType.IncrementalSnapshot;
|
|
275
|
+
/**
|
|
276
|
+
* @description This sits alongside the RRWeb incremental snapshot event type, mobile replay can send any of the RRWeb incremental snapshot event types, which will be passed unchanged to the player - for example to send touch events. removed node mutations are passed unchanged to the player.
|
|
277
|
+
*/
|
|
278
|
+
data: MobileNodeMutationData;
|
|
279
|
+
};
|
|
280
|
+
export type metaEvent = {
|
|
281
|
+
type: EventType.Meta;
|
|
282
|
+
data: {
|
|
283
|
+
href?: string;
|
|
284
|
+
width: number;
|
|
285
|
+
height: number;
|
|
286
|
+
};
|
|
287
|
+
};
|
|
288
|
+
export type keyboardEvent = {
|
|
289
|
+
type: EventType.Custom;
|
|
290
|
+
data: {
|
|
291
|
+
tag: 'keyboard';
|
|
292
|
+
payload: {
|
|
293
|
+
open: true;
|
|
294
|
+
styles?: MobileStyles;
|
|
295
|
+
/**
|
|
296
|
+
* @description x and y are the top left corner of the element, if they are present then the element is absolutely positioned, if they are not present then the keyboard is at the bottom of the screen
|
|
297
|
+
*/
|
|
298
|
+
x?: number;
|
|
299
|
+
y?: number;
|
|
300
|
+
height: number;
|
|
301
|
+
width?: number;
|
|
302
|
+
} | {
|
|
303
|
+
open: false;
|
|
304
|
+
};
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
export type mobileEvent = fullSnapshotEvent | metaEvent | customEvent | incrementalSnapshotEvent | keyboardEvent;
|
|
308
|
+
export type mobileEventWithTime = mobileEvent & {
|
|
309
|
+
timestamp: number;
|
|
310
|
+
delay?: number;
|
|
311
|
+
};
|
|
312
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var NodeType;
|
|
2
|
+
(function (NodeType) {
|
|
3
|
+
NodeType[NodeType["Document"] = 0] = "Document";
|
|
4
|
+
NodeType[NodeType["DocumentType"] = 1] = "DocumentType";
|
|
5
|
+
NodeType[NodeType["Element"] = 2] = "Element";
|
|
6
|
+
NodeType[NodeType["Text"] = 3] = "Text";
|
|
7
|
+
NodeType[NodeType["CDATA"] = 4] = "CDATA";
|
|
8
|
+
NodeType[NodeType["Comment"] = 5] = "Comment";
|
|
9
|
+
})(NodeType || (NodeType = {}));
|
|
10
|
+
|
|
11
|
+
export { NodeType };
|