@crawlee/basic 4.0.0-rc.0 → 4.0.0-rc.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.
Files changed (78) hide show
  1. package/README.md +1 -1
  2. package/index.d.ts +11 -1
  3. package/index.js +11 -0
  4. package/internals/autoscaling/autoscaled_pool.d.ts +178 -0
  5. package/internals/autoscaling/autoscaled_pool.js +387 -0
  6. package/internals/autoscaling/concurrency_system.d.ts +235 -0
  7. package/internals/autoscaling/concurrency_system.js +357 -0
  8. package/internals/autoscaling/cpu_load_signal.d.ts +43 -0
  9. package/internals/autoscaling/cpu_load_signal.js +46 -0
  10. package/internals/autoscaling/event_loop_load_signal.d.ts +51 -0
  11. package/internals/autoscaling/event_loop_load_signal.js +60 -0
  12. package/internals/autoscaling/index.d.ts +9 -0
  13. package/internals/autoscaling/index.js +9 -0
  14. package/internals/autoscaling/load_signal.d.ts +98 -0
  15. package/internals/autoscaling/load_signal.js +103 -0
  16. package/internals/autoscaling/memory_load_signal.d.ts +45 -0
  17. package/internals/autoscaling/memory_load_signal.js +104 -0
  18. package/internals/autoscaling/snapshotter.d.ts +84 -0
  19. package/internals/autoscaling/snapshotter.js +67 -0
  20. package/internals/autoscaling/storage_backend_load_signal.d.ts +56 -0
  21. package/internals/autoscaling/storage_backend_load_signal.js +73 -0
  22. package/internals/autoscaling/system_status.d.ts +112 -0
  23. package/internals/autoscaling/system_status.js +139 -0
  24. package/internals/autoscaling/weighted_avg.d.ts +5 -0
  25. package/internals/autoscaling/weighted_avg.js +14 -0
  26. package/internals/basic-crawler.d.ts +77 -310
  27. package/internals/basic-crawler.js +680 -564
  28. package/internals/cookie_utils.d.ts +40 -0
  29. package/internals/cookie_utils.js +122 -0
  30. package/internals/crawler-run.d.ts +76 -0
  31. package/internals/crawler-run.js +142 -0
  32. package/internals/crawlers/context_pipeline.d.ts +75 -0
  33. package/internals/crawlers/context_pipeline.js +109 -0
  34. package/internals/crawlers/crawler_commons.d.ts +216 -0
  35. package/internals/crawlers/crawler_commons.js +1 -0
  36. package/internals/crawlers/error_snapshotter.d.ts +29 -0
  37. package/internals/crawlers/error_snapshotter.js +116 -0
  38. package/internals/crawlers/error_tracker.d.ts +51 -0
  39. package/internals/crawlers/error_tracker.js +316 -0
  40. package/internals/crawlers/index.d.ts +4 -0
  41. package/internals/crawlers/index.js +3 -0
  42. package/internals/crawlers/internals/types.d.ts +7 -0
  43. package/internals/crawlers/internals/types.js +1 -0
  44. package/internals/crawlers/statistics.d.ts +316 -0
  45. package/internals/crawlers/statistics.js +525 -0
  46. package/internals/crawling_request.d.ts +86 -0
  47. package/internals/crawling_request.js +127 -0
  48. package/internals/enqueue_links/enqueue_links.d.ts +152 -0
  49. package/internals/enqueue_links/enqueue_links.js +76 -0
  50. package/internals/enqueue_links/index.d.ts +3 -0
  51. package/internals/enqueue_links/index.js +3 -0
  52. package/internals/enqueue_links/shared.d.ts +106 -0
  53. package/internals/enqueue_links/shared.js +258 -0
  54. package/internals/errors.d.ts +68 -0
  55. package/internals/errors.js +83 -0
  56. package/internals/http.d.ts +9 -0
  57. package/internals/http.js +28 -0
  58. package/internals/request-timeout.d.ts +6 -0
  59. package/internals/request-timeout.js +27 -2
  60. package/internals/router.d.ts +299 -0
  61. package/internals/router.js +312 -0
  62. package/internals/session_pool/consts.d.ts +1 -0
  63. package/internals/session_pool/consts.js +1 -0
  64. package/internals/session_pool/errors.d.ts +7 -0
  65. package/internals/session_pool/errors.js +11 -0
  66. package/internals/session_pool/fingerprint.d.ts +9 -0
  67. package/internals/session_pool/fingerprint.js +30 -0
  68. package/internals/session_pool/index.d.ts +4 -0
  69. package/internals/session_pool/index.js +4 -0
  70. package/internals/session_pool/session.d.ts +151 -0
  71. package/internals/session_pool/session.js +225 -0
  72. package/internals/session_pool/session_pool.d.ts +193 -0
  73. package/internals/session_pool/session_pool.js +453 -0
  74. package/internals/sitemap_request_loader.d.ts +158 -0
  75. package/internals/sitemap_request_loader.js +432 -0
  76. package/internals/throttling_request_manager.d.ts +229 -0
  77. package/internals/throttling_request_manager.js +843 -0
  78. package/package.json +16 -12
