@duyquangnvx/webnovel-downloader 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +58 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +58 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -473,6 +473,9 @@ var silentLogger = createLogger({ level: "silent" });
|
|
|
473
473
|
// src/index.ts
|
|
474
474
|
init_errors();
|
|
475
475
|
|
|
476
|
+
// src/core/downloader.ts
|
|
477
|
+
var import_undici2 = require("undici");
|
|
478
|
+
|
|
476
479
|
// src/core/registry.ts
|
|
477
480
|
init_errors();
|
|
478
481
|
var AdapterRegistry = class {
|
|
@@ -548,33 +551,46 @@ var EventBus = class {
|
|
|
548
551
|
// src/http/client.ts
|
|
549
552
|
var import_undici = require("undici");
|
|
550
553
|
init_errors();
|
|
554
|
+
init_primitives();
|
|
551
555
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
556
|
+
var MAX_REDIRECTS = 5;
|
|
557
|
+
var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
552
558
|
var UndiciHttpClient = class {
|
|
553
559
|
#logger;
|
|
560
|
+
#dispatcher;
|
|
561
|
+
#ownsDispatcher;
|
|
554
562
|
constructor(opts) {
|
|
555
563
|
this.#logger = opts.logger;
|
|
564
|
+
this.#ownsDispatcher = opts.dispatcher === void 0;
|
|
565
|
+
this.#dispatcher = opts.dispatcher ?? new import_undici.Agent();
|
|
556
566
|
}
|
|
557
567
|
async get(url, opts) {
|
|
558
568
|
const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
559
569
|
const half = Math.max(1, Math.floor(timeoutMs / 2));
|
|
560
570
|
let res;
|
|
561
571
|
try {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
572
|
+
let currentUrl = url;
|
|
573
|
+
for (let hop = 0; ; hop++) {
|
|
574
|
+
res = await (0, import_undici.request)(currentUrl, {
|
|
575
|
+
method: "GET",
|
|
576
|
+
dispatcher: this.#dispatcher,
|
|
577
|
+
...opts?.headers !== void 0 ? { headers: opts.headers } : {},
|
|
578
|
+
...opts?.signal !== void 0 ? { signal: opts.signal } : {},
|
|
579
|
+
headersTimeout: half,
|
|
580
|
+
bodyTimeout: half
|
|
581
|
+
});
|
|
582
|
+
const next = hop < MAX_REDIRECTS ? redirectTarget(res.statusCode, res.headers, currentUrl) : void 0;
|
|
583
|
+
if (next === void 0) break;
|
|
584
|
+
await res.body.dump();
|
|
585
|
+
currentUrl = next;
|
|
586
|
+
}
|
|
570
587
|
const body = await res.body.text();
|
|
571
588
|
const headers = normalizeHeaders(res.headers);
|
|
572
|
-
const finalUrl = url;
|
|
573
589
|
return {
|
|
574
590
|
status: res.statusCode,
|
|
575
591
|
headers,
|
|
576
592
|
body,
|
|
577
|
-
url:
|
|
593
|
+
url: currentUrl
|
|
578
594
|
};
|
|
579
595
|
} catch (cause) {
|
|
580
596
|
if (res !== void 0) await res.body.dump().catch(() => {
|
|
@@ -585,11 +601,27 @@ var UndiciHttpClient = class {
|
|
|
585
601
|
if (cause instanceof import_undici.errors.HeadersTimeoutError || cause instanceof import_undici.errors.BodyTimeoutError) {
|
|
586
602
|
throw new TimeoutError(url, { cause });
|
|
587
603
|
}
|
|
588
|
-
|
|
589
|
-
|
|
604
|
+
const detail = cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause);
|
|
605
|
+
this.#logger.warn({ url, cause }, "undici network error");
|
|
606
|
+
throw new HttpError(0, url, `Network error (${detail})`, { cause });
|
|
590
607
|
}
|
|
591
608
|
}
|
|
609
|
+
/** Closes the dispatcher this client created. A no-op when one was injected — the caller owns that. */
|
|
610
|
+
async dispose() {
|
|
611
|
+
if (this.#ownsDispatcher) await this.#dispatcher.close();
|
|
612
|
+
}
|
|
592
613
|
};
|
|
614
|
+
function redirectTarget(status, headers, currentUrl) {
|
|
615
|
+
if (!REDIRECT_STATUSES.has(status)) return void 0;
|
|
616
|
+
const raw = headers["location"];
|
|
617
|
+
const location = Array.isArray(raw) ? raw[0] : raw;
|
|
618
|
+
if (location === void 0 || location === "") return void 0;
|
|
619
|
+
try {
|
|
620
|
+
return unsafeBrandUrl(new URL(location, currentUrl).href);
|
|
621
|
+
} catch {
|
|
622
|
+
return void 0;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
593
625
|
function normalizeHeaders(h) {
|
|
594
626
|
const out = {};
|
|
595
627
|
for (const [k, v] of Object.entries(h)) {
|
|
@@ -720,9 +752,13 @@ var RateLimiter = class {
|
|
|
720
752
|
#buckets = /* @__PURE__ */ new Map();
|
|
721
753
|
#pauses = /* @__PURE__ */ new Map();
|
|
722
754
|
constructor(opts = DEFAULT_RATE_LIMIT) {
|
|
755
|
+
const burst = opts.burst ?? Math.max(1, opts.requestsPerSecond);
|
|
756
|
+
if (burst < 1) {
|
|
757
|
+
throw new RangeError(`RateLimiter: burst must be >= 1 (got ${burst})`);
|
|
758
|
+
}
|
|
723
759
|
this.#opts = {
|
|
724
760
|
requestsPerSecond: opts.requestsPerSecond,
|
|
725
|
-
burst
|
|
761
|
+
burst
|
|
726
762
|
};
|
|
727
763
|
}
|
|
728
764
|
async acquire(host, signal) {
|
|
@@ -967,7 +1003,10 @@ function normalizeTransport(t) {
|
|
|
967
1003
|
function buildHttpClient(opts = {}) {
|
|
968
1004
|
const logger = opts.logger ?? silentLogger;
|
|
969
1005
|
const cookieJar = opts.cookieJar ?? new CookieJar();
|
|
970
|
-
const leaf = opts.undiciOverride ?? new UndiciHttpClient({
|
|
1006
|
+
const leaf = opts.undiciOverride ?? new UndiciHttpClient({
|
|
1007
|
+
logger,
|
|
1008
|
+
...opts.dispatcher !== void 0 ? { dispatcher: opts.dispatcher } : {}
|
|
1009
|
+
});
|
|
971
1010
|
const limiter = opts.rateLimiter ?? new RateLimiter(opts.rateLimit ?? DEFAULT_RATE_LIMIT);
|
|
972
1011
|
const httpBranch = HttpStack.from(leaf).rotateUserAgent(opts.userAgents ?? new UserAgents(), opts.defaultHeaders, cookieJar).rateLimit(limiter, opts.onEvent).retry(normalizeRetry(opts.retry), logger, limiter, opts.onEvent).build();
|
|
973
1012
|
const { mode } = normalizeTransport(opts.transport);
|
|
@@ -2104,6 +2143,7 @@ var Downloader = class {
|
|
|
2104
2143
|
#transport;
|
|
2105
2144
|
#cookieJar;
|
|
2106
2145
|
#browserPool;
|
|
2146
|
+
#httpDispatcher;
|
|
2107
2147
|
#transportListeners = /* @__PURE__ */ new Set();
|
|
2108
2148
|
/**
|
|
2109
2149
|
* Build a downloader from a set of adapters. `rateLimit`, `retry`, and
|
|
@@ -2138,10 +2178,12 @@ var Downloader = class {
|
|
|
2138
2178
|
await handle.release();
|
|
2139
2179
|
}
|
|
2140
2180
|
} : void 0;
|
|
2181
|
+
this.#httpDispatcher = opts.http === void 0 && opts.undiciOverride === void 0 ? new import_undici2.Agent() : void 0;
|
|
2141
2182
|
this.#http = opts.http ?? buildHttpClient({
|
|
2142
2183
|
logger: this.#logger,
|
|
2143
2184
|
transport: this.#transport,
|
|
2144
2185
|
cookieJar: this.#cookieJar,
|
|
2186
|
+
...this.#httpDispatcher !== void 0 ? { dispatcher: this.#httpDispatcher } : {},
|
|
2145
2187
|
...opts.rateLimit !== void 0 ? { rateLimit: opts.rateLimit } : {},
|
|
2146
2188
|
...opts.retry !== void 0 ? { retry: opts.retry } : {},
|
|
2147
2189
|
onEvent: (e) => {
|
|
@@ -2172,10 +2214,11 @@ var Downloader = class {
|
|
|
2172
2214
|
...onCookieCapture !== void 0 ? { onCookieCapture } : {}
|
|
2173
2215
|
});
|
|
2174
2216
|
}
|
|
2175
|
-
/** Release held resources (closes the browser pool, if any). Safe to call once when done. */
|
|
2217
|
+
/** Release held resources (closes the browser pool and HTTP dispatcher, if any). Safe to call once when done. */
|
|
2176
2218
|
async dispose() {
|
|
2177
2219
|
const pool = this.#browserPool;
|
|
2178
2220
|
if (pool) await pool.dispose();
|
|
2221
|
+
if (this.#httpDispatcher) await this.#httpDispatcher.close();
|
|
2179
2222
|
}
|
|
2180
2223
|
/** True when a built-in/registered adapter can handle this URL. */
|
|
2181
2224
|
canHandle(url) {
|