@crawlee/basic 4.0.0-beta.14 → 4.0.0-beta.141

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,25 +1,35 @@
1
- import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, AutoscaledPoolOptions, BaseHttpClient, CrawlingContext, DatasetExportOptions, EnqueueLinksOptions, EventManager, FinalStatistics, GetUserDataFromRequest, IRequestList, IRequestManager, ProxyConfiguration, ProxyInfo, Request, RequestsLike, RouterHandler, RouterRoutes, Session, SessionPoolOptions, SkippedRequestCallback, Source, StatisticsOptions, StatisticState } from '@crawlee/core';
2
- import { AutoscaledPool, Configuration, ContextPipeline, Dataset, RequestProvider, SessionPool, Statistics } from '@crawlee/core';
3
- import type { Awaitable, BatchAddRequestsResult, Dictionary, SetStatusMessageOptions } from '@crawlee/types';
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';
3
+ import { BaseHttpClient } from '@crawlee/http-client';
4
+ import type { Awaitable, Dictionary, ISession, ISessionPool, ProxyInfo, SetStatusMessageOptions, StorageBackend } from '@crawlee/types';
4
5
  import { RobotsTxtFile } from '@crawlee/utils';
5
- import type { ReadonlyDeep, SetRequired } from 'type-fest';
6
- import type { Log } from '@apify/log';
6
+ import type { ReadonlyDeep } from 'type-fest';
7
+ import { z } from 'zod';
7
8
  import { TimeoutError } from '@apify/timeout';
8
9
  export interface BasicCrawlingContext<UserData extends Dictionary = Dictionary> extends CrawlingContext<UserData> {
9
10
  }
11
+ export { navigationDeadlineKey, remainingNavigationWindowMillis } from './request-timeout.js';
10
12
  export type RequestHandler<Context extends CrawlingContext = CrawlingContext> = (inputs: Context) => Awaitable<void>;
