@crawlee/basic 4.0.0-beta.88 → 4.0.0-beta.89

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,5 +1,5 @@
1
- import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, AutoscaledPoolOptions, Configuration, CrawleeLogger, CrawlingContext, DatasetExportOptions, EnqueueLinksOptions, EventManager, FinalStatistics, GetUserDataFromRequest, IProxyConfiguration, IRequestLoader, IRequestManager, Request, RequestsLike, RouterHandler, RouterRoutes, SkippedRequestCallback, Source, StatisticsOptions, StatisticState, StorageIdentifier, TypedRequestsLike } from '@crawlee/core';
2
- import { AutoscaledPool, ContextPipeline, Dataset, RequestQueue, Statistics } from '@crawlee/core';
1
+ import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, AutoscaledPoolPredicateOptions, ConcurrencySystemOptions, Configuration, CrawleeLogger, CrawlingContext, DatasetExportOptions, EnqueueLinksOptions, EventManager, FinalStatistics, GetUserDataFromRequest, IConcurrencySystem, IProxyConfiguration, IRequestLoader, IRequestManager, Request, RequestsLike, RouterHandler, RouterRoutes, SkippedRequestCallback, Source, StatisticsOptions, StatisticState, StorageIdentifier, TypedRequestsLike } from '@crawlee/core';
2
+ import { AutoscaledPool, ConcurrencySystem, ContextPipeline, Dataset, RequestQueue, Statistics } from '@crawlee/core';
3
3
  import type { Awaitable, BaseHttpClient, BatchAddRequestsResult, Dictionary, ISession, ISessionPool, ProxyInfo, SetStatusMessageOptions, StorageBackend } from '@crawlee/types';
4
4
  import { RobotsTxtFile } from '@crawlee/utils';
5
5
  import type { ReadonlyDeep, SetRequired } from 'type-fest';
@@ -151,29 +151,49 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
151
151
  */
152
152
  maxCrawlDepth?: number;
153
153
  /**
154
- * Custom options passed to the underlying {@link AutoscaledPool} constructor.
155
- * > *NOTE:* The {@link AutoscaledPoolOptions.runTaskFunction|`runTaskFunction`}
156
- * option is provided by the crawler and cannot be overridden.
157
- * However, we can provide custom implementations of {@link AutoscaledPoolOptions.isFinishedFunction|`isFinishedFunction`}
158
- * and {@link AutoscaledPoolOptions.isTaskReadyFunction|`isTaskReadyFunction`}.
154
+ * Lets you override the task-loop predicates (`isFinishedFunction`, `isTaskReadyFunction`) of the crawler's
155
+ * underlying {@link AutoscaledPool}. The `runTaskFunction` is owned by the crawler and cannot be overridden.
156
+ *
157
+ * Concurrency is configured elsewhere through the `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute`
158
+ * shortcuts, or a {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} for finer control.
159
+ */
160
+ autoscaledPoolOptions?: AutoscaledPoolPredicateOptions;
161
+ /**
162
+ * A pre-configured concurrency governor — the component that decides whether there is free compute for one more
163
+ * task. Typically a {@link ConcurrencySystem}, though any {@link IConcurrencySystem} is accepted. All
164
+ * scaling configuration (min/max/desired concurrency, scaling ratios, `maxTasksPerMinute`, snapshotter tuning)
165
+ * lives on the instance itself.
166
+ *
167
+ * Inject the *same* instance into several concurrent crawlers to cap their **combined** concurrency against a
168
+ * single budget. Each crawler still builds and drives its own {@link AutoscaledPool}; only the load/scaling
169
+ * accounting is shared.
170
+ *
171
+ * Mutually exclusive with the `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts, which configure
172
+ * the default system this one replaces — combining the two throws.
173
+ *
174
+ * You own a supplied system's lifecycle: `start()` it before `run()` (which throws otherwise) and `stop()` it once
175
+ * every crawler borrowing it has finished. The crawler does neither on your behalf.
159
176
  */
