@crawlee/basic 4.0.0-beta.21 → 4.0.0-beta.211

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 (86) hide show
  1. package/README.md +14 -14
  2. package/index.d.ts +11 -2
  3. package/index.js +11 -1
  4. package/internals/autoscaling/autoscaled_pool.d.ts +198 -0
  5. package/internals/autoscaling/autoscaled_pool.js +388 -0
  6. package/internals/autoscaling/concurrency_system.d.ts +273 -0
  7. package/internals/autoscaling/concurrency_system.js +359 -0
  8. package/internals/autoscaling/cpu_load_signal.d.ts +43 -0
  9. package/internals/autoscaling/cpu_load_signal.js +46 -0
  10. package/internals/autoscaling/event_loop_load_signal.d.ts +51 -0
  11. package/internals/autoscaling/event_loop_load_signal.js +60 -0
  12. package/internals/autoscaling/index.d.ts +9 -0
  13. package/internals/autoscaling/index.js +9 -0
  14. package/internals/autoscaling/load_signal.d.ts +98 -0
  15. package/internals/autoscaling/load_signal.js +103 -0
  16. package/internals/autoscaling/memory_load_signal.d.ts +47 -0
  17. package/internals/autoscaling/memory_load_signal.js +104 -0
  18. package/internals/autoscaling/snapshotter.d.ts +84 -0
  19. package/internals/autoscaling/snapshotter.js +67 -0
  20. package/internals/autoscaling/storage_backend_load_signal.d.ts +56 -0
  21. package/internals/autoscaling/storage_backend_load_signal.js +73 -0
  22. package/internals/autoscaling/system_status.d.ts +122 -0
  23. package/internals/autoscaling/system_status.js +139 -0
  24. package/internals/autoscaling/weighted_avg.d.ts +5 -0
  25. package/internals/autoscaling/weighted_avg.js +14 -0
  26. package/internals/basic-crawler.d.ts +446 -350
  27. package/internals/basic-crawler.js +1404 -860
  28. package/internals/cookie_utils.d.ts +40 -0
  29. package/internals/cookie_utils.js +122 -0
  30. package/internals/crawler-run.d.ts +76 -0
  31. package/internals/crawler-run.js +142 -0
  32. package/internals/crawlers/context_pipeline.d.ts +75 -0
  33. package/internals/crawlers/context_pipeline.js +109 -0
  34. package/internals/crawlers/crawler_commons.d.ts +194 -0
  35. package/internals/crawlers/crawler_commons.js +1 -0
  36. package/internals/crawlers/error_snapshotter.d.ts +29 -0
  37. package/internals/crawlers/error_snapshotter.js +116 -0
  38. package/internals/crawlers/error_tracker.d.ts +52 -0
  39. package/internals/crawlers/error_tracker.js +316 -0
  40. package/internals/crawlers/index.d.ts +4 -0
  41. package/internals/crawlers/index.js +3 -0
  42. package/internals/crawlers/internals/types.d.ts +7 -0
  43. package/internals/crawlers/internals/types.js +1 -0
  44. package/internals/crawlers/statistics.d.ts +318 -0
  45. package/internals/crawlers/statistics.js +525 -0
  46. package/internals/crawling_request.d.ts +86 -0
  47. package/internals/crawling_request.js +127 -0
  48. package/internals/enqueue_links/enqueue_links.d.ts +152 -0
  49. package/internals/enqueue_links/enqueue_links.js +76 -0
  50. package/internals/enqueue_links/index.d.ts +3 -0
  51. package/internals/enqueue_links/index.js +3 -0
  52. package/internals/enqueue_links/shared.d.ts +104 -0
  53. package/internals/enqueue_links/shared.js +258 -0
  54. package/internals/errors.d.ts +60 -0
  55. package/internals/errors.js +74 -0
  56. package/internals/http.d.ts +9 -0
  57. package/internals/http.js +28 -0
  58. package/internals/request-timeout.d.ts +54 -0
  59. package/internals/request-timeout.js +98 -0
  60. package/internals/router.d.ts +308 -0
  61. package/internals/router.js +312 -0
  62. package/internals/send-request.d.ts +4 -5
  63. package/internals/send-request.js +22 -11
  64. package/internals/session_pool/consts.d.ts +1 -0
  65. package/internals/session_pool/consts.js +1 -0
  66. package/internals/session_pool/errors.d.ts +7 -0
  67. package/internals/session_pool/errors.js +11 -0
  68. package/internals/session_pool/fingerprint.d.ts +9 -0
  69. package/internals/session_pool/fingerprint.js +30 -0
  70. package/internals/session_pool/index.d.ts +4 -0
  71. package/internals/session_pool/index.js +4 -0
  72. package/internals/session_pool/session.d.ts +156 -0
  73. package/internals/session_pool/session.js +225 -0
  74. package/internals/session_pool/session_pool.d.ts +244 -0
  75. package/internals/session_pool/session_pool.js +453 -0
  76. package/internals/sitemap_request_loader.d.ts +197 -0
  77. package/internals/sitemap_request_loader.js +432 -0
  78. package/internals/throttling_request_manager.d.ts +230 -0
  79. package/internals/throttling_request_manager.js +843 -0
  80. package/package.json +19 -13
  81. package/index.d.ts.map +0 -1
  82. package/index.js.map +0 -1
  83. package/internals/basic-crawler.d.ts.map +0 -1
  84. package/internals/basic-crawler.js.map +0 -1
  85. package/internals/send-request.d.ts.map +0 -1
  86. package/internals/send-request.js.map +0 -1
