PageSourceSearch

https://sol.vin/js/rate_limiter.js

js sol.vin collected 2026-10-09 21:58:29 UTC 4,232 bytes, 154 lines download raw bytes

1window.ApiRateLimiter = (function() {
2  'use strict';
3
4  var CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour local storage TTL
5  var BUFFER_DELAY_MS = 500; // 500ms delay between consecutive requests per domain
6
7  var queues = {};
8  var lastRequestTime = {};
9
10  function getCache(key) {
11    try {
12      var data = localStorage.getItem(key);
13      if (!data) return null;
14      var parsed = JSON.parse(data);
15      if (parsed && parsed.timestamp && (Date.now() - parsed.timestamp < CACHE_TTL_MS)) {
16        return parsed.value;
17      }
18    } catch(e) {}
19    return null;
20  }
21
22  function setCache(key, value) {
23    try {
24      if (!value) return;
25      localStorage.setItem(key, JSON.stringify({
26        timestamp: Date.now(),
27        value: value
28      }));
29    } catch(e) {}
30  }
31
32  function clearCache(key) {
33    try {
34      localStorage.removeItem(key);
35    } catch(e) {}
36  }
37
38  function getDomain(url) {
39    try {
40      if (url && url.indexOf('/api/proxy/') !== -1) {
41        var parts = url.split('/api/proxy/')[1].split('?')[0].split('/');
42        return 'proxy-' + (parts[0] || 'local');
43      }
44      if (url && url.indexOf('://') !== -1) {
45        return url.split('/')[2];
46      }
47    } catch(e) {}
48    return 'local';
49  }
50
51
52  function resetDomain(url) {
53    var domain = getDomain(url);
54    if (queues[domain]) {
55      queues[domain].failed = false;
56      queues[domain].probeDone = false;
57    }
58  }
59
60  function processQueue(domain) {
61    var q = queues[domain];
62    if (!q || q.processing || q.items.length === 0) return;
63
64    if (q.failed) {
65      // Single probe request failed previously for this domain; fail remaining items immediately
66      while (q.items.length > 0) {
67        var item = q.items.shift();
68        if (item.onError) item.onError(new Error("API Probe Failed or Rate Limited"));
69      }
70      return;
71    }
72
73    q.processing = true;
74    var now = Date.now();
75    var last = lastRequestTime[domain] || 0;
76    var waitTime = Math.max(0, BUFFER_DELAY_MS - (now - last));
77
78    setTimeout(function() {
79      if (q.items.length === 0) {
80        q.processing = false;
81        return;
82      }
83
84      var current = q.items.shift();
85      lastRequestTime[domain] = Date.now();
86
87      current.task()
88        .then(function(result) {
89          q.probeDone = true;
90          q.processing = false;
91          if (current.onSuccess) current.onSuccess(result);
92          processQueue(domain);
93        })
94        .catch(function(err) {
95          if (!q.probeDone) {
96            // First single probe request failed. Stop flooding this endpoint!
97            q.failed = true;
98          }
99          q.processing = false;
100          if (current.onError) current.onError(err);
101          processQueue(domain);
102        });
103    }, waitTime);
104  }
105
106  function enqueue(url, taskFn, onSuccess, onError) {
107    if (url && typeof url === 'string' && url.startsWith('data:')) {
108      if (typeof onError === 'function') {
109        onError(new Error("Cannot fetch data: URIs via CORS"));
110      }
111      return;
112    }
113    var domain = getDomain(url);
114    if (!queues[domain]) {
115      queues[domain] = { items: [], processing: false, probeDone: false, failed: false };
116    }
117    queues[domain].items.push({
118      task: taskFn,
119      onSuccess: onSuccess,
120      onError: onError
121    });
122    processQueue(domain);
123  }
124
125  function getApiBaseUrl() {
126    try {
127      var host = window.location.host;
128      var hostname = window.location.hostname;
129      var protocol = window.location.protocol;
130
131      var mainHost = host;
132      if (hostname.endsWith('.sol.vin') && hostname !== 'sol.vin' && hostname !== 'www.sol.vin' && hostname !== 'dev.sol.vin') {
133        mainHost = host.replace(/^[^.]+\./, '');
134      } else if (hostname.endsWith('.lvh.me') && hostname !== 'lvh.me') {
135        mainHost = host.replace(/^[^.]+\./, '');
136      } else if (hostname.endsWith('.localhost') && hostname !== 'localhost') {
137        mainHost = host.replace(/^[^.]+\./, '');
138      }
139
140      return protocol + '//' + mainHost;
141    } catch(e) {}
142    return '';
143  }
144
145  return {
146    CACHE_TTL_MS: CACHE_TTL_MS,
147    getCache: getCache,
148    setCache: setCache,
149    clearCache: clearCache,
150    resetDomain: resetDomain,
151    enqueue: enqueue,
152    getApiBaseUrl: getApiBaseUrl
153  };
154})();

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.