160
- autoscaledPoolOptions?: AutoscaledPoolOptions;
177
+ concurrencySystem?: IConcurrencySystem;
161
178
  /**
162
179
  * Sets the minimum concurrency (parallelism) for the crawl. Shortcut for the
163
- * AutoscaledPool {@link AutoscaledPoolOptions.minConcurrency|`minConcurrency`} option.
180
+ * {@link ConcurrencySystemOptions.minConcurrency|`minConcurrency`} option of the crawler's default
181
+ * {@link ConcurrencySystem}.
164
182
  * > *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.
165
183
  * If not sure, it's better to keep the default value and the concurrency will scale up automatically.
166
184
  */
167
185
  minConcurrency?: number;
168
186
  /**
169
187
  * Sets the maximum concurrency (parallelism) for the crawl. Shortcut for the
170
- * AutoscaledPool {@link AutoscaledPoolOptions.maxConcurrency|`maxConcurrency`} option.
188
+ * {@link ConcurrencySystemOptions.maxConcurrency|`maxConcurrency`} option of the crawler's default
189
+ * {@link ConcurrencySystem}.
171
190
  */
172
191
  maxConcurrency?: number;
173
192
  /**
174
193
  * The maximum number of requests per minute the crawler should run.
175
194
  * By default, this is set to `Infinity`, but we can pass any positive, non-zero integer.
176
- * Shortcut for the AutoscaledPool {@link AutoscaledPoolOptions.maxTasksPerMinute|`maxTasksPerMinute`} option.
195
+ * Shortcut for the {@link ConcurrencySystemOptions.maxTasksPerMinute|`maxTasksPerMinute`} option of the
196
+ * crawler's default {@link ConcurrencySystem}.
177
197
  */
178
198
  maxRequestsPerMinute?: number;
179
199
  /**
@@ -335,11 +355,10 @@ export interface BasicCrawlerOptions<Context extends CrawlingContext = CrawlingC
335
355
  *
336
356
  * New requests are only dispatched when there is enough free CPU and memory available,
337
357
  * using the functionality provided by the {@link AutoscaledPool} class.
338
- * All {@link AutoscaledPool} configuration options can be passed to the {@link BasicCrawlerOptions.autoscaledPoolOptions|`autoscaledPoolOptions`}
339
- * parameter of the `BasicCrawler` constructor.
340
- * For user convenience, the {@link AutoscaledPoolOptions.minConcurrency|`minConcurrency`} and
341
- * {@link AutoscaledPoolOptions.maxConcurrency|`maxConcurrency`} options of the
342
- * underlying {@link AutoscaledPool} constructor are available directly in the `BasicCrawler` constructor.
358
+ * Concurrency is tuned via the {@link BasicCrawlerOptions.minConcurrency|`minConcurrency`},
359
+ * {@link BasicCrawlerOptions.maxConcurrency|`maxConcurrency`} and
360
+ * {@link BasicCrawlerOptions.maxRequestsPerMinute|`maxRequestsPerMinute`} shortcuts, or, for finer control, by
361
+ * injecting a pre-configured {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`}.
343
362
  *
344
363
  * **Example usage:**
345
364
  *
@@ -431,11 +450,22 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
431
450
  */
432
451
  private requestManagerTimeoutsApplied;
433
452
  /**
434
- * A reference to the underlying {@link AutoscaledPool} class that manages the concurrency of the crawler.
453
+ * Resolves the governor for one run: either the injected
454
+ * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} (borrowed) or a freshly built default with
455
+ * the concurrency shortcuts folded in (owned, so the crawler starts and stops it).
456
+ */
457
+ private readonly resolveConcurrencySystem;
458
+ /** As resolved by `_init()`. Absent until the first run, so a `teardown()` before it is a no-op. */
459
+ private concurrencySystemDep?;
460
+ /**
461
+ * A reference to the underlying {@link AutoscaledPool} class that runs the crawler's task loop.
435
462
  * > *NOTE:* This property is only initialized after calling the {@link BasicCrawler.run|`crawler.run()`} function.
436
- * We can use it to change the concurrency settings on the fly,
437
- * to pause the crawler by calling {@link AutoscaledPool.pause|`autoscaledPool.pause()`}
463
+ * We can use it to pause the crawler by calling {@link AutoscaledPool.pause|`autoscaledPool.pause()`}
438
464
  * or to abort it by calling {@link AutoscaledPool.abort|`autoscaledPool.abort()`}.
465
+ *
466
+ * The pool only exposes read-only concurrency telemetry. To tune concurrency at runtime, keep a reference to a
467
+ * {@link ConcurrencySystem} and inject it via
468
+ * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`}.
439
469
  */
