PageSourceSearch

https://exchange.isid.org/util/detectErrors.js

js isid.org collected 2026-09-24 15:31:34 UTC 6,133 bytes, 171 lines download raw bytes

1(function() {
2  // Guard against the script being initialized multiple times.
3  if (window.isErrorTrackingInitialized) {
4    return;
5  }
6  window.isErrorTrackingInitialized = true;
7
8  // Use a Set for more efficient and idiomatic storage of hashes.
9  const trackedErrorHashes = new Set();
10  async function getHash(str) {
11    // The Web Crypto API is only available in secure contexts (HTTPS).
12    if (!window.crypto || !window.crypto.subtle) {
13      // Fallback for insecure contexts. Returning the string itself is a simple
14      // way to still allow session-based deduplication without a cryptographic hash.
15      console.warn('Web Crypto API not available. Using simple string for hashing.');
16      return str;
17    }
18    const strBuf = new TextEncoder().encode(str);
19    const hashBuffer = await crypto.subtle.digest("SHA-256", strBuf);
20    const hashArray = Array.from(new Uint8Array(hashBuffer));
21    return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
22  }
23
24  function getBrowserName() {
25    const userAgent = navigator.userAgent || '';
26    // The order of checks is important, as many browser user agent strings contain
27    // identifiers from other browsers. More specific identifiers must be checked first.
28    if (userAgent.includes("Edg/")) return "Microsoft Edge";
29    if (userAgent.includes("Opera") || userAgent.includes("OPR/")) return "Opera";
30    if (userAgent.includes("MSIE") || userAgent.includes("Trident/")) return "Internet Explorer";
31    // Chrome's UA string contains "Safari", so "Chrome" must be checked for first.
32    if (userAgent.includes("Chrome")) return "Chrome";
33    if (userAgent.includes("Safari")) return "Safari";
34    if (userAgent.includes("Firefox")) return "Firefox";
35    return "Unknown";
36  }
37
38  function getOSName() {
39    // navigator.userAgentData.platform is the modern, more reliable API.
40    // Fallback to navigator.platform for older browsers.
41    const platform = window.navigator?.userAgentData?.platform || window.navigator.platform;
42    const userAgent = window.navigator.userAgent;
43
44    const macosPlatforms = ['macOS', 'Macintosh', 'MacIntel', 'MacPPC', 'Mac68K', 'darwin'];
45    const windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'];
46    const iosPlatforms = ['iPhone', 'iPad', 'iPod'];
47
48    if (macosPlatforms.includes(platform)) {
49      return 'Mac OS';
50    }
51    if (iosPlatforms.includes(platform)) {
52      return 'iOS';
53    }
54    if (windowsPlatforms.includes(platform)) {
55      return 'Windows';
56    }
57    // For Android, userAgent is more reliable than platform, which can just be "Linux".
58    if (/Android/.test(userAgent)) {
59      return 'Android';
60    }
61    // Check for Linux last, as Android may also report a Linux platform.
62    if (/Linux/.test(platform)) {
63      return 'Linux';
64    }
65
66    return 'Unknown';
67  }
68
69  // https://developer.mozilla.org/en-US/docs/Web/API/Event
70  function getErrorInfos(event) {
71    let filename = '';
72    let line = 0;
73    let column = 0;
74    let message = '';
75    let eventname = '';
76    let stack = '';
77
78    // Handle ErrorEvent from window.onerror
79    if (event instanceof ErrorEvent) {
80      filename = event.filename || '';
81      line = event.lineno || 0;
82      column = event.colno || 0;
83      message = event.message || '';
84      // Prefer the specific error name (e.g., 'TypeError') over the generic event type ('error').
85      // The actual Error object is often nested inside the event.
86      eventname = event.error?.name || event.name || event.type || 'Error';
87      stack = event.error?.stack || '';
88    } else if (event instanceof Error) {
89      // Handle manually passed Error objects
90      message = event.message;
91      eventname = event.name;
92      stack = event.stack || '';
93    } else if (event !== null && typeof event !== 'object') {
94      // Fallback for primitive types (e.g., strings)
95      message = String(event);
96      eventname = 'Error';
97    }
98
99    // Final fallback for filename if it's still empty
100    if (!filename) {
101      filename = window.location.href
102    }
103    return {
104      filename,
105      line,
106      column,
107      message,
108      eventname,
109      stack
110    }
111  }
112
113  /**
114   * Processes and sends an error report to the backend.
115   * @param {any} errorSource The error event, error object, or promise rejection reason.
116   * @param {object|null} extras Additional custom data to include with the report.
117   */
118  async function processAndSendError(errorSource, extras = null) {
119
120    const errorInfos = getErrorInfos(errorSource);
121
122    const errorSignature = `${errorInfos.eventname}:${errorInfos.message}:${errorInfos.filename}:${errorInfos.line}`;
123    const errorHash = await getHash(errorSignature);
124
125    if (trackedErrorHashes.has(errorHash)) {
126      return; // Avoid sending duplicate errors from the same session.
127    }
128    trackedErrorHashes.add(errorHash);
129
130    // Prepare the extras object, starting with any provided custom data.
131    const extrasPayload = (extras !== null && typeof extras === 'object') ? { ...extras } : {};
132    // Add the stack trace to the extras payload if it exists.
133    if (errorInfos.stack) {
134      extrasPayload.stack = errorInfos.stack;
135    }
136
137    const payload = {
138      browser: getBrowserName(),
139      os: getOSName(),
140      eventname: errorInfos.eventname,
141      filename: errorInfos.filename,
142      line: errorInfos.line,
143      column: errorInfos.column,
144      message: errorInfos.message,
145      extras: JSON.stringify(extrasPayload)
146    }
147
148    // Prevent from tracking extensions errors
149    if (/^[a-z]+\-extension:/.test(payload.filename)) { 
150      return
151    }
152
153    try {
154      await fetch('/record/errors/javascript', {
155        method: 'POST',
156        body: new URLSearchParams(payload),
157      })
158    } catch (apiError) {
159      console.error('Failed to send error report:', apiError);
160    }
161
162  }
163
164  window.addEventListener('error', (event) => processAndSendError(event));
165  window.addEventListener('unhandledrejection', (event) => {
166    processAndSendError(event.reason || event);
167  });
168
169  // Expose a manual reporting function on the window object for custom error handling.
170  window.sendJSErrors = processAndSendError;
171})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.