@@ -1,11 +1,19 @@
1
- import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, ConcurrencySystemOptions, CrawleeLogger, CrawlingContext, DatasetExportOptions, EnqueueUrlsOptions, FinalStatistics, GetUserDataFromRequest, IConcurrencySystem, IProxyConfiguration, IRequestLoader, IRequestManager, IStatistics, RequestsLike, RouterHandler, RouterRoutes, SkippedRequestCallback, Source, StatisticState, StorageIdentifier, StorageWritePolicy, TaskLoopPredicates, TypedRequestsLike } from '@crawlee/core';
2
- import { ConcurrencySystem, Configuration, ContextPipeline, Request, Dataset, EventManager, RequestQueue } from '@crawlee/core';
1
+ import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, CrawleeLogger, DatasetExportOptions, IProxyConfiguration, IRequestLoader, IRequestManager, RequestsLike, Source, StorageIdentifier, StorageWritePolicy } from '@crawlee/core';
2
+ import { Configuration, Request, Dataset, EventManager, RequestQueue } from '@crawlee/core';
3
3
  import { BaseHttpClient } from '@crawlee/http-client';
4
- import type { Awaitable, Dictionary, ISession, ISessionPool, ProxyInfo, SetStatusMessageOptions, StorageBackend } from '@crawlee/types';
4
+ import type { Awaitable, Dictionary, ISessionPool, SetStatusMessageOptions, StorageBackend } from '@crawlee/types';
5
5
  import { RobotsTxtFile } from '@crawlee/utils';
6
6
  import type { ReadonlyDeep } from 'type-fest';
7
7
  import { z } from 'zod';
8
- import { TimeoutError } from '@apify/timeout';
8
+ import type { TaskLoopOptions } from './autoscaling/autoscaled_pool.js';
9
+ import type { ConcurrencySystemOptions, IConcurrencySystem } from './autoscaling/concurrency_system.js';
10
+ import { ConcurrencySystem } from './autoscaling/concurrency_system.js';
11
+ import type { FinalStatistics } from './autoscaling/system_status.js';
12
+ import { ContextPipeline } from './crawlers/context_pipeline.js';
13
+ import type { CrawlingContext, TypedRequestsLike } from './crawlers/crawler_commons.js';
14
+ import type { IStatistics, StatisticState } from './crawlers/statistics.js';
15
+ import type { EnqueueUrlsOptions, SkippedRequestCallback } from './enqueue_links/index.js';
16
+ import type { GetUserDataFromRequest, RouterHandler, RouterRoutes } from './router.js';
9
17
  export interface BasicCrawlingContext<UserData extends Dictionary = Dictionary> extends CrawlingContext<UserData> {
10
18
  }
11
19
  export { navigationDeadlineKey, remainingNavigationWindowMillis } from './request-timeout.js';
@@ -45,7 +53,7 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
45
53
  * To make this work, we should **always**
46
54
  * let our function throw exceptions rather than catch them.
47
55
  * The exceptions are logged to the request using the
48
- * {@link Request.pushErrorMessage|`Request.pushErrorMessage()`} function.
56
+ * {@link CrawlingRequest.pushErrorMessage|`request.pushErrorMessage()`} function.
49
57
  */
50
58
  requestHandler?: RouterHandler<ExtendedContext, Routes> | RequestHandler<ExtendedContext>;
51
59
  /**
@@ -140,8 +148,9 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
140
148
  * Indicates how much time (in seconds) to wait before crawling another same domain request. Subdomains are
141
149
  * paced together with the site they belong to.
142
150
  *
143
- * Wraps the crawler's request manager in a {@link ThrottlingRequestManager}; pass one as `requestManager`
144
- * yourself to configure it further.
151
+ * Offered to the crawler's request manager as a `minIntervalEverywhere` {@link PacingSignal}; a manager that
152
+ * already paces every domain it dispatches to takes it, so no domain ends up with two clocks. Otherwise the
153
+ * crawler wraps its request manager in a {@link ThrottlingRequestManager} of its own.
145
154
  * @default 0
146
155
  */
147
156
  sameDomainDelaySecs?: number;
@@ -165,7 +174,7 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
165
174
  * Concurrency is configured elsewhere — through the `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute`
166
175
  * shortcuts, or a {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} for finer control.
167
176
  */
