@duyquangnvx/webnovel-downloader 0.4.0 → 0.4.1

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 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 {
@@ -551,8 +554,12 @@ init_errors();
551
554
  var DEFAULT_TIMEOUT_MS = 3e4;
552
555
  var UndiciHttpClient = class {
553
556
  #logger;
557
+ #dispatcher;
558
+ #ownsDispatcher;
554
559
  constructor(opts) {
555
560
  this.#logger = opts.logger;
561
+ this.#ownsDispatcher = opts.dispatcher === void 0;
562
+ this.#dispatcher = opts.dispatcher ?? new import_undici.Agent();
556
563
  }
557
564
  async get(url, opts) {
558
565
  const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
@@ -561,6 +568,7 @@ var UndiciHttpClient = class {
561
568
  try {
562
569
  res = await (0, import_undici.request)(url, {
563
570
  method: "GET",
571
+ dispatcher: this.#dispatcher,
564
572
  ...opts?.headers !== void 0 ? { headers: opts.headers } : {},
565
573
  ...opts?.signal !== void 0 ? { signal: opts.signal } : {},
566
574
  headersTimeout: half,
@@ -585,10 +593,15 @@ var UndiciHttpClient = class {
585
593
  if (cause instanceof import_undici.errors.HeadersTimeoutError || cause instanceof import_undici.errors.BodyTimeoutError) {
586
594
  throw new TimeoutError(url, { cause });
587
595
  }
588
- this.#logger.debug({ url, cause }, "undici network error");
589
- throw new HttpError(0, url, "Network error", { cause });
596
+ const detail = cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause);
597
+ this.#logger.warn({ url, cause }, "undici network error");
598
+ throw new HttpError(0, url, `Network error (${detail})`, { cause });
590
599
  }
591
600
  }
601
+ /** Closes the dispatcher this client created. A no-op when one was injected — the caller owns that. */
602
+ async dispose() {
603
+ if (this.#ownsDispatcher) await this.#dispatcher.close();
604
+ }
592
605
  };
593
606
  function normalizeHeaders(h) {
594
607
  const out = {};
@@ -720,9 +733,13 @@ var RateLimiter = class {
720
733
  #buckets = /* @__PURE__ */ new Map();
721
734
  #pauses = /* @__PURE__ */ new Map();
722
735
  constructor(opts = DEFAULT_RATE_LIMIT) {
736
+ const burst = opts.burst ?? Math.max(1, opts.requestsPerSecond);
737
+ if (burst < 1) {
738
+ throw new RangeError(`RateLimiter: burst must be >= 1 (got ${burst})`);
739
+ }
723
740
  this.#opts = {
724
741
  requestsPerSecond: opts.requestsPerSecond,
725
- burst: opts.burst ?? opts.requestsPerSecond
742
+ burst
726
743
  };
727
744
  }
728
745
  async acquire(host, signal) {
@@ -967,7 +984,10 @@ function normalizeTransport(t) {
967
984
  function buildHttpClient(opts = {}) {
968
985
  const logger = opts.logger ?? silentLogger;
969
986
  const cookieJar = opts.cookieJar ?? new CookieJar();
970
- const leaf = opts.undiciOverride ?? new UndiciHttpClient({ logger });
987
+ const leaf = opts.undiciOverride ?? new UndiciHttpClient({
988
+ logger,
989
+ ...opts.dispatcher !== void 0 ? { dispatcher: opts.dispatcher } : {}
990
+ });
971
991
  const limiter = opts.rateLimiter ?? new RateLimiter(opts.rateLimit ?? DEFAULT_RATE_LIMIT);
972
992
  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
993
  const { mode } = normalizeTransport(opts.transport);
@@ -2104,6 +2124,7 @@ var Downloader = class {
2104
2124
  #transport;
2105
2125
  #cookieJar;
2106
2126
  #browserPool;
2127
+ #httpDispatcher;
2107
2128
  #transportListeners = /* @__PURE__ */ new Set();
2108
2129
  /**
2109
2130
  * Build a downloader from a set of adapters. `rateLimit`, `retry`, and
@@ -2138,10 +2159,12 @@ var Downloader = class {
2138
2159
  await handle.release();
2139
2160
  }
2140
2161
  } : void 0;
2162
+ this.#httpDispatcher = opts.http === void 0 && opts.undiciOverride === void 0 ? new import_undici2.Agent() : void 0;
2141
2163
  this.#http = opts.http ?? buildHttpClient({
2142
2164
  logger: this.#logger,
2143
2165
  transport: this.#transport,
2144
2166
  cookieJar: this.#cookieJar,
2167
+ ...this.#httpDispatcher !== void 0 ? { dispatcher: this.#httpDispatcher } : {},
2145
2168
  ...opts.rateLimit !== void 0 ? { rateLimit: opts.rateLimit } : {},
2146
2169
  ...opts.retry !== void 0 ? { retry: opts.retry } : {},
2147
2170
  onEvent: (e) => {
@@ -2172,10 +2195,11 @@ var Downloader = class {
2172
2195
  ...onCookieCapture !== void 0 ? { onCookieCapture } : {}
2173
2196
  });
2174
2197
  }
2175
- /** Release held resources (closes the browser pool, if any). Safe to call once when done. */
2198
+ /** Release held resources (closes the browser pool and HTTP dispatcher, if any). Safe to call once when done. */
2176
2199
  async dispose() {
2177
2200
  const pool = this.#browserPool;
2178
2201
  if (pool) await pool.dispose();
2202
+ if (this.#httpDispatcher) await this.#httpDispatcher.close();
2179
2203
  }
2180
2204
  /** True when a built-in/registered adapter can handle this URL. */
2181
2205
  canHandle(url) {