@crawlee/basic 4.0.0-beta.121 → 4.0.0-beta.123

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,9 +1,10 @@
1
- import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, ConcurrencySystemOptions, Configuration, CrawleeLogger, CrawlingContext, DatasetExportOptions, EnqueueLinksOptions, EventManager, FinalStatistics, GetUserDataFromRequest, IConcurrencySystem, IProxyConfiguration, IRequestLoader, IRequestManager, IStatistics, Request, RequestsLike, RouterHandler, RouterRoutes, SkippedRequestCallback, Source, StatisticState, StorageIdentifier, StorageWritePolicy, TaskLoopPredicates, TypedRequestsLike } from '@crawlee/core';
2
- import { ConcurrencySystem, ContextPipeline, Dataset, RequestQueue } from '@crawlee/core';
3
- import type { Awaitable, BaseHttpClient, BatchAddRequestsResult, Dictionary, ISession, ISessionPool, ProxyInfo, SetStatusMessageOptions, StorageBackend } 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 BasePredicate } from 'ow';
6
- import type { ReadonlyDeep, SetRequired } from 'type-fest';
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
  }
@@ -514,80 +515,93 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
514
515
  protected readonly onSkippedRequest?: SkippedRequestCallback;
515
516
  protected readonly identity: CrawlerIdentity;
516
517
  protected static optionsShape: {
517
- // @ts-ignore optional peer dependency or compatibility with es2022
518
- contextPipelineBuilder: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
519
- // @ts-ignore optional peer dependency or compatibility with es2022
520
- extendContext: import("ow").Predicate<Function> & BasePredicate<Function | undefined>;
521
- // @ts-ignore optional peer dependency or compatibility with es2022
522
- requestList: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
523
- // @ts-ignore optional peer dependency or compatibility with es2022
524
- requestQueue: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
525
- // @ts-ignore optional peer dependency or compatibility with es2022
526
- requestManager: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
527
- // @ts-ignore optional peer dependency or compatibility with es2022
528
- requestHandler: import("ow").Predicate<Function> & BasePredicate<Function | undefined>;
529
- // @ts-ignore optional peer dependency or compatibility with es2022
530
- requestHandlerTimeoutSecs: import("ow").NumberPredicate & BasePredicate<number | undefined>;
531
- // @ts-ignore optional peer dependency or compatibility with es2022
532
- errorHandler: import("ow").Predicate<Function> & BasePredicate<Function | undefined>;
533
- // @ts-ignore optional peer dependency or compatibility with es2022
534
- failedRequestHandler: import("ow").Predicate<Function> & BasePredicate<Function | undefined>;
535
- // @ts-ignore optional peer dependency or compatibility with es2022
536
- maxRequestRetries: import("ow").NumberPredicate & BasePredicate<number | undefined>;
537
- // @ts-ignore optional peer dependency or compatibility with es2022
538
- sameDomainDelaySecs: import("ow").NumberPredicate & BasePredicate<number | undefined>;
539
- // @ts-ignore optional peer dependency or compatibility with es2022
540
- maxRequestsPerCrawl: import("ow").NumberPredicate & BasePredicate<number | undefined>;
541
- // @ts-ignore optional peer dependency or compatibility with es2022
542
- maxCrawlDepth: import("ow").NumberPredicate & BasePredicate<number | undefined>;
543
- // @ts-ignore optional peer dependency or compatibility with es2022
544
- taskLoopOptions: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
545
- // @ts-ignore optional peer dependency or compatibility with es2022
546
- concurrencySystem: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
547
- // @ts-ignore optional peer dependency or compatibility with es2022
548
- sessionPool: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
549
- // @ts-ignore optional peer dependency or compatibility with es2022
550
- proxyConfiguration: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
551
- // @ts-ignore optional peer dependency or compatibility with es2022
552
- statusMessageLoggingInterval: import("ow").NumberPredicate & BasePredicate<number | undefined>;
553
- // @ts-ignore optional peer dependency or compatibility with es2022
554
- statusMessageCallback: import("ow").Predicate<Function> & BasePredicate<Function | undefined>;
555
- // @ts-ignore optional peer dependency or compatibility with es2022
556
- additionalHttpErrorStatusCodes: import("ow").ArrayPredicate<number>;
557
- // @ts-ignore optional peer dependency or compatibility with es2022
558
- ignoreHttpErrorStatusCodes: import("ow").ArrayPredicate<number>;
559
- // @ts-ignore optional peer dependency or compatibility with es2022
560
- blockedStatusCodes: import("ow").ArrayPredicate<number>;
561
- // @ts-ignore optional peer dependency or compatibility with es2022
562
- retryOnBlocked: import("ow").BooleanPredicate & BasePredicate<boolean | undefined>;
563
- // @ts-ignore optional peer dependency or compatibility with es2022
564
- respectRobotsTxtFile: import("ow").AnyPredicate<boolean | object>;
565
- transactionalStorage: BasePredicate<boolean | Partial<StorageWritePolicy> | undefined>;
566
- // @ts-ignore optional peer dependency or compatibility with es2022
567
- onSkippedRequest: import("ow").Predicate<Function> & BasePredicate<Function | undefined>;
568
- // @ts-ignore optional peer dependency or compatibility with es2022
569
- httpClient: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
570
- // @ts-ignore optional peer dependency or compatibility with es2022
571
- configuration: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
572
- // @ts-ignore optional peer dependency or compatibility with es2022
573
- storageBackend: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
574
- // @ts-ignore optional peer dependency or compatibility with es2022
575
- eventManager: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
576
- // @ts-ignore optional peer dependency or compatibility with es2022
577
- logger: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
578
- // @ts-ignore optional peer dependency or compatibility with es2022
579
- minConcurrency: import("ow").NumberPredicate & BasePredicate<number | undefined>;
580
- // @ts-ignore optional peer dependency or compatibility with es2022
581
- maxConcurrency: import("ow").NumberPredicate & BasePredicate<number | undefined>;
582
- // @ts-ignore optional peer dependency or compatibility with es2022
583
- maxRequestsPerMinute: import("ow").NumberPredicate & BasePredicate<number | undefined>;
584
- // @ts-ignore optional peer dependency or compatibility with es2022
585
- keepAlive: import("ow").BooleanPredicate & BasePredicate<boolean | undefined>;
586
- // @ts-ignore optional peer dependency or compatibility with es2022
587
- statistics: import("ow").ObjectPredicate<object> & BasePredicate<object | undefined>;
588
- // @ts-ignore optional peer dependency or compatibility with es2022
589
- id: import("ow").StringPredicate & BasePredicate<string | undefined>;
518
+ contextPipelineBuilder: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
519
+ extendContext: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
520
+ requestList: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
521
+ requestQueue: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
522
+ requestManager: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
523
+ requestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
524
+ requestHandlerTimeoutSecs: z.ZodOptional<z.ZodCustom<number, number>>;
525
+ errorHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
526
+ failedRequestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
527
+ maxRequestRetries: z.ZodDefault<z.ZodCustom<number, number>>;
528
+ sameDomainDelaySecs: z.ZodDefault<z.ZodCustom<number, number>>;
529
+ maxRequestsPerCrawl: z.ZodOptional<z.ZodCustom<number, number>>;
530
+ maxCrawlDepth: z.ZodOptional<z.ZodCustom<number, number>>;
531
+ taskLoopOptions: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
532
+ concurrencySystem: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
533
+ sessionPool: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
534
+ proxyConfiguration: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
535
+ statusMessageLoggingInterval: z.ZodDefault<z.ZodCustom<number, number>>;
536
+ statusMessageCallback: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
537
+ additionalHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
538
+ ignoreHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
539
+ blockedStatusCodes: z.ZodOptional<z.ZodArray<z.ZodCustom<number, number>>>;
540
+ retryOnBlocked: z.ZodDefault<z.ZodBoolean>;
541
+ respectRobotsTxtFile: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCustom<Dictionary, Dictionary>]>>;
542
+ transactionalStorage: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
543
+ requestQueue: z.ZodOptional<z.ZodEnum<{
544
+ deferred: "deferred";
545
+ writeThrough: "writeThrough";
546
+ }>>;
547
+ }, z.core.$strict>]>>;
548
+ onSkippedRequest: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
549
+ httpClient: z.ZodOptional<z.ZodCustom<BaseHttpClient, BaseHttpClient>>;
550
+ configuration: z.ZodOptional<z.ZodCustom<Configuration, Configuration>>;
551
+ storageBackend: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
552
+ eventManager: z.ZodOptional<z.ZodCustom<EventManager, EventManager>>;
553
+ logger: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
554
+ minConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
555
+ maxConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
556
+ maxRequestsPerMinute: z.ZodOptional<z.ZodCustom<number, number>>;
557
+ keepAlive: z.ZodOptional<z.ZodBoolean>;
558
+ statistics: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
559
+ id: z.ZodOptional<z.ZodString>;
590
560
  };