440
470
  autoscaledPool?: AutoscaledPool;
441
471
  /**
@@ -482,6 +512,12 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
482
512
  protected blockedStatusCodes: Set<number>;
483
513
  protected readonly additionalHttpErrorStatusCodes: Set<number>;
484
514
  private ignoreHttpErrorStatusCodes;
515
+ /**
516
+ * The resolved task-loop options for the crawler's own {@link AutoscaledPool} — the crawler-owned
517
+ * `runTaskFunction`, the (possibly user-overridden) ready/finished predicates and cadence/logging. Concurrency
518
+ * configuration lives on the {@link ConcurrencySystem} instead, and the pool's `consumer` identity is the
519
+ * crawler's own, so neither is settable here.
520
+ */
485
521
  private autoscaledPoolOptions;
486
522
  protected readonly httpClient: BaseHttpClient;
487
523
  protected readonly retryOnBlocked: boolean;
@@ -519,6 +555,8 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
519
555
  maxCrawlDepth: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
520
556
  // @ts-ignore optional peer dependency or compatibility with es2022
521
557
  autoscaledPoolOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
558
+ // @ts-ignore optional peer dependency or compatibility with es2022
559
+ concurrencySystem: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
522
560
  // @ts-ignore optional peer dependency or compatibility with es2022
523
561
  sessionPool: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
524
562
  // @ts-ignore optional peer dependency or compatibility with es2022
@@ -566,6 +604,15 @@ export declare class BasicCrawler<Context extends CrawlingContext = CrawlingCont
566
604
  * All `BasicCrawler` parameters are passed via an options object.
567
605
  */
568
606
  constructor(options?: BasicCrawlerOptions<Context, ContextExtension, ExtendedContext, Routes> & RequireContextPipeline<CrawlingContext, Context>);
607
+ /**
608
+ * Builds the crawler-owned default {@link ConcurrencySystem} from the resolved
609
+ * `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts. Not called when a
610
+ * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} was injected.
611
+ *
612
+ * Subclasses may override this to tune the default system (e.g. {@link HttpCrawler} raises the starting
613
+ * concurrency and relaxes the event loop signal) while still honouring the user's shortcuts.
614
+ */
615
+ protected createDefaultConcurrencySystem(options: ConcurrencySystemOptions): ConcurrencySystem;
569
616
  /**
570
617
  * Determines if the given HTTP status code is an error status code given
571
618
  * the default behaviour and user-set preferences.
@@ -1,11 +1,11 @@
1
1
  import { writeFile } from 'node:fs/promises';
2
2
  import { dirname } from 'node:path';
3
- import { AutoscaledPool, bindMethodsToServiceLocator, BLOCKED_STATUS_CODES, ContextPipeline, ContextPipelineCleanupError, ContextPipelineInitializationError, ContextPipelineInterruptedError, CriticalError, Dataset, enqueueLinks, EnqueueStrategy, getObjectType, KeyValueStore, log, LogLevel, mergeCookies, MissingSessionError, NavigationSkippedError, NonRetryableError, OwnedOrInjected, purgeDefaultStorages, RequestHandlerError, RequestManagerTandem, RequestQueue, RequestState, RetryRequestError, Router, ServiceLocator, serviceLocator, Session, SessionError, SessionPool, Statistics, validateUserData, validators, } from '@crawlee/core';
3
+ import { AutoscaledPool, bindMethodsToServiceLocator, BLOCKED_STATUS_CODES, ConcurrencySystem, ContextPipeline, ContextPipelineCleanupError, ContextPipelineInitializationError, ContextPipelineInterruptedError, CriticalError, Dataset, enqueueLinks, EnqueueStrategy, getObjectType, KeyValueStore, log, LogLevel, mergeCookies, MissingSessionError, NavigationSkippedError, NonRetryableError, OwnedOrInjected, purgeDefaultStorages, RequestHandlerError, RequestManagerTandem, RequestQueue, RequestState, RetryRequestError, Router, ServiceLocator, serviceLocator, Session, SessionError, SessionPool, Statistics, validateUserData, validators, } from '@crawlee/core';
4
4
  import { FetchHttpClient } from '@crawlee/http-client';
5
5
  import { isAsyncIterable, isIterable, RobotsTxtFile, ROTATE_PROXY_ERRORS } from '@crawlee/utils';
6
6
  import { stringify } from 'csv-stringify/sync';
7
7
  import { ensureDir, writeJSON } from 'fs-extra/esm';
8
- import ow from 'ow';
8
+ import ow, { ArgumentError } from 'ow';
9
9
  import { getDomain } from 'tldts';
10
10
  import { LruCache } from '@apify/datastructures';
11
11
  import { addTimeoutToPromise, TimeoutError } from '@apify/timeout';
@@ -82,11 +82,22 @@ export class BasicCrawler {
82
82
  */
