@sailfish-ai/recorder 1.1.3 → 1.2.1

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/index.js CHANGED
@@ -6,6 +6,7 @@ import { fetchCaptureSettings, sendDomainsToNotPropagateHeaderTo, startRecording
6
6
  import { initializeRecording } from "./recording";
7
7
  // Default list of domains to ignore
8
8
  const DOMAINS_TO_NOT_PROPAGATE_HEADER_TO_DEFAULT = [
9
+ "t.co",
9
10
  "identitytoolkit.googleapis.com",
10
11
  ];
11
12
  const DOMAINS_TO_NOT_RECORD_NETWORK_REQUESTS_TO = [];
@@ -92,15 +93,27 @@ function storeCredentialsAndConnection({ apiKey, backendApi, }) {
92
93
  }
93
94
  // Utility function to match domains or paths with wildcard support
94
95
  export function matchUrlWithWildcard(url, patterns) {
95
- // Strip any protocol (e.g., http, https, ws, wss, ftp, etc.) from the URL
96
- const strippedUrl = url.replace(/^[a-zA-Z]+:\/\//, "");
96
+ if (!url || typeof url !== "string") {
97
+ throw new Error("Invalid URL input");
98
+ }
99
+ // Ensure the URL has a protocol. If not, prepend "http://"
100
+ const formattedUrl = url.match(/^[a-zA-Z]+:\/\//) ? url : `http://${url}`;
101
+ const strippedUrl = formattedUrl.replace(/^[a-zA-Z]+:\/\//, "");
97
102
  const parsedUrl = new URL("http://" + strippedUrl); // Add a dummy protocol for URL parsing
98
- const { hostname, pathname } = parsedUrl;
99
- const domain = hostname.toLowerCase();
103
+ const { hostname, pathname, port } = parsedUrl;
104
+ // Handle stripping 'www.' and port
105
+ const domain = hostname.startsWith("www.")
106
+ ? hostname.slice(4).toLowerCase()
107
+ : hostname.toLowerCase();
100
108
  return patterns.some((pattern) => {
101
- // Strip any protocol from the pattern
109
+ // Strip any protocol and handle port in the pattern if present
102
110
  const strippedPattern = pattern.replace(/^[a-zA-Z]+:\/\//, "");
103
111
  let [patternDomain, patternPath] = strippedPattern.split("/", 2);
112
+ // Handle port in pattern
113
+ let patternPort = "";
114
+ if (patternDomain.includes(":")) {
115
+ [patternDomain, patternPort] = patternDomain.split(":");
116
+ }
104
117
  // Handle domain wildcards
105
118
  const normalizedPatternDomain = patternDomain
106
119
  .replace(/\./g, "\\.") // Escape dots for regex
@@ -109,6 +122,10 @@ export function matchUrlWithWildcard(url, patterns) {
109
122
  const domainRegex = new RegExp(`^${normalizedPatternDomain}$`, "i");
110
123
  // Strip 'www.' from both the input domain and the pattern domain for comparison
111
124
  const strippedDomain = domain.startsWith("www.") ? domain.slice(4) : domain;
125
+ // If pattern specifies a port, match the exact port
126
+ if (patternPort && port !== patternPort) {
127
+ return false;
128
+ }
112
129
  // Handle subdomain wildcard (*.) to match both base and subdomains
113
130
  if (patternDomain.startsWith("*.") &&
114
131
  (domain === patternDomain.slice(2) ||
@@ -140,40 +157,41 @@ export function matchUrlWithWildcard(url, patterns) {
140
157
  });
141
158
  }
142
159
  // Updated XMLHttpRequest interceptor with single check function
143
- function setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo) {
144
- // Store references to the original (or patched) send method
160
+ // Updated XMLHttpRequest interceptor to bypass for CORS-sensitive domains
161
+ function setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo, domainsToPropagateHeadersTo = []) {
145
162
  const originalSend = XMLHttpRequest.prototype.send;
146
163
  const sessionId = getOrSetSessionId();
147
- // Combine default and passed domains
164
+ // Combine default and passed ignore domains
148
165
  const combinedIgnoreDomains = [
149
166
  ...DOMAINS_TO_NOT_PROPAGATE_HEADER_TO_DEFAULT,
150
167
  ...domainsToNotPropagateHeaderTo,
151
168
  ];
152
- // Override XMLHttpRequest's send method
153
169
  XMLHttpRequest.prototype.send = function (...args) {
154
170
  const url = this._url;
155
- // Check if URL matches any domain or path pattern
156
- const shouldSkipHeader = matchUrlWithWildcard(url, combinedIgnoreDomains);
157
- if (sessionId && !shouldSkipHeader) {
171
+ // Bypass logic for domains listed in the combinedIgnoreDomains
172
+ if (matchUrlWithWildcard(url, combinedIgnoreDomains)) {
173
+ return originalSend.apply(this, args);
174
+ }
175
+ // Check if the domain should propagate headers
176
+ const shouldPropagateHeader = domainsToPropagateHeadersTo.length === 0 ||
177
+ matchUrlWithWildcard(url, domainsToPropagateHeadersTo);
178
+ if (sessionId && shouldPropagateHeader) {
158
179
  this.setRequestHeader("X-Sf3-Rid", sessionId);
159
180
  }
160
- originalSend.apply(this, args);
181
+ return originalSend.apply(this, args);
161
182
  };
162
183
  }
163
- // Updated fetch interceptor with single check function
164
- function setupFetchInterceptor(domainsToNotPropagateHeaderTo) {
165
- // Store a reference to the original fetch function
184
+ // Updated fetch interceptor to bypass for CORS-sensitive domains
185
+ function setupFetchInterceptor(domainsToNotPropagateHeaderTo, domainsToPropagateHeadersTo = []) {
166
186
  const originalFetch = window.fetch;
167
187
  const sessionId = getOrSetSessionId();
168
- // Combine default and passed domains
188
+ // Combine default and passed ignore domains
169
189
  const combinedIgnoreDomains = [
170
190
  ...DOMAINS_TO_NOT_PROPAGATE_HEADER_TO_DEFAULT,
171
191
  ...domainsToNotPropagateHeaderTo,
172
192
  ];
173
- // Define our fetch wrapper
174
193
  window.fetch = function (input, init) {
175
194
  let url;
176
- // Determine the URL based on input type
177
195
  if (typeof input === "string") {
178
196
  url = input;
179
197
  }
@@ -181,60 +199,49 @@ function setupFetchInterceptor(domainsToNotPropagateHeaderTo) {
181
199
  url = input.url;
182
200
  }
183
201
  else {
184
- // Unknown input type; defer to the original fetch
185
202
  return originalFetch.apply(this, arguments);
186
203
  }
187
- // Check if URL matches any domain or path pattern
188
- const shouldSkipHeader = matchUrlWithWildcard(url, combinedIgnoreDomains);
189
- // Use the original fetch if the domain matches the ignore patterns
190
- if (shouldSkipHeader) {
191
- // Call the original fetch with the original arguments
204
+ // Bypass logic for domains listed in the combinedIgnoreDomains
205
+ if (matchUrlWithWildcard(url, combinedIgnoreDomains)) {
192
206
  return originalFetch.apply(this, arguments);
193
207
  }
194
- // Only modify the request if we need to add the header
195
- if (sessionId) {
208
+ // Check if the domain should propagate headers
209
+ const shouldPropagateHeader = domainsToPropagateHeadersTo.length === 0 ||
210
+ matchUrlWithWildcard(url, domainsToPropagateHeadersTo);
211
+ if (sessionId && shouldPropagateHeader) {
196
212
  if (input instanceof Request) {
197
- // Clone the original request
198
213
  const clonedRequest = input.clone();
199
- // Clone the headers and add the custom header
200
214
  const newHeaders = new Headers(clonedRequest.headers);
201
215
  newHeaders.set("X-Sf3-Rid", sessionId);
202
- // Create a new Request with the modified headers
203
216
  const modifiedRequest = new Request(clonedRequest, {
204
217
  headers: newHeaders,
205
218
  });
206
- // Call the original fetch with the modified Request
207
219
  return originalFetch.call(this, modifiedRequest);
208
220
  }
209
221
  else {
210
- // Input is a URL string
211
- // Clone and modify the init object
212
222
  const modifiedInit = { ...init };
213
- // Clone the headers
214
223
  const newHeaders = new Headers(init?.headers || {});
215
224
  newHeaders.set("X-Sf3-Rid", sessionId);
216
225
  modifiedInit.headers = newHeaders;
217
- // Call the original fetch with the modified init
218
226
  return originalFetch.call(this, input, modifiedInit);
219
227
  }
220
228
  }
221
229
  else {
222
- // No sessionId; call the original fetch
223
230
  return originalFetch.apply(this, arguments);
224
231
  }
225
232
  };
226
233
  }
227
234
  // Main Recording Function
228
- export async function startRecording({ apiKey, backendApi, domainsToNotPropagateHeaderTo = [], }) {
235
+ export async function startRecording({ apiKey, backendApi, domainsToPropagateHeaderTo = [], domainsToNotPropagateHeaderTo = [], }) {
229
236
  let sessionId = getOrSetSessionId();
230
237
  storeCredentialsAndConnection({ apiKey, backendApi });
231
238
  // Non-blocking GraphQL request to send the domains if provided
232
239
  if (domainsToNotPropagateHeaderTo.length > 0) {
233
240
  sendDomainsToNotPropagateHeaderTo(apiKey, domainsToNotPropagateHeaderTo, backendApi).catch((error) => console.error("Failed to send domains to not propagate header to:", error));
234
241
  }
235
- // Setup interceptors with custom ignore domains
236
- setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo);
237
- setupFetchInterceptor(domainsToNotPropagateHeaderTo);
242
+ // Setup interceptors with custom ignore and propagate domains
243
+ setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo, domainsToPropagateHeaderTo);
244
+ setupFetchInterceptor(domainsToNotPropagateHeaderTo, domainsToPropagateHeaderTo);
238
245
  gatherAndCacheDeviceInfo();
239
246
  try {
240
247
  const captureSettingsResponse = await fetchCaptureSettings(apiKey, backendApi);