@crawlee/basic 4.0.0-beta.170 → 4.0.0-beta.171

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 (73) hide show
  1. package/index.d.ts +10 -0
  2. package/index.js +10 -0
  3. package/internals/autoscaling/autoscaled_pool.d.ts +196 -0
  4. package/internals/autoscaling/autoscaled_pool.js +385 -0
  5. package/internals/autoscaling/concurrency_system.d.ts +267 -0
  6. package/internals/autoscaling/concurrency_system.js +364 -0
  7. package/internals/autoscaling/cpu_load_signal.d.ts +43 -0
  8. package/internals/autoscaling/cpu_load_signal.js +46 -0
  9. package/internals/autoscaling/event_loop_load_signal.d.ts +51 -0
  10. package/internals/autoscaling/event_loop_load_signal.js +60 -0
  11. package/internals/autoscaling/index.d.ts +9 -0
  12. package/internals/autoscaling/index.js +9 -0
  13. package/internals/autoscaling/load_signal.d.ts +98 -0
  14. package/internals/autoscaling/load_signal.js +103 -0
  15. package/internals/autoscaling/memory_load_signal.d.ts +47 -0
  16. package/internals/autoscaling/memory_load_signal.js +104 -0
  17. package/internals/autoscaling/snapshotter.d.ts +84 -0
  18. package/internals/autoscaling/snapshotter.js +67 -0
  19. package/internals/autoscaling/storage_backend_load_signal.d.ts +56 -0
  20. package/internals/autoscaling/storage_backend_load_signal.js +73 -0
  21. package/internals/autoscaling/system_status.d.ts +122 -0
  22. package/internals/autoscaling/system_status.js +139 -0
  23. package/internals/autoscaling/weighted_avg.d.ts +5 -0
  24. package/internals/autoscaling/weighted_avg.js +14 -0
  25. package/internals/basic-crawler.d.ts +11 -2
  26. package/internals/basic-crawler.js +13 -1
  27. package/internals/cookie_utils.d.ts +44 -0
  28. package/internals/cookie_utils.js +122 -0
  29. package/internals/crawler-run.d.ts +3 -1
  30. package/internals/crawler-run.js +1 -1
  31. package/internals/crawlers/context_pipeline.d.ts +70 -0
  32. package/internals/crawlers/context_pipeline.js +122 -0
  33. package/internals/crawlers/crawler_commons.d.ts +193 -0
  34. package/internals/crawlers/crawler_commons.js +1 -0
  35. package/internals/crawlers/error_snapshotter.d.ts +57 -0
  36. package/internals/crawlers/error_snapshotter.js +117 -0
  37. package/internals/crawlers/error_tracker.d.ts +54 -0
  38. package/internals/crawlers/error_tracker.js +308 -0
  39. package/internals/crawlers/index.d.ts +5 -0
  40. package/internals/crawlers/index.js +4 -0
  41. package/internals/crawlers/internals/types.d.ts +7 -0
  42. package/internals/crawlers/internals/types.js +1 -0
  43. package/internals/crawlers/statistics.d.ts +318 -0
  44. package/internals/crawlers/statistics.js +525 -0
  45. package/internals/enqueue_links/enqueue_links.d.ts +152 -0
  46. package/internals/enqueue_links/enqueue_links.js +76 -0
  47. package/internals/enqueue_links/index.d.ts +2 -0
  48. package/internals/enqueue_links/index.js +2 -0
  49. package/internals/enqueue_links/shared.d.ts +98 -0
  50. package/internals/enqueue_links/shared.js +259 -0
  51. package/internals/errors.d.ts +58 -0
  52. package/internals/errors.js +72 -0
  53. package/internals/http.d.ts +9 -0
  54. package/internals/http.js +28 -0
  55. package/internals/router.d.ts +306 -0
  56. package/internals/router.js +310 -0
  57. package/internals/session_pool/consts.d.ts +3 -0
  58. package/internals/session_pool/consts.js +3 -0
  59. package/internals/session_pool/errors.d.ts +7 -0
  60. package/internals/session_pool/errors.js +11 -0
  61. package/internals/session_pool/fingerprint.d.ts +9 -0
  62. package/internals/session_pool/fingerprint.js +30 -0
  63. package/internals/session_pool/index.d.ts +4 -0
  64. package/internals/session_pool/index.js +4 -0
  65. package/internals/session_pool/session.d.ts +153 -0
  66. package/internals/session_pool/session.js +221 -0
  67. package/internals/session_pool/session_pool.d.ts +242 -0
  68. package/internals/session_pool/session_pool.js +429 -0
  69. package/internals/sitemap_request_loader.d.ts +196 -0
  70. package/internals/sitemap_request_loader.js +432 -0
  71. package/internals/throttling_request_manager.d.ts +217 -0
  72. package/internals/throttling_request_manager.js +829 -0
  73. package/package.json +11 -7