168
- taskLoopOptions?: TaskLoopPredicates;
177
+ taskLoopOptions?: TaskLoopOptions;
169
178
  /**
170
179
  * A pre-configured concurrency governor — the component that decides whether there is free compute for one more
171
180
  * task. Typically a {@link ConcurrencySystem}, though any {@link IConcurrencySystem} is accepted. All
@@ -176,8 +185,8 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
176
185
  * single budget. Each crawler still builds and drives its own {@link AutoscaledPool}; only the load/scaling
177
186
  * accounting is shared.
178
187
  *
179
- * Mutually exclusive with the `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts, which configure
180
- * the default system this one replaces — combining the two throws.
188
+ * Mutually exclusive with the `minConcurrency`/`maxConcurrency`/`initialConcurrency`/`maxRequestsPerMinute`
189
+ * shortcuts, which configure the default system this one replaces — combining the two throws.
181
190
  *
182
191
  * You own a supplied system's lifecycle: `start()` it before `run()` (which throws otherwise) and `stop()` it once
183
192
  * every crawler borrowing it has finished. The crawler does neither on your behalf.
@@ -197,6 +206,12 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
197
206
  * {@link ConcurrencySystem}.
198
207
  */
199
208
  maxConcurrency?: number;
209
+ /**
210
+ * Sets the concurrency (parallelism) the crawl starts with, before any scaling happens. Shortcut for the
211
+ * {@link ConcurrencySystemOptions.desiredConcurrency|`desiredConcurrency`} option of the crawler's default
212
+ * {@link ConcurrencySystem}. Defaults to `minConcurrency`.
213
+ */
214
+ initialConcurrency?: number;
200
215
  /**
201
216
  * The maximum number of requests per minute the crawler should run.
202
217
  * By default, this is set to `Infinity`, but we can pass any positive, non-zero integer.
@@ -207,7 +222,16 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
207
222
  /**
208
223
  * Allows to keep the crawler alive even if the {@link RequestQueue} gets empty.
209
224
  * By default, the `crawler.run()` will resolve once the queue is empty. With `keepAlive: true` it will keep running,
210
- * waiting for more requests to come. Use `crawler.stop()` to exit the crawler gracefully, or `crawler.teardown()` to stop it immediately.
225
+ * waiting for more requests to come.
226
+ *
227
+ * `crawler.stop()` only stops the crawler from dispatching new requests — it does not wait for or drain
228
+ * requests already sitting in the queue. If this crawler is fed by another crawler or an external producer,
229
+ * calling `stop()` as soon as the producer finishes can abandon requests the producer already enqueued but
230
+ * this crawler had not yet picked up.
231
+ *
232
+ * To shut down only once the queue is genuinely empty and the producer has stopped, override
233
+ * {@link BasicCrawlerOptions.taskLoopOptions|`taskLoopOptions.isFinishedFunction`} to check both
234
+ * `(await requestManager.checkReadiness()).status === 'finished'` and the producer's own running/finished flag.
211
235
  */
212
236
  keepAlive?: boolean;
213
237
  /**
@@ -273,7 +297,8 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
273
297
  * 1. based on robots.txt file,
274
298
  * 2. because they don't match enqueueLinks filters,
275
299
  * 3. because they are redirected to a URL that doesn't match the enqueueLinks strategy,
276
- * 4. or because the {@link BasicCrawlerOptions.maxRequestsPerCrawl|`maxRequestsPerCrawl`} limit has been reached
300
+ * 4. because the {@link BasicCrawlerOptions.maxRequestsPerCrawl|`maxRequestsPerCrawl`} limit has been reached,
301
+ * 5. or by {@link RestrictedCrawlingContext.skipRequest|`context.skipRequest()`}.
277
302
  */
278
303
  onSkippedRequest?: SkippedRequestCallback;
279
304
  /**
@@ -367,99 +392,8 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
367
392
  */
368
393
  additionalHttpErrorStatusCodes?: number[];
369
394
  }