561
+ protected static optionsSchema: z.ZodObject<{
562
+ contextPipelineBuilder: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
563
+ extendContext: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
564
+ requestList: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
565
+ requestQueue: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
566
+ requestManager: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
567
+ requestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
568
+ requestHandlerTimeoutSecs: z.ZodOptional<z.ZodCustom<number, number>>;
569
+ errorHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
570
+ failedRequestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
571
+ maxRequestRetries: z.ZodDefault<z.ZodCustom<number, number>>;
572
+ sameDomainDelaySecs: z.ZodDefault<z.ZodCustom<number, number>>;
573
+ maxRequestsPerCrawl: z.ZodOptional<z.ZodCustom<number, number>>;
574
+ maxCrawlDepth: z.ZodOptional<z.ZodCustom<number, number>>;
575
+ taskLoopOptions: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
576
+ concurrencySystem: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
577
+ sessionPool: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
578
+ proxyConfiguration: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
579
+ statusMessageLoggingInterval: z.ZodDefault<z.ZodCustom<number, number>>;
580
+ statusMessageCallback: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
581
+ additionalHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
582
+ ignoreHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
583
+ blockedStatusCodes: z.ZodOptional<z.ZodArray<z.ZodCustom<number, number>>>;
584
+ retryOnBlocked: z.ZodDefault<z.ZodBoolean>;
585
+ respectRobotsTxtFile: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCustom<Dictionary, Dictionary>]>>;
586
+ transactionalStorage: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
587
+ requestQueue: z.ZodOptional<z.ZodEnum<{
588
+ deferred: "deferred";
589
+ writeThrough: "writeThrough";
590
+ }>>;
591
+ }, z.core.$strict>]>>;
592
+ onSkippedRequest: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
593
+ httpClient: z.ZodOptional<z.ZodCustom<BaseHttpClient, BaseHttpClient>>;
594
+ configuration: z.ZodOptional<z.ZodCustom<Configuration, Configuration>>;
595
+ storageBackend: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
596
+ eventManager: z.ZodOptional<z.ZodCustom<EventManager, EventManager>>;
597
+ logger: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
598
+ minConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
599
+ maxConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
600
+ maxRequestsPerMinute: z.ZodOptional<z.ZodCustom<number, number>>;
601
+ keepAlive: z.ZodOptional<z.ZodBoolean>;
602
+ statistics: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
603
+ id: z.ZodOptional<z.ZodString>;
604
+ }, z.core.$strict>;
591
605
  /**
592
606
  * All `BasicCrawler` parameters are passed via an options object.
593
607
  */