@@ -0,0 +1,76 @@
1
+ import { EnqueueStrategy } from '@crawlee/utils';
2
+ import { getDomain } from 'tldts';
3
+ /**
4
+ * @internal
5
+ * This method helps resolve the baseUrl that will be used for filtering in {@link enqueueLinks}.
6
+ * - If a user provides a base url, we always return it
7
+ * - If a user specifies {@link EnqueueStrategy.All} strategy, they do not care if the newly found urls are on the original
8
+ * request domain, or a redirected one
9
+ * - In all other cases, we return the domain of the original request as that's the one we need to use for filtering
10
+ */
11
+ export function resolveBaseUrlForEnqueueLinksFiltering({ enqueueStrategy, finalRequestUrl, originalRequestUrl, userProvidedBaseUrl, }) {
12
+ // User provided base url takes priority
13
+ if (userProvidedBaseUrl) {
14
+ return userProvidedBaseUrl;
15
+ }
16
+ const originalUrlOrigin = new URL(originalRequestUrl).origin;
17
+ const finalUrlOrigin = new URL(finalRequestUrl ?? originalRequestUrl).origin;
18
+ // We can assume users want to go off the domain in this case
19
+ if (enqueueStrategy === EnqueueStrategy.All) {
20
+ return finalUrlOrigin;
21
+ }
22
+ // If the user wants to ensure the same domain is accessed, regardless of subdomains, we check to ensure the domains match
23
+ // If they don't (we went off domain via a redirect), we keep filtering against the original domain - returning undefined would disable the filtering entirely
24
+ if (enqueueStrategy === EnqueueStrategy.SameDomain) {
25
+ const originalHostname = getDomain(originalUrlOrigin, { mixedInputs: false });
26
+ const finalHostname = getDomain(finalUrlOrigin, { mixedInputs: false });
27
+ if (originalHostname === finalHostname) {
28
+ return finalUrlOrigin;
29
+ }
30
+ return originalUrlOrigin;
31
+ }
32
+ // Always enqueue urls that are from the same origin in all other cases, as the filtering happens on the original request url, even if there was a redirect
33
+ // before actually finding the urls
34
+ return originalUrlOrigin;
35
+ }
36
+ /**
37
+ * @internal
38
+ * Builds the glob patterns a URL must match to satisfy the given enqueue `strategy`, anchored at `baseUrl`.
39
+ */
40
+ export function buildEnqueueStrategyPatterns(baseUrl, strategy) {
41
+ const url = new URL(baseUrl);
42
+ switch (strategy) {
43
+ case EnqueueStrategy.SameHostname:
44
+ // We need to get the origin of the passed in domain in the event someone sets baseUrl
45
+ // to an url like https://example.com/deep/default/path and one of the found urls is an
46
+ // absolute relative path (/path/to/page)
47
+ return [{ glob: ignoreHttpSchema(`${url.origin}/**`) }];
48
+ case EnqueueStrategy.SameDomain: {
49
+ // Get the actual hostname from the base url
50
+ const baseUrlHostname = getDomain(url.hostname, { mixedInputs: false });
51
+ if (baseUrlHostname) {
52
+ // We have a hostname, so we can use it to match all links on the page that point to it and any subdomains of it
53
+ url.hostname = baseUrlHostname;
54
+ return [
55
+ { glob: ignoreHttpSchema(`${url.origin.replace(baseUrlHostname, `*.${baseUrlHostname}`)}/**`) },
56
+ { glob: ignoreHttpSchema(`${url.origin}/**`) },
57
+ ];
58
+ }
59
+ // We don't have a hostname (can happen for ips for instance), so reproduce the same behavior
60
+ // as SameDomainAndSubdomain
61
+ return [{ glob: ignoreHttpSchema(`${url.origin}/**`) }];
62
+ }
63
+ case EnqueueStrategy.SameOrigin:
64
+ // The same behavior as SameHostname, but respecting the protocol of the URL
65
+ return [{ glob: `${url.origin}/**` }];
66
+ case EnqueueStrategy.All:
67
+ default:
68
+ return [{ glob: `http{s,}://**` }];
69
+ }
70
+ }
71
+ /**
72
+ * Internal function that changes the enqueue glob patterns to match both http and https
73
+ */
74
+ function ignoreHttpSchema(pattern) {
75
+ return pattern.replace(/^(https?):\/\//, 'http{s,}://');
76
+ }
@@ -0,0 +1,2 @@
1
+ export * from './enqueue_links.js';
2
+ export * from './shared.js';
@@ -0,0 +1,2 @@
1
+ export * from './enqueue_links.js';
2
+ export * from './shared.js';
@@ -0,0 +1,98 @@
1
+ import type { Awaitable, Dictionary } from '@crawlee/types';
2
+ import { z } from 'zod';
3
+ import type { EnqueueStrategyOption, RequestOptions, SkippedRequestReason, Source } from '@crawlee/core';
4
+ import { Request } from '@crawlee/core';
5
+ export interface UrlPatternObject {
6
+ glob?: string;
7
+ regexp?: RegExp;
8
+ }
9
+ export interface GlobObject {
10
+ glob: string;
11
+ }
12
+ export type GlobInput = string | GlobObject;
13
+ export interface RegExpObject {
14
+ regexp: RegExp;
15
+ }
16
+ export type RegExpInput = RegExp | RegExpObject;
17
+ /** Unified URL pattern input — accepts glob strings, glob objects, RegExp instances, or regexp objects. */
18
+ export type UrlPatternInput = GlobInput | RegExpInput;
19
+ /**
20
+ * Accepts one {@link UrlPatternInput} — a glob string, a RegExp instance, or a `{ glob }` / `{ regexp }` object.
21
+ * @internal
22
+ */
23
+ export declare const urlPatternSchema: z.ZodType<UrlPatternInput>;
24
+ export type SkippedRequestCallback = (args: {
25
+ request: Request;
26
+ reason: SkippedRequestReason;
27
+ }) => Awaitable<void>;
28
+ /**
29
+ * Builds the `{ request, reason }` argument passed to a {@link SkippedRequestCallback}, constructing the
30
+ * `Request` lazily on first access to `request` (and caching it) since most skips are never observed by a
31
+ * real callback and building a `Request` isn't free.
32
+ * @ignore
33
+ */
34
+ export declare function createSkippedRequestArgs(source: string | Source, reason: SkippedRequestReason): Parameters<SkippedRequestCallback>[0];
35
+ /**
36
+ * @ignore
37
+ */
38
+ export declare function updateEnqueueLinksPatternCache(item: GlobInput | RegExpInput, pattern: RegExpObject | GlobObject): void;
39
+ /**
40
+ * Helper factory used in the `enqueueLinks()` and enqueueLinksByClickingElements() function
41
+ * to construct Glob objects from Glob pattern strings.
42
+ * @ignore
43
+ */
44
+ export declare function constructGlobObjectsFromGlobs(globs: readonly GlobInput[]): GlobObject[];
45
+ /**
46
+ * @internal
47
+ */
48
+ export declare function validateGlobPattern(glob: string): string;
49
+ /**
50
+ * Helper factory used in the `enqueueLinks()` and enqueueLinksByClickingElements() function
51
+ * to check RegExps input and return valid RegExps.
52
+ * @ignore
53
+ */
54
+ export declare function constructRegExpObjectsFromRegExps(regexps: readonly RegExpInput[]): RegExpObject[];
55
+ /**
56
+ * Helper factory used in the `enqueueLinks()` function to construct UrlPatternObjects
57
+ * from a mixed array of glob strings, glob objects, RegExp instances, and regexp objects.
58
+ * @ignore
59
+ */
60
+ export declare function constructUrlPatternObjects(patterns: readonly UrlPatternInput[]): UrlPatternObject[];
61
+ /**
62
+ * Filters request options by URL patterns.
63
+ *
64
+ * When `includePatterns` is empty/undefined, all options pass through (only exclude filtering applies).
65
+ * @ignore
66
+ */
67
+ export declare function filterRequestOptionsByPatterns(requestOptions: RequestOptions[], includePatterns: UrlPatternObject[] | undefined, excludePatterns?: UrlPatternObject[], strategy?: EnqueueStrategyOption, onSkippedRequestOptions?: (options: RequestOptions) => void): RequestOptions[];
68
+ /**
69
+ * @ignore
70
+ */
71
+ export declare function createRequestOptions(sources: readonly (string | Record<string, unknown>)[], options?: {
72
+ label?: string;
73
+ userData?: Dictionary;
74
+ baseUrl?: string;
75
+ skipNavigation?: boolean;
76
+ sessionId?: string;
77
+ strategy?: EnqueueStrategyOption;
78
+ }): RequestOptions[];
79
+ /**
80
+ * Takes a {@link RequestOptions} object and changes its attributes in a desired way. This user-function is used
81
+ * by {@link enqueueLinks} to modify request options before they are converted to {@link Request} instances.
82
+ */
83
+ export interface RequestTransform {
84
+ /**
85
+ * @param original Request options to be modified.
86
+ * @returns The modified request options to enqueue, `'unchanged'` to keep the original options as-is,
87
+ * or a falsy value / `'skip'` to exclude the request from the queue.
88
+ */
89
+ (original: RequestOptions): RequestOptions | false | undefined | null | 'skip' | 'unchanged';
90
+ }
91
+ /**
92
+ * Applies a {@link RequestTransform} function to a list of request options.
93
+ * Options for which the transform returns a falsy value are removed from the list.
94
+ * @param onSkipped Called with the original request options when the transform returns a falsy value (i.e. the request is skipped).
95
+ * @ignore
96
+ * @internal
97
+ */
98
+ export declare function applyRequestTransform(requestOptions: RequestOptions[], transformFn: RequestTransform, onSkipped?: (requestOptions: RequestOptions) => void): RequestOptions[];
@@ -0,0 +1,259 @@
1
+ import { URL } from 'node:url';
2
+ import { Minimatch } from 'minimatch';
3
+ import { z } from 'zod';
4
+ import { Request } from '@crawlee/core';
5
+ import { schemas } from '@crawlee/utils/internal';
6
+ const MAX_ENQUEUE_LINKS_CACHE_SIZE = 1000;
7
+ /**
8
+ * To keep high performance when the same patterns are passed on every `enqueueLinks()` call,
9
+ * each glob/regexp is only compiled once and then kept in a cache.
10
+ * @ignore
11
+ */
12
+ const enqueueLinksPatternCache = new Map();
13
+ /**
14
+ * Accepts one {@link UrlPatternInput} — a glob string, a RegExp instance, or a `{ glob }` / `{ regexp }` object.
15
+ * @internal
16
+ */
17
+ export const urlPatternSchema = z.union([
18
+ z.string(),
19
+ z.instanceof(RegExp),
20
+ schemas.objectWithKeys(['glob']),
21
+ schemas.objectWithKeys(['regexp']),
22
+ ]);
23
+ /**
24
+ * Builds the `{ request, reason }` argument passed to a {@link SkippedRequestCallback}, constructing the
25
+ * `Request` lazily on first access to `request` (and caching it) since most skips are never observed by a
26
+ * real callback and building a `Request` isn't free.
27
+ * @ignore
28
+ */
29
+ export function createSkippedRequestArgs(source, reason) {
30
+ const sourceReason = typeof source === 'string' ? undefined : source.skippedReason;
31
+ let request;
32
+ return {
33
+ reason: sourceReason ?? reason,
34
+ get request() {
35
+ request ??=
36
+ source instanceof Request
37
+ ? source
38
+ : new Request(typeof source === 'string' ? { url: source } : source);
39
+ return request;
40
+ },
41
+ };
42
+ }
43
+ /**
44
+ * @ignore
45
+ */
46
+ export function updateEnqueueLinksPatternCache(item, pattern) {
47
+ enqueueLinksPatternCache.set(item, pattern);
48
+ if (enqueueLinksPatternCache.size > MAX_ENQUEUE_LINKS_CACHE_SIZE) {
49
+ const key = enqueueLinksPatternCache.keys().next().value;
50
+ enqueueLinksPatternCache.delete(key);
51
+ }
52
+ }
53
+ /**
54
+ * Helper factory used in the `enqueueLinks()` and enqueueLinksByClickingElements() function
55
+ * to construct Glob objects from Glob pattern strings.
56
+ * @ignore
57
+ */
58
+ export function constructGlobObjectsFromGlobs(globs) {
59
+ return globs
60
+ .filter((glob) => {
61
+ // Skip possibly nullish, empty strings
62
+ if (!glob) {
63
+ return false;
64
+ }
65
+ if (typeof glob === 'string') {
66
+ return glob.trim().length > 0;
67
+ }
68
+ if (glob.glob) {
69
+ return glob.glob.trim().length > 0;
70
+ }
71
+ return false;
72
+ })
73
+ .map((item) => {
74
+ // Get glob object from cache.
75
+ let globObject = enqueueLinksPatternCache.get(item);
76
+ if (globObject)
77
+ return globObject;
78
+ if (typeof item === 'string') {
79
+ globObject = { glob: validateGlobPattern(item) };
80
+ }
81
+ else {
82
+ globObject = { glob: validateGlobPattern(item.glob) };
83
+ }
84
+ updateEnqueueLinksPatternCache(item, globObject);
85
+ return globObject;
86
+ });
87
+ }
88
+ /**
89
+ * @internal
90
+ */
91
+ export function validateGlobPattern(glob) {
92
+ const globTrimmed = glob.trim();
93
+ if (globTrimmed.length === 0)
94
+ throw new Error(`Cannot parse Glob pattern '${globTrimmed}': it must be an non-empty string`);
95
+ return globTrimmed;
96
+ }
97
+ /**
98
+ * Helper factory used in the `enqueueLinks()` and enqueueLinksByClickingElements() function
99
+ * to check RegExps input and return valid RegExps.
100
+ * @ignore
101
+ */
102
+ export function constructRegExpObjectsFromRegExps(regexps) {
103
+ return regexps.map((item) => {
104
+ // Get regexp object from cache.
105
+ let regexpObject = enqueueLinksPatternCache.get(item);
106
+ if (regexpObject)
107
+ return regexpObject;
108
+ if (item instanceof RegExp) {
109
+ regexpObject = { regexp: item };
110
+ }
111
+ else {
112
+ regexpObject = { regexp: item.regexp };
113
+ }
114
+ updateEnqueueLinksPatternCache(item, regexpObject);
115
+ return regexpObject;
116
+ });
117
+ }
118
+ /**
119
+ * Helper factory used in the `enqueueLinks()` function to construct UrlPatternObjects
120
+ * from a mixed array of glob strings, glob objects, RegExp instances, and regexp objects.
121
+ * @ignore
122
+ */
123
+ export function constructUrlPatternObjects(patterns) {
124
+ const result = [];
125
+ for (const item of patterns) {
126
+ if (typeof item === 'string' || 'glob' in item) {
127
+ result.push(...constructGlobObjectsFromGlobs([item]));
128
+ }
129
+ else if (item instanceof RegExp || 'regexp' in item) {
130
+ result.push(...constructRegExpObjectsFromRegExps([item]));
131
+ }
132
+ }
133
+ return result;
134
+ }
135
+ /**
136
+ * Filters request options by URL patterns.
137
+ *
138
+ * When `includePatterns` is empty/undefined, all options pass through (only exclude filtering applies).
139
+ * @ignore
140
+ */
141
+ export function filterRequestOptionsByPatterns(requestOptions, includePatterns, excludePatterns = [], strategy, onSkippedRequestOptions) {
142
+ const excludeMatchers = excludePatterns.map(createPatternObjectMatcher);
143
+ const includeMatchers = includePatterns?.length ? includePatterns.map(createPatternObjectMatcher) : undefined;
144
+ return requestOptions
145
+ .filter((opts) => {
146
+ const matchesExclude = excludeMatchers.some(({ match }) => match(opts.url));
147
+ if (matchesExclude) {
148
+ onSkippedRequestOptions?.(opts);
149
+ }
150
+ return !matchesExclude;
151
+ })
152
+ .map((opts) => {
153
+ if (!includeMatchers) {
154
+ return { ...opts, enqueueStrategy: strategy };
155
+ }
156
+ for (const { match } of includeMatchers) {
157
+ if (match(opts.url)) {
158
+ return { ...opts, enqueueStrategy: strategy };
159
+ }
160
+ }
161
+ // didn't match any positive pattern
162
+ onSkippedRequestOptions?.(opts);
163
+ return null;
164
+ })
165
+ .filter((opts) => opts !== null);
166
+ }
167
+ function isAbsoluteUrl(url) {
168
+ try {
169
+ // eslint-disable-next-line no-new
170
+ new URL(url);
171
+ return true;
172
+ }
173
+ catch {
174
+ return false;
175
+ }
176
+ }
177
+ /**
178
+ * @ignore
179
+ */
180
+ export function createRequestOptions(sources, options = {}) {
181
+ return sources
182
+ .map((src) => typeof src === 'string'
183
+ ? { url: src, enqueueStrategy: options.strategy }
184
+ : { ...src, enqueueStrategy: options.strategy })
185
+ .filter(({ url }) => {
186
+ try {
187
+ return new URL(url, options.baseUrl).href;
188
+ }
189
+ catch (err) {
190
+ return false;
191
+ }
192
+ })
193
+ .map((requestOptions) => {
194
+ // Leave already-absolute URLs untouched - re-deriving them via `new URL()` would normalize them
195
+ // (e.g. adding a trailing slash to a bare domain), which is surprising for URLs that didn't need
196
+ // resolving against `baseUrl` in the first place.
197
+ if (!isAbsoluteUrl(requestOptions.url)) {
198
+ requestOptions.url = new URL(requestOptions.url, options.baseUrl).href;
199
+ }
200
+ requestOptions.userData ??= options.userData ?? {};
201
+ if (typeof options.label === 'string') {
202
+ requestOptions.userData = {
203
+ ...requestOptions.userData,
204
+ label: options.label,
205
+ };
206
+ }
207
+ if (options.skipNavigation) {
208
+ requestOptions.skipNavigation = true;
209
+ }
210
+ if (options.sessionId) {
211
+ requestOptions.sessionId = options.sessionId;
212
+ }
213
+ return requestOptions;
214
+ });
215
+ }
216
+ /**
217
+ * @ignore
218
+ */
219
+ function createPatternObjectMatcher(urlPatternObject) {
220
+ const { regexp, glob } = urlPatternObject;
221
+ let match;
222
+ if (regexp) {
223
+ match = (url) => regexp.test(url);
224
+ }
225
+ else if (glob) {
226
+ const m = new Minimatch(glob, { nocase: true });
227
+ match = (url) => m.match(url);
228
+ }
229
+ else {
230
+ match = () => false;
231
+ }
232
+ return { match };
233
+ }
234
+ /**
235
+ * Applies a {@link RequestTransform} function to a list of request options.
236
+ * Options for which the transform returns a falsy value are removed from the list.
237
+ * @param onSkipped Called with the original request options when the transform returns a falsy value (i.e. the request is skipped).
238
+ * @ignore
239
+ * @internal
240
+ */
241
+ export function applyRequestTransform(requestOptions, transformFn, onSkipped) {
242
+ return requestOptions
243
+ .map((opts) => {
244
+ const transformed = transformFn(opts);
245
+ if (transformed === 'skip') {
246
+ onSkipped?.(opts);
247
+ return null;
248
+ }
249
+ if (transformed === 'unchanged') {
250
+ return opts;
251
+ }
252
+ if (!transformed) {
253
+ onSkipped?.(opts);
254
+ return null;
255
+ }
256
+ return transformed;
257
+ })
258
+ .filter((r) => r !== null);
259
+ }
@@ -0,0 +1,58 @@
1
+ import { CriticalError, NonRetryableError } from '@crawlee/core';
2
+ /**
3
+ * @ignore
4
+ */
5
+ export declare class MissingRouteError extends CriticalError {
6
+ }
7
+ /**
8
+ * Errors of `RetryRequestError` type will always be retried by the crawler.
9
+ *
10
+ * *This error overrides the `maxRequestRetries` option, i.e. the request can be retried indefinitely until it succeeds.*
11
+ */
12
+ export declare class RetryRequestError extends Error {
13
+ constructor(message?: string);
14
+ }
15
+ /**
16
+ * Thrown when a domain has rate-limited us and the request should simply be attempted again later.
17
+ *
18
+ * The request is reclaimed without recording a failure: it costs neither a retry nor session reputation, because
19
+ * nothing about the request or the session was at fault. A {@link ThrottlingRequestManager} holds it back until
20
+ * the domain's backoff expires, so retries are paced rather than immediate.
21
+ */
22
+ export declare class RequestThrottledError extends RetryRequestError {
23
+ constructor(message?: string);
24
+ }
25
+ /**
26
+ * Thrown when a domain has rate-limited us for so long that no request has got through, and the crawl is
27
+ * abandoned rather than kept waiting.
28
+ *
29
+ * Waiting longer will not help: at this point the concurrency is too high for the domain, or it has blocked us.
30
+ * The affected requests are deliberately left in their queue, so re-running the crawl without purging storages
31
+ * resumes them once the domain recovers.
32
+ */
33
+ export declare class PersistentRateLimitError extends CriticalError {
34
+ }
35
+ /**
36
+ * Thrown when a requested session is not found in the referenced SessionPool.
37
+ */
38
+ export declare class MissingSessionError extends Error {
39
+ constructor(sessionId?: string);
40
+ }
41
+ export declare class ContextPipelineInterruptedError extends Error {
42
+ constructor(message?: string);
43
+ }
44
+ export declare class ContextPipelineInitializationError extends Error {
45
+ constructor(error: unknown, options?: ErrorOptions);
46
+ }
47
+ export declare class ContextPipelineCleanupError extends CriticalError {
48
+ constructor(error: unknown, options?: ErrorOptions);
49
+ }
50
+ export declare class RequestHandlerError extends Error {
51
+ constructor(error: unknown, options?: ErrorOptions);
52
+ }
53
+ /**
54
+ * Thrown by crawlers when `skipNavigation` is used on a request.
55
+ * Subclasses can catch this error to skip their own navigation-dependent logic.
56
+ */
57
+ export declare class NavigationSkippedError extends NonRetryableError {
58
+ }
@@ -0,0 +1,72 @@
1
+ import { CriticalError, NonRetryableError } from '@crawlee/core';
2
+ /**
3
+ * @ignore
4
+ */
5
+ export class MissingRouteError extends CriticalError {
6
+ }
7
+ /**
8
+ * Errors of `RetryRequestError` type will always be retried by the crawler.
9
+ *
10
+ * *This error overrides the `maxRequestRetries` option, i.e. the request can be retried indefinitely until it succeeds.*
11
+ */
12
+ export class RetryRequestError extends Error {
13
+ constructor(message) {
14
+ super(message ?? "Request is being retried at the user's request");
15
+ }
16
+ }
17
+ /**
18
+ * Thrown when a domain has rate-limited us and the request should simply be attempted again later.
19
+ *
20
+ * The request is reclaimed without recording a failure: it costs neither a retry nor session reputation, because
21
+ * nothing about the request or the session was at fault. A {@link ThrottlingRequestManager} holds it back until
22
+ * the domain's backoff expires, so retries are paced rather than immediate.
23
+ */
24
+ export class RequestThrottledError extends RetryRequestError {
25
+ constructor(message) {
26
+ super(message ?? 'Request is being retried later because its domain is rate-limiting us');
27
+ }
28
+ }
29
+ /**
30
+ * Thrown when a domain has rate-limited us for so long that no request has got through, and the crawl is
31
+ * abandoned rather than kept waiting.
32
+ *
33
+ * Waiting longer will not help: at this point the concurrency is too high for the domain, or it has blocked us.
34
+ * The affected requests are deliberately left in their queue, so re-running the crawl without purging storages
35
+ * resumes them once the domain recovers.
36
+ */
37
+ export class PersistentRateLimitError extends CriticalError {
38
+ }
39
+ /**
40
+ * Thrown when a requested session is not found in the referenced SessionPool.
41
+ */
42
+ export class MissingSessionError extends Error {
43
+ constructor(sessionId) {
44
+ super(`The current SessionPool instance couldn't find a valid session${sessionId ? ` for the following id: ${sessionId}.` : '.'}`);
45
+ }
46
+ }
47
+ export class ContextPipelineInterruptedError extends Error {
48
+ constructor(message) {
49
+ super(`Request handling was interrupted during context initialization ${message ? ` - ${message}` : ''}`);
50
+ }
51
+ }
52
+ export class ContextPipelineInitializationError extends Error {
53
+ constructor(error, options) {
54
+ super(undefined, { cause: error, ...options });
55
+ }
56
+ }
57
+ export class ContextPipelineCleanupError extends CriticalError {
58
+ constructor(error, options) {
59
+ super(undefined, { cause: error, ...options });
60
+ }
61
+ }
62
+ export class RequestHandlerError extends Error {
63
+ constructor(error, options) {
64
+ super(undefined, { cause: error, ...options });
65
+ }
66
+ }
67
+ /**
68
+ * Thrown by crawlers when `skipNavigation` is used on a request.
69
+ * Subclasses can catch this error to skip their own navigation-dependent logic.
70
+ */
71
+ export class NavigationSkippedError extends NonRetryableError {
72
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Parses a `Retry-After` response header into a delay in milliseconds.
3
+ *
4
+ * The header holds either a non-negative number of seconds or an HTTP-date.
5
+ * See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After).
6
+ *
7
+ * @returns The delay in milliseconds, or `null` if the header is absent, unparseable, or already elapsed.
8
+ */
9
+ export declare function parseRetryAfterHeader(value?: string | null): number | null;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Parses a `Retry-After` response header into a delay in milliseconds.
3
+ *
4
+ * The header holds either a non-negative number of seconds or an HTTP-date.
5
+ * See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After).
6
+ *
7
+ * @returns The delay in milliseconds, or `null` if the header is absent, unparseable, or already elapsed.
8
+ */
9
+ export function parseRetryAfterHeader(value) {
10
+ if (!value) {
11
+ return null;
12
+ }
13
+ const trimmed = value.trim();
14
+ // Per the spec this is a `delay-seconds`: digits only, so a negative or fractional value is not one.
15
+ if (/^\d+$/.test(trimmed)) {
16
+ // `Retry-After: 0` names no future deadline, same as an HTTP-date that has already passed. Reporting it
17
+ // as a zero delay would leave the domain unthrottled while still counting as a rate-limit event, so the
18
+ // caller would defer the request for free and re-send it immediately.
19
+ const delayMs = Number(trimmed) * 1000;
20
+ return delayMs > 0 ? delayMs : null;
21
+ }
22
+ const date = Date.parse(trimmed);
23
+ if (!Number.isNaN(date)) {
24
+ const delayMs = date - Date.now();
25
+ return delayMs > 0 ? delayMs : null;
26
+ }
27
+ return null;
28
+ }