@crawlee/basic 4.0.0-beta.10 → 4.0.0-beta.101

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.
@@ -1,46 +1,33 @@
1
- import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, AutoscaledPoolOptions, BaseHttpClient, CrawlingContext, DatasetExportOptions, EnqueueLinksOptions, EventManager, FinalStatistics, GetUserDataFromRequest, IRequestList, LoadedContext, ProxyInfo, Request, RequestOptions, RestrictedCrawlingContext, RouterHandler, RouterRoutes, Session, SessionPoolOptions, SkippedRequestCallback, Source, StatisticsOptions, StatisticState } from '@crawlee/core';
2
- import { AutoscaledPool, Configuration, Dataset, RequestProvider, SessionPool, Statistics } from '@crawlee/core';
3
- import type { Awaitable, BatchAddRequestsResult, Dictionary, SetStatusMessageOptions } from '@crawlee/types';
1
+ import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, ConcurrencySystemOptions, Configuration, CrawleeLogger, CrawlingContext, DatasetExportOptions, EnqueueLinksOptions, EventManager, FinalStatistics, GetUserDataFromRequest, IConcurrencySystem, IProxyConfiguration, IRequestLoader, IRequestManager, Request, RequestsLike, RouterHandler, RouterRoutes, SkippedRequestCallback, Source, StatisticsOptions, StatisticState, StorageIdentifier, TaskLoopPredicates, TypedRequestsLike } from '@crawlee/core';
2
+ import { ConcurrencySystem, ContextPipeline, Dataset, RequestQueue, Statistics } from '@crawlee/core';
3
+ import type { Awaitable, BaseHttpClient, BatchAddRequestsResult, Dictionary, ISession, ISessionPool, ProxyInfo, SetStatusMessageOptions, StorageBackend } from '@crawlee/types';
4
4
  import { RobotsTxtFile } from '@crawlee/utils';
5
- import type { SetRequired } from 'type-fest';
6
- import type { Log } from '@apify/log';
5
+ import type { ReadonlyDeep, SetRequired } from 'type-fest';
7
6
  import { TimeoutError } from '@apify/timeout';
