Skip to content

Commit 3286d27

Browse files
committed
feat: GitHub authenticated code search for .user.js files
When a GitHub token is set via localStorage (sh_pref_ghtoken), the GitHub source additionally searches /search/code with extension:user.js to find individual userscript files across all repos — much more precise than repository-level keyword search. Results include raw install URLs constructed from github.com → raw.githubusercontent.com paths. Token is validated on first use; cleared automatically if 401.
1 parent c7301d3 commit 3286d27

1 file changed

Lines changed: 35 additions & 0 deletions

File tree

index.html

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,38 @@ <h3>Search userscripts everywhere</h3>
672672
license: (item.license && item.license.spdx_id) || '', stars: item.stargazers_count || 0, forks: item.forks_count || 0, language: item.language || '' };
673673
}
674674

675+
/* ===== Source: GitHub Code Search (authenticated) ===== */
676+
var _ghToken = _prefGet('ghtoken', '');
677+
async function srcGHCode(query, page) {
678+
if (!_ghToken) return { items: [], total_count: 0 };
679+
if (_ghCooldown > Date.now()) throw new Error('Rate limited');
680+
var ck = _ck('ghcode', query, page);
681+
var cached = _cGet(ck); if (cached) return cached;
682+
var q = encodeURIComponent(query + ' extension:user.js');
683+
var r = await fetch('https://api.github.com/search/code?q=' + q + '&per_page=30&page=' + page, { headers: { Accept: 'application/vnd.github.v3+json', Authorization: 'token ' + _ghToken }, signal: AbortSignal.timeout(12000) });
684+
if (r.status === 403 || r.status === 429) {
685+
var reset = r.headers.get('x-ratelimit-reset');
686+
var waitMs = reset ? Math.max(0, (parseInt(reset) * 1000) - Date.now()) : 60000;
687+
_ghCooldown = Date.now() + Math.min(waitMs, 120000);
688+
setTimeout(function() { _ghCooldown = 0; }, Math.min(waitMs, 120000));
689+
throw new Error('Rate limited');
690+
}
691+
if (r.status === 401) { _ghToken = ''; _prefSet('ghtoken', ''); throw new Error('Invalid GitHub token'); }
692+
if (!r.ok) throw new Error('GitHub Code HTTP ' + r.status);
693+
var data = await r.json(); _cSet(ck, data); return data;
694+
}
695+
696+
function normGHCode(item) {
697+
var repo = item.repository || {};
698+
return { id: 'ghcode-' + (repo.id || 0) + '-' + item.sha, source: 'github', name: item.name || item.path || 'Untitled',
699+
description: (repo.description || '') + (item.path ? ' (' + item.path + ')' : ''),
700+
author: (repo.owner && repo.owner.login) || 'Unknown', url: item.html_url || repo.html_url || '',
701+
installUrl: item.html_url ? item.html_url.replace('github.com', 'raw.githubusercontent.com').replace('/blob/', '/') : '',
702+
version: '', dailyInstalls: 0, totalInstalls: 0, rating: repo.stargazers_count || 0, badRating: 0,
703+
createdAt: '', updatedAt: repo.pushed_at || '',
704+
license: '', stars: repo.stargazers_count || 0, forks: repo.forks_count || 0, language: '' };
705+
}
706+
675707
/* ===== Source: OpenUserJS ===== */
676708
async function srcOUJS(query, page) {
677709
var ck = _ck('openuserjs', query, page);
@@ -859,6 +891,9 @@ <h3>Search userscripts everywhere</h3>
859891
} else if (srcId === 'github') {
860892
var raw = await srcGH(parsed.query, page);
861893
items = (raw.items || []).map(normGH);
894+
if (_ghToken && parsed.query) {
895+
try { var codeRaw = await srcGHCode(parsed.query, page); items = items.concat((codeRaw.items || []).map(normGHCode)); } catch(ce) {}
896+
}
862897
if (items.length < 30 || (raw.total_count && page * 30 >= raw.total_count)) state.sourceHasMore.github = false;
863898
} else if (srcId === 'openuserjs') {
864899
items = await srcOUJS(parsed.query, page);

0 commit comments

Comments
 (0)