refactor(core): migrate to Bun and converge acquisition policies and Agent boundaries

This commit is contained in:
root committed 2026-10-13 14:37:03 +08:00
1 parent 3f1c6d02f6
commit 27c0f27434
268 files changed
+7468 -7852

No files matched your search

@@ -22,7 +22,7 @@ const CF_JS_PAGE = `<html><head><title>Just a moment...</title></head><body>
<script>window._cf_chl_opt={cType: 'non-interactive'}</script></body></html>`;
const NORMAL = `<html><head><title>种子 - 观众</title><script src="/cdn-cgi/challenge-platform/scripts/jsd/main.js"></script></head><body>ok</body></html>`;
function ctx(fetchImpl: typeof fetch) {
function ctx(fetchImpl: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>) {
return {
siteId: "s1",
fetch: fetchImpl,
@@ -32,7 +32,10 @@ function ctx(fetchImpl: typeof fetch) {
};
}
const reply = (status: number, body: string, headers: Record<string, string> = {}) =>
(async () => new Response(body, { status, headers: { "content-type": "text/html", ...headers } })) as typeof fetch;
(async () => new Response(body, { status, headers: { "content-type": "text/html", ...headers } })) as (
input: RequestInfo | URL,
init?: RequestInit,
) => Promise<Response>;
describe("challengeRuleOfHtml", () => {
it("挑战页标题 / 脚本特征命中;正常页面里注入的 challenge-platform 脚本不算", () => {
@@ -62,7 +65,7 @@ describe("detectChallenge", () => {
describe("siteFetch 挑战分类", () => {
const url = "https://audiences.example/torrents.php";
const kind = async (f: typeof fetch) => {
const kind = async (f: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>) => {
try {
await siteFetch(ctx(f), url);
return "ok";
+1 -1
View File
@@ -130,7 +130,7 @@ async function peekBody(
res: Response,
max: number,
): Promise<{ head: Uint8Array<ArrayBuffer>; complete: boolean; res: Response }> {
const reader = (res.body as ReadableStream<Uint8Array>).getReader();
const reader = (res.body as unknown as ReadableStream<Uint8Array>).getReader();
const chunks: Uint8Array[] = [];
let total = 0;
let complete = false;
+1 -1
View File
@@ -5,7 +5,7 @@ import type { SecretResolver } from "./types.ts";
export interface SiteAdapterDeps {
secrets: SecretResolver;
fetch?: typeof globalThis.fetch;
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
/** 同站共享的限流器;不传则按站点定义新建(列表/搜索/详情/下载共享) */
limiter?: SiteRateLimiter;
now?: () => Date;
+1 -1
View File
@@ -6,7 +6,7 @@ import { SiteError } from "./types.ts";
export interface SiteHttpContext {
siteId: string;
fetch: typeof globalThis.fetch;
fetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
limiter: SiteRateLimiter;
timeoutMs: number;
userAgent?: string;
@@ -53,7 +53,7 @@ export interface NexusLoginInput {
/** base32 TOTP 密钥 */
totpSecret?: string | null;
userAgent: string;
fetch?: typeof globalThis.fetch;
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
limiter?: { acquire(signal?: AbortSignal): Promise<void> };
signal?: AbortSignal;
timeoutMs?: number;
@@ -38,7 +38,7 @@ function adapter(body: string, status = 200, headers: Record<string, string> = {
new Response(body, {
status,
headers: { "content-type": "text/html; charset=utf-8", ...headers },
})) as typeof fetch;
})) as (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
const a = createSpiderSiteAdapter(audiencesDefinition, {
baseUrl: "https://aud.test/",
secrets: staticSecretResolver({ [audiencesDefinition.id]: { cookie: "c_secure_uid=1; c_secure_pass=abc" } }),
@@ -176,7 +176,7 @@ describe("详情页扩展内容(憨憨 2026 真实结构:分节 div,无 #kdescr)
paths.push(`${u.pathname}${u.search}`);
const body = u.pathname === "/viewfilelist.php" ? HHANCLUB_FILELIST_FRAGMENT : HHANCLUB_DETAIL_PAGE_2026;
return new Response(body, { status: 200, headers: { "content-type": "text/html; charset=utf-8" } });
}) as typeof globalThis.fetch;
}) as (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
const limiter = createSiteRateLimiter({ minIntervalMs: 0 });
const a = createSpiderSiteAdapter(hhanclubDefinition, {
fetch,
@@ -30,7 +30,7 @@ export function testDef(
export function adapterWith(
def: SpecializedSiteDefinition,
spider: SpecializedSpider,
fetchImpl: typeof globalThis.fetch,
fetchImpl: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>,
secrets: Partial<Record<SiteCredentialKind, string>> = { cookie: "c=COOKIESECRET", apiKey: "APIKEYSECRET" },
): SiteAdapter {
return createSpecializedSiteAdapter(def, spider, {
@@ -42,10 +42,14 @@ export function adapterWith(
/** 包一层 fetch:命中 predicate 的请求返回合法 .torrent 字节 */
export function withTorrent(
inner: typeof globalThis.fetch,
inner: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>,
predicate: (url: URL) => boolean,
name = "demo",
): { fetch: typeof globalThis.fetch; infoHash: string; hits: Array<{ url: URL; headers: Headers }> } {
): {
fetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
infoHash: string;
hits: Array<{ url: URL; headers: Headers }>;
} {
const t = buildFakeTorrent({ name });
const hits: Array<{ url: URL; headers: Headers }> = [];
const f = (async (input: string | URL | Request, init?: RequestInit) => {
@@ -58,7 +62,7 @@ export function withTorrent(
});
}
return inner(input, init);
}) as typeof globalThis.fetch;
}) as (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
return { fetch: f, infoHash: t.infoHash, hits };
}
@@ -4,7 +4,7 @@ import type { SiteUserInfo, SiteUserParserOptions } from "./types.ts";
export async function runParser(
cls: SiteParserClass,
opts: Partial<SiteUserParserOptions> & { fetch: typeof globalThis.fetch },
opts: Partial<SiteUserParserOptions> & { fetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response> },
): Promise<SiteUserInfo> {
const p = new cls({ siteName: "demo", url: "https://pt.example.com/", ...opts });
await p.parse();
@@ -92,7 +92,7 @@ export interface SiteUserParserOptions {
apiUrl?: string | null;
/** 是否读取未读消息正文(MP 全局设置 `SITE_MESSAGE`);默认 false。注意 MTorrent 读取后会标记已读 */
fetchMessages?: boolean;
fetch?: typeof globalThis.fetch;
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
/** 单请求超时(MP 固定 60s) */
timeoutMs?: number;
/** 共享站点限流器(与 list/search 共用) */