8
- export interface BasicCrawlingContext<UserData extends Dictionary = Dictionary> extends CrawlingContext<BasicCrawler, UserData> {
9
- /**
10
- * This function automatically finds and enqueues links from the current page, adding them to the {@link RequestQueue}
11
- * currently used by the crawler.
12
- *
13
- * Optionally, the function allows you to filter the target links' URLs using an array of globs or regular expressions
14
- * and override settings of the enqueued {@link Request} objects.
15
- *
16
- * Check out the [Crawl a website with relative links](https://crawlee.dev/js/docs/examples/crawl-relative-links) example
17
- * for more details regarding its usage.
18
- *
19
- * **Example usage**
20
- *
21
- * ```ts
22
- * async requestHandler({ enqueueLinks }) {
23
- * await enqueueLinks({
24
- * urls: [...],
25
- * });
26
- * },
27
- * ```
28
- *
29
- * @param [options] All `enqueueLinks()` parameters are passed via an options object.
30
- * @returns Promise that resolves to {@link BatchAddRequestsResult} object.
31
- */
32
- enqueueLinks(options?: SetRequired<EnqueueLinksOptions, 'urls'>): Promise<BatchAddRequestsResult>;
7
+ export interface BasicCrawlingContext<UserData extends Dictionary = Dictionary> extends CrawlingContext<UserData> {
33
8
  }
34
- export type RequestHandler<Context extends CrawlingContext = LoadedContext<BasicCrawlingContext & RestrictedCrawlingContext>> = (inputs: LoadedContext<Context>) => Awaitable<void>;
35
- export type ErrorHandler<Context extends CrawlingContext = LoadedContext<BasicCrawlingContext & RestrictedCrawlingContext>> = (inputs: LoadedContext<Context>, error: Error) => Awaitable<void>;
36
- export interface StatusMessageCallbackParams<Context extends CrawlingContext = BasicCrawlingContext, Crawler extends BasicCrawler<any> = BasicCrawler<Context>> {
9
+ export { navigationDeadlineKey, remainingNavigationWindowMillis } from './request-timeout.js';
10
+ export type RequestHandler<Context extends CrawlingContext = CrawlingContext> = (inputs: Context) => Awaitable<void>;
11
+ /**
12
+ * An error handler receives the crawling context and the error that was thrown while processing the request.
13
+ *
14
+ * Unlike the {@link RequestHandler}, an error handler may run before the context pipeline has finished
15
+ * building the full context (e.g. when navigation or session setup fails). Therefore only `BaseContext` is
16
+ * guaranteed to be present, while the extra properties added by the pipeline and `extendContext` (the
17
+ * difference between `BaseContext` and `ExtendedContext`) are only available as a `Partial`.
18
+ */
19
+ export type ErrorHandler<BaseContext extends CrawlingContext = CrawlingContext, ExtendedContext extends BaseContext = BaseContext> = (inputs: BaseContext & Partial<ExtendedContext>, error: Error) => Awaitable<void>;
20
+ export interface StatusMessageCallbackParams<Context extends CrawlingContext = BasicCrawlingContext, Crawler extends BasicCrawler<any, any, any, any> = BasicCrawler<Context>> {
37
21
  state: StatisticState;
38
22
  crawler: Crawler;
39
23
  previousState: StatisticState;
40
24
  message: string;
41
25
  }
42
- export type StatusMessageCallback<Context extends CrawlingContext = BasicCrawlingContext, Crawler extends BasicCrawler<any> = BasicCrawler<Context>> = (params: StatusMessageCallbackParams<Context, Crawler>) => Awaitable<void>;
43
- export interface BasicCrawlerOptions<Context extends CrawlingContext = BasicCrawlingContext> {
26
+ export type StatusMessageCallback<Context extends CrawlingContext = BasicCrawlingContext, Crawler extends BasicCrawler<any, any, any, any> = BasicCrawler<Context>> = (params: StatusMessageCallbackParams<Context, Crawler>) => Awaitable<void>;
27
+ export type RequireContextPipeline<DefaultContextType extends CrawlingContext, FinalContextType extends DefaultContextType> = DefaultContextType extends FinalContextType ? {} : {
28
+ contextPipelineBuilder: () => ContextPipeline<CrawlingContext, FinalContextType>;
29
+ };
30
+ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingContext, ContextExtension = Dictionary<never>, ExtendedContext extends Context = Context & ContextExtension, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<Context['request']>>> {
44
31
  /**
45
32
  * User-provided function that performs the logic of the crawler. It is called for each URL to crawl.
46
33
  *
@@ -58,21 +45,64 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = BasicCraw
58
45
  * The exceptions are logged to the request using the
59
46
  * {@link Request.pushErrorMessage|`Request.pushErrorMessage()`} function.
60
47
  */
61
- requestHandler?: RequestHandler<Context>;
48
+ requestHandler?: RouterHandler<ExtendedContext, Routes> | RequestHandler<ExtendedContext>;
49
+ /**
50
+ * Allows the user to extend the crawling context with custom functionality (helpers, references, etc.).
51
+ *
52
+ * `extendContext` runs *before* navigation, so the returned members are visible to the
53
+ * `preNavigationHooks`, `postNavigationHooks`, and the `requestHandler` alike. As a consequence,
54
+ * the `context` passed to `extendContext` is the pre-navigation {@link CrawlingContext} and does
55
+ * **not** include navigation-dependent members (e.g. `page`, `response`, `$`, `body`). If you need
56
+ * those, use a `postNavigationHook` or the `requestHandler` instead.
57
+ *
58
+ * **Example usage:**
59
+ *
60
+ * ```javascript
61
+ * import { BasicCrawler } from 'crawlee';
62
+ *
63
+ * // Create a crawler instance
64
+ * const crawler = new BasicCrawler({
65
+ * extendContext(context) => ({
66
+ * async customHelper() {
67
+ * await context.pushData({ url: context.request.url })
68
+ * }
69
+ * }),
70
+ * async requestHandler(context) {
71
+ * await context.customHelper();
72
+ * },
73
+ * });
74
+ * ```
75
+ */
76
+ extendContext?: (context: CrawlingContext) => Awaitable<ContextExtension>;
77
+ /**
78
+ * *Intended for BasicCrawler subclasses*. Prepares a context pipeline that transforms the initial crawling context into the shape given by the `Context` type parameter.
79
+ *
80
+ * The option is not required if your crawler subclass does not extend the crawling context with custom information or helpers.
81
+ */
82
+ contextPipelineBuilder?: () => ContextPipeline<CrawlingContext, Context>;
62
83
  /**
63
84
  * Static list of URLs to be processed.
64
- * If not provided, the crawler will open the default request queue when the {@link BasicCrawler.addRequests|`crawler.addRequests()`} function is called.
65
- * > Alternatively, `requests` parameter of {@link BasicCrawler.run|`crawler.run()`} could be used to enqueue the initial requests -
66
- * it is a shortcut for running `crawler.addRequests()` before the `crawler.run()`.
85
+ *
86
+ * @deprecated Use the `requestManager` option instead. To combine a read-only loader (such as a `RequestList`)
87
+ * with a writable queue, build a tandem with {@link IRequestLoader.toTandem|`requestList.toTandem(requestQueue)`}
88
+ * and pass the result as `requestManager`. When both `requestList` and `requestQueue` are provided, they are
89
+ * combined into a tandem automatically.
67
90
  */
68
- requestList?: IRequestList;
91
+ requestList?: IRequestLoader;
69
92
  /**
70
93
  * Dynamic queue of URLs to be processed. This is useful for recursive crawling of websites.
71
- * If not provided, the crawler will open the default request queue when the {@link BasicCrawler.addRequests|`crawler.addRequests()`} function is called.
72
- * > Alternatively, `requests` parameter of {@link BasicCrawler.run|`crawler.run()`} could be used to enqueue the initial requests -
73
- * it is a shortcut for running `crawler.addRequests()` before the `crawler.run()`.
94
+ *
95
+ * @deprecated Use the `requestManager` option instead. A `RequestQueue` is itself a request manager, so you can
96
+ * pass it directly as `requestManager`.
74
97
  */
75
- requestQueue?: RequestProvider;
98
+ requestQueue?: RequestQueue;
99
+ /**
100
+ * Manager of requests that should be processed by the crawler. Mutually exclusive with the deprecated
101
+ * `requestQueue` and `requestList` options.
102
+ *
103
+ * If not provided, the crawler will open the default {@link RequestQueue} when it is first needed.
104
+ */
105
+ requestManager?: IRequestManager;
76
106
  /**
77
107
  * Timeout in which the function passed as {@link BasicCrawlerOptions.requestHandler|`requestHandler`} needs to finish, in seconds.
78
108
  * @default 60
@@ -87,7 +117,7 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = BasicCraw
87
117
  * Second argument is the `Error` instance that
88
118
  * represents the last error thrown during processing of the request.
89
119
  */
90
- errorHandler?: ErrorHandler<Context>;
120
+ errorHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
91
121
  /**
92
122
  * A function to handle requests that failed more than {@link BasicCrawlerOptions.maxRequestRetries|`maxRequestRetries`} times.
93
123
  *
@@ -96,14 +126,11 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = BasicCraw
96
126
  * Second argument is the `Error` instance that
97
127
  * represents the last error thrown during processing of the request.
98
128
  */
99
- failedRequestHandler?: ErrorHandler<Context>;
129
+ failedRequestHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
100
130
  /**
101
131
  * Specifies the maximum number of retries allowed for a request if its processing fails.
102
- * This includes retries due to navigation errors or errors thrown from user-supplied functions
103
- * (`requestHandler`, `preNavigationHooks`, `postNavigationHooks`).
104
- *
105
- * This limit does not apply to retries triggered by session rotation
106
- * (see {@link BasicCrawlerOptions.maxSessionRotations|`maxSessionRotations`}).
132
+ * This includes retries due to navigation errors, session/proxy errors, or errors thrown from user-supplied
133
+ * functions (`requestHandler`, `preNavigationHooks`, `postNavigationHooks`).
107
134
  * @default 3
108
135
  */
109
136
  maxRequestRetries?: number;
@@ -112,14 +139,6 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = BasicCraw
112
139
  * @default 0
113
140
  */
114
141
  sameDomainDelaySecs?: number;
115
- /**
116
- * Maximum number of session rotations per request.
117
- * The crawler will automatically rotate the session in case of a proxy error or if it gets blocked by the website.
118
- *
119
- * The session rotations are not counted towards the {@link BasicCrawlerOptions.maxRequestRetries|`maxRequestRetries`} limit.
120
- * @default 10
121
- */
122
- maxSessionRotations?: number;
123
142
  /**
124
143
  * Maximum number of pages that the crawler will open. The crawl will stop when this limit is reached.
125
144
  * This value should always be set in order to prevent infinite loops in misconfigured crawlers.
@@ -127,29 +146,56 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = BasicCraw
127
146
  */
128
147
  maxRequestsPerCrawl?: number;
129
148
  /**
130
- * Custom options passed to the underlying {@link AutoscaledPool} constructor.
131
- * > *NOTE:* The {@link AutoscaledPoolOptions.runTaskFunction|`runTaskFunction`}
132
- * and {@link AutoscaledPoolOptions.isTaskReadyFunction|`isTaskReadyFunction`} options
133
- * are provided by the crawler and cannot be overridden.
134
- * However, we can provide a custom implementation of {@link AutoscaledPoolOptions.isFinishedFunction|`isFinishedFunction`}.
149
+ * Maximum depth of the crawl. If not set, the crawl will continue until all requests are processed.
150
+ * Setting this to `0` will only process the initial requests, skipping all links enqueued by `crawlingContext.enqueueLinks` and `crawlingContext.addRequests`.
151
+ * Passing `1` will process the initial requests and all links enqueued by `crawlingContext.enqueueLinks` and `crawlingContext.addRequests` in the handler for initial requests.
152
+ */
153
+ maxCrawlDepth?: number;
154
+ /**
155
+ * Lets you override the predicates that steer the crawler's task loop: `isTaskReadyFunction` (may another request
156
+ * start?) and `isFinishedFunction` (is the crawl over?). The task itself — fetching a request and running it
157
+ * through the pipeline — is owned by the crawler and cannot be overridden.
158
+ *
159
+ * Concurrency is configured elsewhere — through the `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute`
160
+ * shortcuts, or a {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} for finer control.
161
+ */
162
+ taskLoopOptions?: TaskLoopPredicates;
163
+ /**
164
+ * A pre-configured concurrency governor — the component that decides whether there is free compute for one more
165
+ * task. Typically a {@link ConcurrencySystem}, though any {@link IConcurrencySystem} is accepted. All
166
+ * scaling configuration (min/max/desired concurrency, scaling ratios, `maxTasksPerMinute`, snapshotter tuning)
167
+ * lives on the instance itself.
168
+ *
169
+ * Inject the *same* instance into several concurrent crawlers to cap their **combined** concurrency against a
170
+ * single budget. Each crawler still builds and drives its own {@link AutoscaledPool}; only the load/scaling
171
+ * accounting is shared.
172
+ *
173
+ * Mutually exclusive with the `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts, which configure
174
+ * the default system this one replaces — combining the two throws.
175
+ *
176
+ * You own a supplied system's lifecycle: `start()` it before `run()` (which throws otherwise) and `stop()` it once
177
+ * every crawler borrowing it has finished. The crawler does neither on your behalf.
135
178
  */
136
- autoscaledPoolOptions?: AutoscaledPoolOptions;
179
+ concurrencySystem?: IConcurrencySystem;
137
180
  /**
138
181
  * Sets the minimum concurrency (parallelism) for the crawl. Shortcut for the
139
- * AutoscaledPool {@link AutoscaledPoolOptions.minConcurrency|`minConcurrency`} option.
182
+ * {@link ConcurrencySystemOptions.minConcurrency|`minConcurrency`} option of the crawler's default
183
+ * {@link ConcurrencySystem}.
140
184
  * > *WARNING:* If we set this value too high with respect to the available system memory and CPU, our crawler will run extremely slow or crash.
141
185
  * If not sure, it's better to keep the default value and the concurrency will scale up automatically.
142
186
  */
143
187
  minConcurrency?: number;
144
188
  /**
145
189
  * Sets the maximum concurrency (parallelism) for the crawl. Shortcut for the
146
- * AutoscaledPool {@link AutoscaledPoolOptions.maxConcurrency|`maxConcurrency`} option.
190
+ * {@link ConcurrencySystemOptions.maxConcurrency|`maxConcurrency`} option of the crawler's default
191
+ * {@link ConcurrencySystem}.
147
192
  */
148
193
  maxConcurrency?: number;
149
194
  /**
150
195
  * The maximum number of requests per minute the crawler should run.
151
196
  * By default, this is set to `Infinity`, but we can pass any positive, non-zero integer.
152
- * Shortcut for the AutoscaledPool {@link AutoscaledPoolOptions.maxTasksPerMinute|`maxTasksPerMinute`} option.
197
+ * Shortcut for the {@link ConcurrencySystemOptions.maxTasksPerMinute|`maxTasksPerMinute`} option of the
198
+ * crawler's default {@link ConcurrencySystem}.
153
199
  */
154
200
  maxRequestsPerMinute?: number;
155
201
  /**
@@ -159,14 +205,14 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = BasicCraw
159
205
  */
160
206
  keepAlive?: boolean;
161
207
  /**
162
- * Basic crawler will initialize the {@link SessionPool} with the corresponding {@link SessionPoolOptions|`sessionPoolOptions`}.
163
- * The session instance will be than available in the {@link BasicCrawlerOptions.requestHandler|`requestHandler`}.
164
- */
165
- useSessionPool?: boolean;
166
- /**
167
- * The configuration options for {@link SessionPool} to use.
208
+ * An existing session pool instance to use. When provided, the crawler will use this pool directly instead of
209
+ * creating a new one, enabling session sharing across multiple crawlers. The crawler will not tear down a shared
210
+ * pool — the caller is responsible for its lifecycle.
211
+ *
212
+ * Accepts the built-in {@link SessionPool} or any object implementing the {@link ISessionPool} interface,
213
+ * so custom session-management strategies can be plugged in.
168
214
  */
169
- sessionPoolOptions?: SessionPoolOptions;
215
+ sessionPool?: ISessionPool;
170
216
  /**
171
217
  * Defines the length of the interval for calling the `setStatusMessage` in seconds.
172
218
  */
@@ -188,6 +234,11 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = BasicCraw
188
234
  * ```
189
235
  */
190
236
  statusMessageCallback?: StatusMessageCallback;
237
+ /**
238
+ * HTTP status codes that indicate the session should be retired.
239
+ * @default [401, 403, 429]
240
+ */
241
+ blockedStatusCodes?: number[];
191
242
  /**
192
243
  * If set to `true`, the crawler will automatically try to bypass any detected bot protection.
193
244
  *
@@ -199,20 +250,22 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = BasicCraw
199
250
  /**
200
251
  * If set to `true`, the crawler will automatically try to fetch the robots.txt file for each domain,
201
252
  * and skip those that are not allowed. This also prevents disallowed URLs to be added via `enqueueLinks`.
253
+ *
254
+ * If an object is provided, it may contain a `userAgent` property to specify which user-agent
255
+ * should be used when checking the robots.txt file. If not provided, the default user-agent `*` will be used.
202
256
  */
203
- respectRobotsTxtFile?: boolean;
257
+ respectRobotsTxtFile?: boolean | {
258
+ userAgent?: string;
259
+ };
204
260
  /**
205
261
  * When a request is skipped for some reason, you can use this callback to act on it.
206
- * This is currently fired only for requests skipped based on robots.txt file.
262
+ * This is currently fired for requests skipped
263
+ * 1. based on robots.txt file,
264
+ * 2. because they don't match enqueueLinks filters,
265
+ * 3. because they are redirected to a URL that doesn't match the enqueueLinks strategy,
266
+ * 4. or because the {@link BasicCrawlerOptions.maxRequestsPerCrawl|`maxRequestsPerCrawl`} limit has been reached
207
267
  */
208
268
  onSkippedRequest?: SkippedRequestCallback;
209
- /** @internal */
210
- log?: Log;
211
- /**
212
- * Enables experimental features of Crawlee, which can alter the behavior of the crawler.
213
- * WARNING: these options are not guaranteed to be stable and may change or be removed at any time.
214
- */
215
- experiments?: CrawlerExperiments;
216
269
  /**
217
270
  * Customize the way statistics collecting works, such as logging interval or
218
271
  * whether to output them to the Key-Value store.
@@ -220,24 +273,56 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = BasicCraw
220
273
  statisticsOptions?: StatisticsOptions;
221
274
  /**
222
275
  * HTTP client implementation for the `sendRequest` context helper and for plain HTTP crawling.
223
- * Defaults to a new instance of {@link GotScrapingHttpClient}
276
+ * Defaults to {@link ImpitHttpClient} when `@crawlee/impit-client` is installed, otherwise {@link FetchHttpClient}.
224
277
  */
225
278
  httpClient?: BaseHttpClient;
226
- }
227
- /**
228
- * A set of options that you can toggle to enable experimental features in Crawlee.
229
- *
230
- * NOTE: These options will not respect semantic versioning and may be removed or changed at any time. Use at your own risk.
231
- * If you do use these and encounter issues, please report them to us.
232
- */
233
- export interface CrawlerExperiments {
234
279
  /**
235
- * @deprecated This experiment is now enabled by default, and this flag will be removed in a future release.
236
- * If you encounter issues due to this change, please:
237
- * - report it to us: https://github.com/apify/crawlee
238
- * - set `requestLocking` to `false` in the `experiments` option of the crawler
280
+ * If set, the crawler will be configured for all connections to use
281
+ * the Proxy URLs provided and rotated according to the configuration.
282
+ */
283
+ proxyConfiguration?: IProxyConfiguration;
284
+ /**
285
+ * Custom configuration to use for this crawler.
286
+ * If provided, the crawler will use its own ServiceLocator instance instead of the global one.
287
+ */
288
+ configuration?: Configuration;
289
+ /**
290
+ * Custom storage backend to use for this crawler.
291
+ * If provided, the crawler will use its own ServiceLocator instance instead of the global one.
239
292
  */
240
- requestLocking?: boolean;
293
+ storageBackend?: StorageBackend;
294
+ /**
295
+ * Custom event manager to use for this crawler.
296
+ * If provided, the crawler will use its own ServiceLocator instance instead of the global one.
297
+ */
298
+ eventManager?: EventManager;
299
+ /**
300
+ * Custom logger to use for this crawler.
301
+ * If provided, the crawler will use its own ServiceLocator instance instead of the global one.
302
+ */
303
+ logger?: CrawleeLogger;
304
+ /**
305
+ * A unique identifier for the crawler instance. This ID is used to isolate the state returned by
306
+ * {@link BasicCrawler.useState|`crawler.useState()`} from other crawler instances.
307
+ *
308
+ * When multiple crawler instances use `useState()` without an explicit `id`, they will share the same
309
+ * state object for backward compatibility. A warning will be logged in this case.
310
+ *
311
+ * To ensure each crawler has its own isolated state that also persists across script restarts
312
+ * (e.g., during Apify migrations), provide a stable, unique ID for each crawler instance.
313
+ *
314
+ */
315
+ id?: string;
316
+ /**
317
+ * An array of HTTP response [Status Codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) to be excluded from error consideration.
318
+ * By default, status codes >= 500 trigger errors.
319
+ */
320
+ ignoreHttpErrorStatusCodes?: number[];
321
+ /**
322
+ * An array of additional HTTP response [Status Codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) to be treated as errors.
323
+ * By default, status codes >= 500 trigger errors.
324
+ */
325
+ additionalHttpErrorStatusCodes?: number[];
241
326
  }
242
327
  /**
243
328
  * Provides a simple framework for parallel crawling of web pages.
@@ -251,25 +336,31 @@ export interface CrawlerExperiments {
251
336
  *
252
337
  * `BasicCrawler` invokes the user-provided {@link BasicCrawlerOptions.requestHandler|`requestHandler`}
253
338
  * for each {@link Request} object, which represents a single URL to crawl.
254
- * The {@link Request} objects are fed from the {@link RequestList} or {@link RequestQueue}
255
- * instances provided by the {@link BasicCrawlerOptions.requestList|`requestList`} or {@link BasicCrawlerOptions.requestQueue|`requestQueue`}
256
- * constructor options, respectively. If neither `requestList` nor `requestQueue` options are provided,
257
- * the crawler will open the default request queue either when the {@link BasicCrawler.addRequests|`crawler.addRequests()`} function is called,
258
- * or if `requests` parameter (representing the initial requests) of the {@link BasicCrawler.run|`crawler.run()`} function is provided.
339
+ * The {@link Request} objects are fed from the {@link IRequestManager|request manager} provided via the
340
+ * {@link BasicCrawlerOptions.requestManager|`requestManager`} constructor option (a {@link RequestQueue} is
341
+ * itself a request manager). If no `requestManager` is provided, the crawler opens the default {@link RequestQueue}
342
+ * either when the {@link BasicCrawler.addRequests|`crawler.addRequests()`} function is called, or if the `requests`
343
+ * parameter (representing the initial requests) of the {@link BasicCrawler.run|`crawler.run()`} function is provided.
259
344
  *
260
- * If both {@link BasicCrawlerOptions.requestList|`requestList`} and {@link BasicCrawlerOptions.requestQueue|`requestQueue`} options are used,
261
- * the instance first processes URLs from the {@link RequestList} and automatically enqueues all of them
262
- * to the {@link RequestQueue} before it starts their processing. This ensures that a single URL is not crawled multiple times.
345
+ * To read requests from a read-only source such as a {@link RequestList} or {@link SitemapRequestLoader} while
346
+ * still being able to enqueue new ones, combine the loader with a queue into a {@link RequestManagerTandem} using
347
+ * {@link IRequestLoader.toTandem|`requestLoader.toTandem()`} and pass the result as `requestManager`. The tandem
348
+ * first processes URLs from the loader and automatically enqueues them into the queue, ensuring a single URL is not
349
+ * crawled multiple times.
350
+ *
351
+ * > The legacy {@link BasicCrawlerOptions.requestList|`requestList`} and
352
+ * > {@link BasicCrawlerOptions.requestQueue|`requestQueue`} options are deprecated. They are still accepted and
353
+ * > folded into a single `requestManager` (combined into a tandem when both are given), but new code should use
354
+ * > `requestManager` directly.
263
355
  *
264
356
  * The crawler finishes if there are no more {@link Request} objects to crawl.
265
357
  *
266
- * New requests are only dispatched when there is enough free CPU and memory available,
267
- * using the functionality provided by the {@link AutoscaledPool} class.
268
- * All {@link AutoscaledPool} configuration options can be passed to the {@link BasicCrawlerOptions.autoscaledPoolOptions|`autoscaledPoolOptions`}
269
- * parameter of the `BasicCrawler` constructor.
270
- * For user convenience, the {@link AutoscaledPoolOptions.minConcurrency|`minConcurrency`} and
271
- * {@link AutoscaledPoolOptions.maxConcurrency|`maxConcurrency`} options of the
272
- * underlying {@link AutoscaledPool} constructor are available directly in the `BasicCrawler` constructor.
358
+ * New requests are only dispatched when there is enough free CPU and memory available, as judged by the crawler's
359
+ * {@link ConcurrencySystem}.
360
+ * Concurrency is tuned via the {@link BasicCrawlerOptions.minConcurrency|`minConcurrency`},
361
+ * {@link BasicCrawlerOptions.maxConcurrency|`maxConcurrency`} and
362
+ * {@link BasicCrawlerOptions.maxRequestsPerMinute|`maxRequestsPerMinute`} shortcuts, or, for finer control, by
363
+ * injecting a pre-configured {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`}.
273
364
  *
274
365
  * **Example usage:**
275
366
  *
@@ -303,71 +394,157 @@ export interface CrawlerExperiments {
303
394
  * ```
304
395
  * @category Crawlers
305
396
  */
306
- export declare class BasicCrawler<Context extends CrawlingContext = BasicCrawlingContext> {
307
- readonly config: Configuration;
397
+ /**
398
+ * Identifies a crawler instance for storage aliasing, `useState()` and status-message events.
399
+ */
400
+ interface CrawlerIdentity {
401
+ /**
402
+ * 0-based instantiation order across all crawlers in the process.
403
+ * Note that the value can be subject to race conditions between different script invocations.
404
+ */
405
+ readonly instanceIndex: number;
406
+ /** The user-supplied `id` option, or a fallback derived from `instanceIndex`. */
407
+ readonly id: string;
408
+ /** Whether `id` came from the user (as opposed to being derived from `instanceIndex`). */
409
+ readonly hasExplicitId: boolean;
410
+ }
411
+ 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']>>> {
412
+ #private;
308
413
  protected static readonly CRAWLEE_STATE_KEY = "CRAWLEE_STATE";
414
+ /**
415
+ * Tracks the number of crawler instances created. The first crawler uses the default
416
+ * request queue; subsequent ones get their own queue via a unique alias so they don't
417
+ * collide.
418
+ */
419
+ private static instanceCount;
420
+ /**
421
+ * Tracks crawler instances that accessed shared state without having an explicit id.
422
+ * Used to detect and warn about multiple crawlers sharing the same state.
423
+ */
424
+ private static useStateAnonymousIndices;
309
425
  /**
310
426
  * A reference to the underlying {@link Statistics} class that collects and logs run statistics for requests.
311
427
  */
312
428
  readonly stats: Statistics;
313
429
  /**
314
- * A reference to the underlying {@link RequestList} class that manages the crawler's {@link Request|requests}.
315
- * Only available if used by the crawler.
430
+ * The main request-handling component of the crawler. It manages the requests that the crawler processes,
431
+ * combining any provided request loader and/or queue. It's initialized during the crawler startup or lazily
432
+ * via {@link BasicCrawler.getRequestManager|`getRequestManager()`}.
316
433
  */
317
- requestList?: IRequestList;
434
+ protected requestManager?: IRequestManager;
435
+ /** Backs the {@link BasicCrawler.sessionPool|`sessionPool`} getter. */
436
+ private sessionPoolDep;
318
437
  /**
319
- * Dynamic queue of URLs to be processed. This is useful for recursive crawling of websites.
320
- * A reference to the underlying {@link RequestQueue} class that manages the crawler's {@link Request|requests}.
321
- * Only available if used by the crawler.
438
+ * A reference to the underlying session pool that manages the crawler's {@link Session|sessions}. Typed as
439
+ * {@link ISessionPool} so custom implementations can be plugged in via the `sessionPool` constructor option.
322
440
  */
323
- requestQueue?: RequestProvider;
441
+ get sessionPool(): ISessionPool;
324
442
  /**
325
- * A reference to the underlying {@link SessionPool} class that manages the crawler's {@link Session|sessions}.
326
- * Only available if used by the crawler.
443
+ * Tracks **only** the queue the crawler opens for itself not the {@link RequestManagerTandem} that may wrap it
444
+ * around a user-supplied `requestList` so the owned-only purge between repeated `run()` calls never reaches
445
+ * through to a borrowed loader. Filled lazily in {@link BasicCrawler.openOwnedRequestQueue|`openOwnedRequestQueue()`}.
446
+ */
447
+ private ownedRequestQueue;
448
+ /**
449
+ * Whether the request-processing-time hint has already been forwarded to the request manager. The hint
450
+ * derives only from `requestHandlerTimeoutMillis` (constant for the crawler's lifetime) and is raise-only,
451
+ * so it only needs to be applied once, at the first async access of the manager.
327
452
  */
328
- sessionPool?: SessionPool;
453
+ private requestManagerTimeoutsApplied;
329
454
  /**
330
- * A reference to the underlying {@link AutoscaledPool} class that manages the concurrency of the crawler.
331
- * > *NOTE:* This property is only initialized after calling the {@link BasicCrawler.run|`crawler.run()`} function.
332
- * We can use it to change the concurrency settings on the fly,
333
- * to pause the crawler by calling {@link AutoscaledPool.pause|`autoscaledPool.pause()`}
334
- * or to abort it by calling {@link AutoscaledPool.abort|`autoscaledPool.abort()`}.
455
+ * Resolves the governor for one run: either the injected
456
+ * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} (borrowed) or a freshly built default with
457
+ * the concurrency shortcuts folded in (owned, so the crawler starts and stops it).
335
458
  */
336
- autoscaledPool?: AutoscaledPool;
459
+ private readonly resolveConcurrencySystem;
460
+ /** As resolved by `_init()`. Absent until the first run, so a `teardown()` before it is a no-op. */
461
+ private concurrencySystemDep?;
462
+ /**
463
+ * The concurrency governor this run is booking its requests against — either the
464
+ * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} that was injected, or the default the
465
+ * crawler built for itself. Read it for telemetry: `desiredConcurrency`, `currentConcurrency`, `isRunning`.
466
+ *
467
+ * > *NOTE:* `undefined` until {@link BasicCrawler.run|`crawler.run()`} has resolved it. A crawler-owned default
468
+ * is also rebuilt for every run, so the instance is not stable across runs.
469
+ *
470
+ * {@link IConcurrencySystem} is deliberately read-only. Tuning concurrency *while a crawl is running* means
471
+ * owning the instance: build a {@link ConcurrencySystem} yourself and inject it, then set
472
+ * `minConcurrency`/`maxConcurrency`/`desiredConcurrency` on your own reference.
473
+ */
474
+ get concurrencySystem(): IConcurrencySystem | undefined;
475
+ /**
476
+ * The task loop that dispatches this run's requests. Private on purpose — it is a bare parallel task runner with
477
+ * no configuration left of its own (see {@link ConcurrencySystem}), and everything a caller legitimately did
478
+ * with it now has a crawler-level counterpart: {@link BasicCrawler.pause|`pause()`},
479
+ * {@link BasicCrawler.resume|`resume()`}, {@link BasicCrawler.teardown|`teardown()`} and
480
+ * {@link BasicCrawler.concurrencySystem|`concurrencySystem`}.
481
+ */
482
+ private autoscaledPool?;
483
+ /**
484
+ * A reference to the underlying {@link IProxyConfiguration} instance that manages the crawler's proxies.
485
+ * Only available if used by the crawler.
486
+ */
487
+ readonly proxyConfiguration?: IProxyConfiguration;
337
488
  /**
338
489
  * Default {@link Router} instance that will be used if we don't specify any {@link BasicCrawlerOptions.requestHandler|`requestHandler`}.
339
490
  * See {@link Router.addHandler|`router.addHandler()`} and {@link Router.addDefaultHandler|`router.addDefaultHandler()`}.
340
491
  */
341
- readonly router: RouterHandler<LoadedContext<Context>>;
492
+ readonly router: RouterHandler<Context, Routes>;
493
+ private _basicContextPipeline?;
494
+ /**
495
+ * The basic part of the context pipeline. Unlike the subclass pipeline, this
496
+ * part has no major side effects (e.g. launching a browser). It also makes typing more explicit, as subclass
497
+ * pipelines expect the basic crawler fields to already be present in the context at runtime.
498
+ *
499
+ * Context built with this pipeline can be passed into multiple crawler pipelines at once.
500
+ * This is used e.g. in the {@link AdaptivePlaywrightCrawler|`AdaptivePlaywrightCrawler`}.
501
+ */
502
+ get basicContextPipeline(): ContextPipeline<{
503
+ request: Request;
504
+ }, CrawlingContext>;
505
+ private _contextPipeline?;
506
+ get contextPipeline(): ContextPipeline<CrawlingContext, ExtendedContext>;
342
507
  running: boolean;
343
508
  hasFinishedBefore: boolean;
344
- readonly log: Log;
345
- protected requestHandler: RequestHandler<Context>;
346
- protected errorHandler?: ErrorHandler<Context>;
347
- protected failedRequestHandler?: ErrorHandler<Context>;
348
- protected requestHandlerTimeoutMillis: number;
349
- protected internalTimeoutMillis: number;
350
- protected maxRequestRetries: number;
351
- protected sameDomainDelayMillis: number;
352
- protected domainAccessedTime: Map<string, number>;
353
- protected maxSessionRotations: number;
354
- protected handledRequestsCount: number;
355
- protected statusMessageLoggingInterval: number;
356
- protected statusMessageCallback?: StatusMessageCallback;
357
- protected sessionPoolOptions: SessionPoolOptions;
358
- protected useSessionPool: boolean;
359
- protected crawlingContexts: Map<string, Context>;
360
- protected autoscaledPoolOptions: AutoscaledPoolOptions;
361
- protected events: EventManager;
362
- protected httpClient: BaseHttpClient;
363
- protected retryOnBlocked: boolean;
364
- protected respectRobotsTxtFile: boolean;
365
- protected onSkippedRequest?: SkippedRequestCallback;
509
+ private unexpectedStop;
510
+ get log(): CrawleeLogger;
511
+ protected readonly requestHandler: RequestHandler<ExtendedContext>;
512
+ protected readonly errorHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
513
+ protected readonly failedRequestHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
514
+ private requestHandlerTimeoutMillis;
515
+ protected readonly internalTimeoutMillis: number;
516
+ protected readonly maxRequestRetries: number;
517
+ protected readonly maxCrawlDepth?: number;
518
+ private sameDomainDelayMillis;
519
+ private domainAccessedTime;
520
+ protected readonly maxRequestsPerCrawl?: number;
521
+ private get handledRequestsCount();
522
+ private statusMessageLoggingInterval;
523
+ private statusMessageCallback?;
524
+ protected blockedStatusCodes: Set<number>;
525
+ protected readonly additionalHttpErrorStatusCodes: Set<number>;
526
+ private ignoreHttpErrorStatusCodes;
527
+ /**
528
+ * The resolved options for the crawler's own task loop — the crawler-owned `runTaskFunction`, the (possibly
529
+ * user-overridden) ready/finished predicates and cadence/logging. Concurrency configuration lives on the
530
+ * {@link ConcurrencySystem} instead, and the loop's `consumer` identity is the crawler's own, so neither is
531
+ * settable here.
532
+ */
533
+ private taskLoopOptions;
534
+ protected readonly httpClient: BaseHttpClient;
535
+ protected readonly retryOnBlocked: boolean;
536
+ private respectRobotsTxtFile;
537
+ protected readonly onSkippedRequest?: SkippedRequestCallback;
366
538
  private _closeEvents?;
367
- private experiments;
539
+ private loggedPerRun;
368
540
  private readonly robotsTxtFileCache;
369
- private _experimentWarnings;
541
+ protected readonly identity: CrawlerIdentity;
542
+ private readonly contextPipelineOptions;
370
543
  protected static optionsShape: {
544
+ // @ts-ignore optional peer dependency or compatibility with es2022
545
+ contextPipelineBuilder: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
546
+ // @ts-ignore optional peer dependency or compatibility with es2022
547
+ extendContext: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
371
548
  // @ts-ignore optional peer dependency or compatibility with es2022
372
549
  requestList: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
373
550
  // @ts-ignore optional peer dependency or compatibility with es2022
@@ -384,28 +561,44 @@ export declare class BasicCrawler<Context extends CrawlingContext = BasicCrawlin
384
561
  maxRequestRetries: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
385
562
  // @ts-ignore optional peer dependency or compatibility with es2022
386
563
  sameDomainDelaySecs: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
387
- // @ts-ignore optional peer dependency or compatibility with es2022
388
- maxSessionRotations: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
389
564
  // @ts-ignore optional peer dependency or compatibility with es2022
390
565
  maxRequestsPerCrawl: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
391
566
  // @ts-ignore optional peer dependency or compatibility with es2022
392
- autoscaledPoolOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
567
+ maxCrawlDepth: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
568
+ // @ts-ignore optional peer dependency or compatibility with es2022
569
+ taskLoopOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
393
570
  // @ts-ignore optional peer dependency or compatibility with es2022
394
- sessionPoolOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
571
+ concurrencySystem: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
395
572
  // @ts-ignore optional peer dependency or compatibility with es2022
396
- useSessionPool: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
573
+ sessionPool: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
574
+ // @ts-ignore optional peer dependency or compatibility with es2022
575
+ proxyConfiguration: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
397
576
  // @ts-ignore optional peer dependency or compatibility with es2022
398
577
  statusMessageLoggingInterval: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
399
578
  // @ts-ignore optional peer dependency or compatibility with es2022
400
579
  statusMessageCallback: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
580
+ // @ts-ignore optional peer dependency or compatibility with es2022
581
+ additionalHttpErrorStatusCodes: import("ow").ArrayPredicate<number>;
582
+ // @ts-ignore optional peer dependency or compatibility with es2022
583
+ ignoreHttpErrorStatusCodes: import("ow").ArrayPredicate<number>;
584
+ // @ts-ignore optional peer dependency or compatibility with es2022
585
+ blockedStatusCodes: import("ow").ArrayPredicate<number>;
401
586
  // @ts-ignore optional peer dependency or compatibility with es2022
402
587
  retryOnBlocked: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
403
588
  // @ts-ignore optional peer dependency or compatibility with es2022
404
- respectRobotsTxtFile: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
589
+ respectRobotsTxtFile: import("ow").AnyPredicate<boolean | object>;
405
590
  // @ts-ignore optional peer dependency or compatibility with es2022
406
591
  onSkippedRequest: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
407
592
  // @ts-ignore optional peer dependency or compatibility with es2022
408
593
  httpClient: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
594
+ // @ts-ignore optional peer dependency or compatibility with es2022
595
+ configuration: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
596
+ // @ts-ignore optional peer dependency or compatibility with es2022
597
+ storageBackend: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
598
+ // @ts-ignore optional peer dependency or compatibility with es2022
599
+ eventManager: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
600
+ // @ts-ignore optional peer dependency or compatibility with es2022
601
+ logger: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
409
602
  // @ts-ignore optional peer dependency or compatibility with es2022
410
603
  minConcurrency: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
411
604
  // @ts-ignore optional peer dependency or compatibility with es2022
@@ -414,17 +607,47 @@ export declare class BasicCrawler<Context extends CrawlingContext = BasicCrawlin
414
607
  maxRequestsPerMinute: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
415
608
  // @ts-ignore optional peer dependency or compatibility with es2022
416
609
  keepAlive: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
417
- // @ts-ignore optional peer dependency or compatibility with es2022
418
- log: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
419
- // @ts-ignore optional peer dependency or compatibility with es2022
420
- experiments: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
421
610
  // @ts-ignore optional peer dependency or compatibility with es2022
422
611
  statisticsOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
612
+ // @ts-ignore optional peer dependency or compatibility with es2022
613
+ id: import("ow").StringPredicate & import("ow").BasePredicate<string | undefined>;
423
614
  };
424
615
  /**
425
616
  * All `BasicCrawler` parameters are passed via an options object.
426
617
  */
427
- constructor(options?: BasicCrawlerOptions<Context>, config?: Configuration);
618
+ constructor(options?: BasicCrawlerOptions<Context, ContextExtension, ExtendedContext, Routes> & RequireContextPipeline<CrawlingContext, Context>);
619
+ /**
620
+ * Builds the crawler-owned default {@link ConcurrencySystem} from the resolved
621
+ * `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts. Not called when a
622
+ * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} was injected.
623
+ *
624
+ * Subclasses may override this to tune the default system (e.g. {@link HttpCrawler} raises the starting
625
+ * concurrency and relaxes the event loop signal) while still honouring the user's shortcuts.
626
+ */
627
+ protected createDefaultConcurrencySystem(options: ConcurrencySystemOptions): ConcurrencySystem;
628
+ /**
629
+ * Determines if the given HTTP status code is an error status code given
630
+ * the default behaviour and user-set preferences.
631
+ * @param status
632
+ * @returns `true` if the status code is considered an error, `false` otherwise
633
+ */
634
+ protected isErrorStatusCode(status: number): boolean;
635
+ /**
636
+ * Builds the basic context pipeline that transforms `{ request }` into a full `CrawlingContext`.
637
+ * This handles base context creation, session resolution, and context helpers.
638
+ */
639
+ private buildBasicContextPipeline;
640
+ private checkRobotsTxt;
641
+ /**
642
+ * Builds the subclass-specific context pipeline that transforms a `CrawlingContext` into the crawler's target context type.
643
+ * Subclasses should override this to add their own pipeline stages.
644
+ */
645
+ protected buildContextPipeline(): ContextPipeline<CrawlingContext, CrawlingContext>;
646
+ private createBaseContext;
647
+ private resolveRequest;
648
+ private resolveSession;
649
+ private createContextHelpers;
650
+ private buildFinalContextPipeline;
428
651
  /**
429
652
  * Checks if the given error is a proxy error by comparing its message to a list of known proxy error messages.
430
653
  * Used for retrying requests that failed due to proxy errors.
@@ -433,19 +656,20 @@ export declare class BasicCrawler<Context extends CrawlingContext = BasicCrawlin
433
656
  */
434
657
  protected isProxyError(error: Error): boolean;
435
658
  /**
436
- * Checks whether the given crawling context is getting blocked by anti-bot protection using several heuristics.
437
- * Returns `false` if the request is not blocked, otherwise returns a string with a description of the block reason.
438
- * @param _crawlingContext The crawling context to check.
439
- */
440
- protected isRequestBlocked(_crawlingContext: Context): Promise<string | false>;
441
- /**
659
+ * Sets the status message for the current crawler run.
660
+ *
442
661
  * This method is periodically called by the crawler, every `statusMessageLoggingInterval` seconds.
662
+ *
663
+ * The message is logged and broadcast via the {@link EventType.STATUS_MESSAGE|`statusMessage`}
664
+ * event. Integrations such as the Apify SDK subscribe to that event and forward the message to
665
+ * their status-reporting backend (e.g. the Apify platform).
443
666
  */
444
- setStatusMessage(message: string, options?: SetStatusMessageOptions): Promise<void>;
667
+ setStatusMessage(message: string, options?: SetStatusMessageOptions): void;
445
668
  private getPeriodicLogger;
446
669
  /**
447
- * Runs the crawler. Returns a promise that resolves once all the requests are processed
448
- * and `autoscaledPool.isFinished` returns `true`.
670
+ * Runs the crawler. Returns a promise that resolves once every request has been processed and the crawler's
671
+ * finished-check ({@link BasicCrawlerOptions.taskLoopOptions|`taskLoopOptions.isFinishedFunction`}, or the
672
+ * default "the request manager is empty") reports that the crawl is over.
449
673
  *
450
674
  * We can use the `requests` parameter to enqueue the initial requests — it is a shortcut for
451
675
  * running {@link BasicCrawler.addRequests|`crawler.addRequests()`} before {@link BasicCrawler.run|`crawler.run()`}.
@@ -453,15 +677,66 @@ export declare class BasicCrawler<Context extends CrawlingContext = BasicCrawlin
453
677
  * @param [requests] The requests to add.
454
678
  * @param [options] Options for the request queue.
455
679
  */
456
- run(requests?: (string | Request | RequestOptions)[], options?: CrawlerRunOptions): Promise<FinalStatistics>;
680
+ run(requests?: TypedRequestsLike<Routes>, options?: CrawlerRunOptions): Promise<FinalStatistics>;
457
681
  /**
458
682
  * Gracefully stops the current run of the crawler.
459
683
  *
460
684
  * All the tasks active at the time of calling this method will be allowed to finish.
685
+ *
686
+ * To stop the crawler immediately, use {@link BasicCrawler.teardown|`crawler.teardown()`} instead.
687
+ */
688
+ stop(reason?: string): void;
689
+ /**
690
+ * Stops dispatching new requests, letting the in-progress ones finish. Resolves once they have settled, or rejects
691
+ * after `timeoutSecs` if they take too long. Unlike {@link BasicCrawler.stop|`stop()`}, this does not end the
692
+ * run — {@link BasicCrawler.run|`run()`} stays pending until {@link BasicCrawler.resume|`resume()`}.
693
+ *
694
+ * > *NOTE:* The {@link BasicCrawler.concurrencySystem|concurrency system} keeps monitoring and autoscaling
695
+ * throughout, since a shared one may still be serving other crawlers.
696
+ */
697
+ pause(timeoutSecs?: number): Promise<void>;
698
+ /**
699
+ * Resumes a run suspended with {@link BasicCrawler.pause|`pause()`}, letting the crawler dispatch requests
700
+ * again. A no-op on a crawler that is not paused.
701
+ */
702
+ resume(): void;
703
+ /**
704
+ * Returns the crawler's {@link IRequestManager|request manager}, opening the default {@link RequestQueue}
705
+ * if none has been configured or opened yet.
706
+ */
707
+ getRequestManager(): Promise<IRequestManager>;
708
+ /**
709
+ * @deprecated Use {@link BasicCrawler.getRequestManager|`getRequestManager()`} instead. This returns the
710
+ * crawler's request manager, which is no longer guaranteed to be a {@link RequestQueue}.
461
711
  */
462
- stop(message?: string): void;
463
- getRequestQueue(): Promise<RequestProvider>;
712
+ getRequestQueue(): Promise<IRequestManager>;
713
+ /**
714
+ * Opens the default {@link RequestQueue}, applies the crawler's timeouts to it and records it as the
715
+ * crawler-owned queue (so it gets purged between repeated `run()` calls).
716
+ * @private
717
+ */
718
+ private openOwnedRequestQueue;
719
+ /**
720
+ * Tells a request manager how long we expect to hold a fetched request, so that one backed by a
721
+ * locking storage backend keeps it reserved for slightly longer than the request handler timeout
722
+ * (with some padding for overhead), but never for less than a minute. This prevents a long-running
723
+ * request from being handed out a second time while it is still being processed — and it works
724
+ * regardless of whether the manager is a plain {@link RequestQueue} or a `RequestManagerTandem`.
725
+ */
726
+ private applyRequestManagerTimeouts;
727
+ /**
728
+ * Validates a request source's `userData` against the {@link RouteSchemas|Standard Schema} registered
729
+ * for its label on the crawler's schema-router (if any), throwing a {@link RequestValidationError} on
730
+ * mismatch. A no-op when the user's request handler is not a schema-router, or no schema is registered for
731
+ * the request's label. Applied by the crawler on the add paths it owns — `crawler.addRequests`,
732
+ * `crawler.run`, `context.addRequests` and `context.enqueueLinks`.
733
+ */
734
+ private validateRequestUserData;
464
735
  useState<State extends Dictionary = Dictionary>(defaultValue?: State): Promise<State>;
736
+ protected getPendingRequestCountApproximation(): Promise<number>;
737
+ protected calculateEnqueuedRequestLimit(explicitLimit?: number): Promise<number | undefined>;
738
+ protected handleSkippedRequest(options: Parameters<SkippedRequestCallback>[0]): Promise<void>;
739
+ private logOncePerRun;
465
740
  /**
466
741
  * Adds requests to the queue in batches. By default, it will resolve after the initial batch is added, and continue
467
742
  * adding the rest in background. You can configure the batch size via `batchSize` option and the sleep time in between
@@ -473,15 +748,15 @@ export declare class BasicCrawler<Context extends CrawlingContext = BasicCrawlin
473
748
  * @param requests The requests to add
474
749
  * @param options Options for the request queue
475
750
  */
476
- addRequests(requests: (string | Source)[], options?: CrawlerAddRequestsOptions): Promise<CrawlerAddRequestsResult>;
751
+ addRequests(requests: ReadonlyDeep<TypedRequestsLike<Routes>>, options?: CrawlerAddRequestsOptions): Promise<CrawlerAddRequestsResult>;
477
752
  /**
478
753
  * Pushes data to the specified {@link Dataset}, or the default crawler {@link Dataset} by calling {@link Dataset.pushData}.
479
754
  */
480
- pushData(data: Parameters<Dataset['pushData']>[0], datasetIdOrName?: string): Promise<void>;
755
+ pushData(data: Parameters<Dataset['pushData']>[0], datasetIdentifier?: string | StorageIdentifier): Promise<void>;
481
756
  /**
482
757
  * Retrieves the specified {@link Dataset}, or the default crawler {@link Dataset}.
483
758
  */
484
- getDataset(idOrName?: string): Promise<Dataset>;
759
+ getDataset(identifier?: string | StorageIdentifier): Promise<Dataset>;
485
760
  /**
486
761
  * Retrieves data from the default crawler {@link Dataset} by calling {@link Dataset.getData}.
487
762
  */
@@ -491,85 +766,115 @@ export declare class BasicCrawler<Context extends CrawlingContext = BasicCrawlin
491
766
  * Supported formats are currently 'json' and 'csv', and will be inferred from the `path` automatically.
492
767
  */
493
768
  exportData<Data>(path: string, format?: 'json' | 'csv', options?: DatasetExportOptions): Promise<Data[]>;
769
+ /**
770
+ * Initializes the crawler.
771
+ */
494
772
  protected _init(): Promise<void>;
495
- protected _runRequestHandler(crawlingContext: Context): Promise<void>;
773
+ /**
774
+ * The navigation timeout (pre-navigation hooks, navigation, and post-navigation hooks) in milliseconds, used
775
+ * to size the internal request timeout. `BasicCrawler` has no navigation phase, so this is 0; the HTTP and
776
+ * browser crawlers override it with their `navigationTimeoutSecs`.
777
+ */
778
+ protected getNavigationTimeoutMillis(): number;
779
+ /**
780
+ * Races the request against the internal timeout (see {@link raceWithTimeout}), sized to outlast the phases
781
+ * that have their own timeout - the navigation, its hooks, and the request handler - so a legitimately slow
782
+ * request, a per-route override, or a low `CRAWLEE_INTERNAL_TIMEOUT` is not cut short mid-phase. It takes
783
+ * whichever is larger: the configured internal timeout, or this request's combined phase budget.
784
+ */
785
+ private withRequestTimeout;
786
+ /**
787
+ * The request handler timeout for a request with the given route label. A router route may override the
788
+ * crawler's own `requestHandlerTimeoutSecs`; anything else falls back to `fallbackMillis`.
789
+ *
790
+ * @param label The request's route label, or `undefined` for the default route / no specific request.
791
+ * @param fallbackMillis Timeout to use when no route overrides it.
792
+ */
793
+ private resolveRequestHandlerTimeoutMillis;
794
+ /**
795
+ * The timeout the router route with the given label asked for, or `undefined` when it did not override one
796
+ * (or the request handler is not a router at all).
797
+ */
798
+ private getRouteTimeoutMillis;
799
+ protected runRequestHandler(crawlingContext: ExtendedContext): Promise<void>;
496
800
  /**
497
801
  * Handles blocked request
498
802
  */
499
- protected _throwOnBlockedRequest(session: Session, statusCode: number): void;
803
+ protected _throwOnBlockedRequest(statusCode: number): void;
500
804
  private isAllowedBasedOnRobotsTxtFile;
501
805
  protected getRobotsTxtFileForUrl(url: string): Promise<RobotsTxtFile | undefined>;
502
- protected _pauseOnMigration(): Promise<void>;
806
+ private pauseOnMigration;
503
807
  /**
504
- * Fetches request from either RequestList or RequestQueue. If request comes from a RequestList
505
- * and RequestQueue is present then enqueues it to the queue first.
808
+ * Fetches the next request to process from the underlying request provider.
506
809
  */
507
- protected _fetchNextRequest(): Promise<Request<Dictionary> | null | undefined>;
508
- /**
509
- * Executed when `errorHandler` finishes or the request is successful.
510
- * Can be used to clean up orphaned browser pages.
511
- */
512
- protected _cleanupContext(_crawlingContext: Context): Promise<void>;
810
+ private fetchNextRequest;
513
811
  /**
514
812
  * Delays processing of the request based on the `sameDomainDelaySecs` option,
515
813
  * adding it back to the queue after the timeout passes. Returns `true` if the request
516
814
  * should be ignored and will be reclaimed to the queue once ready.
517
815
  */
518
- protected delayRequest(request: Request, source: IRequestList | RequestProvider): boolean;
816
+ private delayRequest;
817
+ /** Handles a single request - runs the request handler with retries, error handling, and lifecycle management. */
818
+ private handleRequest;
819
+ /**
820
+ * Wrapper around the crawling context's `enqueueLinks` method:
821
+ * - Injects `crawlDepth` to each request being added based on the crawling context request.
822
+ * - Provides defaults for the `enqueueLinks` options based on the crawler configuration.
823
+ * - These options can be overridden by the user.
824
+ * @internal
825
+ */
826
+ protected enqueueLinksWithCrawlDepth(options: SetRequired<EnqueueLinksOptions, 'urls'>, request: Request<Dictionary>, requestManager: IRequestManager): Promise<BatchAddRequestsResult>;
519
827
  /**
520
- * Wrapper around requestHandler that fetches requests from RequestList/RequestQueue
521
- * then retries them in a case of an error, etc.
828
+ * Generator function that yields requests injected with the given crawl depth.
829
+ * @internal
522
830
  */
523
- protected _runTaskFunction(): Promise<void>;
831
+ protected addCrawlDepthRequestGenerator(requests: RequestsLike, newRequestDepth: number): AsyncGenerator<Source, void, undefined>;
524
832
  /**
525
- * Run async callback with given timeout and retry.
833
+ * Run async callback with given timeout and retry. Returns the result of the callback.
526
834
  * @ignore
527
835
  */
528
- protected _timeoutAndRetry(handler: () => Promise<unknown>, timeout: number, error: Error | string, maxRetries?: number, retried?: number): Promise<void>;
836
+ private timeoutAndRetry;
529
837
  /**
530
838
  * Returns true if either RequestList or RequestQueue have a request ready for processing.
531
839
  */
532
- protected _isTaskReadyFunction(): Promise<boolean>;
840
+ private isTaskReadyFunction;
533
841
  /**
534
842
  * Returns true if both RequestList and RequestQueue have all requests finished.
535
843
  */
536
- protected _defaultIsFinishedFunction(): Promise<boolean>;
537
- private _rotateSession;
844
+ private defaultIsFinishedFunction;
845
+ /**
846
+ * Unwraps errors thrown by the context pipeline to get the actual user error.
847
+ * RequestHandlerError and ContextPipelineInitializationError wrap the actual error.
848
+ */
849
+ private unwrapError;
538
850
  /**
539
851
  * Handles errors thrown by user provided requestHandler()
852
+ *
853
+ * @param request The request object, passed separately to circumvent potential dynamic logic in crawlingContext.request
540
854
  */
541
- protected _requestFunctionErrorHandler(error: Error, crawlingContext: Context, source: IRequestList | RequestProvider): Promise<void>;
542
- protected _tagUserHandlerError<T>(cb: () => unknown): Promise<T>;
543
- protected _handleFailedRequestHandler(crawlingContext: Context, error: Error): Promise<void>;
855
+ private requestFunctionErrorHandler;
856
+ private handleFailedRequestHandler;
544
857
  /**
545
858
  * Resolves the most verbose error message from a thrown error
546
859
  * @param error The error received
547
860
  * @returns The message to be logged
548
861
  */
549
862
  protected _getMessageFromError(error: Error, forceStack?: boolean): string | TimeoutError | undefined;
550
- protected _canRequestBeRetried(request: Request, error: Error): boolean;
551
- /**
552
- * Updates handledRequestsCount from possibly stored counts,
553
- * usually after worker migration. Since one of the stores
554
- * needs to have priority when both are present,
555
- * it is the request queue, because generally, the request
556
- * list will first be dumped into the queue and then left
557
- * empty.
558
- */
559
- protected _loadHandledRequestCount(): Promise<void>;
560
- protected _executeHooks<HookLike extends (...args: any[]) => Awaitable<void>>(hooks: HookLike[], ...args: Parameters<HookLike>): Promise<void>;
863
+ private canRequestBeRetried;
561
864
  /**
562
- * Function for cleaning up after all request are processed.
563
- * @ignore
865
+ * Stops the crawler immediately.
866
+ *
867
+ * This method doesn't wait for currently active requests to finish.
868
+ *
869
+ * To stop the crawler gracefully (waiting for all running requests to finish), use {@link BasicCrawler.stop|`crawler.stop()`} instead.
564
870
  */
565
871
  teardown(): Promise<void>;
566
872
  protected _getCookieHeaderFromRequest(request: Request): string;
567
- private _getRequestQueue;
568
- protected requestMatchesEnqueueStrategy(request: Request): boolean;
873
+ private requestMatchesEnqueueStrategy;
569
874
  }
570
875
  export interface CreateContextOptions {
571
876
  request: Request;
572
- session?: Session;
877
+ session: ISession;
573
878
  proxyInfo?: ProxyInfo;
574
879
  }
575
880
  export interface CrawlerAddRequestsOptions extends AddRequestsBatchedOptions {
@@ -578,9 +883,14 @@ export interface CrawlerAddRequestsResult extends AddRequestsBatchedResult {
578
883
  }
579
884
  export interface CrawlerRunOptions extends CrawlerAddRequestsOptions {
580
885
  /**
581
- * Whether to purge the RequestQueue before running the crawler again. Defaults to true, so it is possible to reprocess failed requests.
582
- * When disabled, only new requests will be considered. Note that even a failed request is considered as handled.
583
- * @default true
886
+ * Controls whether the request queue is purged between repeated `run()` calls on the same crawler instance.
887
+ * Purging clears all requests and resets internal counters, allowing the same URLs to be processed again.
888
+ *
889
+ * - **`undefined`** (default) — only the crawler's own (auto-created) queue is purged.
890
+ * A user-supplied `requestQueue` is left untouched.
891
+ * - **`true`** — the queue is always purged, even if it was supplied by the user.
892
+ * - **`false`** — nothing is purged. Only genuinely new requests will be processed;
893
+ * note that even a failed request is considered handled.
584
894
  */
585
895
  purgeRequestQueue?: boolean;
586
896
  }
@@ -608,5 +918,5 @@ export interface CrawlerRunOptions extends CrawlerAddRequestsOptions {
608
918
  * await crawler.run();
609
919
  * ```
610
920
  */
611
- export declare function createBasicRouter<Context extends BasicCrawlingContext = BasicCrawlingContext, UserData extends Dictionary = GetUserDataFromRequest<Context['request']>>(routes?: RouterRoutes<Context, UserData>): RouterHandler<Context>;
612
- //# sourceMappingURL=basic-crawler.d.ts.map
921
+ export declare function createBasicRouter<Context extends BasicCrawlingContext = BasicCrawlingContext, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<Context['request']>>>(routes?: RouterRoutes<Context, Routes>): RouterHandler<Context, Routes>;
922
+ export declare function createBasicRouter<Context extends BasicCrawlingContext = BasicCrawlingContext, UserData extends Dictionary = GetUserDataFromRequest<Context['request']>>(routes?: RouterRoutes<Context, Record<string, UserData>>): RouterHandler<Context, Record<string, UserData>>;