83
83
  requestManagerTimeoutsApplied = false;
84
84
  /**
85
- * A reference to the underlying {@link AutoscaledPool} class that manages the concurrency of the crawler.
85
+ * Resolves the governor for one run: either the injected
86
+ * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} (borrowed) or a freshly built default with
87
+ * the concurrency shortcuts folded in (owned, so the crawler starts and stops it).
88
+ */
89
+ resolveConcurrencySystem;
90
+ /** As resolved by `_init()`. Absent until the first run, so a `teardown()` before it is a no-op. */
91
+ concurrencySystemDep;
92
+ /**
93
+ * A reference to the underlying {@link AutoscaledPool} class that runs the crawler's task loop.
86
94
  * > *NOTE:* This property is only initialized after calling the {@link BasicCrawler.run|`crawler.run()`} function.
87
- * We can use it to change the concurrency settings on the fly,
88
- * to pause the crawler by calling {@link AutoscaledPool.pause|`autoscaledPool.pause()`}
95
+ * We can use it to pause the crawler by calling {@link AutoscaledPool.pause|`autoscaledPool.pause()`}
89
96
  * or to abort it by calling {@link AutoscaledPool.abort|`autoscaledPool.abort()`}.
97
+ *
98
+ * The pool only exposes read-only concurrency telemetry. To tune concurrency at runtime, keep a reference to a
99
+ * {@link ConcurrencySystem} and inject it via
100
+ * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`}.
90
101
  */
91
102
  autoscaledPool;
92
103
  /**
@@ -146,6 +157,12 @@ export class BasicCrawler {
146
157
  blockedStatusCodes = new Set();
147
158
  additionalHttpErrorStatusCodes;
148
159
  ignoreHttpErrorStatusCodes;
160
+ /**
161
+ * The resolved task-loop options for the crawler's own {@link AutoscaledPool} — the crawler-owned
162
+ * `runTaskFunction`, the (possibly user-overridden) ready/finished predicates and cadence/logging. Concurrency
163
+ * configuration lives on the {@link ConcurrencySystem} instead, and the pool's `consumer` identity is the
164
+ * crawler's own, so neither is settable here.
165
+ */
149
166
  autoscaledPoolOptions;
150
167
  httpClient;
151
168
  retryOnBlocked;