370
- /**
371
- * Provides a simple framework for parallel crawling of web pages.
372
- * The URLs to crawl are fed either from a static list of URLs
373
- * or from a dynamic queue of URLs enabling recursive crawling of websites.
374
- *
375
- * `BasicCrawler` is a low-level tool that requires the user to implement the page
376
- * download and data extraction functionality themselves.
377
- * If we want a crawler that already facilitates this functionality,
378
- * we should consider using {@link CheerioCrawler}, {@link PuppeteerCrawler} or {@link PlaywrightCrawler}.
379
- *
380
- * `BasicCrawler` invokes the user-provided {@link BasicCrawlerOptions.requestHandler|`requestHandler`}
381
- * for each {@link Request} object, which represents a single URL to crawl.
382
- * The {@link Request} objects are fed from the {@link IRequestManager|request manager} provided via the
383
- * {@link BasicCrawlerOptions.requestManager|`requestManager`} constructor option (a {@link RequestQueue} is
384
- * itself a request manager). If no `requestManager` is provided, the crawler opens the default {@link RequestQueue}
385
- * either when the {@link BasicCrawler.addRequests|`crawler.addRequests()`} function is called, or if the `requests`
386
- * parameter (representing the initial requests) of the {@link BasicCrawler.run|`crawler.run()`} function is provided.
387
- *
388
- * To read requests from a read-only source such as a {@link RequestList} or {@link SitemapRequestLoader} while
389
- * still being able to enqueue new ones, combine the loader with a queue into a {@link RequestManagerTandem} using
390
- * {@link IRequestLoader.toTandem|`requestLoader.toTandem()`} and pass the result as `requestManager`. The tandem
391
- * first processes URLs from the loader and automatically enqueues them into the queue, ensuring a single URL is not
392
- * crawled multiple times.
393
- *
394
- * > The legacy {@link BasicCrawlerOptions.requestList|`requestList`} and
395
- * > {@link BasicCrawlerOptions.requestQueue|`requestQueue`} options are deprecated. They are still accepted and
396
- * > folded into a single `requestManager` (combined into a tandem when both are given), but new code should use
397
- * > `requestManager` directly.
398
- *
399
- * The crawler finishes if there are no more {@link Request} objects to crawl.
400
- *
401
- * New requests are only dispatched when there is enough free CPU and memory available, as judged by the crawler's
402
- * {@link ConcurrencySystem}.
403
- * Concurrency is tuned via the {@link BasicCrawlerOptions.minConcurrency|`minConcurrency`},
404
- * {@link BasicCrawlerOptions.maxConcurrency|`maxConcurrency`} and
405
- * {@link BasicCrawlerOptions.maxRequestsPerMinute|`maxRequestsPerMinute`} shortcuts, or, for finer control, by
406
- * injecting a pre-configured {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`}.
407
- *
408
- * **Example usage:**
409
- *
410
- * ```javascript
411
- * import { BasicCrawler, Dataset } from 'crawlee';
412
- *
413
- * // Create a crawler instance
414
- * const crawler = new BasicCrawler({
415
- * async requestHandler({ request, sendRequest }) {
416
- * // 'request' contains an instance of the Request class
417
- * // Here we simply fetch the HTML of the page and store it to a dataset
418
- * const { body } = await sendRequest({
419
- * url: request.url,
420
- * method: request.method,
421
- * body: request.payload,
422
- * headers: request.headers,
423
- * });
424
- *
425
- * await Dataset.pushData({
426
- * url: request.url,
427
- * html: body,
428
- * })
429
- * },
430
- * });
431
- *
432
- * // Enqueue the initial requests and run the crawler
433
- * await crawler.run([
434
- * 'http://www.example.com/page-1',
435
- * 'http://www.example.com/page-2',
436
- * ]);
437
- * ```
438
- * @category Crawlers
439
- */
440
- /**
441
- * Identifies a crawler instance for storage aliasing, `useState()` and status-message events.
442
- */
443
- interface CrawlerIdentity {
444
- /**
445
- * 0-based instantiation order across all crawlers in the process.
446
- * Note that the value can be subject to race conditions between different script invocations.
447
- */
448
- readonly instanceIndex: number;
449
- /** The user-supplied `id` option, or a fallback derived from `instanceIndex`. */
450
- readonly id: string;
451
- /** Whether `id` came from the user (as opposed to being derived from `instanceIndex`). */
452
- readonly hasExplicitId: boolean;
453
- }
454
395
  export declare class BasicCrawler<Context extends CrawlingContext = CrawlingContext, ContextExtension = Dictionary<never>, ExtendedContext extends Context = Context & ContextExtension, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<Context['request']>>, StatisticStateExtension extends object = {}> {
455
396
  #private;
456
- protected static readonly CRAWLEE_STATE_KEY = "CRAWLEE_STATE";
457
- /**
458
- * Tracks the number of crawler instances created. The first crawler uses the default
459
- * request queue; subsequent ones get their own queue via a unique alias so they don't
460
- * collide.
461
- */
462
- private static instanceCount;
463
397
  /**
464
398
  * The statistics instance collecting the crawler's run statistics - either the injected `statistics` option or a
465
399
  * crawler-built default. Typed as {@link IStatistics} so custom implementations can be plugged in.
@@ -470,19 +404,21 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
470
404
  * combining any provided request loader and/or queue. It's initialized during the crawler startup or lazily
471
405
  * via {@link BasicCrawler.getRequestManager|`getRequestManager()`}.
472
406
  */
473
- protected requestManager?: IRequestManager;
407
+ protected get requestManager(): IRequestManager | undefined;
474
408
  /**
475
409
  * A reference to the underlying session pool that manages the crawler's {@link Session|sessions}. Typed as
476
410
  * {@link ISessionPool} so custom implementations can be plugged in via the `sessionPool` constructor option.
477
411
  */
478
412
  get sessionPool(): ISessionPool;
413
+ /** Whether a {@link BasicCrawler.run|`run()`} is in progress on this instance. */
414
+ get running(): boolean;
479
415
  /**
480
- * The concurrency governor this run is booking its requests against — either the
416
+ * The concurrency governor the run in progress is booking its requests against — either the
481
417
  * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} that was injected, or the default the
482
418
  * crawler built for itself. Read it for telemetry: `desiredConcurrency`, `currentConcurrency`, `isRunning`.
483
419
  *
484
- * > *NOTE:* `undefined` until {@link BasicCrawler.run|`crawler.run()`} has resolved it. A crawler-owned default
485
- * is also rebuilt for every run, so the instance is not stable across runs.
420
+ * > *NOTE:* `undefined` outside a run, and a crawler-owned default is rebuilt for every run — so read it
421
+ * during a run rather than caching it across runs.
486
422
  *
487
423
  * {@link IConcurrencySystem} is deliberately read-only. Tuning concurrency *while a crawl is running* means
488
424
  * owning the instance: build a {@link ConcurrencySystem} yourself and inject it, then set
@@ -499,43 +435,20 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
499
435
  * See {@link Router.addHandler|`router.addHandler()`} and {@link Router.addDefaultHandler|`router.addDefaultHandler()`}.
500
436
  */
501
437
  readonly router: RouterHandler<Context, Routes>;
502
- /**
503
- * The basic part of the context pipeline. Unlike the subclass pipeline, this
504
- * part has no major side effects (e.g. launching a browser). It also makes typing more explicit, as subclass
505
- * pipelines expect the basic crawler fields to already be present in the context at runtime.
506
- *
507
- * Context built with this pipeline can be passed into multiple crawler pipelines at once.
508
- * This is used e.g. in the {@link AdaptivePlaywrightCrawler|`AdaptivePlaywrightCrawler`}.
509
- */
510
- get basicContextPipeline(): ContextPipeline<{
511
- request: Request;
512
- }, CrawlingContext>;
438
+ /** @internal */
513
439
  get contextPipeline(): ContextPipeline<CrawlingContext, ExtendedContext>;
514
- running: boolean;
515
- hasFinishedBefore: boolean;
440
+ /** Whether a `run()` on this instance has already finished - a repeated one continues where it left off. */
441
+ get hasFinishedBefore(): boolean;
516
442
  get log(): CrawleeLogger;
517
443
  protected readonly requestHandler: RequestHandler<ExtendedContext>;
518
- protected readonly errorHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
519
- protected readonly failedRequestHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
520
- private requestHandlerTimeoutMillis;
521
444
  protected readonly internalTimeoutMillis: number;
522
- protected readonly maxRequestRetries: number;
523
- protected readonly maxCrawlDepth?: number;
524
- protected readonly maxRequestsPerCrawl?: number;
525
- private get handledRequestsCount();
526
445
  protected blockedStatusCodes: Set<number>;
527
446
  protected readonly additionalHttpErrorStatusCodes: Set<number>;
528
- /**
529
- * The resolved options for the crawler's own task loop — the crawler-owned `runTaskFunction`, the (possibly
530
- * user-overridden) ready/finished predicates and cadence/logging. Concurrency configuration lives on the
531
- * {@link ConcurrencySystem} instead, and the loop's `consumer` identity is the crawler's own, so neither is
532
- * settable here.
533
- */
534
- private taskLoopOptions;
535
447
  protected readonly httpClient: BaseHttpClient;
536
448
  protected readonly retryOnBlocked: boolean;
537
- protected readonly onSkippedRequest?: SkippedRequestCallback;
538
- protected readonly identity: CrawlerIdentity;
449
+ /**
450
+ * @internal
451
+ */
539
452
  protected static optionsShape: {
540
453
  contextPipelineBuilder: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
541
454
  extendContext: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
@@ -568,69 +481,26 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
568
481
  }>>;
569
482
  }, z.core.$strict>]>>;
