@crawlee/core 4.0.0-beta.182 → 4.0.0-beta.184
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.
- package/package.json +6 -6
- package/request.d.ts +15 -103
- package/request.js +36 -198
- package/storages/request_queue.js +9 -10
- package/storages/transaction.d.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/core",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.184",
|
|
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"
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"@apify/log": "^3.0.1",
|
|
54
54
|
"@apify/timeout": "^1.0.1",
|
|
55
55
|
"@apify/utilities": "^3.0.1",
|
|
56
|
-
"@crawlee/fs-storage": "4.0.0-beta.
|
|
57
|
-
"@crawlee/http-client": "4.0.0-beta.
|
|
58
|
-
"@crawlee/types": "4.0.0-beta.
|
|
59
|
-
"@crawlee/utils": "4.0.0-beta.
|
|
56
|
+
"@crawlee/fs-storage": "4.0.0-beta.184",
|
|
57
|
+
"@crawlee/http-client": "4.0.0-beta.184",
|
|
58
|
+
"@crawlee/types": "4.0.0-beta.184",
|
|
59
|
+
"@crawlee/utils": "4.0.0-beta.184",
|
|
60
60
|
"@sapphire/async-queue": "^1.5.5",
|
|
61
61
|
"@standard-schema/spec": "^1.0.0",
|
|
62
62
|
"@vladfrangu/async_event_emitter": "^2.4.6",
|
|
@@ -76,5 +76,5 @@
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
|
-
"gitHead": "
|
|
79
|
+
"gitHead": "19ebeeb9bb90e38496cbe90565c8ebb02e102193"
|
|
80
80
|
}
|
package/request.d.ts
CHANGED
|
@@ -1,22 +1,12 @@
|
|
|
1
1
|
import type { BinaryLike } from 'node:crypto';
|
|
2
|
-
import type { AllowedHttpMethods, Dictionary } from '@crawlee/types';
|
|
2
|
+
import type { AllowedHttpMethods, Dictionary, RequestSchema } from '@crawlee/types';
|
|
3
3
|
import type { EnqueueStrategy } from '@crawlee/utils';
|
|
4
4
|
/** The `strategy` option accepted by {@link ExtractLinksOptions} and {@link EnqueueUrlsOptions}. */
|
|
5
5
|
export type EnqueueStrategyOption = EnqueueStrategy | 'all' | 'same-domain' | 'same-hostname' | 'same-origin';
|
|
6
|
-
export type SkippedRequestReason = 'robotsTxt' | 'limit' | 'enqueueLimit' | 'filters' | 'transform' | 'redirect' | 'depth';
|
|
7
|
-
export declare enum RequestState {
|
|
8
|
-
UNPROCESSED = 0,
|
|
9
|
-
BEFORE_NAV = 1,
|
|
10
|
-
AFTER_NAV = 2,
|
|
11
|
-
REQUEST_HANDLER = 3,
|
|
12
|
-
DONE = 4,
|
|
13
|
-
ERROR_HANDLER = 5,
|
|
14
|
-
ERROR = 6,
|
|
15
|
-
SKIPPED = 7
|
|
16
|
-
}
|
|
17
6
|
/**
|
|
18
|
-
* Represents a URL to be crawled, optionally including HTTP method, headers, payload and other metadata
|
|
19
|
-
*
|
|
7
|
+
* Represents a URL to be crawled, optionally including HTTP method, headers, payload and other metadata, together
|
|
8
|
+
* with the processing state a request queue keeps about it (`retryCount`, `errorMessages`, `handledAt`, ...).
|
|
9
|
+
* Inside a crawler, requests are {@link CrawlingRequest}s, which add the crawler's own per-request settings.
|
|
20
10
|
*
|
|
21
11
|
* Each `Request` instance has the `uniqueKey` property, which can be either specified
|
|
22
12
|
* manually in the constructor or generated automatically from the URL. Two requests with the same `uniqueKey`
|
|
@@ -32,22 +22,16 @@ export declare enum RequestState {
|
|
|
32
22
|
* const request = new Request({
|
|
33
23
|
* url: 'http://www.example.com',
|
|
34
24
|
* headers: { Accept: 'application/json' },
|
|
25
|
+
* userData: { foo: 'bar' },
|
|
35
26
|
* });
|
|
36
27
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* request.userData.foo = 'bar';
|
|
40
|
-
* request.pushErrorMessage(new Error('Request failed!'));
|
|
41
|
-
*
|
|
42
|
-
* ...
|
|
43
|
-
*
|
|
44
|
-
* const foo = request.userData.foo;
|
|
28
|
+
* await requestQueue.addRequest(request);
|
|
45
29
|
* ```
|
|
46
30
|
* @category Sources
|
|
47
31
|
*/
|
|
48
32
|
declare class CrawleeRequest<UserData extends Dictionary = Dictionary> {
|
|
49
33
|
#private;
|
|
50
|
-
/**
|
|
34
|
+
/** Storage-assigned request ID. Only present on requests that went through a {@link RequestQueue}. */
|
|
51
35
|
id?: string;
|
|
52
36
|
/** URL of the web page to crawl. */
|
|
53
37
|
url: string;
|
|
@@ -91,78 +75,25 @@ declare class CrawleeRequest<UserData extends Dictionary = Dictionary> {
|
|
|
91
75
|
handledAt?: string;
|
|
92
76
|
/**
|
|
93
77
|
* `Request` parameters including the URL, HTTP method and headers, and others.
|
|
78
|
+
*
|
|
79
|
+
* Processing state (`retryCount`, `errorMessages`, `handledAt`, `loadedUrl`, `id`) is not an option;
|
|
80
|
+
* requests coming back from a storage are rebuilt with {@link Request.fromSchema}.
|
|
94
81
|
*/
|
|
95
82
|
constructor(options: RequestOptions<UserData>);
|
|
83
|
+
/**
|
|
84
|
+
* Rebuilds a request from its stored form, including the processing state a
|
|
85
|
+
* {@link RequestOptions} object cannot carry. Subclasses get instances of themselves.
|
|
86
|
+
*/
|
|
87
|
+
static fromSchema<UserData extends Dictionary = Dictionary>(schema: RequestSchema): CrawleeRequest<UserData>;
|
|
96
88
|
/**
|
|
97
89
|
* Converts the Crawlee Request object to a `fetch` API Request object.
|
|
98
90
|
* @returns The native `fetch` API Request object.
|
|
99
91
|
*/
|
|
100
92
|
intoFetchAPIRequest(): Request;
|
|
101
|
-
/**
|
|
102
|
-
* Tells the crawler processing this request to skip the navigation and process the request directly.
|
|
103
|
-
*
|
|
104
|
-
* When this is set to `true`, the crawling context will not contain the results of the navigation
|
|
105
|
-
* (e.g. `response`, `body`, `contentType`, `$` or `request.loadedUrl`).
|
|
106
|
-
* Accessing these properties will throw a {@link NavigationSkippedError} at runtime.
|
|
107
|
-
*/
|
|
108
|
-
get skipNavigation(): boolean;
|
|
109
|
-
/**
|
|
110
|
-
* Tells the crawler processing this request to skip the navigation and process the request directly.
|
|
111
|
-
*
|
|
112
|
-
* When this is set to `true`, the crawling context will not contain the results of the navigation
|
|
113
|
-
* (e.g. `response`, `body`, `contentType`, `$` or `request.loadedUrl`).
|
|
114
|
-
* Accessing these properties will throw a {@link NavigationSkippedError} at runtime.
|
|
115
|
-
*/
|
|
116
|
-
set skipNavigation(value: boolean);
|
|
117
|
-
/**
|
|
118
|
-
* Depth of the request in the current crawl tree.
|
|
119
|
-
* Note that this is dependent on the crawler setup and might produce unexpected results when used with multiple crawlers.
|
|
120
|
-
*/
|
|
121
|
-
get crawlDepth(): number;
|
|
122
|
-
/**
|
|
123
|
-
* Depth of the request in the current crawl tree.
|
|
124
|
-
* Note that this is dependent on the crawler setup and might produce unexpected results when used with multiple crawlers.
|
|
125
|
-
*/
|
|
126
|
-
set crawlDepth(value: number);
|
|
127
|
-
/** ID of a session to use for this request. When set, the crawler will fetch this session from the session pool instead of creating a new one. */
|
|
128
|
-
get sessionId(): string | undefined;
|
|
129
|
-
set sessionId(value: string | undefined);
|
|
130
93
|
/** shortcut for getting `request.userData.label` */
|
|
131
94
|
get label(): string | undefined;
|
|
132
95
|
/** shortcut for setting `request.userData.label` */
|
|
133
96
|
set label(value: string | undefined);
|
|
134
|
-
/** Maximum number of retries for this request. Allows to override the global `maxRequestRetries` option of `BasicCrawler`. */
|
|
135
|
-
get maxRetries(): number | undefined;
|
|
136
|
-
/** Maximum number of retries for this request. Allows to override the global `maxRequestRetries` option of `BasicCrawler`. */
|
|
137
|
-
set maxRetries(value: number | undefined);
|
|
138
|
-
/** Describes the request's current lifecycle state. */
|
|
139
|
-
get state(): RequestState;
|
|
140
|
-
/** Describes the request's current lifecycle state. */
|
|
141
|
-
set state(value: RequestState);
|
|
142
|
-
/**
|
|
143
|
-
* Reason for skipping this request.
|
|
144
|
-
*/
|
|
145
|
-
get skippedReason(): SkippedRequestReason | undefined;
|
|
146
|
-
/**
|
|
147
|
-
* Reason for skipping this request.
|
|
148
|
-
*/
|
|
149
|
-
set skippedReason(value: SkippedRequestReason | undefined);
|
|
150
|
-
private get enqueueStrategy();
|
|
151
|
-
private set enqueueStrategy(value);
|
|
152
|
-
/**
|
|
153
|
-
* Stores information about an error that occurred during processing of this request.
|
|
154
|
-
*
|
|
155
|
-
* You should always use Error instances when throwing errors in JavaScript.
|
|
156
|
-
*
|
|
157
|
-
* Nevertheless, to improve the debugging experience when using third party libraries
|
|
158
|
-
* that may not always throw an Error instance, the function performs a type
|
|
159
|
-
* inspection of the passed argument and attempts to extract as much information
|
|
160
|
-
* as possible, since just throwing a bad type error makes any debugging rather difficult.
|
|
161
|
-
*
|
|
162
|
-
* @param errorOrMessage Error object or error message to be stored in the request.
|
|
163
|
-
* @param [options]
|
|
164
|
-
*/
|
|
165
|
-
pushErrorMessage(errorOrMessage: unknown, options?: PushErrorMessageOptions): void;
|
|
166
97
|
/** @internal */
|
|
167
98
|
static computeUniqueKey({ url, method, payload, keepUrlFragment, useExtendedUniqueKey, alwaysEnqueue, }: ComputeUniqueKeyOptions): string;
|
|
168
99
|
/** @internal */
|
|
@@ -266,32 +197,13 @@ export interface RequestOptions<UserData extends Dictionary = Dictionary> {
|
|
|
266
197
|
* @default 0
|
|
267
198
|
*/
|
|
268
199
|
crawlDepth?: number;
|
|
269
|
-
/**
|
|
270
|
-
* Reason for skipping this request.
|
|
271
|
-
* This is used to provide more information about why the request was skipped.
|
|
272
|
-
* @internal
|
|
273
|
-
*/
|
|
274
|
-
skippedReason?: SkippedRequestReason;
|
|
275
200
|
/**
|
|
276
201
|
* Maximum number of retries for this request. Allows to override the global `maxRequestRetries` option of `BasicCrawler`.
|
|
277
202
|
*/
|
|
278
203
|
maxRetries?: number;
|
|
279
204
|
/** @internal */
|
|
280
|
-
id?: string;
|
|
281
|
-
/** @internal */
|
|
282
|
-
handledAt?: string;
|
|
283
|
-
/** @internal */
|
|
284
|
-
lockExpiresAt?: Date;
|
|
285
|
-
/** @internal */
|
|
286
205
|
enqueueStrategy?: EnqueueStrategyOption;
|
|
287
206
|
}
|
|
288
|
-
export interface PushErrorMessageOptions {
|
|
289
|
-
/**
|
|
290
|
-
* Only push the error message without stack trace when true.
|
|
291
|
-
* @default false
|
|
292
|
-
*/
|
|
293
|
-
omitStack?: boolean;
|
|
294
|
-
}
|
|
295
207
|
interface ComputeUniqueKeyOptions {
|
|
296
208
|
url: string;
|
|
297
209
|
method: AllowedHttpMethods;
|
package/request.js
CHANGED
|
@@ -1,41 +1,21 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
|
-
import util from 'node:util';
|
|
3
2
|
import { z } from 'zod';
|
|
4
3
|
import { cryptoRandomObjectId, normalizeUrl } from '@apify/utilities';
|
|
5
4
|
import { serviceLocator } from './service_locator.js';
|
|
6
5
|
import { keys } from './typedefs.js';
|
|
7
6
|
import { parseArgument, schemas } from './validators.js';
|
|
8
|
-
const dateString = z.string().refine((value) => !Number.isNaN(Date.parse(value)), {
|
|
9
|
-
message: 'Invalid input: expected a date string',
|
|
10
|
-
});
|
|
11
|
-
export var RequestState;
|
|
12
|
-
(function (RequestState) {
|
|
13
|
-
RequestState[RequestState["UNPROCESSED"] = 0] = "UNPROCESSED";
|
|
14
|
-
RequestState[RequestState["BEFORE_NAV"] = 1] = "BEFORE_NAV";
|
|
15
|
-
RequestState[RequestState["AFTER_NAV"] = 2] = "AFTER_NAV";
|
|
16
|
-
RequestState[RequestState["REQUEST_HANDLER"] = 3] = "REQUEST_HANDLER";
|
|
17
|
-
RequestState[RequestState["DONE"] = 4] = "DONE";
|
|
18
|
-
RequestState[RequestState["ERROR_HANDLER"] = 5] = "ERROR_HANDLER";
|
|
19
|
-
RequestState[RequestState["ERROR"] = 6] = "ERROR";
|
|
20
|
-
RequestState[RequestState["SKIPPED"] = 7] = "SKIPPED";
|
|
21
|
-
})(RequestState || (RequestState = {}));
|
|
22
7
|
const requestUrlSchema = z.object({ url: z.string() });
|
|
23
8
|
// new properties on the Request object breaks serialization
|
|
24
9
|
const requestOptionalSchemaShapes = {
|
|
25
|
-
id: z.string().optional(),
|
|
26
|
-
loadedUrl: z.url().optional(),
|
|
27
10
|
uniqueKey: z.string().optional(),
|
|
28
11
|
method: z.string().optional(),
|
|
29
12
|
payload: z.union([z.string(), z.instanceof(Uint8Array)]).optional(),
|
|
30
13
|
noRetry: z.boolean().optional(),
|
|
31
|
-
retryCount: schemas.anyNumber.optional(),
|
|
32
14
|
sessionId: z.string().optional(),
|
|
33
15
|
maxRetries: schemas.anyNumber.optional(),
|
|
34
|
-
errorMessages: schemas.arrayOf(z.string(), 'strings').optional(),
|
|
35
16
|
headers: z.looseObject({}).optional(),
|
|
36
17
|
userData: z.looseObject({}).optional(),
|
|
37
18
|
label: z.string().optional(),
|
|
38
|
-
handledAt: z.union([dateString, z.date()]).optional(),
|
|
39
19
|
keepUrlFragment: z.boolean().optional(),
|
|
40
20
|
useExtendedUniqueKey: z.boolean().optional(),
|
|
41
21
|
alwaysEnqueue: z.boolean().optional(),
|
|
@@ -43,13 +23,13 @@ const requestOptionalSchemaShapes = {
|
|
|
43
23
|
crawlDepth: schemas.anyNumber
|
|
44
24
|
.refine((value) => value >= 0, 'Expected a number greater than or equal to 0')
|
|
45
25
|
.optional(),
|
|
46
|
-
state: z.enum(RequestState).optional(),
|
|
47
26
|
};
|
|
48
27
|
// Each schema is wrapped in a single-key object so validation errors carry the property name.
|
|
49
28
|
const requestOptionalSchemas = Object.fromEntries(Object.entries(requestOptionalSchemaShapes).map(([key, schema]) => [key, z.object({ [key]: schema })]));
|
|
50
29
|
/**
|
|
51
|
-
* Represents a URL to be crawled, optionally including HTTP method, headers, payload and other metadata
|
|
52
|
-
*
|
|
30
|
+
* Represents a URL to be crawled, optionally including HTTP method, headers, payload and other metadata, together
|
|
31
|
+
* with the processing state a request queue keeps about it (`retryCount`, `errorMessages`, `handledAt`, ...).
|
|
32
|
+
* Inside a crawler, requests are {@link CrawlingRequest}s, which add the crawler's own per-request settings.
|
|
53
33
|
*
|
|
54
34
|
* Each `Request` instance has the `uniqueKey` property, which can be either specified
|
|
55
35
|
* manually in the constructor or generated automatically from the URL. Two requests with the same `uniqueKey`
|
|
@@ -65,21 +45,15 @@ const requestOptionalSchemas = Object.fromEntries(Object.entries(requestOptional
|
|
|
65
45
|
* const request = new Request({
|
|
66
46
|
* url: 'http://www.example.com',
|
|
67
47
|
* headers: { Accept: 'application/json' },
|
|
48
|
+
* userData: { foo: 'bar' },
|
|
68
49
|
* });
|
|
69
50
|
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* request.userData.foo = 'bar';
|
|
73
|
-
* request.pushErrorMessage(new Error('Request failed!'));
|
|
74
|
-
*
|
|
75
|
-
* ...
|
|
76
|
-
*
|
|
77
|
-
* const foo = request.userData.foo;
|
|
51
|
+
* await requestQueue.addRequest(request);
|
|
78
52
|
* ```
|
|
79
53
|
* @category Sources
|
|
80
54
|
*/
|
|
81
55
|
class CrawleeRequest {
|
|
82
|
-
/**
|
|
56
|
+
/** Storage-assigned request ID. Only present on requests that went through a {@link RequestQueue}. */
|
|
83
57
|
id;
|
|
84
58
|
/** URL of the web page to crawl. */
|
|
85
59
|
url;
|
|
@@ -125,6 +99,9 @@ class CrawleeRequest {
|
|
|
125
99
|
handledAt;
|
|
126
100
|
/**
|
|
127
101
|
* `Request` parameters including the URL, HTTP method and headers, and others.
|
|
102
|
+
*
|
|
103
|
+
* Processing state (`retryCount`, `errorMessages`, `handledAt`, `loadedUrl`, `id`) is not an option;
|
|
104
|
+
* requests coming back from a storage are rebuilt with {@link Request.fromSchema}.
|
|
128
105
|
*/
|
|
129
106
|
constructor(options) {
|
|
130
107
|
// A bare URL is a common slip — point at the object form instead of a generic type error.
|
|
@@ -149,7 +126,7 @@ class CrawleeRequest {
|
|
|
149
126
|
parseArgument({ [prop]: value }, schema, 'RequestOptions');
|
|
150
127
|
}
|
|
151
128
|
});
|
|
152
|
-
const {
|
|
129
|
+
const { url, uniqueKey, payload, noRetry = false, sessionId, maxRetries, headers = {}, userData = {}, label, keepUrlFragment = false, useExtendedUniqueKey = false, alwaysEnqueue = false, skipNavigation, enqueueStrategy, crawlDepth, } = options;
|
|
153
130
|
let { method = 'GET' } = options;
|
|
154
131
|
method = method.toUpperCase();
|
|
155
132
|
if (method === 'GET' && payload)
|
|
@@ -157,9 +134,7 @@ class CrawleeRequest {
|
|
|
157
134
|
if (uniqueKey && alwaysEnqueue) {
|
|
158
135
|
throw new Error('`alwaysEnqueue` cannot be used together with a custom `uniqueKey`.');
|
|
159
136
|
}
|
|
160
|
-
this.id = id;
|
|
161
137
|
this.url = url;
|
|
162
|
-
this.loadedUrl = loadedUrl;
|
|
163
138
|
this.uniqueKey =
|
|
164
139
|
uniqueKey ||
|
|
165
140
|
CrawleeRequest.computeUniqueKey({
|
|
@@ -173,10 +148,9 @@ class CrawleeRequest {
|
|
|
173
148
|
this.method = method;
|
|
174
149
|
this.payload = payload;
|
|
175
150
|
this.noRetry = noRetry;
|
|
176
|
-
this.retryCount =
|
|
177
|
-
this.errorMessages = [
|
|
151
|
+
this.retryCount = 0;
|
|
152
|
+
this.errorMessages = [];
|
|
178
153
|
this.headers = { ...headers };
|
|
179
|
-
this.handledAt = handledAt instanceof Date ? handledAt.toISOString() : handledAt;
|
|
180
154
|
if (label) {
|
|
181
155
|
userData.label = label;
|
|
182
156
|
}
|
|
@@ -216,18 +190,34 @@ class CrawleeRequest {
|
|
|
216
190
|
});
|
|
217
191
|
// reassign userData to ensure internal `__crawlee` object is non-enumerable
|
|
218
192
|
this.userData = userData;
|
|
193
|
+
const crawlee = (this.userData.__crawlee ??= {});
|
|
219
194
|
if (skipNavigation != null)
|
|
220
|
-
|
|
195
|
+
crawlee.skipNavigation = skipNavigation;
|
|
221
196
|
if (maxRetries != null)
|
|
222
|
-
|
|
197
|
+
crawlee.maxRetries = maxRetries;
|
|
223
198
|
if (crawlDepth != null)
|
|
224
|
-
|
|
199
|
+
crawlee.crawlDepth ??= crawlDepth;
|
|
225
200
|
if (sessionId)
|
|
226
|
-
|
|
227
|
-
//
|
|
228
|
-
if (enqueueStrategy)
|
|
229
|
-
|
|
230
|
-
|
|
201
|
+
crawlee.sessionId = sessionId;
|
|
202
|
+
// A stored request keeps the strategy it was enqueued under.
|
|
203
|
+
if (enqueueStrategy)
|
|
204
|
+
crawlee.enqueueStrategy ??= enqueueStrategy;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Rebuilds a request from its stored form, including the processing state a
|
|
208
|
+
* {@link RequestOptions} object cannot carry. Subclasses get instances of themselves.
|
|
209
|
+
*/
|
|
210
|
+
static fromSchema(schema) {
|
|
211
|
+
const { id, retryCount = 0, errorMessages = [], handledAt, loadedUrl, ...options } = schema;
|
|
212
|
+
const request = new this(options);
|
|
213
|
+
request.id = id;
|
|
214
|
+
request.retryCount = retryCount;
|
|
215
|
+
request.errorMessages = [...errorMessages];
|
|
216
|
+
request.loadedUrl = loadedUrl;
|
|
217
|
+
// `apify-client` parses `*At` fields into `Date`s, whatever the schema says.
|
|
218
|
+
request.handledAt =
|
|
219
|
+
handledAt instanceof Date ? handledAt.toISOString() : handledAt;
|
|
220
|
+
return request;
|
|
231
221
|
}
|
|
232
222
|
/**
|
|
233
223
|
* Converts the Crawlee Request object to a `fetch` API Request object.
|
|
@@ -240,54 +230,6 @@ class CrawleeRequest {
|
|
|
240
230
|
body: this.payload,
|
|
241
231
|
});
|
|
242
232
|
}
|
|
243
|
-
/**
|
|
244
|
-
* Tells the crawler processing this request to skip the navigation and process the request directly.
|
|
245
|
-
*
|
|
246
|
-
* When this is set to `true`, the crawling context will not contain the results of the navigation
|
|
247
|
-
* (e.g. `response`, `body`, `contentType`, `$` or `request.loadedUrl`).
|
|
248
|
-
* Accessing these properties will throw a {@link NavigationSkippedError} at runtime.
|
|
249
|
-
*/
|
|
250
|
-
get skipNavigation() {
|
|
251
|
-
return this.userData.__crawlee?.skipNavigation ?? false;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Tells the crawler processing this request to skip the navigation and process the request directly.
|
|
255
|
-
*
|
|
256
|
-
* When this is set to `true`, the crawling context will not contain the results of the navigation
|
|
257
|
-
* (e.g. `response`, `body`, `contentType`, `$` or `request.loadedUrl`).
|
|
258
|
-
* Accessing these properties will throw a {@link NavigationSkippedError} at runtime.
|
|
259
|
-
*/
|
|
260
|
-
set skipNavigation(value) {
|
|
261
|
-
if (!this.userData.__crawlee) {
|
|
262
|
-
this.userData.__crawlee = { skipNavigation: value };
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
this.userData.__crawlee.skipNavigation = value;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Depth of the request in the current crawl tree.
|
|
270
|
-
* Note that this is dependent on the crawler setup and might produce unexpected results when used with multiple crawlers.
|
|
271
|
-
*/
|
|
272
|
-
get crawlDepth() {
|
|
273
|
-
return this.userData.__crawlee?.crawlDepth ?? 0;
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* Depth of the request in the current crawl tree.
|
|
277
|
-
* Note that this is dependent on the crawler setup and might produce unexpected results when used with multiple crawlers.
|
|
278
|
-
*/
|
|
279
|
-
set crawlDepth(value) {
|
|
280
|
-
this.userData.__crawlee ??= {};
|
|
281
|
-
this.userData.__crawlee.crawlDepth = value;
|
|
282
|
-
}
|
|
283
|
-
/** ID of a session to use for this request. When set, the crawler will fetch this session from the session pool instead of creating a new one. */
|
|
284
|
-
get sessionId() {
|
|
285
|
-
return this.userData.__crawlee?.sessionId;
|
|
286
|
-
}
|
|
287
|
-
set sessionId(value) {
|
|
288
|
-
this.userData.__crawlee ??= {};
|
|
289
|
-
this.userData.__crawlee.sessionId = value;
|
|
290
|
-
}
|
|
291
233
|
/** shortcut for getting `request.userData.label` */
|
|
292
234
|
get label() {
|
|
293
235
|
return this.userData.label;
|
|
@@ -296,110 +238,6 @@ class CrawleeRequest {
|
|
|
296
238
|
set label(value) {
|
|
297
239
|
this.userData.label = value;
|
|
298
240
|
}
|
|
299
|
-
/** Maximum number of retries for this request. Allows to override the global `maxRequestRetries` option of `BasicCrawler`. */
|
|
300
|
-
get maxRetries() {
|
|
301
|
-
return this.userData.__crawlee?.maxRetries;
|
|
302
|
-
}
|
|
303
|
-
/** Maximum number of retries for this request. Allows to override the global `maxRequestRetries` option of `BasicCrawler`. */
|
|
304
|
-
set maxRetries(value) {
|
|
305
|
-
if (!this.userData.__crawlee) {
|
|
306
|
-
this.userData.__crawlee = { maxRetries: value };
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
this.userData.__crawlee.maxRetries = value;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
/** Describes the request's current lifecycle state. */
|
|
313
|
-
get state() {
|
|
314
|
-
return this.userData.__crawlee?.state ?? RequestState.UNPROCESSED;
|
|
315
|
-
}
|
|
316
|
-
/** Describes the request's current lifecycle state. */
|
|
317
|
-
set state(value) {
|
|
318
|
-
if (!this.userData.__crawlee) {
|
|
319
|
-
this.userData.__crawlee = { state: value };
|
|
320
|
-
}
|
|
321
|
-
else {
|
|
322
|
-
this.userData.__crawlee.state = value;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Reason for skipping this request.
|
|
327
|
-
*/
|
|
328
|
-
get skippedReason() {
|
|
329
|
-
return this.userData.__crawlee?.skippedReason;
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Reason for skipping this request.
|
|
333
|
-
*/
|
|
334
|
-
set skippedReason(value) {
|
|
335
|
-
if (!this.userData.__crawlee) {
|
|
336
|
-
this.userData.__crawlee = { skippedReason: value };
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
this.userData.__crawlee.skippedReason = value;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
get enqueueStrategy() {
|
|
343
|
-
return this.userData.__crawlee?.enqueueStrategy;
|
|
344
|
-
}
|
|
345
|
-
set enqueueStrategy(value) {
|
|
346
|
-
if (!this.userData.__crawlee) {
|
|
347
|
-
this.userData.__crawlee = { enqueueStrategy: value };
|
|
348
|
-
}
|
|
349
|
-
else {
|
|
350
|
-
this.userData.__crawlee.enqueueStrategy = value;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Stores information about an error that occurred during processing of this request.
|
|
355
|
-
*
|
|
356
|
-
* You should always use Error instances when throwing errors in JavaScript.
|
|
357
|
-
*
|
|
358
|
-
* Nevertheless, to improve the debugging experience when using third party libraries
|
|
359
|
-
* that may not always throw an Error instance, the function performs a type
|
|
360
|
-
* inspection of the passed argument and attempts to extract as much information
|
|
361
|
-
* as possible, since just throwing a bad type error makes any debugging rather difficult.
|
|
362
|
-
*
|
|
363
|
-
* @param errorOrMessage Error object or error message to be stored in the request.
|
|
364
|
-
* @param [options]
|
|
365
|
-
*/
|
|
366
|
-
pushErrorMessage(errorOrMessage, options = {}) {
|
|
367
|
-
const { omitStack } = options;
|
|
368
|
-
let message;
|
|
369
|
-
const type = typeof errorOrMessage;
|
|
370
|
-
if (type === 'object') {
|
|
371
|
-
if (!errorOrMessage) {
|
|
372
|
-
message = 'null';
|
|
373
|
-
}
|
|
374
|
-
else if (errorOrMessage instanceof Error) {
|
|
375
|
-
message = omitStack
|
|
376
|
-
? errorOrMessage.message
|
|
377
|
-
: // .stack includes the message
|
|
378
|
-
errorOrMessage.stack;
|
|
379
|
-
}
|
|
380
|
-
else if (Reflect.has(Object(errorOrMessage), 'message')) {
|
|
381
|
-
message = Reflect.get(Object(errorOrMessage), 'message');
|
|
382
|
-
}
|
|
383
|
-
else if (errorOrMessage.toString() !== '[object Object]') {
|
|
384
|
-
message = errorOrMessage.toString();
|
|
385
|
-
}
|
|
386
|
-
else {
|
|
387
|
-
try {
|
|
388
|
-
message = util.inspect(errorOrMessage);
|
|
389
|
-
}
|
|
390
|
-
catch (err) {
|
|
391
|
-
message = 'Unable to extract any message from the received object.';
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
else if (type === 'undefined') {
|
|
396
|
-
message = 'undefined';
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
message = errorOrMessage.toString();
|
|
400
|
-
}
|
|
401
|
-
this.errorMessages.push(message);
|
|
402
|
-
}
|
|
403
241
|
/** @internal */
|
|
404
242
|
static computeUniqueKey({ url, method = 'GET', payload, keepUrlFragment = false, useExtendedUniqueKey = false, alwaysEnqueue = false, }) {
|
|
405
243
|
const normalizedMethod = method.toUpperCase();
|
|
@@ -336,7 +336,7 @@ export class RequestQueue {
|
|
|
336
336
|
? // Requests without a snapshot were deduplicated or written through; nothing to replay.
|
|
337
337
|
entry.requests
|
|
338
338
|
.filter((journaled) => journaled.snapshot !== undefined)
|
|
339
|
-
.map((journaled) =>
|
|
339
|
+
.map((journaled) => Request.fromSchema(journaled.snapshot))
|
|
340
340
|
: []);
|
|
341
341
|
if (requests.length === 0)
|
|
342
342
|
continue;
|
|
@@ -473,7 +473,7 @@ export class RequestQueue {
|
|
|
473
473
|
if (opts.url !== undefined && typeof opts.url !== 'string') {
|
|
474
474
|
throw new Error(`Request options are not valid, the 'url' property is not a string. Input: ${inspect(opts)}`);
|
|
475
475
|
}
|
|
476
|
-
if (opts.id !== undefined) {
|
|
476
|
+
if ('id' in opts && opts.id !== undefined) {
|
|
477
477
|
throw new Error(`Request options are not valid, the 'id' property must not be present. Input: ${inspect(opts)}`);
|
|
478
478
|
}
|
|
479
479
|
if (opts.requestsFromUrl !== undefined &&
|
|
@@ -534,12 +534,12 @@ export class RequestQueue {
|
|
|
534
534
|
// Requests buffered by the active transaction (under the `deferred` write policy) are visible to it.
|
|
535
535
|
const buffered = transaction && this.bufferedRequests(transaction).get(uniqueKey);
|
|
536
536
|
if (buffered) {
|
|
537
|
-
return
|
|
537
|
+
return Request.fromSchema(buffered);
|
|
538
538
|
}
|
|
539
|
-
const
|
|
540
|
-
if (!
|
|
539
|
+
const schema = await this.backend.getRequest(uniqueKey);
|
|
540
|
+
if (!schema)
|
|
541
541
|
return null;
|
|
542
|
-
return
|
|
542
|
+
return Request.fromSchema(schema);
|
|
543
543
|
}
|
|
544
544
|
/**
|
|
545
545
|
* Returns a next request in the queue to be processed, or `null` if there are no more pending requests.
|
|
@@ -564,10 +564,10 @@ export class RequestQueue {
|
|
|
564
564
|
return null;
|
|
565
565
|
}
|
|
566
566
|
this.#statsTracker.add('headItemReadCount');
|
|
567
|
-
const
|
|
568
|
-
if (!
|
|
567
|
+
const schema = await this.backend.fetchNextRequest();
|
|
568
|
+
if (!schema)
|
|
569
569
|
return null;
|
|
570
|
-
return
|
|
570
|
+
return Request.fromSchema(schema);
|
|
571
571
|
}
|
|
572
572
|
/**
|
|
573
573
|
* Marks a request that was previously returned by the
|
|
@@ -685,7 +685,6 @@ export class RequestQueue {
|
|
|
685
685
|
isHandled: queueOperationInfo.wasAlreadyHandled,
|
|
686
686
|
uniqueKey: queueOperationInfo.uniqueKey,
|
|
687
687
|
hydrated: null,
|
|
688
|
-
lockExpiresAt: null,
|
|
689
688
|
forefront: queueOperationInfo.forefront,
|
|
690
689
|
});
|
|
691
690
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Awaitable, Dictionary } from '@crawlee/types';
|
|
1
|
+
import type { Awaitable, Dictionary, RequestSchema } from '@crawlee/types';
|
|
2
2
|
import type { RecordOptions } from './key_value_store.js';
|
|
3
3
|
/**
|
|
4
4
|
* Governs whether writes of a given storage type performed inside a {@link StorageTransaction} are
|
|
@@ -72,7 +72,7 @@ export interface JournaledRequest {
|
|
|
72
72
|
* A full JSON snapshot of the request for the commit replay. Only present for buffered additions —
|
|
73
73
|
* deduplicated and write-through ones are journaled for introspection only.
|
|
74
74
|
*/
|
|
75
|
-
snapshot?:
|
|
75
|
+
snapshot?: RequestSchema;
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
78
|
* A batch of request queue additions recorded in a transaction journal.
|