refactor(core): migrate to Bun and converge acquisition policies and Agent boundaries
This commit is contained in:
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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 共用) */
|
||||
|
||||
Reference in new issue
Block a user