11
- export type ErrorHandler<Context extends CrawlingContext = CrawlingContext, ExtendedContext extends Context = Context> = (inputs: Context & Partial<ExtendedContext>, error: Error) => Awaitable<void>;
12
- export interface StatusMessageCallbackParams<Context extends CrawlingContext = BasicCrawlingContext, Crawler extends BasicCrawler<any> = BasicCrawler<Context>> {
13
+ /**
14
+ * An error handler receives the crawling context and the error that was thrown while processing the request.
15
+ *
16
+ * Unlike the {@link RequestHandler}, an error handler may run before the context pipeline has finished
17
+ * building the full context (e.g. when navigation or session setup fails). Therefore only `BaseContext` is
18
+ * guaranteed to be present, while the extra properties added by the pipeline and `extendContext` (the
19
+ * difference between `BaseContext` and `ExtendedContext`) are only available as a `Partial`.
20
+ */
21
+ export type ErrorHandler<BaseContext extends CrawlingContext = CrawlingContext, ExtendedContext extends BaseContext = BaseContext> = (inputs: BaseContext & Partial<ExtendedContext>, error: Error) => Awaitable<void>;
22
+ export interface StatusMessageCallbackParams<Context extends CrawlingContext = BasicCrawlingContext, Crawler extends BasicCrawler<any, any, any, any> = BasicCrawler<Context>> {
13
23
  state: StatisticState;
14
24
  crawler: Crawler;
15
25
  previousState: StatisticState;
16
26
  message: string;
17
27
  }
18
- export type StatusMessageCallback<Context extends CrawlingContext = BasicCrawlingContext, Crawler extends BasicCrawler<any> = BasicCrawler<Context>> = (params: StatusMessageCallbackParams<Context, Crawler>) => Awaitable<void>;
28
+ export type StatusMessageCallback<Context extends CrawlingContext = BasicCrawlingContext, Crawler extends BasicCrawler<any, any, any, any> = BasicCrawler<Context>> = (params: StatusMessageCallbackParams<Context, Crawler>) => Awaitable<void>;
19
29
  export type RequireContextPipeline<DefaultContextType extends CrawlingContext, FinalContextType extends DefaultContextType> = DefaultContextType extends FinalContextType ? {} : {
20
30
  contextPipelineBuilder: () => ContextPipeline<CrawlingContext, FinalContextType>;
21
31
  };
22
- export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingContext, ContextExtension = {}, ExtendedContext extends Context = Context & ContextExtension> {
32
+ 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']>>, StatisticStateExtension extends object = {}> {
23
33
  /**
24
34
  * User-provided function that performs the logic of the crawler. It is called for each URL to crawl.
25
35
  *
@@ -37,9 +47,15 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
37
47
  * The exceptions are logged to the request using the
38
48
  * {@link Request.pushErrorMessage|`Request.pushErrorMessage()`} function.
39
49
  */
40
- requestHandler?: RequestHandler<ExtendedContext>;
50
+ requestHandler?: RouterHandler<ExtendedContext, Routes> | RequestHandler<ExtendedContext>;
41
51
  /**
42
- * Allows the user to extend the crawling context passed to the request handler with custom functionality.
52
+ * Allows the user to extend the crawling context with custom functionality (helpers, references, etc.).
53
+ *
54
+ * `extendContext` runs *before* navigation, so the returned members are visible to the
55
+ * `preNavigationHooks`, `postNavigationHooks`, and the `requestHandler` alike. As a consequence,
56
+ * the `context` passed to `extendContext` is the pre-navigation {@link CrawlingContext} and does
57
+ * **not** include navigation-dependent members (e.g. `page`, `response`, `$`, `body`). If you need
58
+ * those, use a `postNavigationHook` or the `requestHandler` instead.
43
59
  *
44
60
  * **Example usage:**
45
61
  *
@@ -59,7 +75,7 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
59
75
  * });
60
76
  * ```
61
77
  */
62
- extendContext?: (context: Context) => Awaitable<ContextExtension>;
78
+ extendContext?: (context: CrawlingContext) => Awaitable<ContextExtension>;
63
79
  /**
64
80
  * *Intended for BasicCrawler subclasses*. Prepares a context pipeline that transforms the initial crawling context into the shape given by the `Context` type parameter.
65
81
  *
@@ -68,23 +84,25 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
68
84
  contextPipelineBuilder?: () => ContextPipeline<CrawlingContext, Context>;
69
85
  /**
70
86
  * Static list of URLs to be processed.
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()`.
87
+ *
88
+ * @deprecated Use the `requestManager` option instead. To combine a read-only loader (such as a `RequestList`)
89
+ * with a writable queue, build a tandem with {@link IRequestLoader.toTandem|`requestList.toTandem(requestQueue)`}
90
+ * and pass the result as `requestManager`. When both `requestList` and `requestQueue` are provided, they are
91
+ * combined into a tandem automatically.
74
92
  */
75
- requestList?: IRequestList;
93
+ requestList?: IRequestLoader;
76
94
  /**
77
95
  * Dynamic queue of URLs to be processed. This is useful for recursive crawling of websites.
78
- * If not provided, the crawler will open the default request queue when the {@link BasicCrawler.addRequests|`crawler.addRequests()`} function is called.
79
- * > Alternatively, `requests` parameter of {@link BasicCrawler.run|`crawler.run()`} could be used to enqueue the initial requests -
80
- * it is a shortcut for running `crawler.addRequests()` before the `crawler.run()`.
96
+ *
97
+ * @deprecated Use the `requestManager` option instead. A `RequestQueue` is itself a request manager, so you can
98
+ * pass it directly as `requestManager`.
81
99
  */
82
- requestQueue?: RequestProvider;
100
+ requestQueue?: RequestQueue;
83
101
  /**
84
- * Allows explicitly configuring a request manager. Mutually exclusive with the `requestQueue` and `requestList` options.
102
+ * Manager of requests that should be processed by the crawler. Mutually exclusive with the deprecated
103
+ * `requestQueue` and `requestList` options.
85
104
  *
86
- * This enables explicitly configuring the crawler to use `RequestManagerTandem`, for instance.
87
- * If using this, the type of `BasicCrawler.requestQueue` may not be fully compatible with the `RequestProvider` class.
105
+ * If not provided, the crawler will open the default {@link RequestQueue} when it is first needed.
88
106
  */
89
107
  requestManager?: IRequestManager;
90
108
  /**
@@ -113,27 +131,20 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
113
131
  failedRequestHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
114
132
  /**
115
133
  * Specifies the maximum number of retries allowed for a request if its processing fails.
116
- * This includes retries due to navigation errors or errors thrown from user-supplied functions
117
- * (`requestHandler`, `preNavigationHooks`, `postNavigationHooks`).
118
- *
119
- * This limit does not apply to retries triggered by session rotation
120
- * (see {@link BasicCrawlerOptions.maxSessionRotations|`maxSessionRotations`}).
134
+ * This includes retries due to navigation errors, session/proxy errors, or errors thrown from user-supplied
135
+ * functions (`requestHandler`, `preNavigationHooks`, `postNavigationHooks`).
121
136
  * @default 3
122
137
  */
123
138
  maxRequestRetries?: number;
124
139
  /**
125
- * Indicates how much time (in seconds) to wait before crawling another same domain request.
140
+ * Indicates how much time (in seconds) to wait before crawling another same domain request. Subdomains are
141
+ * paced together with the site they belong to.
142
+ *
143
+ * Wraps the crawler's request manager in a {@link ThrottlingRequestManager}; pass one as `requestManager`
144
+ * yourself to configure it further.
126
145
  * @default 0
127
146
  */
128
147
  sameDomainDelaySecs?: number;
129
- /**
130
- * Maximum number of session rotations per request.
131
- * The crawler will automatically rotate the session in case of a proxy error or if it gets blocked by the website.
132
- *
133
- * The session rotations are not counted towards the {@link BasicCrawlerOptions.maxRequestRetries|`maxRequestRetries`} limit.
134
- * @default 10
135
- */
136
- maxSessionRotations?: number;
137
148
  /**
138
149
  * Maximum number of pages that the crawler will open. The crawl will stop when this limit is reached.
139
150
  * This value should always be set in order to prevent infinite loops in misconfigured crawlers.
@@ -147,29 +158,50 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
147
158
  */
148
159
  maxCrawlDepth?: number;
149
160
  /**
150
- * Custom options passed to the underlying {@link AutoscaledPool} constructor.
151
- * > *NOTE:* The {@link AutoscaledPoolOptions.runTaskFunction|`runTaskFunction`}
152
- * option is provided by the crawler and cannot be overridden.
153
- * However, we can provide custom implementations of {@link AutoscaledPoolOptions.isFinishedFunction|`isFinishedFunction`}
154
- * and {@link AutoscaledPoolOptions.isTaskReadyFunction|`isTaskReadyFunction`}.
161
+ * Lets you override the predicates that steer the crawler's task loop: `isTaskReadyFunction` (may another request
162
+ * start?) and `isFinishedFunction` (is the crawl over?). The task itself — fetching a request and running it
163
+ * through the pipeline — is owned by the crawler and cannot be overridden.
164
+ *
165
+ * Concurrency is configured elsewhere — through the `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute`
166
+ * shortcuts, or a {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} for finer control.
167
+ */
168
+ taskLoopOptions?: TaskLoopPredicates;
169
+ /**
170
+ * A pre-configured concurrency governor — the component that decides whether there is free compute for one more
171
+ * task. Typically a {@link ConcurrencySystem}, though any {@link IConcurrencySystem} is accepted. All
172
+ * scaling configuration (min/max/desired concurrency, scaling ratios, `maxTasksPerMinute`, snapshotter tuning)
173
+ * lives on the instance itself.
174
+ *
175
+ * Inject the *same* instance into several concurrent crawlers to cap their **combined** concurrency against a
176
+ * single budget. Each crawler still builds and drives its own {@link AutoscaledPool}; only the load/scaling
177
+ * accounting is shared.
178
+ *
179
+ * Mutually exclusive with the `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts, which configure
180
+ * the default system this one replaces — combining the two throws.
181
+ *
182
+ * You own a supplied system's lifecycle: `start()` it before `run()` (which throws otherwise) and `stop()` it once
183
+ * every crawler borrowing it has finished. The crawler does neither on your behalf.
155
184
  */
156
- autoscaledPoolOptions?: AutoscaledPoolOptions;
185
+ concurrencySystem?: IConcurrencySystem;
157
186
  /**
158
187
  * Sets the minimum concurrency (parallelism) for the crawl. Shortcut for the
159
- * AutoscaledPool {@link AutoscaledPoolOptions.minConcurrency|`minConcurrency`} option.
188
+ * {@link ConcurrencySystemOptions.minConcurrency|`minConcurrency`} option of the crawler's default
189
+ * {@link ConcurrencySystem}.
160
190
  * > *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.
161
191
  * If not sure, it's better to keep the default value and the concurrency will scale up automatically.
162
192
  */
163
193
  minConcurrency?: number;
164
194
  /**
165
195
  * Sets the maximum concurrency (parallelism) for the crawl. Shortcut for the
166
- * AutoscaledPool {@link AutoscaledPoolOptions.maxConcurrency|`maxConcurrency`} option.
196
+ * {@link ConcurrencySystemOptions.maxConcurrency|`maxConcurrency`} option of the crawler's default
197
+ * {@link ConcurrencySystem}.
167
198
  */
168
199
  maxConcurrency?: number;
169
200
  /**
170
201
  * The maximum number of requests per minute the crawler should run.
171
202
  * By default, this is set to `Infinity`, but we can pass any positive, non-zero integer.
172
- * Shortcut for the AutoscaledPool {@link AutoscaledPoolOptions.maxTasksPerMinute|`maxTasksPerMinute`} option.
203
+ * Shortcut for the {@link ConcurrencySystemOptions.maxTasksPerMinute|`maxTasksPerMinute`} option of the
204
+ * crawler's default {@link ConcurrencySystem}.
173
205
  */
174
206
  maxRequestsPerMinute?: number;
175
207
  /**
@@ -179,14 +211,14 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
179
211
  */
180
212
  keepAlive?: boolean;
181
213
  /**
182
- * Basic crawler will initialize the {@link SessionPool} with the corresponding {@link SessionPoolOptions|`sessionPoolOptions`}.
183
- * The session instance will be than available in the {@link BasicCrawlerOptions.requestHandler|`requestHandler`}.
184
- */
185
- useSessionPool?: boolean;
186
- /**
187
- * The configuration options for {@link SessionPool} to use.
214
+ * An existing session pool instance to use. When provided, the crawler will use this pool directly instead of
215
+ * creating a new one, enabling session sharing across multiple crawlers. The crawler will not tear down a shared
216
+ * pool — the caller is responsible for its lifecycle.
217
+ *
218
+ * Accepts the built-in {@link SessionPool} or any object implementing the {@link ISessionPool} interface,
219
+ * so custom session-management strategies can be plugged in.
188
220
  */
189
- sessionPoolOptions?: SessionPoolOptions;
221
+ sessionPool?: ISessionPool;
190
222
  /**
191
223
  * Defines the length of the interval for calling the `setStatusMessage` in seconds.
192
224
  */
@@ -208,6 +240,15 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
208
240
  * ```
209
241
  */
210
242
  statusMessageCallback?: StatusMessageCallback;
243
+ /**
244
+ * HTTP status codes that indicate the session should be retired.
245
+ *
246
+ * A 429 from a domain covered by a {@link ThrottlingRequestManager} is handled as a rate limit before
247
+ * this is consulted, so removing 429 here only affects domains that manager does not cover.
248
+ *
249
+ * @default [401, 403, 429]
250
+ */
251
+ blockedStatusCodes?: number[];
211
252
  /**
212
253
  * If set to `true`, the crawler will automatically try to bypass any detected bot protection.
213
254
  *
@@ -235,256 +276,257 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
235
276
  * 4. or because the {@link BasicCrawlerOptions.maxRequestsPerCrawl|`maxRequestsPerCrawl`} limit has been reached
236
277
  */
237
278
  onSkippedRequest?: SkippedRequestCallback;
238
- /** @internal */
239
- log?: Log;
240
- /**
241
- * Enables experimental features of Crawlee, which can alter the behavior of the crawler.
242
- * WARNING: these options are not guaranteed to be stable and may change or be removed at any time.
243
- */
244
- experiments?: CrawlerExperiments;
245
279
  /**
246
- * Customize the way statistics collecting works, such as logging interval or
247
- * whether to output them to the Key-Value store.
280
+ * A preconfigured statistics instance. When provided, the crawler records into it instead of building its own and
281
+ * will not `reset()` it between `run()` calls. Accepts the built-in {@link Statistics} or any object
282
+ * implementing {@link IStatistics}.
283
+ *
284
+ * Custom fields declared via {@link StatisticsOptions.stateExtension|`stateExtension`} are carried over to
285
+ * {@link BasicCrawler.statistics|`crawler.statistics.state`}:
286
+ *
287
+ * ```ts
288
+ * const statistics = new Statistics({ stateExtension: { defaultState: { productsFound: 0 } } });
289
+ *
290
+ * const crawler = new BasicCrawler({
291
+ * statistics,
292
+ * requestHandler: async () => {
293
+ * statistics.state.productsFound++;
294
+ * },
295
+ * });
296
+ *
297
+ * await crawler.run();
298
+ * // the custom fields are typed on `crawler.statistics` too
299
+ * console.log(crawler.statistics.state.productsFound);
300
+ * ```
248
301
  */
249
- statisticsOptions?: StatisticsOptions;
302
+ statistics?: IStatistics<StatisticStateExtension>;
250
303
  /**
251
304
  * HTTP client implementation for the `sendRequest` context helper and for plain HTTP crawling.
252
- * Defaults to a new instance of {@link GotScrapingHttpClient}
305
+ * Defaults to {@link ImpitHttpClient} when `@crawlee/impit-client` is installed, otherwise {@link FetchHttpClient}.
253
306
  */
254
307
  httpClient?: BaseHttpClient;
255
308
  /**
256
309
  * If set, the crawler will be configured for all connections to use
257
310
  * the Proxy URLs provided and rotated according to the configuration.
258
311
  */
259
- proxyConfiguration?: ProxyConfiguration;
260
- }
261
- /**
262
- * A set of options that you can toggle to enable experimental features in Crawlee.
263
- *
264
- * NOTE: These options will not respect semantic versioning and may be removed or changed at any time. Use at your own risk.
265
- * If you do use these and encounter issues, please report them to us.
266
- */
267
- export interface CrawlerExperiments {
312
+ proxyConfiguration?: IProxyConfiguration;
268
313
  /**
269
- * @deprecated This experiment is now enabled by default, and this flag will be removed in a future release.
270
- * If you encounter issues due to this change, please:
271
- * - report it to us: https://github.com/apify/crawlee
272
- * - set `requestLocking` to `false` in the `experiments` option of the crawler
314
+ * Custom configuration to use for this crawler.
315
+ * If provided, the crawler will use its own ServiceLocator instance instead of the global one.
273
316
  */
274
- requestLocking?: boolean;
275
- }
276
- /**
277
- * Provides a simple framework for parallel crawling of web pages.
278
- * The URLs to crawl are fed either from a static list of URLs
279
- * or from a dynamic queue of URLs enabling recursive crawling of websites.
280
- *
281
- * `BasicCrawler` is a low-level tool that requires the user to implement the page
282
- * download and data extraction functionality themselves.
283
- * If we want a crawler that already facilitates this functionality,
284
- * we should consider using {@link CheerioCrawler}, {@link PuppeteerCrawler} or {@link PlaywrightCrawler}.
285
- *
286
- * `BasicCrawler` invokes the user-provided {@link BasicCrawlerOptions.requestHandler|`requestHandler`}
287
- * for each {@link Request} object, which represents a single URL to crawl.
288
- * The {@link Request} objects are fed from the {@link RequestList} or {@link RequestQueue}
289
- * instances provided by the {@link BasicCrawlerOptions.requestList|`requestList`} or {@link BasicCrawlerOptions.requestQueue|`requestQueue`}
290
- * constructor options, respectively. If neither `requestList` nor `requestQueue` options are provided,
291
- * the crawler will open the default request queue either when the {@link BasicCrawler.addRequests|`crawler.addRequests()`} function is called,
292
- * or if `requests` parameter (representing the initial requests) of the {@link BasicCrawler.run|`crawler.run()`} function is provided.
293
- *
294
- * If both {@link BasicCrawlerOptions.requestList|`requestList`} and {@link BasicCrawlerOptions.requestQueue|`requestQueue`} options are used,
295
- * the instance first processes URLs from the {@link RequestList} and automatically enqueues all of them
296
- * to the {@link RequestQueue} before it starts their processing. This ensures that a single URL is not crawled multiple times.
297
- *
298
- * The crawler finishes if there are no more {@link Request} objects to crawl.
299
- *
300
- * New requests are only dispatched when there is enough free CPU and memory available,
301
- * using the functionality provided by the {@link AutoscaledPool} class.
302
- * All {@link AutoscaledPool} configuration options can be passed to the {@link BasicCrawlerOptions.autoscaledPoolOptions|`autoscaledPoolOptions`}
303
- * parameter of the `BasicCrawler` constructor.
304
- * For user convenience, the {@link AutoscaledPoolOptions.minConcurrency|`minConcurrency`} and
305
- * {@link AutoscaledPoolOptions.maxConcurrency|`maxConcurrency`} options of the
306
- * underlying {@link AutoscaledPool} constructor are available directly in the `BasicCrawler` constructor.
307
- *
308
- * **Example usage:**
309
- *
310
- * ```javascript
311
- * import { BasicCrawler, Dataset } from 'crawlee';
312
- *
313
- * // Create a crawler instance
314
- * const crawler = new BasicCrawler({
315
- * async requestHandler({ request, sendRequest }) {
316
- * // 'request' contains an instance of the Request class
317
- * // Here we simply fetch the HTML of the page and store it to a dataset
318
- * const { body } = await sendRequest({
319
- * url: request.url,
320
- * method: request.method,
321
- * body: request.payload,
322
- * headers: request.headers,
323
- * });
324
- *
325
- * await Dataset.pushData({
326
- * url: request.url,
327
- * html: body,
328
- * })
329
- * },
330
- * });
331
- *
332
- * // Enqueue the initial requests and run the crawler
333
- * await crawler.run([
334
- * 'http://www.example.com/page-1',
335
- * 'http://www.example.com/page-2',
336
- * ]);
337
- * ```
338
- * @category Crawlers
339
- */
340
- export declare class BasicCrawler<Context extends CrawlingContext = CrawlingContext, ContextExtension = {}, ExtendedContext extends Context = Context & ContextExtension> {
341
- readonly config: Configuration;
342
- protected static readonly CRAWLEE_STATE_KEY = "CRAWLEE_STATE";
317
+ configuration?: Configuration;
343
318
  /**
344
- * A reference to the underlying {@link Statistics} class that collects and logs run statistics for requests.
319
+ * Custom storage backend to use for this crawler.
320
+ * If provided, the crawler will use its own ServiceLocator instance instead of the global one.
345
321
  */
346
- readonly stats: Statistics;
322
+ storageBackend?: StorageBackend;
347
323
  /**
348
- * A reference to the underlying {@link RequestList} class that manages the crawler's {@link Request|requests}.
349
- * Only available if used by the crawler.
324
+ * Custom event manager to use for this crawler.
325
+ * If provided, the crawler will use its own ServiceLocator instance instead of the global one.
350
326
  */
351
- requestList?: IRequestList;
327
+ eventManager?: EventManager;
352
328
  /**
353
- * Dynamic queue of URLs to be processed. This is useful for recursive crawling of websites.
354
- * A reference to the underlying {@link RequestQueue} class that manages the crawler's {@link Request|requests}.
355
- * Only available if used by the crawler.
329
+ * Custom logger to use for this crawler.
330
+ * If provided, the crawler will use its own ServiceLocator instance instead of the global one.
331
+ */
332
+ logger?: CrawleeLogger;
333
+ /**
334
+ * A unique identifier for the crawler instance. This ID is used to isolate the state returned by
335
+ * {@link BasicCrawler.useState|`crawler.useState()`} from other crawler instances.
336
+ *
337
+ * When multiple crawler instances use `useState()` without an explicit `id`, they will share the same
338
+ * state object for backward compatibility. A warning will be logged in this case.
339
+ *
340
+ * To ensure each crawler has its own isolated state that also persists across script restarts
341
+ * (e.g., during Apify migrations), provide a stable, unique ID for each crawler instance.
342
+ *
343
+ */
344
+ id?: string;
345
+ /**
346
+ * Makes the storage writes performed while handling a request atomic with respect to the request
347
+ * succeeding: they are recorded in a {@link StorageTransaction} spanning the whole request
348
+ * lifecycle and only applied when the request handler succeeds, so a thrown handler leaves no partial
349
+ * writes behind and a retry does not double-write. Reads within the handler see its own writes.
350
+ *
351
+ * `false` disables the mechanism entirely; an object overrides the per-storage-type
352
+ * {@link StorageWritePolicy} (e.g. `{ requestQueue: 'deferred' }` for all-or-nothing enqueues).
353
+ * {@link withDirectStorageAccess} is the per-call-site escape hatch; `useState()` is deliberately
354
+ * *not* transactional.
355
+ *
356
+ * @default true
356
357
  */
357
- requestQueue?: RequestProvider;
358
+ transactionalStorage?: boolean | Partial<StorageWritePolicy>;
358
359
  /**
359
- * The main request-handling component of the crawler. It's initialized during the crawler startup.
360
+ * An array of HTTP response [Status Codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) to be excluded from error consideration.
361
+ * By default, status codes >= 500 trigger errors.
362
+ */
363
+ ignoreHttpErrorStatusCodes?: number[];
364
+ /**
365
+ * An array of additional HTTP response [Status Codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) to be treated as errors.
366
+ * By default, status codes >= 500 trigger errors.
367
+ */
368
+ additionalHttpErrorStatusCodes?: number[];
369
+ }
370
+ 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 = {}> {
371
+ #private;
372
+ /**
373
+ * Tracks the number of crawler instances created. The first crawler uses the default
374
+ * request queue; subsequent ones get their own queue via a unique alias so they don't
375
+ * collide.
376
+ */
377
+ private static instanceCount;
378
+ /**
379
+ * The statistics instance collecting the crawler's run statistics - either the injected `statistics` option or a
380
+ * crawler-built default. Typed as {@link IStatistics} so custom implementations can be plugged in.
381
+ */
382
+ get statistics(): IStatistics<StatisticStateExtension>;
383
+ /**
384
+ * The main request-handling component of the crawler. It manages the requests that the crawler processes,
385
+ * combining any provided request loader and/or queue. It's initialized during the crawler startup or lazily
386
+ * via {@link BasicCrawler.getRequestManager|`getRequestManager()`}.
360
387
  */
361
388
  protected requestManager?: IRequestManager;
362
389
  /**
363
- * A reference to the underlying {@link SessionPool} class that manages the crawler's {@link Session|sessions}.
364
- * Only available if used by the crawler.
390
+ * A reference to the underlying session pool that manages the crawler's {@link Session|sessions}. Typed as
391
+ * {@link ISessionPool} so custom implementations can be plugged in via the `sessionPool` constructor option.
365
392
  */
366
- sessionPool?: SessionPool;
393
+ get sessionPool(): ISessionPool;
367
394
  /**
368
- * A reference to the underlying {@link AutoscaledPool} class that manages the concurrency of the crawler.
369
- * > *NOTE:* This property is only initialized after calling the {@link BasicCrawler.run|`crawler.run()`} function.
370
- * We can use it to change the concurrency settings on the fly,
371
- * to pause the crawler by calling {@link AutoscaledPool.pause|`autoscaledPool.pause()`}
372
- * or to abort it by calling {@link AutoscaledPool.abort|`autoscaledPool.abort()`}.
395
+ * The concurrency governor this run is booking its requests against either the
396
+ * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} that was injected, or the default the
397
+ * crawler built for itself. Read it for telemetry: `desiredConcurrency`, `currentConcurrency`, `isRunning`.
398
+ *
399
+ * > *NOTE:* `undefined` until {@link BasicCrawler.run|`crawler.run()`} has resolved it. A crawler-owned default
400
+ * is also rebuilt for every run, so the instance is not stable across runs.
401
+ *
402
+ * {@link IConcurrencySystem} is deliberately read-only. Tuning concurrency *while a crawl is running* means
403
+ * owning the instance: build a {@link ConcurrencySystem} yourself and inject it, then set
404
+ * `minConcurrency`/`maxConcurrency`/`desiredConcurrency` on your own reference.
373
405
  */
374
- autoscaledPool?: AutoscaledPool;
406
+ get concurrencySystem(): IConcurrencySystem | undefined;
375
407
  /**
376
- * A reference to the underlying {@link ProxyConfiguration} class that manages the crawler's proxies.
408
+ * A reference to the underlying {@link IProxyConfiguration} instance that manages the crawler's proxies.
377
409
  * Only available if used by the crawler.
378
410
  */
379
- proxyConfiguration?: ProxyConfiguration;
411
+ readonly proxyConfiguration?: IProxyConfiguration;
380
412
  /**
381
413
  * Default {@link Router} instance that will be used if we don't specify any {@link BasicCrawlerOptions.requestHandler|`requestHandler`}.
382
414
  * See {@link Router.addHandler|`router.addHandler()`} and {@link Router.addDefaultHandler|`router.addDefaultHandler()`}.
383
415
  */
384
- readonly router: RouterHandler<Context>;
385
- private contextPipelineBuilder;
386
- private _contextPipeline?;
416
+ readonly router: RouterHandler<Context, Routes>;
417
+ /**
418
+ * The basic part of the context pipeline. Unlike the subclass pipeline, this
419
+ * part has no major side effects (e.g. launching a browser). It also makes typing more explicit, as subclass
420
+ * pipelines expect the basic crawler fields to already be present in the context at runtime.
421
+ *
422
+ * Context built with this pipeline can be passed into multiple crawler pipelines at once.
423
+ * This is used e.g. in the {@link AdaptivePlaywrightCrawler|`AdaptivePlaywrightCrawler`}.
424
+ */
425
+ get basicContextPipeline(): ContextPipeline<{
426
+ request: Request;
427
+ }, CrawlingContext>;
387
428
  get contextPipeline(): ContextPipeline<CrawlingContext, ExtendedContext>;
388
429
  running: boolean;
389
430
  hasFinishedBefore: boolean;
390
- readonly log: Log;
391
- protected requestHandler: RequestHandler<ExtendedContext>;
392
- protected errorHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
393
- protected failedRequestHandler?: ErrorHandler<CrawlingContext, ExtendedContext>;
394
- protected requestHandlerTimeoutMillis: number;
395
- protected internalTimeoutMillis: number;
396
- protected maxRequestRetries: number;
397
- protected maxCrawlDepth?: number;
398
- protected sameDomainDelayMillis: number;
399
- protected domainAccessedTime: Map<string, number>;
400
- protected maxSessionRotations: number;
401
- protected maxRequestsPerCrawl?: number;
402
- protected handledRequestsCount: number;
403
- protected statusMessageLoggingInterval: number;
404
- protected statusMessageCallback?: StatusMessageCallback;
405
- protected sessionPoolOptions: SessionPoolOptions;
406
- protected useSessionPool: boolean;
407
- protected autoscaledPoolOptions: AutoscaledPoolOptions;
408
- protected events: EventManager;
409
- protected httpClient: BaseHttpClient;
410
- protected retryOnBlocked: boolean;
411
- protected respectRobotsTxtFile: boolean | {
412
- userAgent?: string;
413
- };
414
- protected onSkippedRequest?: SkippedRequestCallback;
415
- private _closeEvents?;
416
- private shouldLogMaxProcessedRequestsExceeded;
417
- private shouldLogMaxEnqueuedRequestsExceeded;
418
- private experiments;
419
- private readonly robotsTxtFileCache;
420
- private _experimentWarnings;
431
+ get log(): CrawleeLogger;
432
+ protected readonly requestHandler: RequestHandler<ExtendedContext>;
433
+ private requestHandlerTimeoutMillis;
434
+ protected readonly internalTimeoutMillis: number;
435
+ private get handledRequestsCount();
436
+ protected blockedStatusCodes: Set<number>;
437
+ protected readonly additionalHttpErrorStatusCodes: Set<number>;
438
+ /**
439
+ * The resolved options for the crawler's own task loop — the crawler-owned `runTaskFunction`, the (possibly
440
+ * user-overridden) ready/finished predicates and cadence/logging. Concurrency configuration lives on the
441
+ * {@link ConcurrencySystem} instead, and the loop's `consumer` identity is the crawler's own, so neither is
442
+ * settable here.
443
+ */
444
+ private taskLoopOptions;
445
+ protected readonly httpClient: BaseHttpClient;
446
+ protected readonly retryOnBlocked: boolean;
447
+ /**
448
+ * @internal
449
+ */
421
450
  protected static optionsShape: {
422
- // @ts-ignore optional peer dependency or compatibility with es2022
423
- contextPipelineBuilder: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
424
- // @ts-ignore optional peer dependency or compatibility with es2022
425
- extendContext: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
426
- // @ts-ignore optional peer dependency or compatibility with es2022
427
- requestList: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
428
- // @ts-ignore optional peer dependency or compatibility with es2022
429
- requestQueue: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
430
- // @ts-ignore optional peer dependency or compatibility with es2022
431
- requestHandler: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
432
- // @ts-ignore optional peer dependency or compatibility with es2022
433
- requestHandlerTimeoutSecs: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
434
- // @ts-ignore optional peer dependency or compatibility with es2022
435
- errorHandler: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
436
- // @ts-ignore optional peer dependency or compatibility with es2022
437
- failedRequestHandler: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
438
- // @ts-ignore optional peer dependency or compatibility with es2022
439
- maxRequestRetries: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
440
- // @ts-ignore optional peer dependency or compatibility with es2022
441
- sameDomainDelaySecs: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
442
- // @ts-ignore optional peer dependency or compatibility with es2022
443
- maxSessionRotations: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
444
- // @ts-ignore optional peer dependency or compatibility with es2022
445
- maxRequestsPerCrawl: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
446
- // @ts-ignore optional peer dependency or compatibility with es2022
447
- maxCrawlDepth: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
448
- // @ts-ignore optional peer dependency or compatibility with es2022
449
- autoscaledPoolOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
450
- // @ts-ignore optional peer dependency or compatibility with es2022
451
- sessionPoolOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
452
- // @ts-ignore optional peer dependency or compatibility with es2022
453
- useSessionPool: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
454
- // @ts-ignore optional peer dependency or compatibility with es2022
455
- proxyConfiguration: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
456
- // @ts-ignore optional peer dependency or compatibility with es2022
457
- statusMessageLoggingInterval: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
458
- // @ts-ignore optional peer dependency or compatibility with es2022
459
- statusMessageCallback: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
460
- // @ts-ignore optional peer dependency or compatibility with es2022
461
- retryOnBlocked: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
462
- // @ts-ignore optional peer dependency or compatibility with es2022
463
- respectRobotsTxtFile: import("ow").AnyPredicate<boolean | object>;
464
- // @ts-ignore optional peer dependency or compatibility with es2022
465
- onSkippedRequest: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
466
- // @ts-ignore optional peer dependency or compatibility with es2022
467
- httpClient: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
468
- // @ts-ignore optional peer dependency or compatibility with es2022
469
- minConcurrency: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
470
- // @ts-ignore optional peer dependency or compatibility with es2022
471
- maxConcurrency: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
472
- // @ts-ignore optional peer dependency or compatibility with es2022
473
- maxRequestsPerMinute: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
474
- // @ts-ignore optional peer dependency or compatibility with es2022
475
- keepAlive: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
476
- // @ts-ignore optional peer dependency or compatibility with es2022
477
- log: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
478
- // @ts-ignore optional peer dependency or compatibility with es2022
479
- experiments: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
480
- // @ts-ignore optional peer dependency or compatibility with es2022
481
- statisticsOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
451
+ contextPipelineBuilder: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
452
+ extendContext: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
453
+ requestList: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
454
+ requestQueue: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
455
+ requestManager: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
456
+ requestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
457
+ requestHandlerTimeoutSecs: z.ZodOptional<z.ZodCustom<number, number>>;
458
+ errorHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
459
+ failedRequestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
460
+ maxRequestRetries: z.ZodDefault<z.ZodCustom<number, number>>;
461
+ sameDomainDelaySecs: z.ZodDefault<z.ZodCustom<number, number>>;
462
+ maxRequestsPerCrawl: z.ZodOptional<z.ZodCustom<number, number>>;
463
+ maxCrawlDepth: z.ZodOptional<z.ZodCustom<number, number>>;
464
+ taskLoopOptions: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
465
+ concurrencySystem: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
466
+ sessionPool: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
467
+ proxyConfiguration: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
468
+ statusMessageLoggingInterval: z.ZodDefault<z.ZodCustom<number, number>>;
469
+ statusMessageCallback: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
470
+ additionalHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
471
+ ignoreHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
472
+ blockedStatusCodes: z.ZodOptional<z.ZodArray<z.ZodCustom<number, number>>>;
473
+ retryOnBlocked: z.ZodDefault<z.ZodBoolean>;
474
+ respectRobotsTxtFile: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCustom<Dictionary, Dictionary>]>>;
475
+ transactionalStorage: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
476
+ requestQueue: z.ZodOptional<z.ZodEnum<{
477
+ deferred: "deferred";
478
+ writeThrough: "writeThrough";
479
+ }>>;
480
+ }, z.core.$strict>]>>;
481
+ onSkippedRequest: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
482
+ httpClient: z.ZodOptional<z.ZodCustom<BaseHttpClient, BaseHttpClient>>;
483
+ configuration: z.ZodOptional<z.ZodCustom<Configuration, Configuration>>;
484
+ storageBackend: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
485
+ eventManager: z.ZodOptional<z.ZodCustom<EventManager, EventManager>>;
486
+ logger: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
487
+ minConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
488
+ maxConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
489
+ maxRequestsPerMinute: z.ZodOptional<z.ZodCustom<number, number>>;
490
+ keepAlive: z.ZodOptional<z.ZodBoolean>;
491
+ statistics: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
492
+ id: z.ZodOptional<z.ZodString>;
482
493
  };
483
494
  /**
484
495
  * All `BasicCrawler` parameters are passed via an options object.
485
496
  */
486
- constructor(options?: BasicCrawlerOptions<Context, ContextExtension, ExtendedContext> & RequireContextPipeline<CrawlingContext, Context>, // cast because the constructor logic handles missing `contextPipelineBuilder` - the type is just for DX
487
- config?: Configuration);
497
+ constructor(options?: BasicCrawlerOptions<Context, ContextExtension, ExtendedContext, Routes, StatisticStateExtension> & RequireContextPipeline<CrawlingContext, Context>);
498
+ /**
499
+ * Builds the crawler-owned default {@link ConcurrencySystem} from the resolved
500
+ * `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts. Not called when a
501
+ * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} was injected.
502
+ *
503
+ * Subclasses may override this to tune the default system (e.g. {@link HttpCrawler} raises the starting
504
+ * concurrency and relaxes the event loop signal) while still honouring the user's shortcuts.
505
+ */
506
+ protected createDefaultConcurrencySystem(options: ConcurrencySystemOptions): ConcurrencySystem;
507
+ /**
508
+ * Determines if the given HTTP status code is an error status code given
509
+ * the default behaviour and user-set preferences.
510
+ * @param status
511
+ * @returns `true` if the status code is considered an error, `false` otherwise
512
+ */
513
+ protected isErrorStatusCode(status: number): boolean;
514
+ /**
515
+ * Builds the basic context pipeline that transforms `{ request }` into a full `CrawlingContext`.
516
+ * This handles base context creation, session resolution, and context helpers.
517
+ */
518
+ private buildBasicContextPipeline;
519
+ private checkRobotsTxt;
520
+ /**
521
+ * Builds the subclass-specific context pipeline that transforms a `CrawlingContext` into the crawler's target context type.
522
+ * Subclasses should override this to add their own pipeline stages.
523
+ */
524
+ protected buildContextPipeline(): ContextPipeline<CrawlingContext, CrawlingContext>;
525
+ private createBaseContext;
526
+ private resolveRequest;
527
+ private resolveSession;
528
+ private createContextHelpers;
529
+ private buildFinalContextPipeline;
488
530
  /**
489
531
  * Checks if the given error is a proxy error by comparing its message to a list of known proxy error messages.
490
532
  * Used for retrying requests that failed due to proxy errors.
@@ -493,13 +535,20 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
493
535
  */
494
536
  protected isProxyError(error: Error): boolean;
495
537
  /**
538
+ * Sets the status message for the current crawler run.
539
+ *
496
540
  * This method is periodically called by the crawler, every `statusMessageLoggingInterval` seconds.
541
+ *
542
+ * The message is logged and broadcast via the {@link EventType.STATUS_MESSAGE|`statusMessage`}
543
+ * event. Integrations such as the Apify SDK subscribe to that event and forward the message to
544
+ * their status-reporting backend (e.g. the Apify platform).
497
545
  */
498
- setStatusMessage(message: string, options?: SetStatusMessageOptions): Promise<void>;
546
+ setStatusMessage(message: string, options?: SetStatusMessageOptions): void;
499
547
  private getPeriodicLogger;
500
548
  /**
501
- * Runs the crawler. Returns a promise that resolves once all the requests are processed
502
- * and `autoscaledPool.isFinished` returns `true`.
549
+ * Runs the crawler. Returns a promise that resolves once every request has been processed and the crawler's
550
+ * finished-check ({@link BasicCrawlerOptions.taskLoopOptions|`taskLoopOptions.isFinishedFunction`}, or the
551
+ * default "the request manager is empty") reports that the crawl is over.
503
552
  *
504
553
  * We can use the `requests` parameter to enqueue the initial requests — it is a shortcut for
505
554
  * running {@link BasicCrawler.addRequests|`crawler.addRequests()`} before {@link BasicCrawler.run|`crawler.run()`}.
@@ -507,7 +556,7 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
507
556
  * @param [requests] The requests to add.
508
557
  * @param [options] Options for the request queue.
509
558
  */
510
- run(requests?: RequestsLike, options?: CrawlerRunOptions): Promise<FinalStatistics>;
559
+ run(requests?: TypedRequestsLike<Routes>, options?: CrawlerRunOptions): Promise<FinalStatistics>;
511
560
  /**
512
561
  * Gracefully stops the current run of the crawler.
513
562
  *
@@ -515,32 +564,80 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
515
564
  *
516
565
  * To stop the crawler immediately, use {@link BasicCrawler.teardown|`crawler.teardown()`} instead.
517
566
  */
518
- stop(message?: string): void;
519
- getRequestQueue(): Promise<RequestProvider>;
567
+ stop(reason?: string): void;
568
+ /**
569
+ * Stops dispatching new requests, letting the in-progress ones finish. Resolves once they have settled, or rejects
570
+ * after `timeoutSecs` if they take too long. Unlike {@link BasicCrawler.stop|`stop()`}, this does not end the
571
+ * run — {@link BasicCrawler.run|`run()`} stays pending until {@link BasicCrawler.resume|`resume()`}.
572
+ *
573
+ * > *NOTE:* The {@link BasicCrawler.concurrencySystem|concurrency system} keeps monitoring and autoscaling
574
+ * throughout, since a shared one may still be serving other crawlers.
575
+ */
576
+ pause(timeoutSecs?: number): Promise<void>;
577
+ /**
578
+ * Resumes a run suspended with {@link BasicCrawler.pause|`pause()`}, letting the crawler dispatch requests
579
+ * again. A no-op on a crawler that is not paused.
580
+ */
581
+ resume(): void;
582
+ /**
583
+ * Returns the crawler's {@link IRequestManager|request manager}, opening the default {@link RequestQueue}
584
+ * if none has been configured or opened yet.
585
+ */
586
+ getRequestManager(): Promise<IRequestManager>;
587
+ /**
588
+ * @deprecated Use {@link BasicCrawler.getRequestManager|`getRequestManager()`} instead. This returns the
589
+ * crawler's request manager, which is no longer guaranteed to be a {@link RequestQueue}.
590
+ */
591
+ getRequestQueue(): Promise<IRequestManager>;
592
+ /**
593
+ * Opens the default {@link RequestQueue}, applies the crawler's timeouts to it and records it as the
594
+ * crawler-owned queue (so it gets purged between repeated `run()` calls).
595
+ * @private
596
+ */
597
+ private openOwnedRequestQueue;
598
+ /**
599
+ * Tells a request manager how long we expect to hold a fetched request, so that one backed by a
600
+ * locking storage backend keeps it reserved for slightly longer than the request handler timeout
601
+ * (with some padding for overhead), but never for less than a minute. This prevents a long-running
602
+ * request from being handed out a second time while it is still being processed — and it works
603
+ * regardless of whether the manager is a plain {@link RequestQueue} or a `RequestManagerTandem`.
604
+ */
605
+ private applyRequestManagerTimeouts;
606
+ /**
607
+ * Validates a request source's `userData` against the {@link RouteSchemas|Standard Schema} registered
608
+ * for its label on the crawler's schema-router (if any), throwing a {@link RequestValidationError} on
609
+ * mismatch. A no-op when the user's request handler is not a schema-router, or no schema is registered for
610
+ * the request's label. Applied by the crawler on the add paths it owns — `crawler.addRequests`,
611
+ * `crawler.run`, `context.addRequests` and `context.enqueueLinks`.
612
+ */
613
+ private validateRequestUserData;
520
614
  useState<State extends Dictionary = Dictionary>(defaultValue?: State): Promise<State>;
521
- protected get pendingRequestCountApproximation(): number;
522
- protected calculateEnqueuedRequestLimit(explicitLimit?: number): number | undefined;
523
- protected handleSkippedRequest(options: Parameters<SkippedRequestCallback>[0]): Promise<void>;
615
+ private logOncePerRun;
524
616
  /**
525
617
  * Adds requests to the queue in batches. By default, it will resolve after the initial batch is added, and continue
526
618
  * adding the rest in background. You can configure the batch size via `batchSize` option and the sleep time in between
527
619
  * the batches via `waitBetweenBatchesMillis`. If you want to wait for all batches to be added to the queue, you can use
528
620
  * the `waitForAllRequestsToBeAdded` promise you get in the response object.
529
621
  *
622
+ * Optionally, the requests can be filtered using `include`/`exclude` glob or regexp patterns and an
623
+ * enqueue `strategy` (both AND-ed together, same as {@link CrawlingContext.enqueueLinks|`enqueueLinks`}),
624
+ * relative to `baseUrl`. Unlike `enqueueLinks`, there is no implicit "current page" to anchor the strategy
625
+ * to, so `strategy` defaults to {@link EnqueueStrategy.All|`all`} here.
626
+ *
530
627
  * This is an alias for calling `addRequestsBatched()` on the implicit `RequestQueue` for this crawler instance.
531
628
  *
532
629
  * @param requests The requests to add
533
630
  * @param options Options for the request queue
534
631
  */
535
- addRequests(requests: ReadonlyDeep<RequestsLike>, options?: CrawlerAddRequestsOptions): Promise<CrawlerAddRequestsResult>;
632
+ addRequests(requests: ReadonlyDeep<TypedRequestsLike<Routes>>, options?: CrawlerAddRequestsOptions): Promise<CrawlerAddRequestsResult>;
536
633
  /**
537
634
  * Pushes data to the specified {@link Dataset}, or the default crawler {@link Dataset} by calling {@link Dataset.pushData}.
538
635
  */
539
- pushData(data: Parameters<Dataset['pushData']>[0], datasetIdOrName?: string): Promise<void>;
636
+ pushData(data: Parameters<Dataset['pushData']>[0], datasetIdentifier?: string | StorageIdentifier): Promise<void>;
540
637
  /**
541
638
  * Retrieves the specified {@link Dataset}, or the default crawler {@link Dataset}.
542
639
  */
543
- getDataset(idOrName?: string): Promise<Dataset>;
640
+ getDataset(identifier?: string | StorageIdentifier): Promise<Dataset>;
544
641
  /**
545
642
  * Retrieves data from the default crawler {@link Dataset} by calling {@link Dataset.getData}.
546
643
  */
@@ -553,42 +650,68 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
553
650
  /**
554
651
  * Initializes the crawler.
555
652
  */
556
- protected _init(): Promise<void>;
557
- protected runRequestHandler(crawlingContext: CrawlingContext): Promise<void>;
653
+ protected init(): Promise<void>;
558
654
  /**
559
- * Handles blocked request
655
+ * The navigation timeout (pre-navigation hooks, navigation, and post-navigation hooks) in milliseconds, used
656
+ * to size the internal request timeout. `BasicCrawler` has no navigation phase, so this is 0; the HTTP and
657
+ * browser crawlers override it with their `navigationTimeoutSecs`.
560
658
  */
561
- protected _throwOnBlockedRequest(session: Session, statusCode: number): void;
562
- private isAllowedBasedOnRobotsTxtFile;
563
- protected getRobotsTxtFileForUrl(url: string): Promise<RobotsTxtFile | undefined>;
564
- protected _pauseOnMigration(): Promise<void>;
659
+ protected getNavigationTimeoutMillis(): number;
565
660
  /**
566
- * Initializes the RequestManager based on the configured requestList and requestQueue.
661
+ * Races the request against the internal timeout (see {@link raceWithTimeout}), sized to outlast the phases
662
+ * that have their own timeout - the navigation, its hooks, and the request handler - so a legitimately slow
663
+ * request, a per-route override, or a low `CRAWLEE_INTERNAL_TIMEOUT` is not cut short mid-phase. It takes
664
+ * whichever is larger: the configured internal timeout, or this request's combined phase budget.
567
665
  */
568
- private initializeRequestManager;
666
+ private withRequestTimeout;
569
667
  /**
570
- * Fetches the next request to process from the underlying request provider.
668
+ * The request handler timeout for a request with the given route label. A router route may override the
669
+ * crawler's own `requestHandlerTimeoutSecs`; anything else falls back to `fallbackMillis`.
670
+ *
671
+ * @param label The request's route label, or `undefined` for the default route / no specific request.
672
+ * @param fallbackMillis Timeout to use when no route overrides it.
571
673
  */
572
- protected _fetchNextRequest(): Promise<Request<Dictionary> | null>;
674
+ private resolveRequestHandlerTimeoutMillis;
573
675
  /**
574
- * Delays processing of the request based on the `sameDomainDelaySecs` option,
575
- * adding it back to the queue after the timeout passes. Returns `true` if the request
576
- * should be ignored and will be reclaimed to the queue once ready.
676
+ * The timeout the router route with the given label asked for, or `undefined` when it did not override one
677
+ * (or the request handler is not a router at all).
577
678
  */
578
- protected delayRequest(request: Request, source: IRequestList | RequestProvider | IRequestManager): boolean;
679
+ private getRouteTimeoutMillis;
680
+ protected runRequestHandler(crawlingContext: ExtendedContext): Promise<void>;
579
681
  /**
580
- * Wrapper around requestHandler that fetches requests from RequestList/RequestQueue
581
- * then retries them in a case of an error, etc.
682
+ * Runs `callback` inside a {@link StorageTransaction}, unless transactional storage is disabled.
683
+ * Deliberately does **not** commit on return - `handleRequest` swallows request handler failures, so
684
+ * a normal return says nothing about success. `handleRequest` owns the outcome.
582
685
  */
583
- protected _runTaskFunction(): Promise<void>;
686
+ private runInStorageTransaction;
584
687
  /**
585
- * Wrapper around the crawling context's `enqueueLinks` method:
586
- * - Injects `crawlDepth` to each request being added based on the crawling context request.
587
- * - Provides defaults for the `enqueueLinks` options based on the crawler configuration.
588
- * - These options can be overridden by the user.
589
- * @internal
688
+ * Handles blocked request
590
689
  */
591
- protected enqueueLinksWithCrawlDepth(options: SetRequired<EnqueueLinksOptions, 'urls'>, request: Request<Dictionary>, requestQueue: RequestProvider): Promise<BatchAddRequestsResult>;
690
+ protected throwOnBlockedRequest(statusCode: number): void;
691
+ private isAllowedBasedOnRobotsTxtFile;
692
+ /**
693
+ * Records an HTTP 429 against the URL's domain so the request manager can pace the retry.
694
+ *
695
+ * @param retryAfterHeader The raw `Retry-After` response header, if the server sent one.
696
+ * @returns `true` if a manager took responsibility for the delay, in which case the caller should throw
697
+ * {@link RequestThrottledError} rather than treating the response as a blocked session.
698
+ */
699
+ protected recordDomainRateLimit(url: string, retryAfterHeader?: string | null): boolean;
700
+ /**
701
+ * Hands a robots.txt `Crawl-delay` to the request manager, warning if nothing is able to honour it.
702
+ *
703
+ * The warning is driven by whether the delay was actually accepted rather than by the type of the manager,
704
+ * because a manager that does throttle still drops the delay for a domain missing from its `domains` list.
705
+ */
706
+ private applyCrawlDelay;
707
+ protected getRobotsTxtFileForUrl(url: string): Promise<RobotsTxtFile | undefined>;
708
+ private pauseOnMigration;
709
+ /**
710
+ * Fetches the next request to process from the underlying request provider.
711
+ */
712
+ private fetchNextRequest;
713
+ /** Handles a single request - runs the request handler with retries, error handling, and lifecycle management. */
714
+ private handleRequest;
592
715
  /**
593
716
  * Generator function that yields requests injected with the given crawl depth.
594
717
  * @internal
@@ -598,16 +721,15 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
598
721
  * Run async callback with given timeout and retry. Returns the result of the callback.
599
722
  * @ignore
600
723
  */
601
- protected _timeoutAndRetry<T>(handler: () => Promise<T>, timeout: number, error: Error | string, maxRetries?: number, retried?: number): Promise<T>;
724
+ private timeoutAndRetry;
602
725
  /**
603
726
  * Returns true if either RequestList or RequestQueue have a request ready for processing.
604
727
  */
605
- protected _isTaskReadyFunction(): Promise<boolean>;
728
+ private isTaskReadyFunction;
606
729
  /**
607
730
  * Returns true if both RequestList and RequestQueue have all requests finished.
608
731
  */
609
- protected _defaultIsFinishedFunction(): Promise<boolean>;
610
- private _rotateSession;
732
+ private defaultIsFinishedFunction;
611
733
  /**
612
734
  * Unwraps errors thrown by the context pipeline to get the actual user error.
613
735
  * RequestHandlerError and ContextPipelineInitializationError wrap the actual error.
@@ -615,22 +737,23 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
615
737
  private unwrapError;
616
738
  /**
617
739
  * Handles errors thrown by user provided requestHandler()
740
+ *
741
+ * @param request The request object, passed separately to circumvent potential dynamic logic in crawlingContext.request
618
742
  */
619
- protected _requestFunctionErrorHandler(error: Error, crawlingContext: CrawlingContext, source: IRequestList | IRequestManager): Promise<void>;
620
- protected _tagUserHandlerError<T>(cb: () => unknown): Promise<T>;
621
- protected _handleFailedRequestHandler(crawlingContext: CrawlingContext, error: Error): Promise<void>;
743
+ private requestFunctionErrorHandler;
744
+ private handleFailedRequestHandler;
622
745
  /**
623
746
  * Resolves the most verbose error message from a thrown error
624
747
  * @param error The error received
625
748
  * @returns The message to be logged
626
749
  */
627
- protected _getMessageFromError(error: Error, forceStack?: boolean): string | TimeoutError | undefined;
628
- protected _canRequestBeRetried(request: Request, error: Error): boolean;
750
+ protected getMessageFromError(error: Error, forceStack?: boolean): string | TimeoutError | undefined;
629
751
  /**
630
- * Updates handledRequestsCount from possibly stored counts, usually after worker migration.
752
+ * Whether the session should be spared for this error - either because it was already retired, or because the
753
+ * failure says nothing about the session (a rate limit is a property of the domain).
631
754
  */
632
- protected _loadHandledRequestCount(): Promise<void>;
633
- protected _executeHooks<HookLike extends (...args: any[]) => Awaitable<void>>(hooks: HookLike[], ...args: Parameters<HookLike>): Promise<void>;
755
+ private errorAbsolvesSession;
756
+ private canRequestBeRetried;
634
757
  /**
635
758
  * Stops the crawler immediately.
636
759
  *
@@ -639,24 +762,28 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
639
762
  * To stop the crawler gracefully (waiting for all running requests to finish), use {@link BasicCrawler.stop|`crawler.stop()`} instead.
640
763
  */
641
764
  teardown(): Promise<void>;
642
- protected _getCookieHeaderFromRequest(request: Request): string;
643
- private _getRequestQueue;
765
+ protected getCookieHeaderFromRequest(request: Request): string;
644
766
  private requestMatchesEnqueueStrategy;
645
767
  }
