@sailfish-ai/recorder 1.1.2 → 1.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/README.md CHANGED
@@ -1,3 +1,3 @@
1
1
  # JS/TS Record-Only Package
2
2
 
3
- ## TODO - Rename all Sailfish to GrepLion
3
+ ## TODO - Rename all Sailfish to GrepLion!
package/dist/index.js CHANGED
@@ -92,15 +92,22 @@ function storeCredentialsAndConnection({ apiKey, backendApi, }) {
92
92
  }
93
93
  // Utility function to match domains or paths with wildcard support
94
94
  export function matchUrlWithWildcard(url, patterns) {
95
- // Strip any protocol (e.g., http, https, ws, wss, ftp, etc.) from the URL
96
95
  const strippedUrl = url.replace(/^[a-zA-Z]+:\/\//, "");
97
96
  const parsedUrl = new URL("http://" + strippedUrl); // Add a dummy protocol for URL parsing
98
- const { hostname, pathname } = parsedUrl;
99
- const domain = hostname.toLowerCase();
97
+ const { hostname, pathname, port } = parsedUrl;
98
+ // Handle stripping 'www.' and port
99
+ const domain = hostname.startsWith("www.")
100
+ ? hostname.slice(4).toLowerCase()
101
+ : hostname.toLowerCase();
100
102
  return patterns.some((pattern) => {
101
- // Strip any protocol from the pattern
103
+ // Strip any protocol and handle port in the pattern if present
102
104
  const strippedPattern = pattern.replace(/^[a-zA-Z]+:\/\//, "");
103
105
  let [patternDomain, patternPath] = strippedPattern.split("/", 2);
106
+ // Handle port in pattern
107
+ let patternPort = "";
108
+ if (patternDomain.includes(":")) {
109
+ [patternDomain, patternPort] = patternDomain.split(":");
110
+ }
104
111
  // Handle domain wildcards
105
112
  const normalizedPatternDomain = patternDomain
106
113
  .replace(/\./g, "\\.") // Escape dots for regex
@@ -109,6 +116,10 @@ export function matchUrlWithWildcard(url, patterns) {
109
116
  const domainRegex = new RegExp(`^${normalizedPatternDomain}$`, "i");
110
117
  // Strip 'www.' from both the input domain and the pattern domain for comparison
111
118
  const strippedDomain = domain.startsWith("www.") ? domain.slice(4) : domain;
119
+ // If pattern specifies a port, match the exact port
120
+ if (patternPort && port !== patternPort) {
121
+ return false;
122
+ }
112
123
  // Handle subdomain wildcard (*.) to match both base and subdomains
113
124
  if (patternDomain.startsWith("*.") &&
114
125
  (domain === patternDomain.slice(2) ||
@@ -140,41 +151,36 @@ export function matchUrlWithWildcard(url, patterns) {
140
151
  });
141
152
  }
142
153
  // Updated XMLHttpRequest interceptor with single check function
143
- function setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo) {
144
- const originalOpen = XMLHttpRequest.prototype.open;
154
+ function setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo, domainsToPropagateHeadersTo = []) {
145
155
  const originalSend = XMLHttpRequest.prototype.send;
146
156
  const sessionId = getOrSetSessionId();
147
- // Combine default and passed domains
148
157
  const combinedIgnoreDomains = [
149
158
  ...DOMAINS_TO_NOT_PROPAGATE_HEADER_TO_DEFAULT,
150
159
  ...domainsToNotPropagateHeaderTo,
151
160
  ];
152
- XMLHttpRequest.prototype.open = function (...args) {
153
- this._url = args[1]; // Store the request URL
154
- originalOpen.apply(this, args);
155
- };
156
161
  XMLHttpRequest.prototype.send = function (...args) {
157
162
  const url = this._url;
158
- // Check if URL matches any domain or path pattern
163
+ // Check if URL matches the propagation and exclusion patterns
159
164
  const shouldSkipHeader = matchUrlWithWildcard(url, combinedIgnoreDomains);
160
- if (sessionId && !shouldSkipHeader) {
165
+ const shouldPropagateHeader = domainsToPropagateHeadersTo.length === 0 ||
166
+ matchUrlWithWildcard(url, domainsToPropagateHeadersTo);
167
+ if (sessionId && shouldPropagateHeader && !shouldSkipHeader) {
161
168
  this.setRequestHeader("X-Sf3-Rid", sessionId);
162
169
  }
163
170
  originalSend.apply(this, args);
164
171
  };
165
172
  }
166
173
  // Updated fetch interceptor with single check function
167
- function setupFetchInterceptor(domainsToNotPropagateHeaderTo) {
174
+ function setupFetchInterceptor(domainsToNotPropagateHeaderTo, domainsToPropagateHeadersTo = []) {
168
175
  const originalFetch = window.fetch;
169
176
  const sessionId = getOrSetSessionId();
170
- // Combine default and passed domains
177
+ // Combine default and passed ignore domains
171
178
  const combinedIgnoreDomains = [
172
179
  ...DOMAINS_TO_NOT_PROPAGATE_HEADER_TO_DEFAULT,
173
180
  ...domainsToNotPropagateHeaderTo,
174
181
  ];
175
- window.fetch = async function (input, init = {}) {
182
+ window.fetch = function (input, init) {
176
183
  let url;
177
- // Check if input is a string (URL) or a Request object
178
184
  if (typeof input === "string") {
179
185
  url = input;
180
186
  }
@@ -182,30 +188,48 @@ function setupFetchInterceptor(domainsToNotPropagateHeaderTo) {
182
188
  url = input.url;
183
189
  }
184
190
  else {
185
- throw new Error("Invalid input type for fetch");
191
+ return originalFetch.apply(this, arguments);
186
192
  }
187
- // Check if URL matches any domain or path pattern
193
+ // Check if URL matches the propagation and exclusion patterns
188
194
  const shouldSkipHeader = matchUrlWithWildcard(url, combinedIgnoreDomains);
189
- if (sessionId && !shouldSkipHeader) {
190
- init.headers = {
191
- ...init.headers,
192
- "X-Sf3-Rid": sessionId,
193
- };
195
+ const shouldPropagateHeader = domainsToPropagateHeadersTo.length === 0 ||
196
+ matchUrlWithWildcard(url, domainsToPropagateHeadersTo);
197
+ // Proceed with fetch if header should propagate and not be excluded
198
+ if (sessionId && shouldPropagateHeader && !shouldSkipHeader) {
199
+ if (input instanceof Request) {
200
+ const clonedRequest = input.clone();
201
+ const newHeaders = new Headers(clonedRequest.headers);
202
+ newHeaders.set("X-Sf3-Rid", sessionId);
203
+ const modifiedRequest = new Request(clonedRequest, {
204
+ headers: newHeaders,
205
+ });
206
+ return originalFetch.call(this, modifiedRequest);
207
+ }
208
+ else {
209
+ const modifiedInit = { ...init };
210
+ const newHeaders = new Headers(init?.headers || {});
211
+ newHeaders.set("X-Sf3-Rid", sessionId);
212
+ modifiedInit.headers = newHeaders;
213
+ return originalFetch.call(this, input, modifiedInit);
214
+ }
215
+ }
216
+ else {
217
+ return originalFetch.apply(this, arguments);
194
218
  }
195
- return originalFetch(input, init);
196
219
  };
197
220
  }
198
221
  // Main Recording Function
199
- export async function startRecording({ apiKey, backendApi, domainsToNotPropagateHeaderTo = [], }) {
222
+ // Main Recording Function
223
+ export async function startRecording({ apiKey, backendApi, domainsToPropagateHeaderTo = [], domainsToNotPropagateHeaderTo = [], }) {
200
224
  let sessionId = getOrSetSessionId();
201
225
  storeCredentialsAndConnection({ apiKey, backendApi });
202
226
  // Non-blocking GraphQL request to send the domains if provided
203
227
  if (domainsToNotPropagateHeaderTo.length > 0) {
204
228
  sendDomainsToNotPropagateHeaderTo(apiKey, domainsToNotPropagateHeaderTo, backendApi).catch((error) => console.error("Failed to send domains to not propagate header to:", error));
205
229
  }
206
- // Setup interceptors with custom ignore domains
207
- setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo);
208
- setupFetchInterceptor(domainsToNotPropagateHeaderTo);
230
+ // Setup interceptors with custom ignore and propagate domains
231
+ setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo, domainsToPropagateHeaderTo);
232
+ setupFetchInterceptor(domainsToNotPropagateHeaderTo, domainsToPropagateHeaderTo);
209
233
  gatherAndCacheDeviceInfo();
210
234
  try {
211
235
  const captureSettingsResponse = await fetchCaptureSettings(apiKey, backendApi);