@@ -719,6 +733,11 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
719
733
  * the batches via `waitBetweenBatchesMillis`. If you want to wait for all batches to be added to the queue, you can use
720
734
  * the `waitForAllRequestsToBeAdded` promise you get in the response object.
721
735
  *
736
+ * Optionally, the requests can be filtered using `include`/`exclude` glob or regexp patterns and an
737
+ * enqueue `strategy` (both AND-ed together, same as {@link CrawlingContext.enqueueLinks|`enqueueLinks`}),
738
+ * relative to `baseUrl`. Unlike `enqueueLinks`, there is no implicit "current page" to anchor the strategy
739
+ * to, so `strategy` defaults to {@link EnqueueStrategy.All|`all`} here.
740
+ *
722
741
  * This is an alias for calling `addRequestsBatched()` on the implicit `RequestQueue` for this crawler instance.
723
742
  *
724
743
  * @param requests The requests to add
@@ -813,14 +832,6 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
813
832
  private delayRequest;
814
833
  /** Handles a single request - runs the request handler with retries, error handling, and lifecycle management. */
815
834
  private handleRequest;
816
- /**
817
- * Wrapper around the crawling context's `enqueueLinks` method:
818
- * - Injects `crawlDepth` to each request being added based on the crawling context request.
819
- * - Provides defaults for the `enqueueLinks` options based on the crawler configuration.
820
- * - These options can be overridden by the user.
821
- * @internal
822
- */
823
- protected enqueueLinksWithCrawlDepth(options: SetRequired<EnqueueLinksOptions, 'urls'>, request: Request<Dictionary>, requestManager: IRequestManager): Promise<BatchAddRequestsResult>;
824
835
  /**
825
836
  * Generator function that yields requests injected with the given crawl depth.
826
837
  * @internal
@@ -879,7 +890,7 @@ export interface CreateContextOptions {
879
890
  session: ISession;
880
891
  proxyInfo?: ProxyInfo;
881
892
  }
882
- export interface CrawlerAddRequestsOptions extends AddRequestsBatchedOptions {
893
+ export interface CrawlerAddRequestsOptions extends AddRequestsBatchedOptions, EnqueueUrlsOptions {
883
894
  }
884
895
  export interface CrawlerAddRequestsResult extends AddRequestsBatchedResult {
885
896
  }
@@ -1,19 +1,20 @@
1
1
  import { mkdir, writeFile } from 'node:fs/promises';
2
2
  import { dirname } from 'node:path';
3
- import { AutoscaledPool, bindMethodsToServiceLocator, BLOCKED_STATUS_CODES, ConcurrencySystem, ContextPipeline, ContextPipelineCleanupError, ContextPipelineInitializationError, ContextPipelineInterruptedError, createStorageTransaction, CriticalError, currentStorageTransaction, Dataset, enqueueLinks, EnqueueStrategy, EventType, getObjectType, KeyValueStore, log, LogLevel, mergeCookies, MissingSessionError, NavigationSkippedError, NonRetryableError, OwnedOrInjected, purgeDefaultStorages, RequestHandlerError, parseRetryAfterHeader, RequestThrottledError, RequestManagerTandem, RequestQueue, RequestState, RetryRequestError, supportsDomainThrottling, Router, ServiceLocator, serviceLocator, Session, SessionError, SessionPool, Statistics, validateUserData, validators, withDirectStorageAccess, } from '@crawlee/core';
4
- import { FetchHttpClient } from '@crawlee/http-client';
3
+ import { applyRequestTransform, AutoscaledPool, bindMethodsToServiceLocator, BLOCKED_STATUS_CODES, buildEnqueueStrategyPatterns, ConcurrencySystem, Configuration, constructUrlPatternObjects, ContextPipeline, ContextPipelineCleanupError, ContextPipelineInitializationError, ContextPipelineInterruptedError, createRequestOptions, createStorageTransaction, Request, CriticalError, currentStorageTransaction, Dataset, EnqueueStrategy, EventManager, EventType, filterRequestOptionsByPatterns, getObjectType, KeyValueStore, log, LogLevel, mergeCookies, MissingSessionError, NavigationSkippedError, NonRetryableError, OwnedOrInjected, parseArgument, purgeDefaultStorages, RequestHandlerError, parseRetryAfterHeader, RequestThrottledError, RequestManagerTandem, RequestQueue, RequestState, RetryRequestError, supportsDomainThrottling, Router, schemas, ServiceLocator, serviceLocator, Session, SessionError, SessionPool, Statistics, validateUserData, validators, withDirectStorageAccess, } from '@crawlee/core';
4
+ import { BaseHttpClient, FetchHttpClient } from '@crawlee/http-client';
5
5
  import { isAsyncIterable, isIterable, ROTATE_PROXY_ERRORS } from '@crawlee/utils/internal';
6
6
  import { RobotsTxtFile } from '@crawlee/utils';
7
- import ow, { ArgumentError } from 'ow';
8
7
  import { getDomain } from 'tldts';
8
+ import { z } from 'zod';
9
9
  import { LruCache } from '@apify/datastructures';
10
10
  import { addTimeoutToPromise, extendTimeout, TimeoutError } from '@apify/timeout';
11
11
  import { cryptoRandomObjectId } from '@apify/utilities';
12
12
  import { extendTimeoutKey, navigationDeadlineKey, raceWithTimeout, timeoutExpiredKey, } from './request-timeout.js';
13
13
  import { createSendRequest } from './send-request.js';
14
- class LazyDefaultHttpClient {
14
+ class LazyDefaultHttpClient extends BaseHttpClient {
15
15
  #delegatePromise;
16
16
  constructor(options) {
17
+ super(options);
17
18
  this.#delegatePromise = import('@crawlee/impit-client')
18
19
  .then(({ ImpitHttpClient }) => new ImpitHttpClient(options))
19
20
  .catch(() => {
@@ -22,6 +23,9 @@ class LazyDefaultHttpClient {
22
23
  return new FetchHttpClient(options);
23
24
  });
24
25
  }
26
+ fetch() {
27
+ throw new Error('LazyDefaultHttpClient delegates `sendRequest` entirely; `fetch` is never called.');
28
+ }
25
29
  async sendRequest(...args) {
26
30
  return (await this.#delegatePromise).sendRequest(...args);
27
31
  }
@@ -39,6 +43,33 @@ const SAFE_MIGRATION_WAIT_MILLIS = 20000;
39
43
  const deferredCleanupKey = Symbol('deferredCleanup');
40
44
  // The request timeout plumbing (the window helper, the context symbols, and the race) lives in its own module.
41
45
  export { navigationDeadlineKey, remainingNavigationWindowMillis } from './request-timeout.js';
46
+ const urlPatternSchema = z.union([
47
+ z.string(),
48
+ z.instanceof(RegExp),
49
+ schemas.objectWithKeys(['glob']),
50
+ schemas.objectWithKeys(['regexp']),
51
+ ]);
52
+ // `looseObject` (rather than `strictObject`) lets subclasses forward their own extraction-only options
53
+ // (e.g. `selector`) straight through without having to strip them out first.
54
+ const addRequestsOptionsSchema = z.looseObject({
55
+ forefront: z.boolean().optional(),
56
+ cache: z.boolean().optional(),
57
+ waitForAllRequestsToBeAdded: z.boolean().optional(),
58
+ batchSize: schemas.anyNumber.optional(),
59
+ waitBetweenBatchesMillis: schemas.anyNumber.optional(),
60
+ maxNewRequests: schemas.anyNumber.optional(),
61
+ limit: schemas.anyNumber.optional(),
62
+ baseUrl: z.string().optional(),
63
+ userData: schemas.anyObject.optional(),
64
+ label: z.string().optional(),
65
+ sessionId: z.string().optional(),
66
+ skipNavigation: z.boolean().optional(),
67
+ include: schemas.arrayOf(urlPatternSchema, 'URL patterns').min(1).optional(),
68
+ exclude: schemas.arrayOf(urlPatternSchema, 'URL patterns').optional(),
69
+ transformRequestFunction: schemas.anyFunction.optional(),
70
+ strategy: z.enum(EnqueueStrategy).optional(),
71
+ onSkippedRequest: schemas.anyFunction.optional(),
72
+ });
42
73
  export class BasicCrawler {
43
74
  static CRAWLEE_STATE_KEY = 'CRAWLEE_STATE';
44
75
  /**
@@ -200,72 +231,77 @@ export class BasicCrawler {
200
231
  identity;
201
232
  #contextPipelineOptions;
202
233
  static optionsShape = {
203
- contextPipelineBuilder: ow.optional.object,
204
- extendContext: ow.optional.function,
205
- requestList: ow.optional.object.validate(validators.requestList),
206
- requestQueue: ow.optional.object.validate(validators.requestQueue),
207
- requestManager: ow.optional.object,
234
+ contextPipelineBuilder: schemas.anyObject.optional(),
235
+ extendContext: schemas.anyFunction.optional(),
236
+ requestList: validators.requestList.optional(),
237
+ requestQueue: validators.requestQueue.optional(),
238
+ requestManager: validators.requestManager.optional(),
208
239
  // Subclasses override this function instead of passing it
209
240
  // in constructor, so this validation needs to apply only
210
241
  // if the user creates an instance of BasicCrawler directly.
211
- requestHandler: ow.optional.function,
212
- requestHandlerTimeoutSecs: ow.optional.number,
213
- errorHandler: ow.optional.function,
214
- failedRequestHandler: ow.optional.function,
215
- maxRequestRetries: ow.optional.number,
216
- sameDomainDelaySecs: ow.optional.number,
217
- maxRequestsPerCrawl: ow.optional.number,
218
- maxCrawlDepth: ow.optional.number,
219
- taskLoopOptions: ow.optional.object,
220
- concurrencySystem: ow.optional.object,
221
- sessionPool: ow.optional.object.validate(validators.sessionPool),
222
- proxyConfiguration: ow.optional.object.validate(validators.proxyConfiguration),
223
- statusMessageLoggingInterval: ow.optional.number,
224
- statusMessageCallback: ow.optional.function,
225
- additionalHttpErrorStatusCodes: ow.optional.array.ofType(ow.number),
226
- ignoreHttpErrorStatusCodes: ow.optional.array.ofType(ow.number),
227
- blockedStatusCodes: ow.optional.array.ofType(ow.number),
228
- retryOnBlocked: ow.optional.boolean,
229
- respectRobotsTxtFile: ow.optional.any(ow.boolean, ow.object),
230
- transactionalStorage: ow.optional.any(ow.boolean, ow.object.exactShape({
231
- requestQueue: ow.optional.string.oneOf(['deferred', 'writeThrough']),
232
- })),
233
- onSkippedRequest: ow.optional.function,
234
- httpClient: ow.optional.object,
235
- configuration: ow.optional.object,
236
- storageBackend: ow.optional.object,
237
- eventManager: ow.optional.object,
238
- logger: ow.optional.object,
242
+ requestHandler: schemas.anyFunction.optional(),
243
+ requestHandlerTimeoutSecs: schemas.anyNumber.optional(),
244
+ errorHandler: schemas.anyFunction.optional(),
245
+ failedRequestHandler: schemas.anyFunction.optional(),
246
+ maxRequestRetries: schemas.anyNumber.default(3),
247
+ sameDomainDelaySecs: schemas.anyNumber.default(0),
248
+ maxRequestsPerCrawl: schemas.anyNumber.optional(),
249
+ maxCrawlDepth: schemas.anyNumber.optional(),
250
+ // No zod default — subclasses provide their own fallback (e.g. HTTP-optimized pool options).
251
+ taskLoopOptions: schemas.anyObject.optional(),
252
+ concurrencySystem: schemas.anyObject.optional(),
253
+ sessionPool: validators.sessionPool.optional(),
254
+ proxyConfiguration: validators.proxyConfiguration.optional(),
255
+ statusMessageLoggingInterval: schemas.anyNumber.default(10),
256
+ statusMessageCallback: schemas.anyFunction.optional(),
257
+ additionalHttpErrorStatusCodes: schemas.arrayOf(schemas.anyNumber, 'numbers').default(() => []),
258
+ ignoreHttpErrorStatusCodes: schemas.arrayOf(schemas.anyNumber, 'numbers').default(() => []),
259
+ blockedStatusCodes: schemas.arrayOf(schemas.anyNumber, 'numbers').optional(),
260
+ retryOnBlocked: z.boolean().default(false),
261
+ respectRobotsTxtFile: z.union([z.boolean(), schemas.anyObject]).default(false),
262
+ transactionalStorage: z
263
+ .union([z.boolean(), z.strictObject({ requestQueue: z.enum(['deferred', 'writeThrough']).optional() })])
264
+ .optional(),
265
+ onSkippedRequest: schemas.anyFunction.optional(),
266
+ httpClient: schemas.httpClient.optional(),
267
+ configuration: z.instanceof(Configuration).optional(),
268
+ storageBackend: validators.storageBackend.optional(),
269
+ eventManager: z.instanceof(EventManager).optional(),
270
+ logger: validators.logger.optional(),
239
271
  // AutoscaledPool shorthands
240
- minConcurrency: ow.optional.number,
241
- maxConcurrency: ow.optional.number,
242
- maxRequestsPerMinute: ow.optional.number.integerOrInfinite.positive.greaterThanOrEqual(1),
243
- keepAlive: ow.optional.boolean,
244
- statistics: ow.optional.object,
245
- id: ow.optional.string,
272
+ minConcurrency: schemas.anyNumber.optional(),
273
+ maxConcurrency: schemas.anyNumber.optional(),
274
+ maxRequestsPerMinute: schemas.anyNumber
275
+ .refine((value) => Number.isInteger(value) || value === Infinity, 'Expected an integer or infinite number')
276
+ .refine((value) => value >= 1, 'Expected a number greater than or equal to 1')
277
+ .optional(),
278
+ keepAlive: z.boolean().optional(),
279
+ statistics: schemas.anyObject.optional(),
280
+ id: z.string().optional(),
246
281
  };
282
+ static optionsSchema = z.strictObject(BasicCrawler.optionsShape);
247
283
  /**
248
284
  * All `BasicCrawler` parameters are passed via an options object.
249
285
  */