570
483
  onSkippedRequest: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
571
- httpClient: z.ZodOptional<z.ZodCustom<BaseHttpClient, BaseHttpClient>>;
572
- configuration: z.ZodOptional<z.ZodCustom<Configuration, Configuration>>;
484
+ httpClient: z.ZodOptional<z.ZodInstanceOf<BaseHttpClient>>;
485
+ configuration: z.ZodOptional<z.ZodInstanceOf<Configuration>>;
573
486
  storageBackend: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
574
- eventManager: z.ZodOptional<z.ZodCustom<EventManager, EventManager>>;
487
+ eventManager: z.ZodOptional<z.ZodInstanceOf<EventManager>>;
575
488
  logger: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
576
489
  minConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
577
490
  maxConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
491
+ initialConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
578
492
  maxRequestsPerMinute: z.ZodOptional<z.ZodCustom<number, number>>;
579
493
  keepAlive: z.ZodOptional<z.ZodBoolean>;
580
494
  statistics: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
581
495
  id: z.ZodOptional<z.ZodString>;
582
496
  };
583
- protected static optionsSchema: z.ZodObject<{
584
- contextPipelineBuilder: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
585
- extendContext: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
586
- requestList: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
587
- requestQueue: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
588
- requestManager: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
589
- requestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
590
- requestHandlerTimeoutSecs: z.ZodOptional<z.ZodCustom<number, number>>;
591
- errorHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
592
- failedRequestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
593
- maxRequestRetries: z.ZodDefault<z.ZodCustom<number, number>>;
594
- sameDomainDelaySecs: z.ZodDefault<z.ZodCustom<number, number>>;
595
- maxRequestsPerCrawl: z.ZodOptional<z.ZodCustom<number, number>>;
596
- maxCrawlDepth: z.ZodOptional<z.ZodCustom<number, number>>;
597
- taskLoopOptions: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
598
- concurrencySystem: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
599
- sessionPool: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
600
- proxyConfiguration: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
601
- statusMessageLoggingInterval: z.ZodDefault<z.ZodCustom<number, number>>;
602
- statusMessageCallback: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
603
- additionalHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
604
- ignoreHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
605
- blockedStatusCodes: z.ZodOptional<z.ZodArray<z.ZodCustom<number, number>>>;
606
- retryOnBlocked: z.ZodDefault<z.ZodBoolean>;
607
- respectRobotsTxtFile: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCustom<Dictionary, Dictionary>]>>;
608
- transactionalStorage: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
609
- requestQueue: z.ZodOptional<z.ZodEnum<{
610
- deferred: "deferred";
611
- writeThrough: "writeThrough";
612
- }>>;
613
- }, z.core.$strict>]>>;
614
- onSkippedRequest: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
615
- httpClient: z.ZodOptional<z.ZodCustom<BaseHttpClient, BaseHttpClient>>;
616
- configuration: z.ZodOptional<z.ZodCustom<Configuration, Configuration>>;
617
- storageBackend: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
618
- eventManager: z.ZodOptional<z.ZodCustom<EventManager, EventManager>>;
619
- logger: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
620
- minConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
621
- maxConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
622
- maxRequestsPerMinute: z.ZodOptional<z.ZodCustom<number, number>>;
623
- keepAlive: z.ZodOptional<z.ZodBoolean>;
624
- statistics: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
625
- id: z.ZodOptional<z.ZodString>;
626
- }, z.core.$strict>;
627
497
  /**
628
498
  * All `BasicCrawler` parameters are passed via an options object.
629
499
  */
