@crawlee/core 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.
Files changed (45) hide show
  1. package/autoscaling/autoscaled_pool.js +20 -12
  2. package/autoscaling/concurrency_system.d.ts +2 -2
  3. package/autoscaling/concurrency_system.js +31 -20
  4. package/autoscaling/index.d.ts +1 -1
  5. package/autoscaling/index.js +1 -1
  6. package/autoscaling/load_signal.d.ts +7 -6
  7. package/autoscaling/load_signal.js +2 -1
  8. package/autoscaling/snapshotter.d.ts +6 -6
  9. package/autoscaling/snapshotter.js +9 -9
  10. package/autoscaling/{client_load_signal.d.ts → storage_backend_load_signal.d.ts} +13 -12
  11. package/autoscaling/{client_load_signal.js → storage_backend_load_signal.js} +11 -11
  12. package/autoscaling/system_status.d.ts +8 -8
  13. package/autoscaling/system_status.js +2 -2
  14. package/configuration.d.ts +15 -15
  15. package/configuration.js +3 -3
  16. package/crawlers/crawler_commons.d.ts +8 -54
  17. package/crawlers/statistics.d.ts +1 -1
  18. package/crawlers/statistics.js +14 -14
  19. package/debug.js +4 -4
  20. package/enqueue_links/enqueue_links.d.ts +33 -61
  21. package/enqueue_links/enqueue_links.js +35 -152
  22. package/enqueue_links/shared.d.ts +17 -4
  23. package/enqueue_links/shared.js +28 -1
  24. package/memory-storage/resource-clients/dataset.js +2 -8
  25. package/memory-storage/resource-clients/key-value-store.js +23 -26
  26. package/memory-storage/resource-clients/request-queue.js +9 -22
  27. package/package.json +7 -8
  28. package/proxy_configuration.js +10 -6
  29. package/request.d.ts +2 -2
  30. package/request.js +44 -31
  31. package/router.d.ts +5 -5
  32. package/serialization.js +6 -4
  33. package/session_pool/session.js +22 -20
  34. package/session_pool/session_pool.js +20 -17
  35. package/storages/dataset.js +11 -9
  36. package/storages/key_value_store.js +30 -27
  37. package/storages/request_list.d.ts +2 -1
  38. package/storages/request_list.js +26 -21
  39. package/storages/request_queue.js +64 -59
  40. package/storages/sitemap_request_loader.d.ts +1 -1
  41. package/storages/sitemap_request_loader.js +22 -22
  42. package/storages/throttling_request_manager.js +11 -9
  43. package/storages/utils.d.ts +2 -1
  44. package/validators.d.ts +22 -25
  45. package/validators.js +13 -25
@@ -1,8 +1,26 @@
1
- import ow from 'ow';
1
+ import { z } from 'zod';
2
2
  import { addTimeoutToPromise } from '@apify/timeout';
3
3
  import { betterClearInterval, betterSetInterval } from '@apify/utilities';
4
4
  import { CriticalError } from '../errors.js';
5
5
  import { serviceLocator } from '../service_locator.js';
6
+ import { parseArgument, schemas, validators } from '../validators.js';
7
+ // `schemas.anyObject` and `objectWithKeys`-based validators pass values through by reference
8
+ // (object schemas return a pruned plain copy), so class instances like loggers and the
9
+ // concurrency system keep their prototype.
10
+ const autoscaledPoolOptionsSchema = z.strictObject({
11
+ runTaskFunction: schemas.anyFunction,
12
+ isFinishedFunction: schemas.anyFunction,
13
+ isTaskReadyFunction: schemas.anyFunction,
14
+ maybeRunIntervalSecs: schemas.anyNumber
15
+ .refine((value) => value > 0, 'Expected a number greater than 0')
16
+ .default(0.5),
17
+ taskTimeoutSecs: schemas.anyNumber
18
+ .refine((value) => value >= 0, 'Expected a number greater than or equal to 0')
19
+ .default(0),
20
+ log: validators.logger.default(() => serviceLocator.getLogger()),
21
+ concurrencySystem: schemas.anyObject,
22
+ consumer: schemas.anyObject.refine((value) => typeof value.id === 'string' && value.id.length > 0, "Expected an object with a non-empty string 'id'"),
23
+ });
6
24
  /**
7
25
  * Manages a pool of asynchronous resource-intensive tasks that are executed in parallel.
8
26
  * The pool only starts new tasks while its {@link IConcurrencySystem|concurrency system} reports free capacity —
@@ -78,17 +96,7 @@ export class AutoscaledPool {
78
96
  */
79
97
  #ownConcurrency = 0;
