@crawlee/core 4.0.0-beta.19 → 4.0.0-beta.191
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/README.md +14 -14
- package/byte_utils.d.ts +17 -0
- package/byte_utils.js +42 -0
- package/configuration.d.ts +90 -226
- package/configuration.js +164 -225
- package/debug.d.ts +36 -0
- package/debug.js +70 -0
- package/errors.d.ts +40 -19
- package/errors.js +53 -28
- package/events/event_manager.d.ts +35 -9
- package/events/event_manager.js +12 -13
- package/events/index.d.ts +1 -1
- package/events/index.js +0 -1
- package/events/local_event_manager.d.ts +15 -3
- package/events/local_event_manager.js +39 -13
- package/events/system_info.d.ts +38 -0
- package/events/system_info.js +1 -0
- package/index.d.ts +7 -11
- package/index.js +8 -10
- package/internal.d.ts +8 -0
- package/internal.js +9 -0
- package/iterables.d.ts +79 -0
- package/iterables.js +134 -0
- package/log.d.ts +77 -1
- package/log.js +134 -1
- package/memory-storage/consts.d.ts +4 -0
- package/memory-storage/consts.js +4 -0
- package/memory-storage/index.d.ts +1 -0
- package/memory-storage/index.js +1 -0
- package/memory-storage/memory-storage.d.ts +44 -0
- package/memory-storage/memory-storage.js +160 -0
- package/memory-storage/resource-clients/common/base-client.d.ts +4 -0
- package/memory-storage/resource-clients/common/base-client.js +6 -0
- package/memory-storage/resource-clients/dataset.d.ts +40 -0
- package/memory-storage/resource-clients/dataset.js +106 -0
- package/memory-storage/resource-clients/key-value-store.d.ts +63 -0
- package/memory-storage/resource-clients/key-value-store.js +199 -0
- package/memory-storage/resource-clients/request-queue.d.ts +77 -0
- package/memory-storage/resource-clients/request-queue.js +414 -0
- package/memory-storage/utils.d.ts +16 -0
- package/memory-storage/utils.js +41 -0
- package/owned_or_injected.d.ts +58 -0
- package/owned_or_injected.js +98 -0
- package/package.json +18 -19
- package/proxy_configuration.d.ts +22 -134
- package/proxy_configuration.js +34 -146
- package/recoverable_state.d.ts +106 -52
- package/recoverable_state.js +200 -72
- package/request.d.ts +48 -93
- package/request.js +116 -217
- package/serialization.d.ts +0 -1
- package/serialization.js +15 -15
- package/service_locator.d.ts +165 -0
- package/service_locator.js +254 -0
- package/storages/batched_adds.d.ts +37 -0
- package/storages/batched_adds.js +73 -0
- package/storages/dataset.d.ts +114 -56
- package/storages/dataset.js +292 -149
- package/storages/index.d.ts +7 -9
- package/storages/index.js +5 -9
- package/storages/key_value_store.d.ts +187 -50
- package/storages/key_value_store.js +453 -172
- package/storages/key_value_store_codec.d.ts +32 -0
- package/storages/key_value_store_codec.js +113 -0
- package/storages/request_dedup_cache.d.ts +22 -0
- package/storages/request_dedup_cache.js +48 -0
- package/storages/request_list.d.ts +54 -129
- package/storages/request_list.js +272 -245
- package/storages/request_loader.d.ts +127 -0
- package/storages/request_loader.js +36 -0
- package/storages/request_manager.d.ts +109 -0
- package/storages/request_manager.js +1 -0
- package/storages/request_manager_tandem.d.ts +50 -23
- package/storages/request_manager_tandem.js +132 -57
- package/storages/request_queue.d.ts +286 -48
- package/storages/request_queue.js +756 -218
- package/storages/storage_instance_manager.d.ts +87 -0
- package/storages/storage_instance_manager.js +256 -0
- package/storages/storage_stats.d.ts +48 -0
- package/storages/storage_stats.js +29 -0
- package/storages/transaction.d.ts +260 -0
- package/storages/transaction.js +287 -0
- package/storages/utils.d.ts +59 -11
- package/storages/utils.js +75 -15
- package/system-info/cpu-info.d.ts +67 -0
- package/system-info/cpu-info.js +216 -0
- package/system-info/memory-info.d.ts +31 -0
- package/system-info/memory-info.js +115 -0
- package/system-info/ps-tree.d.ts +17 -0
- package/system-info/ps-tree.js +144 -0
- package/system-info/runtime.d.ts +14 -0
- package/system-info/runtime.js +80 -0
- package/typedefs.d.ts +0 -6
- package/typedefs.js +0 -1
- package/url.d.ts +9 -0
- package/url.js +11 -0
- package/validators.d.ts +23 -18
- package/validators.js +14 -18
- package/autoscaling/autoscaled_pool.d.ts +0 -300
- package/autoscaling/autoscaled_pool.d.ts.map +0 -1
- package/autoscaling/autoscaled_pool.js +0 -533
- package/autoscaling/autoscaled_pool.js.map +0 -1
- package/autoscaling/index.d.ts +0 -4
- package/autoscaling/index.d.ts.map +0 -1
- package/autoscaling/index.js +0 -4
- package/autoscaling/index.js.map +0 -1
- package/autoscaling/snapshotter.d.ts +0 -189
- package/autoscaling/snapshotter.d.ts.map +0 -1
- package/autoscaling/snapshotter.js +0 -285
- package/autoscaling/snapshotter.js.map +0 -1
- package/autoscaling/system_status.d.ts +0 -181
- package/autoscaling/system_status.d.ts.map +0 -1
- package/autoscaling/system_status.js +0 -169
- package/autoscaling/system_status.js.map +0 -1
- package/configuration.d.ts.map +0 -1
- package/configuration.js.map +0 -1
- package/cookie_utils.d.ts +0 -43
- package/cookie_utils.d.ts.map +0 -1
- package/cookie_utils.js +0 -111
- package/cookie_utils.js.map +0 -1
- package/crawlers/context_pipeline.d.ts +0 -61
- package/crawlers/context_pipeline.d.ts.map +0 -1
- package/crawlers/context_pipeline.js +0 -99
- package/crawlers/context_pipeline.js.map +0 -1
- package/crawlers/crawler_commons.d.ts +0 -195
- package/crawlers/crawler_commons.d.ts.map +0 -1
- package/crawlers/crawler_commons.js +0 -108
- package/crawlers/crawler_commons.js.map +0 -1
- package/crawlers/crawler_utils.d.ts +0 -10
- package/crawlers/crawler_utils.d.ts.map +0 -1
- package/crawlers/crawler_utils.js +0 -12
- package/crawlers/crawler_utils.js.map +0 -1
- package/crawlers/error_snapshotter.d.ts +0 -60
- package/crawlers/error_snapshotter.d.ts.map +0 -1
- package/crawlers/error_snapshotter.js +0 -118
- package/crawlers/error_snapshotter.js.map +0 -1
- package/crawlers/error_tracker.d.ts +0 -55
- package/crawlers/error_tracker.d.ts.map +0 -1
- package/crawlers/error_tracker.js +0 -309
- package/crawlers/error_tracker.js.map +0 -1
- package/crawlers/index.d.ts +0 -7
- package/crawlers/index.d.ts.map +0 -1
- package/crawlers/index.js +0 -7
- package/crawlers/index.js.map +0 -1
- package/crawlers/internals/types.d.ts +0 -8
- package/crawlers/internals/types.d.ts.map +0 -1
- package/crawlers/internals/types.js +0 -2
- package/crawlers/internals/types.js.map +0 -1
- package/crawlers/statistics.d.ts +0 -204
- package/crawlers/statistics.d.ts.map +0 -1
- package/crawlers/statistics.js +0 -346
- package/crawlers/statistics.js.map +0 -1
- package/enqueue_links/enqueue_links.d.ts +0 -249
- package/enqueue_links/enqueue_links.d.ts.map +0 -1
- package/enqueue_links/enqueue_links.js +0 -269
- package/enqueue_links/enqueue_links.js.map +0 -1
- package/enqueue_links/index.d.ts +0 -3
- package/enqueue_links/index.d.ts.map +0 -1
- package/enqueue_links/index.js +0 -3
- package/enqueue_links/index.js.map +0 -1
- package/enqueue_links/shared.d.ts +0 -73
- package/enqueue_links/shared.d.ts.map +0 -1
- package/enqueue_links/shared.js +0 -212
- package/enqueue_links/shared.js.map +0 -1
- package/errors.d.ts.map +0 -1
- package/errors.js.map +0 -1
- package/events/event_manager.d.ts.map +0 -1
- package/events/event_manager.js.map +0 -1
- package/events/index.d.ts.map +0 -1
- package/events/index.js.map +0 -1
- package/events/local_event_manager.d.ts.map +0 -1
- package/events/local_event_manager.js.map +0 -1
- package/http_clients/base-http-client.d.ts +0 -91
- package/http_clients/base-http-client.d.ts.map +0 -1
- package/http_clients/base-http-client.js +0 -44
- package/http_clients/base-http-client.js.map +0 -1
- package/http_clients/form-data-like.d.ts +0 -67
- package/http_clients/form-data-like.d.ts.map +0 -1
- package/http_clients/form-data-like.js +0 -5
- package/http_clients/form-data-like.js.map +0 -1
- package/http_clients/got-scraping-http-client.d.ts +0 -20
- package/http_clients/got-scraping-http-client.d.ts.map +0 -1
- package/http_clients/got-scraping-http-client.js +0 -102
- package/http_clients/got-scraping-http-client.js.map +0 -1
- package/http_clients/index.d.ts +0 -3
- package/http_clients/index.d.ts.map +0 -1
- package/http_clients/index.js +0 -3
- package/http_clients/index.js.map +0 -1
- package/index.d.ts.map +0 -1
- package/index.js.map +0 -1
- package/log.d.ts.map +0 -1
- package/log.js.map +0 -1
- package/proxy_configuration.d.ts.map +0 -1
- package/proxy_configuration.js.map +0 -1
- package/recoverable_state.d.ts.map +0 -1
- package/recoverable_state.js.map +0 -1
- package/request.d.ts.map +0 -1
- package/request.js.map +0 -1
- package/router.d.ts +0 -134
- package/router.d.ts.map +0 -1
- package/router.js +0 -164
- package/router.js.map +0 -1
- package/serialization.d.ts.map +0 -1
- package/serialization.js.map +0 -1
- package/session_pool/consts.d.ts +0 -4
- package/session_pool/consts.d.ts.map +0 -1
- package/session_pool/consts.js +0 -4
- package/session_pool/consts.js.map +0 -1
- package/session_pool/errors.d.ts +0 -8
- package/session_pool/errors.d.ts.map +0 -1
- package/session_pool/errors.js +0 -12
- package/session_pool/errors.js.map +0 -1
- package/session_pool/events.d.ts +0 -3
- package/session_pool/events.d.ts.map +0 -1
- package/session_pool/events.js +0 -3
- package/session_pool/events.js.map +0 -1
- package/session_pool/index.d.ts +0 -6
- package/session_pool/index.d.ts.map +0 -1
- package/session_pool/index.js +0 -6
- package/session_pool/index.js.map +0 -1
- package/session_pool/session.d.ts +0 -203
- package/session_pool/session.d.ts.map +0 -1
- package/session_pool/session.js +0 -278
- package/session_pool/session.js.map +0 -1
- package/session_pool/session_pool.d.ts +0 -258
- package/session_pool/session_pool.d.ts.map +0 -1
- package/session_pool/session_pool.js +0 -387
- package/session_pool/session_pool.js.map +0 -1
- package/storages/access_checking.d.ts +0 -13
- package/storages/access_checking.d.ts.map +0 -1
- package/storages/access_checking.js +0 -14
- package/storages/access_checking.js.map +0 -1
- package/storages/dataset.d.ts.map +0 -1
- package/storages/dataset.js.map +0 -1
- package/storages/index.d.ts.map +0 -1
- package/storages/index.js.map +0 -1
- package/storages/key_value_store.d.ts.map +0 -1
- package/storages/key_value_store.js.map +0 -1
- package/storages/request_list.d.ts.map +0 -1
- package/storages/request_list.js.map +0 -1
- package/storages/request_list_adapter.d.ts +0 -58
- package/storages/request_list_adapter.d.ts.map +0 -1
- package/storages/request_list_adapter.js +0 -81
- package/storages/request_list_adapter.js.map +0 -1
- package/storages/request_manager_tandem.d.ts.map +0 -1
- package/storages/request_manager_tandem.js.map +0 -1
- package/storages/request_provider.d.ts +0 -371
- package/storages/request_provider.d.ts.map +0 -1
- package/storages/request_provider.js +0 -585
- package/storages/request_provider.js.map +0 -1
- package/storages/request_queue.d.ts.map +0 -1
- package/storages/request_queue.js.map +0 -1
- package/storages/request_queue_v2.d.ts +0 -87
- package/storages/request_queue_v2.d.ts.map +0 -1
- package/storages/request_queue_v2.js +0 -438
- package/storages/request_queue_v2.js.map +0 -1
- package/storages/sitemap_request_list.d.ts +0 -245
- package/storages/sitemap_request_list.d.ts.map +0 -1
- package/storages/sitemap_request_list.js +0 -430
- package/storages/sitemap_request_list.js.map +0 -1
- package/storages/storage_manager.d.ts +0 -58
- package/storages/storage_manager.d.ts.map +0 -1
- package/storages/storage_manager.js +0 -105
- package/storages/storage_manager.js.map +0 -1
- package/storages/utils.d.ts.map +0 -1
- package/storages/utils.js.map +0 -1
- package/typedefs.d.ts.map +0 -1
- package/typedefs.js.map +0 -1
- package/validators.d.ts.map +0 -1
- package/validators.js.map +0 -1
package/request.js
CHANGED
|
@@ -1,46 +1,35 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { log as defaultLog } from './log.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { cryptoRandomObjectId, normalizeUrl } from '@apify/utilities';
|
|
4
|
+
import { serviceLocator } from './service_locator.js';
|
|
6
5
|
import { keys } from './typedefs.js';
|
|
6
|
+
import { parseArgument, schemas } from './validators.js';
|
|
7
|
+
const requestUrlSchema = z.object({ url: z.string() });
|
|
7
8
|
// new properties on the Request object breaks serialization
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
useExtendedUniqueKey: ow.optional.boolean,
|
|
26
|
-
skipNavigation: ow.optional.boolean,
|
|
27
|
-
crawlDepth: ow.optional.number.greaterThanOrEqual(0),
|
|
28
|
-
state: ow.optional.number.greaterThanOrEqual(0).lessThanOrEqual(6),
|
|
9
|
+
const requestOptionalSchemaShapes = {
|
|
10
|
+
uniqueKey: z.string().optional(),
|
|
11
|
+
method: z.string().optional(),
|
|
12
|
+
payload: z.union([z.string(), z.instanceof(Uint8Array)]).optional(),
|
|
13
|
+
noRetry: z.boolean().optional(),
|
|
14
|
+
sessionId: z.string().optional(),
|
|
15
|
+
maxRetries: schemas.anyNumber.optional(),
|
|
16
|
+
headers: z.looseObject({}).optional(),
|
|
17
|
+
userData: z.looseObject({}).optional(),
|
|
18
|
+
label: z.string().optional(),
|
|
19
|
+
keepUrlFragment: z.boolean().optional(),
|
|
20
|
+
useExtendedUniqueKey: z.boolean().optional(),
|
|
21
|
+
alwaysEnqueue: z.boolean().optional(),
|
|
22
|
+
skipNavigation: z.boolean().optional(),
|
|
23
|
+
crawlDepth: schemas.anyNumber
|
|
24
|
+
.refine((value) => value >= 0, 'Expected a number greater than or equal to 0')
|
|
25
|
+
.optional(),
|
|
29
26
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
RequestState[RequestState["UNPROCESSED"] = 0] = "UNPROCESSED";
|
|
33
|
-
RequestState[RequestState["BEFORE_NAV"] = 1] = "BEFORE_NAV";
|
|
34
|
-
RequestState[RequestState["AFTER_NAV"] = 2] = "AFTER_NAV";
|
|
35
|
-
RequestState[RequestState["REQUEST_HANDLER"] = 3] = "REQUEST_HANDLER";
|
|
36
|
-
RequestState[RequestState["DONE"] = 4] = "DONE";
|
|
37
|
-
RequestState[RequestState["ERROR_HANDLER"] = 5] = "ERROR_HANDLER";
|
|
38
|
-
RequestState[RequestState["ERROR"] = 6] = "ERROR";
|
|
39
|
-
RequestState[RequestState["SKIPPED"] = 7] = "SKIPPED";
|
|
40
|
-
})(RequestState || (RequestState = {}));
|
|
27
|
+
// Each schema is wrapped in a single-key object so validation errors carry the property name.
|
|
28
|
+
const requestOptionalSchemas = Object.fromEntries(Object.entries(requestOptionalSchemaShapes).map(([key, schema]) => [key, z.object({ [key]: schema })]));
|
|
41
29
|
/**
|
|
42
|
-
* Represents a URL to be crawled, optionally including HTTP method, headers, payload and other metadata
|
|
43
|
-
*
|
|
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.
|
|
44
33
|
*
|
|
45
34
|
* Each `Request` instance has the `uniqueKey` property, which can be either specified
|
|
46
35
|
* manually in the constructor or generated automatically from the URL. Two requests with the same `uniqueKey`
|
|
@@ -56,21 +45,15 @@ export var RequestState;
|
|
|
56
45
|
* const request = new Request({
|
|
57
46
|
* url: 'http://www.example.com',
|
|
58
47
|
* headers: { Accept: 'application/json' },
|
|
48
|
+
* userData: { foo: 'bar' },
|
|
59
49
|
* });
|
|
60
50
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* request.userData.foo = 'bar';
|
|
64
|
-
* request.pushErrorMessage(new Error('Request failed!'));
|
|
65
|
-
*
|
|
66
|
-
* ...
|
|
67
|
-
*
|
|
68
|
-
* const foo = request.userData.foo;
|
|
51
|
+
* await requestQueue.addRequest(request);
|
|
69
52
|
* ```
|
|
70
53
|
* @category Sources
|
|
71
54
|
*/
|
|
72
55
|
class CrawleeRequest {
|
|
73
|
-
/**
|
|
56
|
+
/** Storage-assigned request ID. Only present on requests that went through a {@link RequestQueue}. */
|
|
74
57
|
id;
|
|
75
58
|
/** URL of the web page to crawl. */
|
|
76
59
|
url;
|
|
@@ -101,8 +84,13 @@ class CrawleeRequest {
|
|
|
101
84
|
/** Object with HTTP headers. Key is header name, value is the value. */
|
|
102
85
|
headers;
|
|
103
86
|
/** Private store for the custom user data assigned to the request. */
|
|
104
|
-
|
|
105
|
-
/**
|
|
87
|
+
#userData = {};
|
|
88
|
+
/**
|
|
89
|
+
* Custom user data assigned to the request.
|
|
90
|
+
*
|
|
91
|
+
* All data stored in `userData` must be JSON-serializable.
|
|
92
|
+
* Storing non-serializable values (e.g. functions, symbols) may result in unexpected results.
|
|
93
|
+
*/
|
|
106
94
|
userData = {};
|
|
107
95
|
/**
|
|
108
96
|
* ISO datetime string that indicates the time when the request has been processed.
|
|
@@ -111,94 +99,125 @@ class CrawleeRequest {
|
|
|
111
99
|
handledAt;
|
|
112
100
|
/**
|
|
113
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}.
|
|
114
105
|
*/
|
|
115
106
|
constructor(options) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
107
|
+
// A bare URL is a common slip — point at the object form instead of a generic type error.
|
|
108
|
+
if (typeof options === 'string') {
|
|
109
|
+
throw new TypeError(`\`Request\` options must be an object, got the string '${options}'. ` +
|
|
110
|
+
'Did you mean `new Request({ url })`?');
|
|
111
|
+
}
|
|
112
|
+
parseArgument(options, schemas.anyObject, 'RequestOptions');
|
|
113
|
+
parseArgument(options, requestUrlSchema, 'RequestOptions');
|
|
114
|
+
// Full-shape validation is slow, because it checks all predicates
|
|
119
115
|
// even if the validated object has only 1 property.
|
|
120
116
|
// This custom validation loop iterates only over existing
|
|
121
117
|
// properties and speeds up the validation cca 3-fold.
|
|
122
|
-
// See https://github.com/sindresorhus/ow/issues/193
|
|
123
118
|
keys(options).forEach((prop) => {
|
|
124
119
|
// skip url, because it is validated above
|
|
125
120
|
if (prop === 'url') {
|
|
126
121
|
return;
|
|
127
122
|
}
|
|
128
|
-
const
|
|
123
|
+
const schema = requestOptionalSchemas[prop];
|
|
129
124
|
const value = options[prop];
|
|
130
|
-
if (
|
|
131
|
-
|
|
125
|
+
if (schema) {
|
|
126
|
+
parseArgument({ [prop]: value }, schema, 'RequestOptions');
|
|
132
127
|
}
|
|
133
128
|
});
|
|
134
|
-
const {
|
|
129
|
+
const { url, uniqueKey, payload, noRetry = false, sessionId, maxRetries, headers = {}, userData = {}, label, keepUrlFragment = false, useExtendedUniqueKey = false, alwaysEnqueue = false, skipNavigation, enqueueStrategy, crawlDepth, } = options;
|
|
135
130
|
let { method = 'GET' } = options;
|
|
136
131
|
method = method.toUpperCase();
|
|
137
132
|
if (method === 'GET' && payload)
|
|
138
133
|
throw new Error('Request with GET method cannot have a payload.');
|
|
139
|
-
|
|
134
|
+
if (uniqueKey && alwaysEnqueue) {
|
|
135
|
+
throw new Error('`alwaysEnqueue` cannot be used together with a custom `uniqueKey`.');
|
|
136
|
+
}
|
|
140
137
|
this.url = url;
|
|
141
|
-
this.loadedUrl = loadedUrl;
|
|
142
138
|
this.uniqueKey =
|
|
143
139
|
uniqueKey ||
|
|
144
|
-
CrawleeRequest.computeUniqueKey({
|
|
140
|
+
CrawleeRequest.computeUniqueKey({
|
|
141
|
+
url,
|
|
142
|
+
method,
|
|
143
|
+
payload,
|
|
144
|
+
keepUrlFragment,
|
|
145
|
+
useExtendedUniqueKey,
|
|
146
|
+
alwaysEnqueue,
|
|
147
|
+
});
|
|
145
148
|
this.method = method;
|
|
146
149
|
this.payload = payload;
|
|
147
150
|
this.noRetry = noRetry;
|
|
148
|
-
this.retryCount =
|
|
149
|
-
this.
|
|
150
|
-
this.errorMessages = [...errorMessages];
|
|
151
|
+
this.retryCount = 0;
|
|
152
|
+
this.errorMessages = [];
|
|
151
153
|
this.headers = { ...headers };
|
|
152
|
-
this.handledAt = handledAt instanceof Date ? handledAt.toISOString() : handledAt;
|
|
153
154
|
if (label) {
|
|
154
155
|
userData.label = label;
|
|
155
156
|
}
|
|
157
|
+
// Read `__crawlee` explicitly - on a `userData` coming from another Request instance the
|
|
158
|
+
// bag is non-enumerable, so the spread alone would silently drop the internal state
|
|
159
|
+
// (e.g. `skipNavigation`) when a request is re-wrapped after a storage round trip.
|
|
160
|
+
this.#userData = { __crawlee: userData.__crawlee ?? {}, ...userData };
|
|
161
|
+
// `userData` must stay an enumerable own accessor — serialization in the storages relies on it
|
|
156
162
|
Object.defineProperties(this, {
|
|
157
|
-
_userData: {
|
|
158
|
-
value: { __crawlee: {}, ...userData },
|
|
159
|
-
enumerable: false,
|
|
160
|
-
writable: true,
|
|
161
|
-
},
|
|
162
163
|
userData: {
|
|
163
|
-
get: () => this
|
|
164
|
+
get: () => this.#userData,
|
|
164
165
|
set: (value) => {
|
|
165
166
|
Object.defineProperties(value, {
|
|
166
167
|
__crawlee: {
|
|
167
|
-
value: this.
|
|
168
|
+
value: this.#userData.__crawlee,
|
|
168
169
|
enumerable: false,
|
|
169
170
|
writable: true,
|
|
170
171
|
},
|
|
171
172
|
toJSON: {
|
|
172
173
|
value: () => {
|
|
173
|
-
if (Object.keys(this.
|
|
174
|
+
if (Object.keys(this.#userData.__crawlee).length > 0) {
|
|
174
175
|
return {
|
|
175
|
-
...this
|
|
176
|
-
__crawlee: this.
|
|
176
|
+
...this.#userData,
|
|
177
|
+
__crawlee: this.#userData.__crawlee,
|
|
177
178
|
};
|
|
178
179
|
}
|
|
179
|
-
return this
|
|
180
|
+
return this.#userData;
|
|
180
181
|
},
|
|
181
182
|
enumerable: false,
|
|
182
183
|
writable: true,
|
|
183
184
|
},
|
|
184
185
|
});
|
|
185
|
-
this
|
|
186
|
+
this.#userData = value;
|
|
186
187
|
},
|
|
187
188
|
enumerable: true,
|
|
188
189
|
},
|
|
189
190
|
});
|
|
190
191
|
// reassign userData to ensure internal `__crawlee` object is non-enumerable
|
|
191
192
|
this.userData = userData;
|
|
193
|
+
const crawlee = (this.userData.__crawlee ??= {});
|
|
192
194
|
if (skipNavigation != null)
|
|
193
|
-
|
|
195
|
+
crawlee.skipNavigation = skipNavigation;
|
|
194
196
|
if (maxRetries != null)
|
|
195
|
-
|
|
197
|
+
crawlee.maxRetries = maxRetries;
|
|
196
198
|
if (crawlDepth != null)
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
199
|
+
crawlee.crawlDepth ??= crawlDepth;
|
|
200
|
+
if (sessionId)
|
|
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;
|
|
202
221
|
}
|
|
203
222
|
/**
|
|
204
223
|
* Converts the Crawlee Request object to a `fetch` API Request object.
|
|
@@ -211,47 +230,6 @@ class CrawleeRequest {
|
|
|
211
230
|
body: this.payload,
|
|
212
231
|
});
|
|
213
232
|
}
|
|
214
|
-
/** Tells the crawler processing this request to skip the navigation and process the request directly. */
|
|
215
|
-
get skipNavigation() {
|
|
216
|
-
return this.userData.__crawlee?.skipNavigation ?? false;
|
|
217
|
-
}
|
|
218
|
-
/** Tells the crawler processing this request to skip the navigation and process the request directly. */
|
|
219
|
-
set skipNavigation(value) {
|
|
220
|
-
if (!this.userData.__crawlee) {
|
|
221
|
-
this.userData.__crawlee = { skipNavigation: value };
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
this.userData.__crawlee.skipNavigation = value;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Depth of the request in the current crawl tree.
|
|
229
|
-
* Note that this is dependent on the crawler setup and might produce unexpected results when used with multiple crawlers.
|
|
230
|
-
*/
|
|
231
|
-
get crawlDepth() {
|
|
232
|
-
return this.userData.__crawlee?.crawlDepth ?? 0;
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Depth of the request in the current crawl tree.
|
|
236
|
-
* Note that this is dependent on the crawler setup and might produce unexpected results when used with multiple crawlers.
|
|
237
|
-
*/
|
|
238
|
-
set crawlDepth(value) {
|
|
239
|
-
this.userData.__crawlee ??= {};
|
|
240
|
-
this.userData.__crawlee.crawlDepth = value;
|
|
241
|
-
}
|
|
242
|
-
/** Indicates the number of times the crawling of the request has rotated the session due to a session or a proxy error. */
|
|
243
|
-
get sessionRotationCount() {
|
|
244
|
-
return this.userData.__crawlee?.sessionRotationCount ?? 0;
|
|
245
|
-
}
|
|
246
|
-
/** Indicates the number of times the crawling of the request has rotated the session due to a session or a proxy error. */
|
|
247
|
-
set sessionRotationCount(value) {
|
|
248
|
-
if (!this.userData.__crawlee) {
|
|
249
|
-
this.userData.__crawlee = { sessionRotationCount: value };
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
this.userData.__crawlee.sessionRotationCount = value;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
233
|
/** shortcut for getting `request.userData.label` */
|
|
256
234
|
get label() {
|
|
257
235
|
return this.userData.label;
|
|
@@ -260,107 +238,29 @@ class CrawleeRequest {
|
|
|
260
238
|
set label(value) {
|
|
261
239
|
this.userData.label = value;
|
|
262
240
|
}
|
|
263
|
-
/** Maximum number of retries for this request. Allows to override the global `maxRequestRetries` option of `BasicCrawler`. */
|
|
264
|
-
get maxRetries() {
|
|
265
|
-
return this.userData.__crawlee?.maxRetries;
|
|
266
|
-
}
|
|
267
|
-
/** Maximum number of retries for this request. Allows to override the global `maxRequestRetries` option of `BasicCrawler`. */
|
|
268
|
-
set maxRetries(value) {
|
|
269
|
-
if (!this.userData.__crawlee) {
|
|
270
|
-
this.userData.__crawlee = { maxRetries: value };
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
this.userData.__crawlee.maxRetries = value;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
/** Describes the request's current lifecycle state. */
|
|
277
|
-
get state() {
|
|
278
|
-
return this.userData.__crawlee?.state ?? RequestState.UNPROCESSED;
|
|
279
|
-
}
|
|
280
|
-
/** Describes the request's current lifecycle state. */
|
|
281
|
-
set state(value) {
|
|
282
|
-
if (!this.userData.__crawlee) {
|
|
283
|
-
this.userData.__crawlee = { state: value };
|
|
284
|
-
}
|
|
285
|
-
else {
|
|
286
|
-
this.userData.__crawlee.state = value;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
get enqueueStrategy() {
|
|
290
|
-
return this.userData.__crawlee?.enqueueStrategy;
|
|
291
|
-
}
|
|
292
|
-
set enqueueStrategy(value) {
|
|
293
|
-
if (!this.userData.__crawlee) {
|
|
294
|
-
this.userData.__crawlee = { enqueueStrategy: value };
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
this.userData.__crawlee.enqueueStrategy = value;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Stores information about an error that occurred during processing of this request.
|
|
302
|
-
*
|
|
303
|
-
* You should always use Error instances when throwing errors in JavaScript.
|
|
304
|
-
*
|
|
305
|
-
* Nevertheless, to improve the debugging experience when using third party libraries
|
|
306
|
-
* that may not always throw an Error instance, the function performs a type
|
|
307
|
-
* inspection of the passed argument and attempts to extract as much information
|
|
308
|
-
* as possible, since just throwing a bad type error makes any debugging rather difficult.
|
|
309
|
-
*
|
|
310
|
-
* @param errorOrMessage Error object or error message to be stored in the request.
|
|
311
|
-
* @param [options]
|
|
312
|
-
*/
|
|
313
|
-
pushErrorMessage(errorOrMessage, options = {}) {
|
|
314
|
-
const { omitStack } = options;
|
|
315
|
-
let message;
|
|
316
|
-
const type = typeof errorOrMessage;
|
|
317
|
-
if (type === 'object') {
|
|
318
|
-
if (!errorOrMessage) {
|
|
319
|
-
message = 'null';
|
|
320
|
-
}
|
|
321
|
-
else if (errorOrMessage instanceof Error) {
|
|
322
|
-
message = omitStack
|
|
323
|
-
? errorOrMessage.message
|
|
324
|
-
: // .stack includes the message
|
|
325
|
-
errorOrMessage.stack;
|
|
326
|
-
}
|
|
327
|
-
else if (Reflect.has(Object(errorOrMessage), 'message')) {
|
|
328
|
-
message = Reflect.get(Object(errorOrMessage), 'message');
|
|
329
|
-
}
|
|
330
|
-
else if (errorOrMessage.toString() !== '[object Object]') {
|
|
331
|
-
message = errorOrMessage.toString();
|
|
332
|
-
}
|
|
333
|
-
else {
|
|
334
|
-
try {
|
|
335
|
-
message = util.inspect(errorOrMessage);
|
|
336
|
-
}
|
|
337
|
-
catch (err) {
|
|
338
|
-
message = 'Unable to extract any message from the received object.';
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
else if (type === 'undefined') {
|
|
343
|
-
message = 'undefined';
|
|
344
|
-
}
|
|
345
|
-
else {
|
|
346
|
-
message = errorOrMessage.toString();
|
|
347
|
-
}
|
|
348
|
-
this.errorMessages.push(message);
|
|
349
|
-
}
|
|
350
241
|
/** @internal */
|
|
351
|
-
static computeUniqueKey({ url, method = 'GET', payload, keepUrlFragment = false, useExtendedUniqueKey = false, }) {
|
|
242
|
+
static computeUniqueKey({ url, method = 'GET', payload, keepUrlFragment = false, useExtendedUniqueKey = false, alwaysEnqueue = false, }) {
|
|
352
243
|
const normalizedMethod = method.toUpperCase();
|
|
353
244
|
const normalizedUrl = normalizeUrl(url, keepUrlFragment) || url; // It returns null when url is invalid, causing weird errors.
|
|
245
|
+
let uniqueKey;
|
|
354
246
|
if (!useExtendedUniqueKey) {
|
|
355
247
|
if (normalizedMethod !== 'GET' && payload) {
|
|
356
|
-
|
|
248
|
+
serviceLocator
|
|
249
|
+
.getLogger()
|
|
250
|
+
.warningOnce(`We've encountered a ${normalizedMethod} Request with a payload. ` +
|
|
357
251
|
'This is fine. Just letting you know that if your requests point to the same URL ' +
|
|
358
252
|
'and differ only in method and payload, you should see the "useExtendedUniqueKey" option of Request constructor.');
|
|
359
253
|
}
|
|
360
|
-
|
|
254
|
+
uniqueKey = normalizedUrl;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
const payloadHash = payload ? CrawleeRequest.hashPayload(payload) : '';
|
|
258
|
+
uniqueKey = `${normalizedMethod}|${payloadHash}|${normalizedUrl}`;
|
|
259
|
+
}
|
|
260
|
+
if (alwaysEnqueue) {
|
|
261
|
+
uniqueKey = `${cryptoRandomObjectId(17)}|${uniqueKey}`;
|
|
361
262
|
}
|
|
362
|
-
|
|
363
|
-
return `${normalizedMethod}(${payloadHash}):${normalizedUrl}`;
|
|
263
|
+
return uniqueKey;
|
|
364
264
|
}
|
|
365
265
|
/** @internal */
|
|
366
266
|
static hashPayload(payload) {
|
|
@@ -368,4 +268,3 @@ class CrawleeRequest {
|
|
|
368
268
|
}
|
|
369
269
|
}
|
|
370
270
|
export { CrawleeRequest as Request };
|
|
371
|
-
//# sourceMappingURL=request.js.map
|
package/serialization.d.ts
CHANGED
package/serialization.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { pipeline as streamPipeline, Readable, Writable } from 'node:stream';
|
|
2
2
|
import util from 'node:util';
|
|
3
3
|
import zlib from 'node:zlib';
|
|
4
|
-
import ow from 'ow';
|
|
5
4
|
import StreamArray from 'stream-json/streamers/StreamArray.js';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { parseArgument, schemas } from './validators.js';
|
|
6
7
|
const pipeline = util.promisify(streamPipeline);
|
|
8
|
+
const uint8ArraySchema = z.instanceof(Uint8Array);
|
|
7
9
|
/**
|
|
8
10
|
* Transforms an array of items to a JSON in a streaming
|
|
9
11
|
* fashion to save memory. It operates in batches to speed
|
|
@@ -11,32 +13,31 @@ const pipeline = util.promisify(streamPipeline);
|
|
|
11
13
|
* @internal
|
|
12
14
|
*/
|
|
13
15
|
class ArrayToJson extends Readable {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
#offset = 0;
|
|
17
|
+
#batchSize;
|
|
18
|
+
#data;
|
|
17
19
|
constructor(data, options = {}) {
|
|
18
20
|
super({
|
|
19
21
|
...options,
|
|
20
22
|
autoDestroy: true,
|
|
21
23
|
emitClose: true,
|
|
22
24
|
});
|
|
23
|
-
this.data = data;
|
|
24
25
|
const { batchSize = 10000 } = options;
|
|
25
|
-
this
|
|
26
|
-
this
|
|
26
|
+
this.#batchSize = batchSize;
|
|
27
|
+
this.#data = data;
|
|
27
28
|
this.push('[');
|
|
28
29
|
}
|
|
29
30
|
_read() {
|
|
30
31
|
try {
|
|
31
|
-
const items = this
|
|
32
|
+
const items = this.#data.slice(this.#offset, this.#offset + this.#batchSize);
|
|
32
33
|
if (items.length) {
|
|
33
34
|
const json = JSON.stringify(items);
|
|
34
35
|
// Strip brackets to flatten the batch.
|
|
35
36
|
const itemString = json.substring(1, json.length - 1);
|
|
36
|
-
if (this
|
|
37
|
+
if (this.#offset > 0)
|
|
37
38
|
this.push(',', 'utf8');
|
|
38
39
|
this.push(itemString, 'utf8');
|
|
39
|
-
this
|
|
40
|
+
this.#offset += this.#batchSize;
|
|
40
41
|
}
|
|
41
42
|
else {
|
|
42
43
|
this.push(']');
|
|
@@ -58,7 +59,7 @@ class ArrayToJson extends Readable {
|
|
|
58
59
|
* @internal
|
|
59
60
|
*/
|
|
60
61
|
export async function serializeArray(data) {
|
|
61
|
-
|
|
62
|
+
parseArgument(data, schemas.anyArray);
|
|
62
63
|
const { chunks, collector } = createChunkCollector();
|
|
63
64
|
await pipeline(new ArrayToJson(data), zlib.createGzip(), collector);
|
|
64
65
|
return Buffer.concat(chunks);
|
|
@@ -73,7 +74,7 @@ export async function serializeArray(data) {
|
|
|
73
74
|
* @internal
|
|
74
75
|
*/
|
|
75
76
|
export async function deserializeArray(compressedData) {
|
|
76
|
-
|
|
77
|
+
parseArgument(compressedData, uint8ArraySchema);
|
|
77
78
|
const { chunks, collector } = createChunkCollector({ fromValuesStream: true });
|
|
78
79
|
await pipeline(Readable.from([compressedData]), zlib.createGunzip(), StreamArray.withParser(), collector);
|
|
79
80
|
return chunks;
|
|
@@ -88,7 +89,7 @@ export async function deserializeArray(compressedData) {
|
|
|
88
89
|
* @internal
|
|
89
90
|
*/
|
|
90
91
|
export function createDeserialize(compressedData) {
|
|
91
|
-
|
|
92
|
+
parseArgument(compressedData, uint8ArraySchema);
|
|
92
93
|
const streamArray = StreamArray.withParser();
|
|
93
94
|
const destination = pluckValue(streamArray);
|
|
94
95
|
streamPipeline(Readable.from([compressedData]), zlib.createGunzip(), destination, (err) => destination.emit(err));
|
|
@@ -116,7 +117,6 @@ function createChunkCollector(options = {}) {
|
|
|
116
117
|
}
|
|
117
118
|
function pluckValue(streamArray) {
|
|
118
119
|
const realPush = streamArray.push.bind(streamArray);
|
|
119
|
-
streamArray.push = (obj) => realPush(obj
|
|
120
|
+
streamArray.push = (obj) => realPush(obj?.value ?? null);
|
|
120
121
|
return streamArray;
|
|
121
122
|
}
|
|
122
|
-
//# sourceMappingURL=serialization.js.map
|