630
500
  constructor(options?: BasicCrawlerOptions<Context, ContextExtension, ExtendedContext, Routes, StatisticStateExtension> & RequireContextPipeline<CrawlingContext, Context>);
631
501
  /**
632
502
  * Builds the crawler-owned default {@link ConcurrencySystem} from the resolved
633
- * `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts. Not called when a
503
+ * `minConcurrency`/`maxConcurrency`/`initialConcurrency`/`maxRequestsPerMinute` shortcuts. Not called when a
634
504
  * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} was injected.
635
505
  *
636
506
  * Subclasses may override this to tune the default system (e.g. {@link HttpCrawler} raises the starting
@@ -644,22 +514,6 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
644
514
  * @returns `true` if the status code is considered an error, `false` otherwise
645
515
  */
646
516
  protected isErrorStatusCode(status: number): boolean;
647
- /**
648
- * Builds the basic context pipeline that transforms `{ request }` into a full `CrawlingContext`.
649
- * This handles base context creation, session resolution, and context helpers.
650
- */
651
- private buildBasicContextPipeline;
652
- private checkRobotsTxt;
653
- /**
654
- * Builds the subclass-specific context pipeline that transforms a `CrawlingContext` into the crawler's target context type.
655
- * Subclasses should override this to add their own pipeline stages.
656
- */
657
- protected buildContextPipeline(): ContextPipeline<CrawlingContext, CrawlingContext>;
658
- private createBaseContext;
659
- private resolveRequest;
660
- private resolveSession;
661
- private createContextHelpers;
662
- private buildFinalContextPipeline;
663
517
  /**
664
518
  * Checks if the given error is a proxy error by comparing its message to a list of known proxy error messages.
665
519
  * Used for retrying requests that failed due to proxy errors.
@@ -677,7 +531,6 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
677
531
  * their status-reporting backend (e.g. the Apify platform).
678
532
  */
679
533
  setStatusMessage(message: string, options?: SetStatusMessageOptions): void;
680
- private getPeriodicLogger;
681
534
  /**
682
535
  * Runs the crawler. Returns a promise that resolves once every request has been processed and the crawler's
683
536
  * finished-check ({@link BasicCrawlerOptions.taskLoopOptions|`taskLoopOptions.isFinishedFunction`}, or the
@@ -686,8 +539,12 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
686
539
  * We can use the `requests` parameter to enqueue the initial requests — it is a shortcut for
687
540
  * running {@link BasicCrawler.addRequests|`crawler.addRequests()`} before {@link BasicCrawler.run|`crawler.run()`}.
688
541
  *
542
+ * Calling `run()` again on the same instance keeps crawling the same request manager - requests the previous
543
+ * run handled (a failed one counts as handled) are not processed again. Purge the queue or open a fresh one
544
+ * if that is what you want.
545
+ *
689
546
  * @param [requests] The requests to add.
690
- * @param [options] Options for the request queue.
547
+ * @param [options] Options for adding the initial requests.
691
548
  */
692
549
  run(requests?: TypedRequestsLike<Routes>, options?: CrawlerRunOptions): Promise<FinalStatistics>;
