Update project structure and dependencies; add architecture documentation and improve build scripts. Introduce new versioning and permissions for enhanced functionality.

This commit is contained in:
go0p
2026-07-17 23:43:21 +08:00
parent f9ed85c068
commit 726a97849b
113 changed files with 15944 additions and 6556 deletions
+20
View File
@@ -0,0 +1,20 @@
import { vi, describe, expect, it } from 'vitest';
vi.mock('wxt/browser', () => ({ browser: { declarativeNetRequest: {} } }));
import { buildUserAgentDnrRules } from './user-agent';
describe('User-Agent DNR rules', () => {
it('normalizes domains and covers browser request resource types', () => {
const [rule] = buildUserAgentDnrRules([{
id: 'ua-1', name: 'Test', enabled: true, userAgent: 'Yakit-E2E/1.0', domains: ['https://*.example.test/path'],
}]);
expect(rule.condition.urlFilter).toBe('||example.test^');
expect(rule.condition.resourceTypes).toContain('websocket');
expect(rule.action).toMatchObject({ requestHeaders: [{ header: 'user-agent', operation: 'set', value: 'Yakit-E2E/1.0' }] });
});
it('ignores disabled rules', () => {
expect(buildUserAgentDnrRules([{ id: 'x', name: 'X', enabled: false, userAgent: 'x', domains: [] }])).toHaveLength(0);
});
});
+48
View File
@@ -0,0 +1,48 @@
import { browser } from 'wxt/browser';
import type { UserAgentRule } from '@/types/models';
const RULE_ID_BASE = 20_000;
const MAX_UA_RULES = 5_000;
function domainFilter(domain: string): string {
const normalized = domain.trim().replace(/^https?:\/\//, '').replace(/\/.*$/, '').replace(/^\*\./, '');
return normalized ? `||${normalized}^` : '*';
}
export function buildUserAgentDnrRules(rules: UserAgentRule[]): Browser.declarativeNetRequest.Rule[] {
const addRules: Browser.declarativeNetRequest.Rule[] = [];
let nextRuleId = RULE_ID_BASE;
for (const rule of rules.filter((item) => item.enabled)) {
const domains = rule.domains.length > 0 ? [...new Set(rule.domains)] : [''];
for (const domain of domains) {
if (nextRuleId >= RULE_ID_BASE + MAX_UA_RULES) {
throw new Error(`User-Agent 动态规则超过 ${MAX_UA_RULES} 条限制`);
}
addRules.push({
id: nextRuleId,
priority: nextRuleId - RULE_ID_BASE + 1,
action: {
type: 'modifyHeaders',
requestHeaders: [{ header: 'user-agent', operation: 'set', value: rule.userAgent }],
},
condition: {
urlFilter: domainFilter(domain),
resourceTypes: [
'main_frame', 'sub_frame', 'xmlhttprequest', 'script', 'image', 'stylesheet',
'font', 'media', 'websocket', 'other',
],
},
});
nextRuleId += 1;
}
}
return addRules;
}
export async function applyUserAgentRules(rules: UserAgentRule[]): Promise<void> {
const oldRuleIds = (await browser.declarativeNetRequest.getDynamicRules())
.map((rule) => rule.id)
.filter((id) => id >= RULE_ID_BASE && id < RULE_ID_BASE + 10_000);
await browser.declarativeNetRequest.updateDynamicRules({ removeRuleIds: oldRuleIds, addRules: buildUserAgentDnrRules(rules) });
}