80
98
  constructor(options) {
81
- ow(options, ow.object.exactShape({
82
- runTaskFunction: ow.function,
83
- isFinishedFunction: ow.function,
84
- isTaskReadyFunction: ow.function,
85
- maybeRunIntervalSecs: ow.optional.number.greaterThan(0),
86
- taskTimeoutSecs: ow.optional.number.greaterThanOrEqual(0),
87
- log: ow.optional.object,
88
- concurrencySystem: ow.object,
89
- consumer: ow.object.partialShape({ id: ow.string.nonEmpty }),
90
- }));
91
- const { runTaskFunction, isFinishedFunction, isTaskReadyFunction, maybeRunIntervalSecs = 0.5, taskTimeoutSecs = 0, log = serviceLocator.getLogger(), concurrencySystem, consumer, } = options;
99
+ const { runTaskFunction, isFinishedFunction, isTaskReadyFunction, maybeRunIntervalSecs, taskTimeoutSecs, log, concurrencySystem, consumer, } = parseArgument(options, autoscaledPoolOptionsSchema, 'AutoscaledPoolOptions');
92
100
  this.#log = log.child({ prefix: 'AutoscaledPool' });
93
101
  // Configurable properties.
94
102
  this.#maybeRunIntervalMillis = maybeRunIntervalSecs * 1000;
@@ -53,8 +53,8 @@ export interface ConcurrencySystemOptions {
53
53
  autoscaleIntervalSecs?: number;
54
54
  /**
55
55
  * The signals that tell the system whether the machine is overloaded: per-resource tuning for the built-in four
56
- * (memory, event loop, CPU, client) plus any {@link LoadSignalsOptions.custom|`custom`} implementations of
57
- * your own. See {@link LoadSignalsOptions}.
56
+ * (memory, event loop, CPU, storage backend) plus any {@link LoadSignalsOptions.custom|`custom`}
57
+ * implementations of your own. See {@link LoadSignalsOptions}.
58
58
  */
59
59
  loadSignals?: LoadSignalsOptions;
60
60
  /**
@@ -1,8 +1,34 @@
1
- import ow from 'ow';
1
+ import { z } from 'zod';
2
2
  import { betterClearInterval, betterSetInterval } from '@apify/utilities';
3
3
  import { serviceLocator } from '../service_locator.js';
4
+ import { parseArgument, schemas, validators } from '../validators.js';
4
5
  import { Snapshotter } from './snapshotter.js';
5
6
  import { SystemStatus } from './system_status.js';
7
+ const concurrencySchema = z.number().int().gte(1).optional();
8
+ // `schemas.anyObject` passes values through by reference, so the load signal instances inside
9
+ // `loadSignals` and class instances like loggers keep their prototype.
10
+ const concurrencySystemOptionsSchema = z.strictObject({
11
+ maxConcurrency: z.number().int().gte(1).default(200),
12
+ minConcurrency: z.number().int().gte(1).default(1),
13
+ desiredConcurrency: z.number().int().gte(1).optional(),
14
+ desiredConcurrencyRatio: z.number().gt(0).lt(1).default(0.9),
15
+ scaleUpStepRatio: z.number().gt(0).lt(1).default(0.05),
16
+ scaleDownStepRatio: z.number().gt(0).lt(1).default(0.05),
17
+ loggingIntervalSecs: schemas.anyNumber
18
+ .refine((value) => value > 0, 'Expected a number greater than 0')
19
+ .nullish()
20
+ .default(60),
21
+ autoscaleIntervalSecs: schemas.anyNumber
22
+ .refine((value) => value > 0, 'Expected a number greater than 0')
23
+ .default(10),
24
+ loadSignals: schemas.anyObject.default(() => ({})),
25
+ snapshotHistorySecs: schemas.anyNumber.refine((value) => value > 0, 'Expected a number greater than 0').optional(),
26
+ currentHistorySecs: schemas.anyNumber.refine((value) => value > 0, 'Expected a number greater than 0').optional(),
27
+ log: validators.logger.default(() => serviceLocator.getLogger()),
28
+ maxTasksPerMinute: z
29
+ .union([z.number().int().gte(1), z.literal(Number.POSITIVE_INFINITY)])
30
+ .default(Number.POSITIVE_INFINITY),
31
+ });
6
32
  /**
7
33
  * The shareable "governor" behind an {@link AutoscaledPool}: it decides whether there is free compute for one more
8
34
  * task by combining live system load (via an internal {@link Snapshotter}) with a concurrency budget it autoscales
@@ -44,22 +70,7 @@ export class ConcurrencySystem {
44
70
  /** Set once per session, so a pool outliving `stop()` is reported once rather than every half second. */
45
71
  #warnedAboutQueryWhileStopped = false;
46
72
  constructor(options = {}) {
47
- ow(options, ow.object.exactShape({
48
- maxConcurrency: ow.optional.number.integer.greaterThanOrEqual(1),
49
- minConcurrency: ow.optional.number.integer.greaterThanOrEqual(1),
50
- desiredConcurrency: ow.optional.number.integer.greaterThanOrEqual(1),
51
- desiredConcurrencyRatio: ow.optional.number.greaterThan(0).lessThan(1),
52
- scaleUpStepRatio: ow.optional.number.greaterThan(0).lessThan(1),
53
- scaleDownStepRatio: ow.optional.number.greaterThan(0).lessThan(1),
54
- loggingIntervalSecs: ow.any(ow.number.greaterThan(0), ow.nullOrUndefined),
55
- autoscaleIntervalSecs: ow.optional.number.greaterThan(0),
56
- loadSignals: ow.optional.object,
57
- snapshotHistorySecs: ow.optional.number.greaterThan(0),
58
- currentHistorySecs: ow.optional.number.greaterThan(0),
59
- log: ow.optional.object,
60
- maxTasksPerMinute: ow.optional.number.integerOrInfinite.greaterThanOrEqual(1),
61
- }));
62
- const { maxConcurrency = 200, minConcurrency = 1, desiredConcurrency, desiredConcurrencyRatio = 0.9, scaleUpStepRatio = 0.05, scaleDownStepRatio = 0.05, loggingIntervalSecs = 60, autoscaleIntervalSecs = 10, loadSignals = {}, snapshotHistorySecs, currentHistorySecs, log = serviceLocator.getLogger(), maxTasksPerMinute = Infinity, } = options;
73
+ const { maxConcurrency, minConcurrency, desiredConcurrency, desiredConcurrencyRatio, scaleUpStepRatio, scaleDownStepRatio, loggingIntervalSecs, autoscaleIntervalSecs, loadSignals, snapshotHistorySecs, currentHistorySecs, log, maxTasksPerMinute, } = parseArgument(options, concurrencySystemOptionsSchema, 'ConcurrencySystemOptions');
63
74
  this.log = log.child({ prefix: 'ConcurrencySystem' });
64
75
  this.desiredConcurrencyRatio = desiredConcurrencyRatio;
65
76
  this.scaleUpStepRatio = scaleUpStepRatio;
@@ -99,7 +110,7 @@ export class ConcurrencySystem {
99
110
  * If you're not sure, just keep the default value and the concurrency will scale up automatically.
100
111
  */
101
112
  set minConcurrency(value) {
102
- ow(value, ow.optional.number.integer.greaterThanOrEqual(1));
113
+ parseArgument(value, concurrencySchema);
103
114
  this.#minConcurrency = value;
104
115
  this.clampDesiredConcurrency();
105
116
  }
@@ -116,7 +127,7 @@ export class ConcurrencySystem {
116
127
  * limit as they settle).
117
128
  */
118
129
  set maxConcurrency(value) {
119
- ow(value, ow.optional.number.integer.greaterThanOrEqual(1));
130
+ parseArgument(value, concurrencySchema);
120
131
  this.#maxConcurrency = value;
121
132
  this.clampDesiredConcurrency();
122
133
  }
@@ -132,7 +143,7 @@ export class ConcurrencySystem {
132
143
  * in parallel if there's large enough supply of tasks.
133
144
  */
134
145
  set desiredConcurrency(value) {
135
- ow(value, ow.optional.number.integer.greaterThanOrEqual(1));
146
+ parseArgument(value, concurrencySchema);
136
147
  this.#desiredConcurrency = value;
137
148
  this.clampDesiredConcurrency();
138
149
  }
@@ -1,9 +1,9 @@
1
1
  export * from './autoscaled_pool.js';
2
2
  export * from './concurrency_system.js';
3
- export * from './client_load_signal.js';
4
3
  export * from './cpu_load_signal.js';
5
4
  export * from './event_loop_load_signal.js';
6
5
  export * from './load_signal.js';
7
6
  export * from './memory_load_signal.js';
8
7
  export * from './snapshotter.js';
8
+ export * from './storage_backend_load_signal.js';
9
9
  export * from './system_status.js';
@@ -1,9 +1,9 @@
1
1
  export * from './autoscaled_pool.js';
2
2
  export * from './concurrency_system.js';
3
- export * from './client_load_signal.js';
4
3
  export * from './cpu_load_signal.js';
5
4
  export * from './event_loop_load_signal.js';
6
5
  export * from './load_signal.js';
7
6
  export * from './memory_load_signal.js';
8
7
  export * from './snapshotter.js';
8
+ export * from './storage_backend_load_signal.js';
9
9
  export * from './system_status.js';
@@ -1,4 +1,4 @@
1
- import type { ClientInfo } from './system_status.js';
1
+ import type { LoadSignalInfo } from './system_status.js';
2
2
  /**
3
3
  * A snapshot of a resource's overload state at a point in time.
4
4
  */
@@ -22,7 +22,7 @@ export interface LoadSignalStartContext {
22
22
  * A signal that reports whether a particular resource is overloaded. The {@link ConcurrencySystem} aggregates
23
23
  * several of them — if any one reports overload, the system is overloaded.
24
24
  *
25
- * The built-in signals cover memory, CPU, event loop and storage-client rate limits. Implement this interface to add
25
+ * The built-in signals cover memory, CPU, event loop and storage backend rate limits. Implement this interface to add
26
26
  * your own (navigation timeouts, proxy health, …) and pass them via
27
27
  * {@link LoadSignalsOptions.custom|`loadSignals.custom`}; {@link SnapshotStore} does the time-windowed
28
28
  * bookkeeping if you want it. Each built-in is also a public class, so one can be *wrapped* rather than reimplemented
@@ -32,8 +32,8 @@ export interface LoadSignal {
32
32
  /**
33
33
  * This signal's key in the reported {@link SystemInfo}, also used in logging — so it must be unique among the
34
34
  * signals of one {@link ConcurrencySystem}, which throws on a duplicate. The four built-in names (`memInfo`,
35
- * `eventLoopInfo`, `cpuInfo`, `clientInfo`) land in the correspondingly named `SystemInfo` fields rather than the
36
- * `loadSignalInfo` bag; taking one over means switching that built-in off.
35
+ * `eventLoopInfo`, `cpuInfo`, `storageBackendInfo`) land in the correspondingly named `SystemInfo` fields rather
36
+ * than the `loadSignalInfo` bag; taking one over means switching that built-in off.
37
37
  */
38
38
  readonly name: string;
39
39
  /**
@@ -79,7 +79,8 @@ export declare class SnapshotStore<T extends LoadSnapshot = LoadSnapshot> {
79
79
  getSample(sampleDurationMillis?: number): T[];
80
80
  /**
81
81
  * Direct, unwindowed access to the underlying array — used by signals whose handler needs the previous snapshot
82
- * to compute a delta (e.g. the event loop and client signals read the last entry to measure change since it).
82
+ * to compute a delta (e.g. the event loop and storage backend signals read the last entry to measure change since
83
+ * it).
83
84
  */
84
85
  getAll(): T[];
85
86
  /**
@@ -96,4 +97,4 @@ export declare class SnapshotStore<T extends LoadSnapshot = LoadSnapshot> {
96
97
  * evaluation logic used by `SystemStatus` for all signal types.
97
98
  * @internal
98
99
  */
99
- export declare function evaluateLoadSignalSample(sample: LoadSnapshot[], overloadedRatio: number): ClientInfo;
100
+ export declare function evaluateLoadSignalSample(sample: LoadSnapshot[], overloadedRatio: number): LoadSignalInfo;
@@ -57,7 +57,8 @@ export class SnapshotStore {
57
57
  }
58
58
  /**
59
59
  * Direct, unwindowed access to the underlying array — used by signals whose handler needs the previous snapshot
60
- * to compute a delta (e.g. the event loop and client signals read the last entry to measure change since it).
60
+ * to compute a delta (e.g. the event loop and storage backend signals read the last entry to measure change since
61
+ * it).
61
62
  */
62
63
  getAll() {
63
64
  return this.#snapshots;
@@ -1,8 +1,8 @@
1
- import type { ClientLoadSignalOptions } from './client_load_signal.js';
2
1
  import type { CpuLoadSignalOptions } from './cpu_load_signal.js';
3
2
  import type { EventLoopLoadSignalOptions } from './event_loop_load_signal.js';
4
3
  import type { LoadSignal, LoadSignalStartContext } from './load_signal.js';
5
4
  import type { MemoryLoadSignalOptions } from './memory_load_signal.js';
5
+ import type { StorageBackendLoadSignalOptions } from './storage_backend_load_signal.js';
6
6
  /**
7
7
  * The load signals a {@link ConcurrencySystem} watches to decide whether the machine is overloaded.
8
8
  *
@@ -31,11 +31,11 @@ export interface LoadSignalsOptions {
31
31
  */
32
32
  cpu?: CpuLoadSignalOptions | false;
33
33
  /**
34
- * Tuning for the built-in {@link ClientLoadSignal} (snapshot interval + error limit + overload ratio), or
35
- * `false` to switch it off — worth doing when the storage backend reports no rate-limit statistics, since the
34
+ * Tuning for the built-in {@link StorageBackendLoadSignal} (snapshot interval + error limit + overload ratio),
35
+ * or `false` to switch it off — worth doing when the storage backend reports no rate-limit statistics, since the
36
36
  * signal otherwise polls it every second to no purpose.
37
37
  */
38
- client?: ClientLoadSignalOptions | false;
38
+ storageBackend?: StorageBackendLoadSignalOptions | false;
39
39
  /**
40
40
  * Additional {@link LoadSignal} implementations — e.g. navigation timeouts or proxy health — evaluated
41
41
  * alongside the built-in four. If any signal reports overload, the system counts as overloaded. Their lifecycle
@@ -53,8 +53,8 @@ export interface LoadSignalsOptions {
53
53
  export type SnapshotterOptions = Omit<LoadSignalsOptions, 'custom'>;
54
54
  /**
55
55
  * Owns the four built-in {@link LoadSignal} instances — {@link MemoryLoadSignal},
56
- * {@link EventLoopLoadSignal}, {@link CpuLoadSignal} and {@link ClientLoadSignal} — constructing the ones
57
- * that were not switched off and driving their shared lifecycle.
56
+ * {@link EventLoopLoadSignal}, {@link CpuLoadSignal} and {@link StorageBackendLoadSignal} — constructing
57
+ * the ones that were not switched off and driving their shared lifecycle.
58
58
  *
59
59
  * Configured indirectly through {@link ConcurrencySystemOptions.loadSignals|`loadSignals`}, whose per-signal bags
60
60
  * are simply forwarded to the corresponding constructor.
@@ -1,22 +1,22 @@
1
- import { ClientLoadSignal } from './client_load_signal.js';
2
1
  import { CpuLoadSignal } from './cpu_load_signal.js';
3
2
  import { EventLoopLoadSignal } from './event_loop_load_signal.js';
4
3
  import { MemoryLoadSignal } from './memory_load_signal.js';
4
+ import { StorageBackendLoadSignal } from './storage_backend_load_signal.js';
5
5
  /**
6
6
  * Owns the four built-in {@link LoadSignal} instances — {@link MemoryLoadSignal},
7
- * {@link EventLoopLoadSignal}, {@link CpuLoadSignal} and {@link ClientLoadSignal} — constructing the ones
8
- * that were not switched off and driving their shared lifecycle.
7
+ * {@link EventLoopLoadSignal}, {@link CpuLoadSignal} and {@link StorageBackendLoadSignal} — constructing
8
+ * the ones that were not switched off and driving their shared lifecycle.
9
9
  *
10
10
  * Configured indirectly through {@link ConcurrencySystemOptions.loadSignals|`loadSignals`}, whose per-signal bags
11
11
  * are simply forwarded to the corresponding constructor.
12
12
  * @internal
13
13
  */
14
14
  export class Snapshotter {
15
- // Absent when switched off through the corresponding option (e.g. `client: false`).
15
+ // Absent when switched off through the corresponding option (e.g. `storageBackend: false`).
16
16
  #memorySignal;
17
17
  #eventLoopSignal;
18
18
  #cpuSignal;
19
- #clientSignal;
19
+ #storageBackendSignal;
20
20
  /**
21
21
  * Returns the enabled built-in signals, so `SystemStatus` can iterate them alongside any custom `LoadSignal`
22
22
  * instances. Signals switched off through the options are simply absent — the system status reports them as
@@ -27,7 +27,7 @@ export class Snapshotter {
27
27
  this.#memorySignal,
28
28
  this.#eventLoopSignal,
29
29
  this.#cpuSignal,
30
- this.#clientSignal,
30
+ this.#storageBackendSignal,
31
31
  ];
32
32
  return builtin.filter((signal) => signal !== undefined);
33
33
  }
@@ -35,7 +35,7 @@ export class Snapshotter {
35
35
  * @param [options] All `Snapshotter` configuration options.
36
36
  */
37
37
  constructor(options = {}) {
38
- const { memory = {}, eventLoop = {}, cpu = {}, client = {} } = options;
38
+ const { memory = {}, eventLoop = {}, cpu = {}, storageBackend = {} } = options;
39
39
  // Each signal resolves its own ambient dependencies when started, and is told the window it will be sampled
40
40
  // over then too - so there is nothing to thread in here beyond the caller's tuning.
41
41
  if (memory !== false)
@@ -44,8 +44,8 @@ export class Snapshotter {
44
44
  this.#eventLoopSignal = new EventLoopLoadSignal(eventLoop);
45
45
  if (cpu !== false)
46
46
  this.#cpuSignal = new CpuLoadSignal(cpu);
47
- if (client !== false)
48
- this.#clientSignal = new ClientLoadSignal(client);
47
+ if (storageBackend !== false)
48
+ this.#storageBackendSignal = new StorageBackendLoadSignal(storageBackend);
49
49
  }
50
50
  /**
51
51
  * Starts capturing snapshots at configured intervals. The `context` carries the sample window the signals will
@@ -1,18 +1,19 @@
1
1
  import type { LoadSignal, LoadSignalStartContext, LoadSnapshot } from './load_signal.js';
2
2
  /**
3
- * A snapshot produced by the built-in client (rate-limit) signal.
3
+ * A snapshot produced by the built-in storage backend (rate-limit) signal.
4
4
  * @internal
5
5
  */
6
- export interface ClientSnapshot extends LoadSnapshot {
6
+ export interface StorageBackendSnapshot extends LoadSnapshot {
7
7
  rateLimitErrorCount: number;
8
8
  }
9
9
  /**
10
- * Tuning for the built-in **client** (rate-limit) load signal, as accepted both by {@link ClientLoadSignal} and by
11
- * the {@link LoadSignalsOptions.client|`client`} shorthand on {@link LoadSignalsOptions}.
10
+ * Tuning for the built-in **storage backend** (rate-limit) load signal, as accepted both by
11
+ * {@link StorageBackendLoadSignal} and by the
12
+ * {@link LoadSignalsOptions.storageBackend|`storageBackend`} shorthand on {@link LoadSignalsOptions}.
12
13
  */
13
- export interface ClientLoadSignalOptions {
14
+ export interface StorageBackendLoadSignalOptions {
14
15
  /**
15
- * Defines the interval of checking the current state of the remote API client, in seconds.
16
+ * Defines the interval of checking the current state of the storage backend, in seconds.
16
17
  * @default 1
17
18
  */
18
19
  snapshotIntervalSecs?: number;
@@ -22,7 +23,7 @@ export interface ClientLoadSignalOptions {
22
23
  */
23
24
  maxErrors?: number;
24
25
  /**
25
- * Maximum ratio of overloaded snapshots in a sample before the client counts as overloaded.
26
+ * Maximum ratio of overloaded snapshots in a sample before the storage backend counts as overloaded.
26
27
  * @default 0.3
27
28
  */
28
29
  overloadedRatio?: number;
@@ -33,16 +34,16 @@ export interface ClientLoadSignalOptions {
33
34
  *
34
35
  * Built by default; construct one yourself only to wrap or adapt it — see {@link LoadSignal}.
35
36
  *
36
- * Switch it off entirely ({@link LoadSignalsOptions.client|`client: false`}) if the storage backend reports no
37
- * rate-limit statistics, since it otherwise polls it every second to no purpose.
37
+ * Switch it off entirely ({@link LoadSignalsOptions.storageBackend|`storageBackend: false`}) if the storage backend
38
+ * reports no rate-limit statistics, since it otherwise polls it every second to no purpose.
38
39
  *
39
40
  * @category Scaling
40
41
  */
41
- export declare class ClientLoadSignal implements LoadSignal {
42
+ export declare class StorageBackendLoadSignal implements LoadSignal {
42
43
  #private;
43
- readonly name = "clientInfo";
44
+ readonly name = "storageBackendInfo";
44
45
  readonly overloadedRatio: number;
45
- constructor(options?: ClientLoadSignalOptions);
46
+ constructor(options?: StorageBackendLoadSignalOptions);
46
47
  start(context: LoadSignalStartContext): Promise<void>;
47
48
  stop(): Promise<void>;
48
49
  getSample(sampleDurationMillis?: number): LoadSnapshot[];
@@ -1,26 +1,26 @@
1
1
  import { betterClearInterval, betterSetInterval } from '@apify/utilities';
2
2
  import { serviceLocator } from '../service_locator.js';
3
3
  import { SnapshotStore } from './load_signal.js';
4
- const CLIENT_RATE_LIMIT_ERROR_RETRY_COUNT = 2;
4
+ const RATE_LIMIT_ERROR_RETRY_COUNT = 2;
5
5
  /**
6
6
  * Periodically checks the storage backend for rate-limit errors (HTTP 429) and reports overload when the error delta
7
7
  * exceeds a threshold.
8
8
  *
9
9
  * Built by default; construct one yourself only to wrap or adapt it — see {@link LoadSignal}.
10
10
  *
11
- * Switch it off entirely ({@link LoadSignalsOptions.client|`client: false`}) if the storage backend reports no
12
- * rate-limit statistics, since it otherwise polls it every second to no purpose.
11
+ * Switch it off entirely ({@link LoadSignalsOptions.storageBackend|`storageBackend: false`}) if the storage backend
12
+ * reports no rate-limit statistics, since it otherwise polls it every second to no purpose.
13
13
  *
14
14
  * @category Scaling
15
15
  */
16
- export class ClientLoadSignal {
17
- name = 'clientInfo';
16
+ export class StorageBackendLoadSignal {
17
+ name = 'storageBackendInfo';
18
18
  overloadedRatio;
19
19
  #store = new SnapshotStore();
20
20
  #intervalMillis;
21
21
  #maxErrors;
22
22
  #interval;
23
- #client;
23
+ #storageBackend;
24
24
  constructor(options = {}) {
25
25
  this.overloadedRatio = options.overloadedRatio ?? 0.3;
26
26
  this.#intervalMillis = (options.snapshotIntervalSecs ?? 1) * 1000;
@@ -30,18 +30,18 @@ export class ClientLoadSignal {
30
30
  async start(context) {
31
31
  this.#store.useSampleWindow(context.maxSampleWindowMillis);
32
32
  // A new session starts from a clean slate, or its first measurement diffs the error count against the previous
33
- // session's — possibly against a different backend, since the client is resolved afresh just below.
33
+ // session's — possibly against a different backend, since it is resolved afresh just below.
34
34
  this.#store.clear();
35
35
  // Resolved here rather than in the constructor, where asking for the backend would instantiate a default one
36
36
  // as a side effect - long before the crawler that owns the run has had a chance to register its own.
37
- this.#client = serviceLocator.getStorageBackend();
37
+ this.#storageBackend = serviceLocator.getStorageBackend();
38
38
  this.#interval = betterSetInterval(this.handle, this.#intervalMillis);
39
39
  }
40
40
  async stop() {
41
41
  if (this.#interval)
42
42
  betterClearInterval(this.#interval);
43
43
  this.#interval = undefined;
44
- this.#client = undefined;
44
+ this.#storageBackend = undefined;
45
45
  }
46
46
  getSample(sampleDurationMillis) {
47
47
  return this.#store.getSample(sampleDurationMillis);
@@ -53,8 +53,8 @@ export class ClientLoadSignal {
53
53
  */
54
54
  handle(intervalCallback) {
55
55
  const now = new Date();
56
- const allErrorCounts = this.#client?.stats?.rateLimitErrors ?? [];
57
- const currentErrCount = allErrorCounts[CLIENT_RATE_LIMIT_ERROR_RETRY_COUNT] || 0;
56
+ const allErrorCounts = this.#storageBackend?.stats?.rateLimitErrors ?? [];
57
+ const currentErrCount = allErrorCounts[RATE_LIMIT_ERROR_RETRY_COUNT] || 0;
58
58
  const snapshot = {
59
59
  createdAt: now,
60
60
  isOverloaded: false,
@@ -6,10 +6,10 @@ import type { Snapshotter } from './snapshotter.js';
6
6
  export interface SystemInfo {
7
7
  /** If false, system is being overloaded. */
8
8
  isSystemIdle: boolean;
9
- memInfo: ClientInfo;
10
- eventLoopInfo: ClientInfo;
11
- cpuInfo: ClientInfo;
12
- clientInfo: ClientInfo;
9
+ memInfo: LoadSignalInfo;
10
+ eventLoopInfo: LoadSignalInfo;
11
+ cpuInfo: LoadSignalInfo;
12
+ storageBackendInfo: LoadSignalInfo;
13
13
  memTotalBytes?: number;
14
14
  memCurrentBytes?: number;
15
15
  /**
@@ -31,7 +31,7 @@ export interface SystemInfo {
31
31
  * Status of additional load signals beyond the built-in four.
32
32
  * Keys are `LoadSignal.name` values, values are overload info.
33
33
  */
34
- loadSignalInfo?: Record<string, ClientInfo>;
34
+ loadSignalInfo?: Record<string, LoadSignalInfo>;
35
35
  }
36
36
  /**
37
37
  * How far back the *current* system status looks by default — the window that gates task dispatch.
@@ -69,12 +69,12 @@ export interface SystemStatusOptions {
69
69
  /**
70
70
  * Additional load signals to include in the system status evaluation.
71
71
  * These are evaluated alongside the built-in memory, CPU, event loop,
72
- * and client signals. If any signal reports overload, the system is
73
- * considered overloaded. Each signal carries its own overload ratio.
72
+ * and storage backend signals. If any signal reports overload, the system
73
+ * is considered overloaded. Each signal carries its own overload ratio.
74
74
  */
75
75
  loadSignals?: LoadSignal[];
76
76
  }
77
- export interface ClientInfo {
77
+ export interface LoadSignalInfo {
78
78
  isOverloaded: boolean;
79
79
  limitRatio: number;
80
80
  actualRatio: number;
@@ -15,7 +15,7 @@ const BUILTIN_SIGNAL_OPTION_KEYS = {
15
15
  memInfo: 'memory',
16
16
  eventLoopInfo: 'eventLoop',
17
17
  cpuInfo: 'cpu',
18
- clientInfo: 'client',
18
+ storageBackendInfo: 'storageBackend',
19
19
  };
20
20
  const BUILTIN_SIGNAL_NAMES = new Set(Object.keys(BUILTIN_SIGNAL_OPTION_KEYS));
21
21
  /**
@@ -114,7 +114,7 @@ export class SystemStatus {
114
114
  memInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
115
115
  eventLoopInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
116
116
  cpuInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
117
- clientInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
117
+ storageBackendInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
118
118
  };
119
119
  let loadSignalInfo;
120
120
  for (const signal of this.#signals) {
@@ -6,47 +6,47 @@ export interface ConfigField<T extends z.ZodType = z.ZodType> {
6
6
  }
7
7
  export declare function field<T extends z.ZodType>(schema: T, envVar?: string | string[]): ConfigField<T>;
8
8
  /** Zod preprocessor treating `'0'` and `'false'` as falsy. */
9
- export declare const coerceBoolean: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodBoolean>;
10
- export declare const coerceNumber: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>;
9
+ export declare const coerceBoolean: z.ZodPreprocess<z.ZodBoolean>;
10
+ export declare const coerceNumber: z.ZodPreprocess<z.ZodNumber>;
11
11
  export declare const crawleeConfigFields: {
12
12
  /** @default 'default' */
13
13
  defaultDatasetId: ConfigField<z.ZodDefault<z.ZodString>>;
14
14
  /** @default true */
15
- purgeOnStart: ConfigField<z.ZodDefault<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodBoolean>>>;
15
+ purgeOnStart: ConfigField<z.ZodDefault<z.ZodPreprocess<z.ZodBoolean>>>;
16
16
  /** @default 'default' */
17
17
  defaultKeyValueStoreId: ConfigField<z.ZodDefault<z.ZodString>>;
18
18
  /** @default 'default' */
19
19
  defaultRequestQueueId: ConfigField<z.ZodDefault<z.ZodString>>;
20
20
  /** @default 0.95 */
21
- maxUsedCpuRatio: ConfigField<z.ZodDefault<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>>>;
21
+ maxUsedCpuRatio: ConfigField<z.ZodDefault<z.ZodPreprocess<z.ZodNumber>>>;
22
22
  /** @default 0.25 */
23
- availableMemoryRatio: ConfigField<z.ZodDefault<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>>>;
24
- memoryMbytes: ConfigField<z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>>>;
23
+ availableMemoryRatio: ConfigField<z.ZodDefault<z.ZodPreprocess<z.ZodNumber>>>;
24
+ memoryMbytes: ConfigField<z.ZodOptional<z.ZodPreprocess<z.ZodNumber>>>;
25
25
  /** @default 60_000 */
26
- persistStateIntervalMillis: ConfigField<z.ZodDefault<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>>>;
26
+ persistStateIntervalMillis: ConfigField<z.ZodDefault<z.ZodPreprocess<z.ZodNumber>>>;
27
27
  /**
28
28
  * Internal safety-net timeout for a single request, in milliseconds. When unset the crawler derives it from
29
29
  * the request handler timeout (twice it, and never below 5 minutes).
30
30
  */
31
- internalTimeoutMillis: ConfigField<z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>>>;
31
+ internalTimeoutMillis: ConfigField<z.ZodOptional<z.ZodPreprocess<z.ZodNumber>>>;
32
32
  /** @default 1_000 */
33
- systemInfoIntervalMillis: ConfigField<z.ZodDefault<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodNumber>>>;
33
+ systemInfoIntervalMillis: ConfigField<z.ZodDefault<z.ZodPreprocess<z.ZodNumber>>>;
34
34
  /** @default 'INPUT' */
35
35
  inputKey: ConfigField<z.ZodDefault<z.ZodString>>;
36
36
  /** @default true */
37
- headless: ConfigField<z.ZodDefault<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodBoolean>>>;
37
+ headless: ConfigField<z.ZodDefault<z.ZodPreprocess<z.ZodBoolean>>>;
38
38
  /** @default false */
39
- xvfb: ConfigField<z.ZodDefault<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodBoolean>>>;
39
+ xvfb: ConfigField<z.ZodDefault<z.ZodPreprocess<z.ZodBoolean>>>;
40
40
  chromeExecutablePath: ConfigField<z.ZodOptional<z.ZodString>>;
41
41
  defaultBrowserPath: ConfigField<z.ZodOptional<z.ZodString>>;
42
42
  /** @default false */
43
- disableBrowserSandbox: ConfigField<z.ZodDefault<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodBoolean>>>;
44
- logLevel: ConfigField<z.ZodOptional<z.ZodPipe<z.ZodTransform<{} | null | undefined, unknown>, z.ZodEnum<typeof LogLevel>>>>;
43
+ disableBrowserSandbox: ConfigField<z.ZodDefault<z.ZodPreprocess<z.ZodBoolean>>>;
44
+ logLevel: ConfigField<z.ZodOptional<z.ZodPreprocess<z.ZodEnum<typeof LogLevel>>>>;
45
45
  /** @default true */
46
- persistStorage: ConfigField<z.ZodDefault<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodBoolean>>>;
46
+ persistStorage: ConfigField<z.ZodDefault<z.ZodPreprocess<z.ZodBoolean>>>;
47
47
  /** @default './storage' */
48
48
  storageDir: ConfigField<z.ZodDefault<z.ZodString>>;
49
- containerized: ConfigField<z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodBoolean>>>;
49
+ containerized: ConfigField<z.ZodOptional<z.ZodPreprocess<z.ZodBoolean>>>;
50
50
  };
51
51
  export type FieldsInput<F extends Record<string, ConfigField>> = {
52
52
  [K in keyof F]?: z.output<F[K]['schema']>;
package/configuration.js CHANGED
@@ -36,7 +36,7 @@ const logLevelSchema = z.preprocess((val) => {
36
36
  if (key in LogLevel)
37
37
  return LogLevel[key];
38
38
  return val;
39
- }, z.nativeEnum(LogLevel));
39
+ }, z.enum(LogLevel));
40
40
  // --- Crawlee config field definitions ---
41
41
  export const crawleeConfigFields = {
42
42
  /** @default 'default' */
@@ -196,8 +196,8 @@ export class Configuration {
196
196
  continue;
197
197
  }
198
198
  // 4. Schema default (by parsing undefined through the schema)
199
- const result = fieldDef.schema.safeParse(undefined);
200
- values[key] = result.success ? result.data : undefined;
199
+ const parsed = fieldDef.schema.safeParse(undefined);
200
+ values[key] = parsed.success ? parsed.data : undefined;
201
201
  }
202
202
  return values;
203
203
  }