693
550
  /**
@@ -722,33 +579,7 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
722
579
  * crawler's request manager, which is no longer guaranteed to be a {@link RequestQueue}.
723
580
  */
724
581
  getRequestQueue(): Promise<IRequestManager>;
725
- /**
726
- * Opens the default {@link RequestQueue}, applies the crawler's timeouts to it and records it as the
727
- * crawler-owned queue (so it gets purged between repeated `run()` calls).
728
- * @private
729
- */
730
- private openOwnedRequestQueue;
731
- /**
732
- * Tells a request manager how long we expect to hold a fetched request, so that one backed by a
733
- * locking storage backend keeps it reserved for slightly longer than the request handler timeout
734
- * (with some padding for overhead), but never for less than a minute. This prevents a long-running
735
- * request from being handed out a second time while it is still being processed — and it works
736
- * regardless of whether the manager is a plain {@link RequestQueue} or a `RequestManagerTandem`.
737
- */
738
- private applyRequestManagerTimeouts;
739
- /**
740
- * Validates a request source's `userData` against the {@link RouteSchemas|Standard Schema} registered
741
- * for its label on the crawler's schema-router (if any), throwing a {@link RequestValidationError} on
742
- * mismatch. A no-op when the user's request handler is not a schema-router, or no schema is registered for
743
- * the request's label. Applied by the crawler on the add paths it owns — `crawler.addRequests`,
744
- * `crawler.run`, `context.addRequests` and `context.enqueueLinks`.
745
- */
746
- private validateRequestUserData;
747
582
  useState<State extends Dictionary = Dictionary>(defaultValue?: State): Promise<State>;
748
- protected getPendingRequestCountApproximation(): Promise<number>;
749
- protected calculateEnqueuedRequestLimit(explicitLimit?: number): Promise<number | undefined>;
750
- protected handleSkippedRequest(options: Parameters<SkippedRequestCallback>[0]): Promise<void>;
751
- private logOncePerRun;
752
583
  /**
753
584
  * Adds requests to the queue in batches. By default, it will resolve after the initial batch is added, and continue
754
585
  * adding the rest in background. You can configure the batch size via `batchSize` option and the sleep time in between
@@ -793,84 +624,34 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
793
624
  * browser crawlers override it with their `navigationTimeoutSecs`.
794
625
  */
795
626
  protected getNavigationTimeoutMillis(): number;
796
- /**
797
- * Races the request against the internal timeout (see {@link raceWithTimeout}), sized to outlast the phases
798
- * that have their own timeout - the navigation, its hooks, and the request handler - so a legitimately slow
799
- * request, a per-route override, or a low `CRAWLEE_INTERNAL_TIMEOUT` is not cut short mid-phase. It takes
800
- * whichever is larger: the configured internal timeout, or this request's combined phase budget.
801
- */
802
- private withRequestTimeout;
803
- /**
804
- * The request handler timeout for a request with the given route label. A router route may override the
805
- * crawler's own `requestHandlerTimeoutSecs`; anything else falls back to `fallbackMillis`.
806
- *
807
- * @param label The request's route label, or `undefined` for the default route / no specific request.
808
- * @param fallbackMillis Timeout to use when no route overrides it.
809
- */
810
- private resolveRequestHandlerTimeoutMillis;
811
- /**
812
- * The timeout the router route with the given label asked for, or `undefined` when it did not override one
813
- * (or the request handler is not a router at all).
814
- */
815
- private getRouteTimeoutMillis;
816
627
  protected runRequestHandler(crawlingContext: ExtendedContext): Promise<void>;
817
628
  /**
818
629
  * Runs `callback` inside a {@link StorageTransaction}, unless transactional storage is disabled.
819
- * Deliberately does **not** commit on return - `handleRequest` swallows request handler failures, so
820
- * a normal return says nothing about success. `handleRequest` owns the outcome.
630
+ * Deliberately does **not** commit on return - the pipeline hands failures to `handleRequestFailure`, so a
631
+ * normal return says nothing about success. `handleRequest` commits on success.
821
632
  */
822
633
  private runInStorageTransaction;
823
634
  /**
824
635
  * Handles blocked request
825
636
  */
826
637
  protected throwOnBlockedRequest(statusCode: number): void;
827
- private isAllowedBasedOnRobotsTxtFile;
828
638
  /**
829
- * Records an HTTP 429 against the URL's domain so the request manager can pace the retry.
639
+ * Records an HTTP 429 against the URL's domain so the request manager can hold the retry back.
830
640
  *
831
641
  * @param retryAfterHeader The raw `Retry-After` response header, if the server sent one.
832
- * @returns `true` if a manager took responsibility for the delay, in which case the caller should throw
642
+ * @returns `true` if the manager took responsibility for the delay, in which case the caller should throw
833
643
  * {@link RequestThrottledError} rather than treating the response as a blocked session.
834
644
  */
835
645
  protected recordDomainRateLimit(url: string, retryAfterHeader?: string | null): boolean;