646
768
  export interface CreateContextOptions {
647
769
  request: Request;
648
- session?: Session;
770
+ session: ISession;
649
771
  proxyInfo?: ProxyInfo;
650
772
  }
651
- export interface CrawlerAddRequestsOptions extends AddRequestsBatchedOptions {
773
+ export interface CrawlerAddRequestsOptions extends AddRequestsBatchedOptions, EnqueueUrlsOptions {
652
774
  }
653
775
  export interface CrawlerAddRequestsResult extends AddRequestsBatchedResult {
654
776
  }
655
777
  export interface CrawlerRunOptions extends CrawlerAddRequestsOptions {
656
778
  /**
657
- * Whether to purge the RequestQueue before running the crawler again. Defaults to true, so it is possible to reprocess failed requests.
658
- * When disabled, only new requests will be considered. Note that even a failed request is considered as handled.
659
- * @default true
779
+ * Controls whether the request queue is purged between repeated `run()` calls on the same crawler instance.
780
+ * Purging clears all requests and resets internal counters, allowing the same URLs to be processed again.
781
+ *
782
+ * - **`undefined`** (default) — only the crawler's own (auto-created) queue is purged.
783
+ * A user-supplied `requestQueue` is left untouched.
784
+ * - **`true`** — the queue is always purged, even if it was supplied by the user.
785
+ * - **`false`** — nothing is purged. Only genuinely new requests will be processed;
786
+ * note that even a failed request is considered handled.
660
787
  */
661
788
  purgeRequestQueue?: boolean;
662
789
  }
@@ -684,5 +811,5 @@ export interface CrawlerRunOptions extends CrawlerAddRequestsOptions {
684
811
  * await crawler.run();
685
812
  * ```
686
813
  */
687
- export declare function createBasicRouter<Context extends BasicCrawlingContext = BasicCrawlingContext, UserData extends Dictionary = GetUserDataFromRequest<Context['request']>>(routes?: RouterRoutes<Context, UserData>): RouterHandler<Context>;
688
- //# sourceMappingURL=basic-crawler.d.ts.map
814
+ 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>;
815
+ 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>>;