@crawlee/basic 4.0.0-beta.125 → 4.0.0-beta.127

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.
@@ -282,7 +282,7 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
282
282
  * implementing {@link IStatistics}.
283
283
  *
284
284
  * Custom fields declared via {@link StatisticsOptions.stateExtension|`stateExtension`} are carried over to
285
- * {@link BasicCrawler.stats|`crawler.stats.state`}:
285
+ * {@link BasicCrawler.statistics|`crawler.statistics.state`}:
286
286
  *
287
287
  * ```ts
288
288
  * const statistics = new Statistics({ stateExtension: { defaultState: { productsFound: 0 } } });
@@ -295,8 +295,8 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
295
295
  * });
296
296
  *
297
297
  * await crawler.run();
298
- * // the custom fields are typed on `crawler.stats` too
299
- * console.log(crawler.stats.state.productsFound);
298
+ * // the custom fields are typed on `crawler.statistics` too
299
+ * console.log(crawler.statistics.state.productsFound);
300
300
  * ```
301
301
  */
302
302
  statistics?: IStatistics<StatisticStateExtension>;
@@ -464,7 +464,7 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
464
464
  * The statistics instance collecting the crawler's run statistics - either the injected `statistics` option or a
465
465
  * crawler-built default. Typed as {@link IStatistics} so custom implementations can be plugged in.
466
466
  */
467
- get stats(): IStatistics<StatisticStateExtension>;
467
+ get statistics(): IStatistics<StatisticStateExtension>;
468
468
  /**
469
469
  * The main request-handling component of the crawler. It manages the requests that the crawler processes,
470
470
  * combining any provided request loader and/or queue. It's initialized during the crawler startup or lazily
@@ -84,14 +84,14 @@ export class BasicCrawler {
84
84
  * Used to detect and warn about multiple crawlers sharing the same state.
85
85
  */
86
86
  static #useStateAnonymousIndices = new Set();
87
- /** Backs the {@link BasicCrawler.stats|`stats`} getter. */
88
- #statsDep;
87
+ /** Backs the {@link BasicCrawler.statistics|`statistics`} getter. */
88
+ #statisticsDep;
89
89
  /**
90
90
  * The statistics instance collecting the crawler's run statistics - either the injected `statistics` option or a
91
91
  * crawler-built default. Typed as {@link IStatistics} so custom implementations can be plugged in.
92
92
  */