836
- /**
837
- * Hands a robots.txt `Crawl-delay` to the request manager, warning if nothing is able to honour it.
838
- *
839
- * The warning is driven by whether the delay was actually accepted rather than by the type of the manager,
840
- * because a manager that does throttle still drops the delay for a domain missing from its `domains` list.
841
- */
842
- private applyCrawlDelay;
646
+ /** @internal */
843
647
  protected getRobotsTxtFileForUrl(url: string): Promise<RobotsTxtFile | undefined>;
844
- private pauseOnMigration;
845
- /**
846
- * Fetches the next request to process from the underlying request provider.
847
- */
848
- private fetchNextRequest;
849
- /** Handles a single request - runs the request handler with retries, error handling, and lifecycle management. */
648
+ /** Runs the request handler and marks the request as handled. Failures are left to `handleRequestFailure`. */
850
649
  private handleRequest;
851
650
  /**
852
651
  * Generator function that yields requests injected with the given crawl depth.
853
652
  * @internal
854
653
  */
855
654
  protected addCrawlDepthRequestGenerator(requests: RequestsLike, newRequestDepth: number): AsyncGenerator<Source, void, undefined>;
856
- /**
857
- * Run async callback with given timeout and retry. Returns the result of the callback.
858
- * @ignore
859
- */
860
- private timeoutAndRetry;
861
- /**
862
- * Returns true if either RequestList or RequestQueue have a request ready for processing.
863
- */
864
- private isTaskReadyFunction;
865
- /**
866
- * Returns true if both RequestList and RequestQueue have all requests finished.
867
- */
868
- private defaultIsFinishedFunction;
869
- /**
870
- * Unwraps errors thrown by the context pipeline to get the actual user error.
871
- * RequestHandlerError and ContextPipelineInitializationError wrap the actual error.
872
- */
873
- private unwrapError;
874
655
  /**
875
656
  * Handles errors thrown by user provided requestHandler()
876
657
  *
@@ -883,45 +664,31 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
883
664
  * @param error The error received
884
665
  * @returns The message to be logged
885
666
  */
886
- protected getMessageFromError(error: Error, forceStack?: boolean): string | TimeoutError | undefined;
887
- /**
888
- * Whether the session should be spared for this error - either because it was already retired, or because the
889
- * failure says nothing about the session (a rate limit is a property of the domain).
890
- */
891
- private errorAbsolvesSession;
892
- private canRequestBeRetried;
667
+ protected getMessageFromError(error: Error, forceStack?: boolean): string;
893
668
  /**
894
- * Stops the crawler immediately.
669
+ * Ends the run in progress immediately, without waiting for the requests already in flight.
895
670
  *
896
- * This method doesn't wait for currently active requests to finish.
671
+ * This runs at the end of every {@link BasicCrawler.run|`run()`}, so what it releases is per-run only, and
672
+ * a repeated `run()` has to find everything it needs either intact or re-establishable. What outlives a run is
673
+ * released by {@link BasicCrawler.destroy|`destroy()`} instead.
897
674
  *
898
675
  * To stop the crawler gracefully (waiting for all running requests to finish), use {@link BasicCrawler.stop|`crawler.stop()`} instead.
899
676
  */
900
677
  teardown(): Promise<void>;
678
+ /**
679
+ * Releases what the crawler owns beyond a single run — in the browser crawlers, the browser pool. A finished
680
+ * `run()` has already released everything a run owns, so this is only needed before dropping a crawler you
681
+ * could otherwise have run again.
682
+ */
683
+ destroy(): Promise<void>;
684
+ [Symbol.asyncDispose](): Promise<void>;
901
685
  protected getCookieHeaderFromRequest(request: Request): string;
902
- private requestMatchesEnqueueStrategy;
903
- }
904
- export interface CreateContextOptions {
905
- request: Request;
906
- session: ISession;
907
- proxyInfo?: ProxyInfo;
908
686
  }
909
687
  export interface CrawlerAddRequestsOptions extends AddRequestsBatchedOptions, EnqueueUrlsOptions {
910
688
  }
911
689
  export interface CrawlerAddRequestsResult extends AddRequestsBatchedResult {
912
690
  }
913
691
  export interface CrawlerRunOptions extends CrawlerAddRequestsOptions {
914
- /**
915
- * Controls whether the request queue is purged between repeated `run()` calls on the same crawler instance.
916
- * Purging clears all requests and resets internal counters, allowing the same URLs to be processed again.
917
- *
918
- * - **`undefined`** (default) — only the crawler's own (auto-created) queue is purged.
919
- * A user-supplied `requestQueue` is left untouched.
920
- * - **`true`** — the queue is always purged, even if it was supplied by the user.
921
- * - **`false`** — nothing is purged. Only genuinely new requests will be processed;
922
- * note that even a failed request is considered handled.
923
- */
924
- purgeRequestQueue?: boolean;
925
692
  }
926
693
  /**
927
694
  * Creates new {@link Router} instance that works based on request labels.