@@ -0,0 +1,230 @@
1
+ import type { Dictionary } from '@crawlee/types';
2
+ import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, Configuration, IRequestManager, PacingSignal, RequestQueueOperationInfo, RequestQueueOperationOptions, RequestsLike, Source, StorageIdentifier, RequestSourceStatus, StorageOpenOptions } from '@crawlee/core';
3
+ import { Request } from '@crawlee/core';
4
+ /**
5
+ * Opens a request manager, matching the shape of storage `open` methods such as
6
+ * {@link RequestQueue.open|`RequestQueue.open`}.
7
+ *
8
+ * {@link ThrottlingRequestManager} calls this once per configured domain, so every per-domain queue shares the
9
+ * concrete type and storage backend of the manager being wrapped.
10
+ *
11
+ * Not exported: the only option that takes one is `@internal`.
12
+ */
13
+ type RequestManagerOpener<T extends IRequestManager = IRequestManager> = (identifier?: string | StorageIdentifier | null, options?: StorageOpenOptions) => Promise<T>;
14
+ /** Options for {@link ThrottlingRequestManager}. */
15
+ export interface ThrottlingRequestManagerOptions<T extends IRequestManager = IRequestManager> {
16
+ /**
17
+ * The request manager to wrap, usually a {@link RequestQueue}. Requests for domains that are not throttled
18
+ * are stored here. May be a factory, so that the throttler can be constructed synchronously and the manager
19
+ * under it opened only on first use.
20
+ *
21
+ * Omitted, the default request queue is opened on first use through
22
+ * {@link ThrottlingRequestManagerOptions.requestManagerOpener|`requestManagerOpener`}.
23
+ */
24
+ inner?: T | (() => T | Promise<T>);
25
+ /**
26
+ * Which domains to throttle: a list of hostnames, or `'all'` for every domain the crawl encounters.
27
+ *
28
+ * Matching a listed hostname is case-insensitive and exact - wildcards such as `*.example.com` are not
29
+ * supported, so list each subdomain you care about (or set
30
+ * {@link ThrottlingRequestManagerOptions.throttleBy|`throttleBy: 'registrableDomain'`}). An
31
+ * internationalized domain may be given in either its unicode or its punycode form, and an IPv6 address has
32
+ * to be bracketed (`[::1]`). Requests for any other domain bypass throttling entirely.
33
+ *
34
+ * `'all'` gives each domain a queue of its own the first time it is seen, so that it can be held back
35
+ * without its requests being repeatedly popped and re-enqueued. One request queue per domain is not free,
36
+ * which is what {@link ThrottlingRequestManagerOptions.maxThrottledDomains|`maxThrottledDomains`} is
37
+ * there to bound.
38
+ */
39
+ domains: string[] | 'all';
40
+ /**
41
+ * A floor under the crawl delay of every throttled domain, in seconds - the proactive clock described on
42
+ * {@link ThrottlingRequestManager}. A domain whose robots.txt asks for a longer `Crawl-delay` gets the
43
+ * longer one; this is a minimum, not an override. A `minIntervalEverywhere` {@link PacingSignal} raises
44
+ * this floor at runtime, and never lowers it.
45
+ * @default 0
46
+ */
47
+ minCrawlDelaySecs?: number;
48
+ /**
49
+ * What counts as "the same domain": the exact hostname, or the registrable domain it belongs to
50
+ * (`example.com` for `www.example.com`, `a.example.co.uk` and so on). Hosts with no registrable domain -
51
+ * IP addresses, `localhost` - are always throttled per hostname.
52
+ *
53
+ * Grouping by registrable domain gives subdomains a single pair of clocks and a single queue, which is what
54
+ * you want when the pacing is there to be polite to one server rather than to satisfy a specific host's
55
+ * rate limit.
56
+ * @default 'hostname'
57
+ */
58
+ throttleBy?: 'hostname' | 'registrableDomain';
59
+ /**
60
+ * The most domains a run may throttle at once. Exceeding it throws, rather than silently letting the
61
+ * throttling lapse - one request queue per domain is not free, and a crawl that discovers domains without
62
+ * bound would drown the storage backend in them.
63
+ *
64
+ * Only domains discovered under `domains: 'all'` count against this; an explicit list is taken at face value.
65
+ * @default 100
66
+ */
67
+ maxThrottledDomains?: number;
68
+ /**
69
+ * The key under which the discovered domain list is kept in the default key-value store, so that a restart
70
+ * with `purgeOnStart` disabled reopens their queues instead of stranding whatever they still hold. Only
71
+ * written under `domains: 'all'`.
72
+ *
73
+ * Give each manager its own key when running several of them against the same storage.
74
+ * @default 'CRAWLEE_THROTTLED_DOMAINS'
75
+ */
76
+ persistStateKey?: string;
77
+ /**
78
+ * Opens the per-domain queues, one per throttled domain, each under the alias `throttled-<domain>`.
79
+ * @default RequestQueue.open
80
+ * @internal
81
+ */
82
+ requestManagerOpener?: RequestManagerOpener<T>;
83
+ /**
84
+ * The delay applied after a domain's first HTTP 429, doubled on each subsequent one.
85
+ * @default 2
86
+ */
87
+ baseDelaySecs?: number;
88
+ /**
89
+ * Upper bound on the delay between requests to a rate-limited domain, applied to both the exponential
90
+ * backoff and a `Retry-After` value.
91
+ * @default 60
92
+ */
93
+ maxDelaySecs?: number;
94
+ /**
95
+ * How long a domain may rate-limit us without a single request getting through before the crawl is
96
+ * abandoned with a {@link PersistentRateLimitError}.
97
+ *
98
+ * A domain that keeps answering 429 for this long is not going to be crawled by waiting longer - the
99
+ * concurrency is too high for it, or it has blocked us outright. Its requests are deliberately left in
100
+ * their queue, so re-running the crawl with `purgeOnStart` disabled picks them up once the domain recovers.
101
+ *
102
+ * A crawler running with `keepAlive` is exempt - outliving a domain that will not let us through is the
103
+ * whole point there.
104
+ * @default 900
105
+ */
106
+ maxDomainStallSecs?: number;
107
+ }
108
+ /**
109
+ * A request manager that wraps another one and paces requests per domain.
110
+ *
111
+ * Requests for a throttled domain are routed into their own queue when they are added, so each request lives in
112
+ * exactly one place and deduplication keeps working. Everything else goes to the wrapped manager untouched.
113
+ *
114
+ * {@link ThrottlingRequestManager.fetchNextRequest|`fetchNextRequest()`} serves the domain that has been waiting
115
+ * longest and skips any that are backing off, falling back to the wrapped manager. It never blocks: while every
116
+ * remaining request belongs to a throttled domain it returns `null` and
117
+ * {@link ThrottlingRequestManager.checkReadiness|`checkReadiness()`} reports `waiting` with the moment the
118
+ * earliest of them comes due, so the crawler idles instead of holding a concurrency slot open.
119
+ *
120
+ * Each throttled domain runs two independent clocks, and may be dispatched to once **both** have run out:
121
+ * - **Backoff**, set by HTTP 429 responses - honouring `Retry-After`, and otherwise doubling from `baseDelaySecs`.
122
+ * Reactive and temporary: it decays once the domain stops turning us away. The crawlers report the 429s
123
+ * themselves; a request held back this way is retried later without counting against `maxRequestRetries` and
124
+ * without penalising its session.
125
+ * - **Crawl delay**, the minimum interval between two dispatches to the domain, armed after each one. Proactive
126
+ * and constant: whatever the domain's robots.txt asks for, floored by
127
+ * {@link ThrottlingRequestManagerOptions.minCrawlDelaySecs|`minCrawlDelaySecs`}. Either may be absent, in
128
+ * which case the other one is the delay.
129
+ *
130
+ * Which domains get those clocks is {@link ThrottlingRequestManagerOptions.domains|`domains`} - a list, or
131
+ * `'all'` for every domain the crawl encounters.
132
+ *
133
+ * Pass one as a crawler's `requestManager`; the `sameDomainDelaySecs` shorthand builds one with `domains: 'all'`
134
+ * and `throttleBy: 'registrableDomain'`. Construct it yourself to name the domains or tune the delays - one
135
+ * covering every domain also makes `sameDomainDelaySecs` land on it as a floor rather than adding a second pacer.
136
+ *
137
+ * Signals - 429s, robots.txt `Crawl-delay`, that floor - arrive through
138
+ * {@link IRequestManager.recordPacingSignal|`recordPacingSignal`}, which wrapping managers forward, so this
139
+ * works wherever it sits in a composition, including inside a {@link RequestManagerTandem}.
140
+ *
141
+ * **Example usage:**
142
+ *
143
+ * ```ts
144
+ * const crawler = new CheerioCrawler({
145
+ * requestManager: new ThrottlingRequestManager({
146
+ * domains: ['api.example.com', 'slow-site.org'],
147
+ * }),
148
+ * requestHandler: async ({ request }) => { ... },
149
+ * });
150
+ * ```
151
+ *
152
+ * @category Sources
153
+ */
154
+ export declare class ThrottlingRequestManager<T extends IRequestManager = IRequestManager> implements IRequestManager {
155
+ #private;
156
+ private readonly config;
157
+ constructor(options: ThrottlingRequestManagerOptions<T>, config?: Configuration);
158
+ /**
159
+ * The wrapped manager, holding every request whose domain is not throttled. `undefined` until an `inner`
160
+ * passed as a factory is resolved - reading this never forces it, because a getter should not open a queue
161
+ * behind a caller's back.
162
+ *
163
+ * @internal
164
+ */
165
+ get innerManager(): T | undefined;
166
+ /**
167
+ * Records a pacing signal: a refusal puts the URL's domain into backoff, a declared interval becomes its
168
+ * crawl delay, and a crawl-wide floor raises {@link recordEverywhereFloor|the floor under all of them}.
169
+ *
170
+ * @returns `false` if the domain the signal covers is not throttled, in which case this is a no-op.
171
+ * @throws If the signal's scope is one this manager cannot honour - see {@link assertScopeHonourable}.
172
+ * @inheritdoc
173
+ */
174
+ recordPacingSignal(signal: PacingSignal): boolean;
175
+ addRequest(requestLike: Source, options?: RequestQueueOperationOptions): Promise<RequestQueueOperationInfo>;
176
+ /**
177
+ * Adds requests in batches, routing each one to the manager that owns its domain.
178
+ *
179
+ * Batching, validation, deduplication and `Retry-After`-free bookkeeping are all delegated to the target
180
+ * managers - this only decides where each request goes, one batch at a time, so a lazy or unbounded input
181
+ * iterable is never fully materialized.
182
+ */
183
+ addRequestsBatched(requests: RequestsLike, options?: AddRequestsBatchedOptions): Promise<AddRequestsBatchedResult>;
184
+ reclaimRequest(request: Request, options?: RequestQueueOperationOptions): Promise<RequestQueueOperationInfo | null>;
185
+ markRequestAsHandled(request: Request): Promise<RequestQueueOperationInfo | void | null>;
186
+ getTotalCount(): Promise<number>;
187
+ getPendingCount(): Promise<number>;
188
+ getHandledCount(): Promise<number>;
189
+ /**
190
+ * Reports whether anything can be dispatched right now, and if not, when — or why never.
191
+ *
192
+ * One traversal of the domain clocks answers all of it: only domains whose delays have run out are probed,
193
+ * the rest merely contribute the moment they come due. Throttled requests count as outstanding work, so a
194
+ * crawler gated on this idles for the backoff instead of concluding it is done.
195
+ *
196
+ * `ready` from anywhere else outranks a stalling domain and is returned without looking at the stall clocks,
197
+ * so one hopeless domain never ends a crawl making progress elsewhere. It cannot outrank itself, though -
198
+ * see the traversal.
199
+ */
200
+ checkReadiness(): Promise<RequestSourceStatus>;
201
+ /**
202
+ * Empties every manager and clears the accumulated backoff. A robots.txt `Crawl-delay` is a property of the
203
+ * site rather than of the run, so it survives.
204
+ */
205
+ purge(): Promise<void>;
206
+ setExpectedRequestProcessingTimeSecs(secs: number): Promise<void>;
207
+ /**
208
+ * @inheritdoc
209
+ * Extending a lock must not consume the in-flight marker that later routes
210
+ * {@link ThrottlingRequestManager.markRequestAsHandled} / {@link ThrottlingRequestManager.reclaimRequest}.
211
+ */
212
+ extendRequestProcessingTimeSecs(request: Request, secs: number): Promise<boolean>;
213
+ /**
214
+ * Returns the next request from a domain that is not backing off, or from the inner manager.
215
+ *
216
+ * Returns `null` while every remaining request belongs to a throttled domain - it never waits the backoff
217
+ * out, because a consumer parked in here holds a concurrency slot, which the autoscaler reads as spare
218
+ * capacity and answers by scaling up. Callers poll instead, and
219
+ * {@link ThrottlingRequestManager.checkReadiness|`checkReadiness()`} reports `waiting` meanwhile so the
220
+ * crawler's task loop idles rather than spins.
221
+ */
222
+ fetchNextRequest<R extends Dictionary = Dictionary>(): Promise<Request<R> | null>;
223
+ [Symbol.asyncIterator](): AsyncGenerator<Request<Dictionary>, void, unknown>;
224
+ /**
225
+ * Drops every managed storage - the wrapped manager and each per-domain queue.
226
+ * @internal
227
+ */
228
+ drop(): Promise<void>;
229
+ }
230
+ export {};