250
286
  constructor(options = {}) {
251
- ow(options, 'BasicCrawlerOptions', ow.object.exactShape(BasicCrawler.optionsShape));
287
+ const parsedOptions = parseArgument(options, BasicCrawler.optionsSchema, 'BasicCrawlerOptions');
252
288
  const {
253
289
  // oxlint-disable-next-line typescript/no-deprecated -- still accepted and folded into `requestManager` for back-compat
254
290
  requestList,
255
291
  // oxlint-disable-next-line typescript/no-deprecated -- still accepted and folded into `requestManager` for back-compat
256
- requestQueue, requestManager, maxRequestRetries = 3, sameDomainDelaySecs = 0, maxRequestsPerCrawl, maxCrawlDepth, taskLoopOptions = {}, concurrencySystem, keepAlive, sessionPool, proxyConfiguration, additionalHttpErrorStatusCodes = [], ignoreHttpErrorStatusCodes = [],
292
+ requestQueue, requestManager, maxRequestRetries, sameDomainDelaySecs, maxRequestsPerCrawl, maxCrawlDepth, taskLoopOptions = {}, concurrencySystem, keepAlive, sessionPool, proxyConfiguration, additionalHttpErrorStatusCodes, ignoreHttpErrorStatusCodes,
257
293
  // Service locator options
258
294
  configuration, storageBackend, eventManager, logger,
259
295
  // AutoscaledPool shorthands
260
- minConcurrency, maxConcurrency, maxRequestsPerMinute, blockedStatusCodes: blockedStatusCodesInput, retryOnBlocked = false, respectRobotsTxtFile = false, transactionalStorage, onSkippedRequest, requestHandler, requestHandlerTimeoutSecs, errorHandler, failedRequestHandler, statusMessageLoggingInterval = 10, statusMessageCallback, statistics, httpClient, id, } = options;
296
+ minConcurrency, maxConcurrency, maxRequestsPerMinute, blockedStatusCodes: blockedStatusCodesInput, retryOnBlocked, respectRobotsTxtFile, transactionalStorage, onSkippedRequest, requestHandler, requestHandlerTimeoutSecs, errorHandler, failedRequestHandler, statusMessageLoggingInterval, statusMessageCallback, statistics, httpClient, id, } = parsedOptions;
261
297
  // All concurrency configuration lives on the `ConcurrencySystem`, so the shortcuts have nowhere to go once
