@sailfish-ai/recorder 1.0.3 → 1.0.5
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/dist/graphql.js +5 -0
- package/dist/index.js +41 -22
- package/dist/recording.js +0 -1
- package/dist/sailfish-recorder.es.js +461 -20
- package/dist/sailfish-recorder.umd.js +472 -31
- package/dist/types/graphql.d.ts +1 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/websocket.js +2 -1
- package/package.json +2 -1
package/dist/graphql.js
CHANGED
|
@@ -52,3 +52,8 @@ export function startRecordingSession(apiKey, recordingId, backendApi) {
|
|
|
52
52
|
backendApi,
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
+
export function sendDomainsToNotPropagateHeaderTo(apiKey, domains, backendApi) {
|
|
56
|
+
return sendGraphQLRequest("DomainsToNotPassHeaderTo", `mutation DomainsToNotPassHeaderTo($apiKey: String!, $domains: [String!]!) {
|
|
57
|
+
domainsToNotPassHeaderTo(apiKey: $apiKey, domains: $domains)
|
|
58
|
+
}`, { apiKey, domains, backendApi });
|
|
59
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { parse as parseDomain } from "tldts"; // Importing tldts
|
|
1
2
|
import { v4 as uuidv4 } from "uuid";
|
|
2
3
|
import { gatherAndCacheDeviceInfo } from "./deviceInfo";
|
|
3
4
|
import { sendRecordingEvents } from "./eventCache";
|
|
4
|
-
import { fetchCaptureSettings, startRecordingSession } from "./graphql";
|
|
5
|
+
import { fetchCaptureSettings, sendDomainsToNotPropagateHeaderTo, startRecordingSession, } from "./graphql";
|
|
5
6
|
import { initializeRecording } from "./recording";
|
|
6
|
-
// Default
|
|
7
|
-
|
|
7
|
+
// Default list of domains to ignore
|
|
8
|
+
const DOMAINS_TO_NOT_PROPAGATE_HEADER_TO_DEFAULT = [
|
|
9
|
+
"identitytoolkit.googleapis.com",
|
|
10
|
+
];
|
|
11
|
+
const DOMAINS_TO_NOT_RECORD_NETWORK_REQUESTS_TO = [];
|
|
8
12
|
export const DEFAULT_CAPTURE_SETTINGS = {
|
|
9
13
|
recordCanvas: false,
|
|
10
14
|
recordCrossOriginIframes: false,
|
|
@@ -30,8 +34,8 @@ export const DEFAULT_CONSOLE_RECORDING_SETTINGS = {
|
|
|
30
34
|
export const DEFAULT_NETWORK_CAPTURE_SETTINGS = {
|
|
31
35
|
initiatorTypes: ["fetch", "xmlhttprequest"],
|
|
32
36
|
ignoreRequestFn: (request) => {
|
|
33
|
-
const domain =
|
|
34
|
-
return
|
|
37
|
+
const domain = getEffectiveDomain(request.url);
|
|
38
|
+
return DOMAINS_TO_NOT_RECORD_NETWORK_REQUESTS_TO.includes(domain);
|
|
35
39
|
},
|
|
36
40
|
recordHeaders: true,
|
|
37
41
|
ignoreHeaders: {
|
|
@@ -45,6 +49,14 @@ export const DEFAULT_NETWORK_CAPTURE_SETTINGS = {
|
|
|
45
49
|
recordBody: true,
|
|
46
50
|
recordInitialRequests: false,
|
|
47
51
|
};
|
|
52
|
+
// Function to strip 'www.' and get the effective domain from a URL using tldts
|
|
53
|
+
function getEffectiveDomain(url) {
|
|
54
|
+
const parsed = parseDomain(url);
|
|
55
|
+
const domain = parsed.subdomain !== ""
|
|
56
|
+
? `${parsed.subdomain}.${parsed.domain}`
|
|
57
|
+
: parsed.domain;
|
|
58
|
+
return domain.startsWith("www.") ? domain.slice(4) : domain;
|
|
59
|
+
}
|
|
48
60
|
// Functions
|
|
49
61
|
// Function to get the current sessionId from sessionStorage
|
|
50
62
|
function getOrSetSessionId(forceNew = false) {
|
|
@@ -71,27 +83,37 @@ function storeCredentialsAndConnection({ apiKey, backendApi, }) {
|
|
|
71
83
|
sessionStorage.setItem("sailfishBackendApi", backendApi);
|
|
72
84
|
}
|
|
73
85
|
// Intercepting XMLHttpRequest
|
|
74
|
-
|
|
86
|
+
function setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo) {
|
|
75
87
|
const originalOpen = XMLHttpRequest.prototype.open;
|
|
76
88
|
const originalSend = XMLHttpRequest.prototype.send;
|
|
77
89
|
const sessionId = getOrSetSessionId();
|
|
90
|
+
const combinedIgnoreDomains = [
|
|
91
|
+
...DOMAINS_TO_NOT_PROPAGATE_HEADER_TO_DEFAULT,
|
|
92
|
+
...domainsToNotPropagateHeaderTo,
|
|
93
|
+
].map((domain) => (domain.startsWith("www.") ? domain.slice(4) : domain)); // Remove 'www.' from ignore domains
|
|
78
94
|
XMLHttpRequest.prototype.open = function (...args) {
|
|
79
95
|
this._url = args[1]; // Store the request URL
|
|
80
96
|
originalOpen.apply(this, args);
|
|
81
97
|
};
|
|
82
98
|
XMLHttpRequest.prototype.send = function (...args) {
|
|
83
|
-
|
|
99
|
+
const domain = getEffectiveDomain(this._url); // Use tldts to get the domain
|
|
100
|
+
if (sessionId && !combinedIgnoreDomains.includes(domain)) {
|
|
84
101
|
this.setRequestHeader("X-Sf3-Rid", sessionId);
|
|
85
102
|
}
|
|
86
103
|
originalSend.apply(this, args);
|
|
87
104
|
};
|
|
88
|
-
}
|
|
105
|
+
}
|
|
89
106
|
// Intercepting fetch API
|
|
90
|
-
|
|
107
|
+
function setupFetchInterceptor(domainsToNotPropagateHeaderTo) {
|
|
91
108
|
const originalFetch = window.fetch;
|
|
92
109
|
const sessionId = getOrSetSessionId();
|
|
110
|
+
const combinedIgnoreDomains = [
|
|
111
|
+
...DOMAINS_TO_NOT_PROPAGATE_HEADER_TO_DEFAULT,
|
|
112
|
+
...domainsToNotPropagateHeaderTo,
|
|
113
|
+
].map((domain) => (domain.startsWith("www.") ? domain.slice(4) : domain)); // Remove 'www.' from ignore domains
|
|
93
114
|
window.fetch = async function (input, init = {}) {
|
|
94
|
-
|
|
115
|
+
const domain = typeof input === "string" ? getEffectiveDomain(input) : "";
|
|
116
|
+
if (sessionId && !combinedIgnoreDomains.includes(domain)) {
|
|
95
117
|
init.headers = {
|
|
96
118
|
...init.headers,
|
|
97
119
|
"X-Sf3-Rid": sessionId,
|
|
@@ -99,11 +121,18 @@ function storeCredentialsAndConnection({ apiKey, backendApi, }) {
|
|
|
99
121
|
}
|
|
100
122
|
return originalFetch(input, init);
|
|
101
123
|
};
|
|
102
|
-
}
|
|
124
|
+
}
|
|
103
125
|
// Main Recording Function
|
|
104
|
-
export async function startRecording({ apiKey, backendApi, }) {
|
|
126
|
+
export async function startRecording({ apiKey, backendApi, domainsToNotPropagateHeaderTo = [], }) {
|
|
105
127
|
let sessionId = getOrSetSessionId();
|
|
106
128
|
storeCredentialsAndConnection({ apiKey, backendApi });
|
|
129
|
+
// Non-blocking GraphQL request to send the domains if provided
|
|
130
|
+
if (domainsToNotPropagateHeaderTo.length > 0) {
|
|
131
|
+
sendDomainsToNotPropagateHeaderTo(apiKey, domainsToNotPropagateHeaderTo, backendApi).catch((error) => console.error("Failed to send domains to not propagate header to:", error));
|
|
132
|
+
}
|
|
133
|
+
// Setup interceptors with custom ignore domains
|
|
134
|
+
setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo);
|
|
135
|
+
setupFetchInterceptor(domainsToNotPropagateHeaderTo);
|
|
107
136
|
gatherAndCacheDeviceInfo();
|
|
108
137
|
try {
|
|
109
138
|
const captureSettingsResponse = await fetchCaptureSettings(apiKey, backendApi);
|
|
@@ -111,15 +140,11 @@ export async function startRecording({ apiKey, backendApi, }) {
|
|
|
111
140
|
DEFAULT_CAPTURE_SETTINGS;
|
|
112
141
|
const sessionResponse = await startRecordingSession(apiKey, sessionId, backendApi);
|
|
113
142
|
if (sessionResponse.data?.startRecordingSession) {
|
|
114
|
-
// Initialize recording
|
|
115
143
|
const websocket = await initializeRecording(captureSettings, DEFAULT_CONSOLE_RECORDING_SETTINGS, DEFAULT_NETWORK_CAPTURE_SETTINGS, backendApi, apiKey, sessionId);
|
|
116
|
-
// Set up an interval to send recording events
|
|
117
144
|
setInterval(() => {
|
|
118
145
|
if (websocket && websocket.readyState === WebSocket.OPEN) {
|
|
119
146
|
sendRecordingEvents(websocket);
|
|
120
147
|
}
|
|
121
|
-
else {
|
|
122
|
-
}
|
|
123
148
|
}, 10000);
|
|
124
149
|
}
|
|
125
150
|
else {
|
|
@@ -130,12 +155,6 @@ export async function startRecording({ apiKey, backendApi, }) {
|
|
|
130
155
|
console.error("Error starting recording:", error);
|
|
131
156
|
}
|
|
132
157
|
}
|
|
133
|
-
function extractHostname(url) {
|
|
134
|
-
let hostname = url.indexOf("//") > -1 ? url.split("/")[2] : url.split("/")[0];
|
|
135
|
-
hostname = hostname.split(":")[0];
|
|
136
|
-
hostname = hostname.split("?")[0];
|
|
137
|
-
return hostname;
|
|
138
|
-
}
|
|
139
158
|
// Re-export from other modules
|
|
140
159
|
export * from "./eventCache";
|
|
141
160
|
export * from "./graphql";
|
package/dist/recording.js
CHANGED
|
@@ -3,7 +3,6 @@ import { getRecordConsolePlugin, } from "@sailfish-rrweb/rrweb-plugin-console-re
|
|
|
3
3
|
import { cacheEvents, sendRecordingEvents } from "./eventCache";
|
|
4
4
|
import { initializeWebSocket } from "./websocket";
|
|
5
5
|
const MASK_CLASS = "sailfishSanitize";
|
|
6
|
-
const DEFAULT_DOMAINS_TO_IGNORE = [];
|
|
7
6
|
function maskInputFn(text, node) {
|
|
8
7
|
// Exclude input[type=hidden] fields
|
|
9
8
|
if (node.type === "hidden") {
|