- rss: port of MP RssHelper._parse_impl/get_rss_link semantics with safe XML (entities off, DTD/ENTITY rejected, size/item caps), ETag/Last-Modified, backoff + Retry-After, circuit breaker with manual reset, passkey redaction, overlap-window GUID+digest dedupe - sites/spider: TS port of MP SiteSpider (indexer schema, search URL builder, field DSL, filters, Jinja templates via nunjucks, login-page detection); audiences definition hand-written as JSON; raw category id preserved; shared per-site token bucket - sites/mteam: port of MP MTorrentSpider (params, result mapping, promo factors, genDlToken flow) without embedding the API key in enclosures; business code/message checks - testkit: mock RSS server, mock NexusPHP site, mock M-Team API - docs: W1-3b migration mapping appended to porting ledger Co-Authored-By: Claude Fable 5.1 <[email protected]>
805 B
805 B
testkit/rss
mock RSS/Atom 服务器(127.0.0.1 随机端口,node:http,无外部依赖)。
const feed = await createMockRssServer({ items, passkey: "pk", format: "rss" | "atom" });
feed.feedUrl; // 带 ?passkey= 的完整 URL
feed.addItem(item); // 新条目置前
feed.updateItem(guid, {title}); // 同 GUID 内容变化
feed.pinItem(guid); feed.shuffle(seed); // 置顶 / 乱序
feed.failNext(2, { status: 503, retryAfter: "120" });
feed.setRawBody(maliciousXml); // DTD/XXE/超大响应样本;null 恢复
feed.setConditional(false); // 关闭 ETag/Last-Modified
feed.requests; feed.notModifiedCount;
await feed.close();
ETag 随内容与版本变化;If-None-Match 命中或 If-Modified-Since 不早于最后修改时间返回 304。passkey 错误返回 403。