93
- get stats() {
94
- return this.#statsDep.value;
93
+ get statistics() {
94
+ return this.#statisticsDep.value;
95
95
  }
96
96
  /**
97
97
  * The main request-handling component of the crawler. It manages the requests that the crawler processes,
@@ -201,7 +201,7 @@ export class BasicCrawler {
201
201
  #sameDomainDelaySecs;
202
202
  maxRequestsPerCrawl;
203
203
  get handledRequestsCount() {
204
- return this.stats.state.requestsFinished + this.stats.state.requestsFailed;
204
+ return this.statistics.state.requestsFinished + this.statistics.state.requestsFailed;
205
205
  }
206
206
  #statusMessageLoggingInterval;
207
207
  #statusMessageCallback;
@@ -387,7 +387,7 @@ export class BasicCrawler {
387
387
  this.maxRequestRetries = maxRequestRetries;
388
388
  this.maxCrawlDepth = maxCrawlDepth;
389
389
  this.#sameDomainDelaySecs = sameDomainDelaySecs;
390
- this.#statsDep = OwnedOrInjected.resolve(statistics,
390
+ this.#statisticsDep = OwnedOrInjected.resolve(statistics,
391
391
  // A crawler-built default tracks the built-in fields only. A non-empty `StatisticStateExtension` can
392
392
  // only be satisfied by an injected instance carrying the matching `state`, so this branch does
393
393
  // not run in that case - hence the cast.
@@ -436,7 +436,7 @@ export class BasicCrawler {
436
436
  // Started here, rather than in `handleRequest`, so that a failure during context pipeline
437
437
  // initialization (e.g. a browser page timing out before the request handler ever runs) is
438
438
  // still accounted for by `failJob` below - which is a no-op without a matching `startJob`.
439
- this.stats.startJob(request.id || request.uniqueKey);
439
+ this.statistics.startJob(request.id || request.uniqueKey);
440
440
  const crawlingContext = { request };
441
441
  try {
442
442
  // The transaction spans the whole pipeline call, covering the navigation hooks
@@ -453,7 +453,7 @@ export class BasicCrawler {
453
453
  // ContextPipelineInterruptedError means the request was intentionally skipped
454
454
  // (e.g., doesn't match enqueue strategy after redirect). Just return gracefully.
455
455
  if (error instanceof ContextPipelineInterruptedError) {
456
- this.stats.discardJob(request.id || request.uniqueKey);
456
+ this.statistics.discardJob(request.id || request.uniqueKey);
457
457
  await this.timeoutAndRetry(async () => this.requestManager?.markRequestAsHandled(request), this.internalTimeoutMillis, `Marking request ${crawlingContext.request.url} (${crawlingContext.request.id}) as handled timed out after ${this.internalTimeoutMillis / 1e3} seconds.`);
458
458
  return;
459
459
  }
@@ -714,11 +714,11 @@ export class BasicCrawler {
714
714
  });
715
715
  }
716
716
  getPeriodicLogger() {
717
- let previousState = { ...this.stats.state };
717
+ let previousState = { ...this.statistics.state };
718
718
  const getOperationMode = () => {
719
- const { requestsFailed } = this.stats.state;
719
+ const { requestsFailed } = this.statistics.state;
720
720
  const { requestsFailed: previousRequestsFailed } = previousState;
721
- previousState = { ...this.stats.state };
721
+ previousState = { ...this.statistics.state };
722
722
  const failedDelta = requestsFailed - previousRequestsFailed;
723
723
  if (failedDelta > 0) {
724
724
  return { mode: 'ERROR', failedDelta };
@@ -733,12 +733,12 @@ export class BasicCrawler {
733
733
  }
734
734
  else {
735
735
  const total = await this.requestManager?.getTotalCount();
736
- message = `Crawled ${this.stats.state.requestsFinished}${total ? `/${total}` : ''} pages, ${this.stats.state.requestsFailed} failed requests, desired concurrency ${this.concurrencySystem?.desiredConcurrency ?? 0}.`;
736
+ message = `Crawled ${this.statistics.state.requestsFinished}${total ? `/${total}` : ''} pages, ${this.statistics.state.requestsFailed} failed requests, desired concurrency ${this.concurrencySystem?.desiredConcurrency ?? 0}.`;
737
737
  }
738
738
  if (this.#statusMessageCallback) {
739
739
  await this.#statusMessageCallback({
740
740
  crawler: this,
741
- state: this.stats.state,
741
+ state: this.statistics.state,
742
742
  previousState,
743
743
  message,
744
744
  });
@@ -785,7 +785,7 @@ export class BasicCrawler {
785
785
  }
786
786
  }
787
787
  // A supplied statistics instance keeps whatever state it was handed - only wipe a default we built.
788
- await this.#statsDep.ifOwned(async (stats) => {
788
+ await this.#statisticsDep.ifOwned(async (stats) => {
789
789
  stats.reset();
790
790
  await stats.resetStore();
791
791
  });
@@ -804,7 +804,7 @@ export class BasicCrawler {
804
804
  }
805
805
  try {
806
806
  await this.init();
807
- await this.stats.startCapturing();
807
+ await this.statistics.startCapturing();
808
808
  }
809
809
  catch (error) {
810
810
  // Clean up here before propagating, otherwise a failed startup would leave the process hanging.
@@ -834,24 +834,24 @@ export class BasicCrawler {
834
834
  }
835
835
  finally {
836
836
  await this.teardown();
837
- await this.stats.stopCapturing();
837
+ await this.statistics.stopCapturing();
838
838
  process.off('SIGINT', sigintHandler);
839
839
  eventManager.off(EventType.MIGRATING, boundPauseOnMigration);
840
840
  eventManager.off(EventType.ABORTING, boundPauseOnMigration);
841
- const finalStats = this.stats.calculate();
841
+ const finalStats = this.statistics.calculate();
842
842
  stats = {
843
- requestsFinished: this.stats.state.requestsFinished,
844
- requestsFailed: this.stats.state.requestsFailed,
845
- retryHistogram: this.stats.requestRetryHistogram,
843
+ requestsFinished: this.statistics.state.requestsFinished,
844
+ requestsFailed: this.statistics.state.requestsFailed,
845
+ retryHistogram: this.statistics.requestRetryHistogram,
846
846
  ...finalStats,
847
847
  };
848
848
  this.log.info('Final request statistics:', stats);
849
- if (this.stats.errorTracker.total !== 0) {
849
+ if (this.statistics.errorTracker.total !== 0) {
850
850
  const prettify = ([count, info]) => `${count}x: ${info.at(-1).trim()} (${info[0]})`;
851
851
  this.log.info(`Error analysis:`, {
852
- totalErrors: this.stats.errorTracker.total,
853
- uniqueErrors: this.stats.errorTracker.getUniqueErrorCount(),
854
- mostCommonErrors: this.stats.errorTracker.getMostPopularErrors(3).map(prettify),
852
+ totalErrors: this.statistics.errorTracker.total,
853
+ uniqueErrors: this.statistics.errorTracker.getUniqueErrorCount(),
854
+ mostCommonErrors: this.statistics.errorTracker.getMostPopularErrors(3).map(prettify),
855
855
  });
856
856
  }
857
857
  const client = serviceLocator.getStorageBackend();
@@ -866,7 +866,7 @@ export class BasicCrawler {
866
866
  finished = true;
867
867
  }
868
868
  periodicLogger.stop();
869
- this.setStatusMessage(`Finished! Total ${this.stats.state.requestsFinished + this.stats.state.requestsFailed} requests: ${this.stats.state.requestsFinished} succeeded, ${this.stats.state.requestsFailed} failed.`, { isStatusMessageTerminal: true, level: 'INFO' });
869
+ this.setStatusMessage(`Finished! Total ${this.statistics.state.requestsFinished + this.statistics.state.requestsFailed} requests: ${this.statistics.state.requestsFinished} succeeded, ${this.statistics.state.requestsFailed} failed.`, { isStatusMessageTerminal: true, level: 'INFO' });
870
870
  this.running = false;
871
871
  this.hasFinishedBefore = true;
872
872
  }
@@ -1447,7 +1447,7 @@ export class BasicCrawler {
1447
1447
  });
1448
1448
  }
1449
1449
  })();
1450
- await Promise.all([requestManagerPersistPromise, this.stats.persistState?.()]);
1450
+ await Promise.all([requestManagerPersistPromise, this.statistics.persistState?.()]);
1451
1451
  }
1452
1452
  /**
1453
1453
  * Fetches the next request to process from the underlying request provider.
@@ -1478,7 +1478,7 @@ export class BasicCrawler {
1478
1478
  await transaction?.commit();
1479
1479
  await this.timeoutAndRetry(async () => requestSource.markRequestAsHandled(request), this.internalTimeoutMillis, `Marking request ${request.url} (${request.id}) as handled timed out after ${this.internalTimeoutMillis / 1e3} seconds.`);
1480
1480
  isRequestLocked = false; // markRequestAsHandled succeeded and unlocked the request
1481
- this.stats.finishJob(statisticsId, request.retryCount);
1481
+ this.statistics.finishJob(statisticsId, request.retryCount);
1482
1482
  // reclaim session if request finishes successfully
1483
1483
  request.state = RequestState.DONE;
1484
1484
  crawlingContext.session.markGood();
@@ -1608,7 +1608,7 @@ export class BasicCrawler {
1608
1608
  }
1609
1609
  const shouldRetryRequest = this.canRequestBeRetried(request, error);
1610
1610
  if (shouldRetryRequest) {
1611
- await this.stats.errorTrackerRetry.addAsync(error, crawlingContext);
1611
+ await this.statistics.errorTrackerRetry.addAsync(error, crawlingContext);
1612
1612
  await this.errorHandler?.(crawlingContext, // valid cast - ExtendedContext transitively extends CrawlingContext
1613
1613
  error);
1614
1614
  if (error instanceof SessionError) {
@@ -1637,16 +1637,16 @@ export class BasicCrawler {
1637
1637
  // This is to make sure the error snapshot is not duplicated in the errorTrackerRetry and errorTracker objects.
1638
1638
  const { noRetry, maxRetries } = request;
1639
1639
  if (noRetry || !maxRetries) {
1640
- await this.stats.errorTracker.addAsync(error, crawlingContext);
1640
+ await this.statistics.errorTracker.addAsync(error, crawlingContext);
1641
1641
  }
1642
1642
  else {
1643
- this.stats.errorTracker.add(error);
1643
+ this.statistics.errorTracker.add(error);
1644
1644
  }
1645
1645
  // If we get here, the request is either not retryable
1646
1646
  // or failed more than retryCount times and will not be retried anymore.
1647
1647
  // Mark the request as failed and do not retry.
1648
1648
  await source.markRequestAsHandled(request);
1649
- this.stats.failJob(request.id || request.uniqueKey, request.retryCount);
1649
+ this.statistics.failJob(request.id || request.uniqueKey, request.retryCount);
1650
1650
  await this.handleFailedRequestHandler(crawlingContext, error); // This function prints an error message.
1651
1651
  }
1652
1652
  async handleFailedRequestHandler(crawlingContext, error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crawlee/basic",
3
- "version": "4.0.0-beta.125",
3
+ "version": "4.0.0-beta.127",
4
4
  "description": "The scalable web crawling and scraping library for JavaScript/Node.js. Enables development of data extraction and web automation jobs (not only) with headless Chrome and Puppeteer.",
5
5
  "engines": {
6
6
  "node": ">=22.0.0"
@@ -42,10 +42,10 @@
42
42
  "@apify/datastructures": "^2.0.0",
43
43
  "@apify/timeout": "^0.4.4",
44
44
  "@apify/utilities": "^2.15.5",
45
- "@crawlee/core": "4.0.0-beta.125",
46
- "@crawlee/http-client": "4.0.0-beta.125",
47
- "@crawlee/types": "4.0.0-beta.125",
48
- "@crawlee/utils": "4.0.0-beta.125",
45
+ "@crawlee/core": "4.0.0-beta.127",
46
+ "@crawlee/http-client": "4.0.0-beta.127",
47
+ "@crawlee/types": "4.0.0-beta.127",
48
+ "@crawlee/utils": "4.0.0-beta.127",
49
49
  "csv-stringify": "^6.5.2",
50
50
  "tldts": "^7.0.6",
51
51
  "tslib": "^2.8.1",
@@ -53,7 +53,7 @@
53
53
  "zod": "^4.4.3"
54
54
  },
55
55
  "optionalDependencies": {
56
- "@crawlee/impit-client": "^4.0.0-beta.125"
56
+ "@crawlee/impit-client": "^4.0.0-beta.127"
57
57
  },
58
58
  "lerna": {
59
59
  "command": {
@@ -62,5 +62,5 @@
62
62
  }
63
63
  }
64
64
  },
65
- "gitHead": "04a7212dd4abe4a5f94f5fc9632acb6089819c8b"
65
+ "gitHead": "4aa4a4d8d105bb530649ac5cd9197a3169106bd9"
66
66
  }