@@ -173,6 +190,7 @@ export class BasicCrawler {
173
190
  maxRequestsPerCrawl: ow.optional.number,
174
191
  maxCrawlDepth: ow.optional.number,
175
192
  autoscaledPoolOptions: ow.optional.object,
193
+ concurrencySystem: ow.optional.object,
176
194
  sessionPool: ow.optional.object.validate(validators.sessionPool),
177
195
  proxyConfiguration: ow.optional.object.validate(validators.proxyConfiguration),
178
196
  statusMessageLoggingInterval: ow.optional.number,
@@ -205,11 +223,20 @@ export class BasicCrawler {
205
223
  // oxlint-disable-next-line typescript/no-deprecated -- still accepted and folded into `requestManager` for back-compat
206
224
  requestList,
207
225
  // oxlint-disable-next-line typescript/no-deprecated -- still accepted and folded into `requestManager` for back-compat
208
- requestQueue, requestManager, maxRequestRetries = 3, sameDomainDelaySecs = 0, maxRequestsPerCrawl, maxCrawlDepth, autoscaledPoolOptions = {}, keepAlive, sessionPool, proxyConfiguration, additionalHttpErrorStatusCodes = [], ignoreHttpErrorStatusCodes = [],
226
+ requestQueue, requestManager, maxRequestRetries = 3, sameDomainDelaySecs = 0, maxRequestsPerCrawl, maxCrawlDepth, autoscaledPoolOptions = {}, concurrencySystem, keepAlive, sessionPool, proxyConfiguration, additionalHttpErrorStatusCodes = [], ignoreHttpErrorStatusCodes = [],
209
227
  // Service locator options
210
228
  configuration, storageBackend, eventManager, logger,
211
229
  // AutoscaledPool shorthands
212
230
  minConcurrency, maxConcurrency, maxRequestsPerMinute, blockedStatusCodes: blockedStatusCodesInput, retryOnBlocked = false, respectRobotsTxtFile = false, onSkippedRequest, requestHandler, requestHandlerTimeoutSecs, errorHandler, failedRequestHandler, statusMessageLoggingInterval = 10, statusMessageCallback, statisticsOptions, httpClient, id, } = options;
231
+ // All concurrency configuration lives on the `ConcurrencySystem`, so the shortcuts have nowhere to go once
232
+ // one is supplied - and silently dropping a `maxConcurrency` the user asked for is how crawls end up
233
+ // hammering a site.
234
+ if (concurrencySystem !== undefined &&
235
+ (minConcurrency !== undefined || maxConcurrency !== undefined || maxRequestsPerMinute !== undefined)) {
236
+ throw new ArgumentError('The `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts cannot be combined with ' +
237
+ '`concurrencySystem` - they configure the default `ConcurrencySystem` that a supplied one ' +
238
+ 'replaces. Pass them to the `ConcurrencySystem` constructor instead.', this.constructor);
239
+ }
213
240
  // Create per-crawler service locator if custom services were provided.
214
241
  // This wraps every method on the crawler instance so that calls to the global `serviceLocator`
215
242
  // (via AsyncLocalStorage) resolve to this scoped instance instead.
@@ -317,9 +344,6 @@ export class BasicCrawler {
317
344
  isFinishedFunction = async () => false;
318
345
  }
319
346
  const basicCrawlerAutoscaledPoolConfiguration = {
320
- minConcurrency: minConcurrency ?? autoscaledPoolOptions?.minConcurrency,
321
- maxConcurrency: maxConcurrency ?? autoscaledPoolOptions?.maxConcurrency,
322
- maxTasksPerMinute: maxRequestsPerMinute ?? autoscaledPoolOptions?.maxTasksPerMinute,
323
347
  runTaskFunction: async () => {
324
348
  const source = this.requestManager;
325
349
  if (!source)
@@ -405,11 +429,28 @@ export class BasicCrawler {
405
429
  log: this.log,
406
430
  };
407
431
  this.autoscaledPoolOptions = { ...autoscaledPoolOptions, ...basicCrawlerAutoscaledPoolConfiguration };
432
+ this.resolveConcurrencySystem = () => OwnedOrInjected.resolve(concurrencySystem, () => this.createDefaultConcurrencySystem({
433
+ minConcurrency,
434
+ maxConcurrency,
435
+ maxTasksPerMinute: maxRequestsPerMinute,
436
+ log: this.log,
437
+ }));
408
438
  }
409
439
  finally {
410
440
  serviceLocatorScope.exitScope();
411
441
  }
412
442
  }
443
+ /**
444
+ * Builds the crawler-owned default {@link ConcurrencySystem} from the resolved
445
+ * `minConcurrency`/`maxConcurrency`/`maxRequestsPerMinute` shortcuts. Not called when a
446
+ * {@link BasicCrawlerOptions.concurrencySystem|`concurrencySystem`} was injected.
447
+ *
448
+ * Subclasses may override this to tune the default system (e.g. {@link HttpCrawler} raises the starting
449
+ * concurrency and relaxes the event loop signal) while still honouring the user's shortcuts.
450
+ */
451
+ createDefaultConcurrencySystem(options) {
452
+ return new ConcurrencySystem(options);
453
+ }
413
454
  /**
414
455
  * Determines if the given HTTP status code is an error status code given
415
456
  * the default behaviour and user-set preferences.
@@ -648,8 +689,19 @@ export class BasicCrawler {
648
689
  if (requests) {
649
690
  await this.addRequests(requests, addRequestsOptions);
650
691
  }
651
- await this._init();
652
- await this.stats.startCapturing();
692
+ try {
693
+ await this._init();
694
+ await this.stats.startCapturing();
695
+ }
696
+ catch (error) {
697
+ // Clean up here before propagating, otherwise a failed startup would leave the process hanging.
698
+ await this.teardown().catch((teardownError) => {
699
+ this.log.exception(teardownError, 'Cleaning up after a failed crawler startup failed.');
700
+ });
701
+ // The run never began, so let the instance be run again instead of leaving it wedged as `running`.
702
+ this.running = false;
703
+ throw error;
704
+ }
653
705
  const periodicLogger = this.getPeriodicLogger();
654
706
  this.setStatusMessage('Starting the crawler.', { level: 'INFO' });
655
707
  const sigintHandler = async () => {
@@ -977,7 +1029,16 @@ export class BasicCrawler {
977
1029
  await eventManager.init();
978
1030
  this._closeEvents = true;
979
1031
  }
980
- this.autoscaledPool = new AutoscaledPool(this.autoscaledPoolOptions);
1032
+ // An owned governor is rebuilt (and started) for every run, so it always starts from a clean slate — stale
1033
+ // resource snapshots or a previous run's scaled desired concurrency would otherwise distort this run's
1034
+ // scaling. An injected one is long-lived and its lifecycle belongs to the caller.
1035
+ this.concurrencySystemDep = this.resolveConcurrencySystem();
1036
+ await this.concurrencySystemDep.ifOwned((system) => system.start());
1037
+ this.autoscaledPool = new AutoscaledPool({
1038
+ ...this.autoscaledPoolOptions,
1039
+ concurrencySystem: this.concurrencySystemDep.value,
1040
+ consumer: this.identity,
1041
+ });
981
1042
  await this.getRequestManager();
982
1043
  }
983
1044
  async runRequestHandler(crawlingContext) {
@@ -1356,6 +1417,7 @@ export class BasicCrawler {
1356
1417
  }
1357
1418
  await this.sessionPoolDep.ifOwned((pool) => pool.teardown());
1358
1419
  await this.autoscaledPool?.abort();
1420
+ await this.concurrencySystemDep?.ifOwned((system) => system.stop());
1359
1421
  }
1360
1422
  _getCookieHeaderFromRequest(request) {
1361
1423
  if (request.headers?.Cookie && request.headers?.cookie) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crawlee/basic",
3
- "version": "4.0.0-beta.88",
3
+ "version": "4.0.0-beta.89",
4
4
  "description": "The scalable web crawling and scraping library for JavaScript/Node.js. Enables development of data extraction and web automation jobs (not only) with headless Chrome and Puppeteer.",
5
5
  "engines": {
6
6
  "node": ">=22.0.0"
@@ -42,10 +42,10 @@
42
42
  "@apify/datastructures": "^2.0.0",
43
43
  "@apify/timeout": "^0.3.2",
44
44
  "@apify/utilities": "^2.15.5",
45
- "@crawlee/core": "4.0.0-beta.88",
46
- "@crawlee/http-client": "4.0.0-beta.88",
47
- "@crawlee/types": "4.0.0-beta.88",
48
- "@crawlee/utils": "4.0.0-beta.88",
45
+ "@crawlee/core": "4.0.0-beta.89",
46
+ "@crawlee/http-client": "4.0.0-beta.89",
47
+ "@crawlee/types": "4.0.0-beta.89",
48
+ "@crawlee/utils": "4.0.0-beta.89",
49
49
  "csv-stringify": "^6.5.2",
50
50
  "fs-extra": "^11.3.0",
51
51
  "ow": "^2.0.0",
@@ -54,7 +54,7 @@
54
54
  "type-fest": "^4.41.0"
55
55
  },
56
56
  "optionalDependencies": {
57
- "@crawlee/impit-client": "^4.0.0-beta.88"
57
+ "@crawlee/impit-client": "^4.0.0-beta.89"
58
58
  },
59
59
  "lerna": {
60
60
  "command": {
@@ -63,5 +63,5 @@
63
63
  }
64
64
  }
65
65
  },
66
- "gitHead": "4f7d17669d59250cf802591e73498793de28967d"
66
+ "gitHead": "9279941162eb4be0a9113f768b5bd79d27e66eba"
67
67
  }