262
298
  // one is supplied - and silently dropping a `maxConcurrency` the user asked for is how crawls end up
263
299
  // hammering a site.
264
300
  if (concurrencySystem !== undefined &&
265
301
  (minConcurrency !== undefined || maxConcurrency !== undefined || maxRequestsPerMinute !== undefined)) {
266
- throw new ArgumentError('The `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts cannot be combined with ' +
302
+ throw new Error('The `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts cannot be combined with ' +
267
303
  '`concurrencySystem` - they configure the default `ConcurrencySystem` that a supplied one ' +
268
- 'replaces. Pass them to the `ConcurrencySystem` constructor instead.', this.constructor);
304
+ 'replaces. Pass them to the `ConcurrencySystem` constructor instead.');
269
305
  }
270
306
  // Create per-crawler service locator if custom services were provided.
271
307
  // This wraps every method on the crawler instance so that calls to the global `serviceLocator`
@@ -287,8 +323,8 @@ export class BasicCrawler {
287
323
  try {
288
324
  serviceLocatorScope.enterScope();
289
325
  this.#contextPipelineOptions = {
290
- contextPipelineBuilder: options.contextPipelineBuilder,
291
- extendContext: options.extendContext,
326
+ contextPipelineBuilder: parsedOptions.contextPipelineBuilder,
327
+ extendContext: parsedOptions.extendContext,
292
328
  };
293
329
  this.#log = serviceLocator.getLogger().child({ prefix: this.constructor.name });
294
330
  // Initialize the Configuration instance to avoid lazy loading in the components
@@ -589,17 +625,13 @@ export class BasicCrawler {
589
625
  return { session, proxyInfo: session?.proxyInfo };
590
626
  }
591
627
  async createContextHelpers({ request, session }) {
592
- const enqueueLinksWrapper = async (options) => {
593
- const requestManager = await this.getRequestManager();
594
- return await this.enqueueLinksWithCrawlDepth(options, request, requestManager);
595
- };
596
628
  const addRequests = async (requests, options = {}) => {
597
629
  const newCrawlDepth = request.crawlDepth + 1;
598
630
  const requestsGenerator = this.addCrawlDepthRequestGenerator(requests, newCrawlDepth);
599
- await this.addRequests(requestsGenerator, options);
631
+ return await this.addRequests(requestsGenerator, options);
600
632
  };
601
633
  const sendRequest = createSendRequest(this.httpClient, request, session);
602
- return { enqueueLinks: enqueueLinksWrapper, addRequests, sendRequest };
634
+ return { addRequests, sendRequest };
603
635
  }
604
636
  buildFinalContextPipeline() {
605
637
  const subclassPipeline = (this.#contextPipelineOptions.contextPipelineBuilder?.() ??
@@ -996,6 +1028,11 @@ export class BasicCrawler {
996
1028
  * the batches via `waitBetweenBatchesMillis`. If you want to wait for all batches to be added to the queue, you can use
997
1029
  * the `waitForAllRequestsToBeAdded` promise you get in the response object.
998
1030
  *
1031
+ * Optionally, the requests can be filtered using `include`/`exclude` glob or regexp patterns and an
1032
+ * enqueue `strategy` (both AND-ed together, same as {@link CrawlingContext.enqueueLinks|`enqueueLinks`}),
1033
+ * relative to `baseUrl`. Unlike `enqueueLinks`, there is no implicit "current page" to anchor the strategy
1034
+ * to, so `strategy` defaults to {@link EnqueueStrategy.All|`all`} here.
1035
+ *
999
1036
  * This is an alias for calling `addRequestsBatched()` on the implicit `RequestQueue` for this crawler instance.
1000
1037
  *
1001
1038
  * @param requests The requests to add
@@ -1003,55 +1040,95 @@ export class BasicCrawler {
1003
1040
  */
1004
1041
  async addRequests(requests, options = {}) {
1005
1042
  await this.getRequestManager();
1006
- const requestLimit = await this.calculateEnqueuedRequestLimit();
1007
- const skippedBecauseOfRobots = new Set();
1008
- const skippedBecauseOfMaxCrawlDepth = new Set();
1043
+ if (!isIterable(requests) && !isAsyncIterable(requests)) {
1044
+ throw new Error(`Expected an iterable or async iterable, got ${getObjectType(requests)}`);
1045
+ }
1046
+ parseArgument(options, addRequestsOptionsSchema, 'EnqueueUrlsOptions');
1047
+ // `label`/`userData` apply to every request this call produces, so a single upfront validation
1048
+ // against the label's schema covers them all and fails the whole call fast, rather than failing
1049
+ // lazily once the generator below is drained. Skipped when neither is set - each item still gets
1050
+ // its own per-item validation below, and validating an absent label/userData here would spuriously
1051
+ // check them against a registered default-route schema.
1052
+ if (options.label !== undefined || options.userData !== undefined) {
1053
+ await this.validateRequestUserData({ label: options.label, userData: options.userData });
1054
+ }
1055
+ const requestLimit = await this.calculateEnqueuedRequestLimit(options.limit);
1056
+ const strategy = options.strategy ?? EnqueueStrategy.All;
1057
+ const urlExcludePatternObjects = options.exclude?.length
1058
+ ? constructUrlPatternObjects(options.exclude)
1059
+ : [];
1060
+ const urlPatternObjects = options.include?.length
1061
+ ? constructUrlPatternObjects(options.include)
1062
+ : [];
1063
+ // The strategy always applies, even when `include` patterns are provided - the two are AND-ed together
1064
+ // (a URL must match an `include` pattern *and* satisfy the strategy). This mirrors crawlee-python.
1065
+ const enqueueStrategyPatterns = options.baseUrl
1066
+ ? buildEnqueueStrategyPatterns(options.baseUrl, strategy)
1067
+ : [];
1009
1068
  const isAllowedBasedOnRobotsTxtFile = this.isAllowedBasedOnRobotsTxtFile.bind(this);
1010
1069
  const maxCrawlDepth = this.maxCrawlDepth;
1011
1070
  const validateRequestUserData = this.validateRequestUserData.bind(this);
1012
- ow(requests, ow.object
1013
- .is((value) => isIterable(value) || isAsyncIterable(value))
1014
- .message((value) => `Expected an iterable or async iterable, got ${getObjectType(value)}`));
1071
+ const allSkipped = [];
1015
1072
  async function* filteredRequests() {
1016
1073
  for await (const request of requests) {
1017
- const url = typeof request === 'string' ? request : request.url;
1018
- if (maxCrawlDepth !== undefined && request.crawlDepth > maxCrawlDepth) {
1019
- skippedBecauseOfMaxCrawlDepth.add(url);
1074
+ const [requestOptions] = createRequestOptions([typeof request === 'string' ? request : request], { ...options, strategy });
1075
+ if (!requestOptions) {
1076
+ continue; // invalid URL, silently dropped (matches `createRequestOptions`'s own filtering)
1077
+ }
1078
+ if (maxCrawlDepth !== undefined && requestOptions.crawlDepth > maxCrawlDepth) {
1079
+ allSkipped.push({ url: requestOptions.url, reason: 'depth' });
1020
1080
  continue;
1021
1081
  }
1022
- if (await isAllowedBasedOnRobotsTxtFile(url)) {
1023
- await validateRequestUserData(request);
1024
- yield request;
1082
+ if (!(await isAllowedBasedOnRobotsTxtFile(requestOptions.url))) {
1083
+ allSkipped.push({ url: requestOptions.url, reason: 'robotsTxt' });
1084
+ continue;
1025
1085
  }
1026
- else {
1027
- skippedBecauseOfRobots.add(url);
1086
+ const onSkippedFilterUrl = (url) => allSkipped.push({ url, reason: 'filters' });
1087
+ // Filter by user patterns first (with exclude)...
1088
+ let filtered = filterRequestOptionsByPatterns([requestOptions], urlPatternObjects.length > 0 ? urlPatternObjects : undefined, urlExcludePatternObjects, strategy, onSkippedFilterUrl);
1089
+ // ...then filter by the enqueue strategy (making this an AND check)
1090
+ filtered = filterRequestOptionsByPatterns(filtered, enqueueStrategyPatterns.length > 0 ? enqueueStrategyPatterns : undefined, [], strategy, onSkippedFilterUrl);
1091
+ if (filtered.length === 0) {
1092
+ continue;
1093
+ }
1094
+ let [finalOptions] = filtered;
1095
+ if (options.transformRequestFunction) {
1096
+ const transformed = applyRequestTransform([finalOptions], options.transformRequestFunction, (r) => allSkipped.push({ url: r.url, reason: r.skippedReason ?? 'transform' }));
1097
+ if (transformed.length === 0) {
1098
+ continue;
1099
+ }
1100
+ [finalOptions] = transformed;
1028
1101
  }
1102
+ await validateRequestUserData(finalOptions);
1103
+ yield new Request(finalOptions);
1029
1104
  }
1030
1105
  }
1031
1106
  const result = await this.requestManager.addRequestsBatched(filteredRequests(), {
1032
- ...options,
1107
+ forefront: options.forefront,
1108
+ waitForAllRequestsToBeAdded: options.waitForAllRequestsToBeAdded,
1109
+ batchSize: options.batchSize,
1110
+ waitBetweenBatchesMillis: options.waitBetweenBatchesMillis,
1033
1111
  maxNewRequests: requestLimit,
1034
1112
  });
1035
- // Report requests skipped due to the maxNewRequests budget (i.e. maxRequestsPerCrawl limit)
1036
- const skippedBecauseOfLimit = result.requestsOverLimit ?? [];
1037
- if (skippedBecauseOfRobots.size > 0) {
1038
- this.log.warning(`Some requests were skipped because they were disallowed based on the robots.txt file`, {
1039
- skipped: [...skippedBecauseOfRobots],
1040
- });
1041
- }
1042
- if (skippedBecauseOfRobots.size > 0 ||
1043
- skippedBecauseOfLimit.length > 0 ||
1044
- skippedBecauseOfMaxCrawlDepth.size > 0) {
1045
- await Promise.all([...skippedBecauseOfRobots]
1046
- .map((url) => {
1047
- return this.handleSkippedRequest({ url, reason: 'robotsTxt' });
1048
- })
1049
- .concat(skippedBecauseOfLimit.map((request) => {
1050
- const url = typeof request === 'string' ? request : request.url;
1051
- return this.handleSkippedRequest({ url, reason: 'limit' });
1052
- }), [...skippedBecauseOfMaxCrawlDepth].map((url) => {
1053
- return this.handleSkippedRequest({ url, reason: 'depth' });
1054
- })));
1113
+ // Report requests skipped due to the maxNewRequests budget (i.e. maxRequestsPerCrawl limit, or an
1114
+ // explicit `limit` option)
1115
+ for (const request of result.requestsOverLimit ?? []) {
1116
+ allSkipped.push({ url: typeof request === 'string' ? request : request.url, reason: 'limit' });
1117
+ }
1118
+ if (allSkipped.length > 0) {
1119
+ const skippedRobotsUrls = allSkipped.filter((s) => s.reason === 'robotsTxt').map((s) => s.url);
1120
+ if (skippedRobotsUrls.length > 0) {
1121
+ this.log.warning(`Some requests were skipped because they were disallowed based on the robots.txt file`, { skipped: skippedRobotsUrls });
1122
+ }
1123
+ // Only log the limit message when an explicit `limit` was passed (not the internal
1124
+ // `maxRequestsPerCrawl`-derived one), and only once per call.
1125
+ if (options.limit !== undefined && allSkipped.some((s) => s.reason === 'limit')) {
1126
+ this.log.info(`Skipping requests in this call due to the enqueueLinks limit of ${options.limit}.`);
1127
+ }
1128
+ await Promise.all(allSkipped.map(async ({ url, reason }) => {
1129
+ await this.handleSkippedRequest({ url, reason });
1130
+ await options.onSkippedRequest?.({ url, reason });
1131
+ }));
1055
1132
  }
1056
1133
  return result;
1057
1134
  }
@@ -1450,52 +1527,6 @@ export class BasicCrawler {
1450
1527
  }
1451
1528
  }
1452
1529
  }
1453
- /**
1454
- * Wrapper around the crawling context's `enqueueLinks` method:
1455
- * - Injects `crawlDepth` to each request being added based on the crawling context request.
1456
- * - Provides defaults for the `enqueueLinks` options based on the crawler configuration.
1457
- * - These options can be overridden by the user.
1458
- * @internal
1459
- */
1460
- async enqueueLinksWithCrawlDepth(options, request, requestManager) {
1461
- const transformRequestFunctionWrapper = (requestOptions) => {
1462
- requestOptions.crawlDepth = request.crawlDepth + 1;
1463
- if (this.maxCrawlDepth !== undefined && requestOptions.crawlDepth > this.maxCrawlDepth) {
1464
- // Setting `skippedReason` before returning `false` ensures that `reportSkippedRequests`
1465
- // reports `'depth'` as the reason (via `request.skippedReason ?? reason` fallback),
1466
- // rather than the generic `'transform'` reason.
1467
- requestOptions.skippedReason = 'depth';
1468
- return false;
1469
- }
1470
- // After injecting the crawlDepth, we call the user-provided transform function, if there is one.
1471
- return options.transformRequestFunction?.(requestOptions) ?? requestOptions;
1472
- };
1473
- // Create a request-scoped callback that logs enqueueLimit once per request handler call
1474
- // Only log if an explicit limit was passed to enqueueLinks (not the internal maxRequestsPerCrawl-derived limit)
1475
- let loggedEnqueueLimitForThisRequest = false;
1476
- const onSkippedRequest = async (skippedOptions) => {
1477
- if (skippedOptions.reason === 'enqueueLimit') {
1478
- if (!loggedEnqueueLimitForThisRequest && options.limit !== undefined) {
1479
- this.log.info(`Skipping URLs in the handler for ${request.url} due to the enqueueLinks limit of ${options.limit}.`);
1480
- loggedEnqueueLimitForThisRequest = true;
1481
- }
1482
- }
1483
- await this.handleSkippedRequest(skippedOptions);
1484
- };
1485
- // `enqueueLinks` applies `options.label`/`options.userData` to every newly enqueued request, so a single
1486
- // validation against the label's schema covers them all (a no-op unless the router declares a schema).
1487
- await this.validateRequestUserData({ label: options.label, userData: options.userData });
1488
- return await enqueueLinks({
1489
- requestManager,
1490
- robotsTxtFile: await this.getRobotsTxtFileForUrl(request.url),
1491
- respectRobotsTxtFile: this.#respectRobotsTxtFile,
1492
- onSkippedRequest,
1493
- limit: await this.calculateEnqueuedRequestLimit(options.limit),
1494
- // Allow user options to override defaults set above ⤴
1495
- ...options,
1496
- transformRequestFunction: transformRequestFunctionWrapper,
1497
- });
1498
- }
1499
1530
  /**
1500
1531
  * Generator function that yields requests injected with the given crawl depth.
1501
1532
  * @internal
@@ -1,5 +1,6 @@
1
1
  import type { Request as CrawleeRequest } from '@crawlee/core';
2
- import type { BaseHttpClient, HttpRequestOptions, ISession, SendRequestOptions } from '@crawlee/types';
2
+ import type { BaseHttpClient } from '@crawlee/http-client';
3
+ import type { HttpRequestOptions, ISession, SendRequestOptions } from '@crawlee/types';
3
4
  /**
4
5
  * Prepares a function to be used as the `sendRequest` context helper.
5
6
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crawlee/basic",
3
- "version": "4.0.0-beta.121",
3
+ "version": "4.0.0-beta.123",
4
4
  "description": "The scalable web crawling and scraping library for JavaScript/Node.js. Enables development of data extraction and web automation jobs (not only) with headless Chrome and Puppeteer.",
5
5
  "engines": {
6
6
  "node": ">=22.0.0"
@@ -42,18 +42,18 @@
42
42
  "@apify/datastructures": "^2.0.0",
43
43
  "@apify/timeout": "^0.4.4",
44
44
  "@apify/utilities": "^2.15.5",
45
- "@crawlee/core": "4.0.0-beta.121",
46
- "@crawlee/http-client": "4.0.0-beta.121",
47
- "@crawlee/types": "4.0.0-beta.121",
48
- "@crawlee/utils": "4.0.0-beta.121",
45
+ "@crawlee/core": "4.0.0-beta.123",
46
+ "@crawlee/http-client": "4.0.0-beta.123",
47
+ "@crawlee/types": "4.0.0-beta.123",
48
+ "@crawlee/utils": "4.0.0-beta.123",
49
49
  "csv-stringify": "^6.5.2",
50
- "ow": "^2.0.0",
51
50
  "tldts": "^7.0.6",
52
51
  "tslib": "^2.8.1",
53
- "type-fest": "^4.41.0"
52
+ "type-fest": "^4.41.0",
53
+ "zod": "^4.4.3"
54
54
  },
55
55
  "optionalDependencies": {
56
- "@crawlee/impit-client": "^4.0.0-beta.121"
56
+ "@crawlee/impit-client": "^4.0.0-beta.123"
57
57
  },
58
58
  "lerna": {
59
59
  "command": {
@@ -62,5 +62,5 @@
62
62
  }
63
63
  }
64
64
  },
65
- "gitHead": "5027317de626f5ba6de5047ae9341a898258cc5a"
65
+ "gitHead": "f77648095c6a3f5ed8815c7620ea765db430ae44"
66
66
  }