@crawlee/core 4.0.0-beta.12 → 4.0.0-beta.121
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 +17 -13
- package/autoscaling/autoscaled_pool.d.ts +67 -172
- package/autoscaling/autoscaled_pool.js +165 -320
- package/autoscaling/client_load_signal.d.ts +55 -0
- package/autoscaling/client_load_signal.js +73 -0
- package/autoscaling/concurrency_system.d.ts +268 -0
- package/autoscaling/concurrency_system.js +351 -0
- package/autoscaling/cpu_load_signal.d.ts +43 -0
- package/autoscaling/cpu_load_signal.js +47 -0
- package/autoscaling/event_loop_load_signal.d.ts +51 -0
- package/autoscaling/event_loop_load_signal.js +60 -0
- package/autoscaling/index.d.ts +6 -1
- package/autoscaling/index.js +6 -1
- package/autoscaling/load_signal.d.ts +99 -0
- package/autoscaling/load_signal.js +104 -0
- package/autoscaling/memory_load_signal.d.ts +47 -0
- package/autoscaling/memory_load_signal.js +106 -0
- package/autoscaling/snapshotter.d.ts +58 -163
- package/autoscaling/snapshotter.js +45 -263
- package/autoscaling/system_status.d.ts +62 -84
- package/autoscaling/system_status.js +92 -122
- package/autoscaling/weighted_avg.d.ts +5 -0
- package/autoscaling/weighted_avg.js +14 -0
- package/byte_utils.d.ts +17 -0
- package/byte_utils.js +42 -0
- package/configuration.d.ts +96 -223
- package/configuration.js +170 -222
- package/cookie_utils.d.ts +4 -3
- package/cookie_utils.js +22 -13
- package/crawlers/context_pipeline.d.ts +10 -1
- package/crawlers/context_pipeline.js +31 -8
- package/crawlers/crawler_commons.d.ts +90 -83
- package/crawlers/crawler_commons.js +1 -116
- package/crawlers/error_snapshotter.d.ts +2 -5
- package/crawlers/error_snapshotter.js +7 -8
- package/crawlers/error_tracker.d.ts +0 -1
- package/crawlers/error_tracker.js +0 -1
- package/crawlers/index.d.ts +1 -3
- package/crawlers/index.js +0 -3
- package/crawlers/internals/types.d.ts +0 -1
- package/crawlers/internals/types.js +0 -1
- package/crawlers/statistics.d.ts +143 -59
- package/crawlers/statistics.js +243 -153
- package/debug.d.ts +36 -0
- package/debug.js +70 -0
- package/enqueue_links/enqueue_links.d.ts +59 -68
- package/enqueue_links/enqueue_links.js +57 -62
- package/enqueue_links/index.d.ts +0 -1
- package/enqueue_links/index.js +0 -1
- package/enqueue_links/shared.d.ts +40 -27
- package/enqueue_links/shared.js +90 -68
- package/errors.d.ts +72 -4
- package/errors.js +89 -5
- package/events/event_manager.d.ts +35 -9
- package/events/event_manager.js +10 -12
- package/events/index.d.ts +0 -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/http.d.ts +9 -0
- package/http.js +28 -0
- package/index.d.ts +7 -4
- package/index.js +6 -3
- package/iterables.d.ts +79 -0
- package/iterables.js +134 -0
- package/log.d.ts +82 -3
- package/log.js +106 -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 +38 -0
- package/memory-storage/memory-storage.js +130 -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 +114 -0
- package/memory-storage/resource-clients/key-value-store.d.ts +63 -0
- package/memory-storage/resource-clients/key-value-store.js +204 -0
- package/memory-storage/resource-clients/request-queue.d.ts +77 -0
- package/memory-storage/resource-clients/request-queue.js +422 -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 +13 -12
- package/proxy_configuration.d.ts +24 -132
- package/proxy_configuration.js +24 -143
- package/recoverable_state.d.ts +140 -0
- package/recoverable_state.js +212 -0
- package/request.d.ts +86 -17
- package/request.js +120 -41
- package/router.d.ts +193 -21
- package/router.js +188 -43
- package/serialization.d.ts +0 -1
- package/serialization.js +9 -11
- package/service_locator.d.ts +165 -0
- package/service_locator.js +253 -0
- package/session_pool/consts.d.ts +1 -2
- package/session_pool/consts.js +1 -2
- package/session_pool/errors.d.ts +0 -1
- package/session_pool/errors.js +0 -1
- package/session_pool/fingerprint.d.ts +9 -0
- package/session_pool/fingerprint.js +30 -0
- package/session_pool/index.d.ts +0 -2
- package/session_pool/index.js +0 -2
- package/session_pool/session.d.ts +35 -89
- package/session_pool/session.js +82 -142
- package/session_pool/session_pool.d.ts +69 -90
- package/session_pool/session_pool.js +151 -150
- package/storages/batched_adds.d.ts +37 -0
- package/storages/batched_adds.js +73 -0
- package/storages/dataset.d.ts +114 -54
- package/storages/dataset.js +285 -144
- package/storages/index.d.ts +10 -8
- package/storages/index.js +8 -8
- package/storages/key_value_store.d.ts +185 -42
- package/storages/key_value_store.js +424 -151
- 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 +52 -116
- package/storages/request_list.js +159 -133
- package/storages/request_loader.d.ts +101 -0
- package/storages/request_loader.js +1 -0
- package/storages/request_manager.d.ts +33 -0
- package/storages/request_manager.js +1 -0
- package/storages/request_manager_tandem.d.ts +97 -0
- package/storages/request_manager_tandem.js +197 -0
- package/storages/request_queue.d.ts +290 -47
- package/storages/request_queue.js +757 -216
- package/storages/{sitemap_request_list.d.ts → sitemap_request_loader.d.ts} +37 -88
- package/storages/{sitemap_request_list.js → sitemap_request_loader.js} +137 -143
- 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/throttling_request_manager.d.ts +216 -0
- package/storages/throttling_request_manager.js +453 -0
- package/storages/transaction.d.ts +252 -0
- package/storages/transaction.js +251 -0
- package/storages/utils.d.ts +58 -11
- package/storages/utils.js +64 -13
- 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 +8 -1
- package/validators.js +10 -3
- package/autoscaling/autoscaled_pool.d.ts.map +0 -1
- package/autoscaling/autoscaled_pool.js.map +0 -1
- package/autoscaling/index.d.ts.map +0 -1
- package/autoscaling/index.js.map +0 -1
- package/autoscaling/snapshotter.d.ts.map +0 -1
- package/autoscaling/snapshotter.js.map +0 -1
- package/autoscaling/system_status.d.ts.map +0 -1
- 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.map +0 -1
- package/cookie_utils.js.map +0 -1
- package/crawlers/context_pipeline.d.ts.map +0 -1
- package/crawlers/context_pipeline.js.map +0 -1
- package/crawlers/crawler_commons.d.ts.map +0 -1
- 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.map +0 -1
- package/crawlers/error_snapshotter.js.map +0 -1
- package/crawlers/error_tracker.d.ts.map +0 -1
- package/crawlers/error_tracker.js.map +0 -1
- package/crawlers/index.d.ts.map +0 -1
- package/crawlers/index.js.map +0 -1
- package/crawlers/internals/types.d.ts.map +0 -1
- package/crawlers/internals/types.js.map +0 -1
- package/crawlers/statistics.d.ts.map +0 -1
- package/crawlers/statistics.js.map +0 -1
- package/enqueue_links/enqueue_links.d.ts.map +0 -1
- package/enqueue_links/enqueue_links.js.map +0 -1
- package/enqueue_links/index.d.ts.map +0 -1
- package/enqueue_links/index.js.map +0 -1
- package/enqueue_links/shared.d.ts.map +0 -1
- 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 -134
- package/http_clients/base-http-client.d.ts.map +0 -1
- package/http_clients/base-http-client.js +0 -33
- 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 -15
- package/http_clients/got-scraping-http-client.d.ts.map +0 -1
- package/http_clients/got-scraping-http-client.js +0 -69
- 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/request.d.ts.map +0 -1
- package/request.js.map +0 -1
- package/router.d.ts.map +0 -1
- 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.map +0 -1
- package/session_pool/consts.js.map +0 -1
- package/session_pool/errors.d.ts.map +0 -1
- 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.map +0 -1
- package/session_pool/index.js.map +0 -1
- package/session_pool/session.d.ts.map +0 -1
- package/session_pool/session.js.map +0 -1
- package/session_pool/session_pool.d.ts.map +0 -1
- 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_provider.d.ts +0 -308
- package/storages/request_provider.d.ts.map +0 -1
- package/storages/request_provider.js +0 -555
- 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.map +0 -1
- 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/tsconfig.build.tsbuildinfo +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
|
@@ -2,37 +2,35 @@ import { Transform } from 'node:stream';
|
|
|
2
2
|
import { parseSitemap } from '@crawlee/utils';
|
|
3
3
|
import { minimatch } from 'minimatch';
|
|
4
4
|
import ow from 'ow';
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import { constructGlobObjectsFromGlobs, constructRegExpObjectsFromRegExps } from '../enqueue_links/shared.js';
|
|
5
|
+
import { constructUrlPatternObjects } from '../enqueue_links/shared.js';
|
|
6
|
+
import { EventType } from '../events/event_manager.js';
|
|
8
7
|
import { Request } from '../request.js';
|
|
8
|
+
import { serviceLocator } from '../service_locator.js';
|
|
9
9
|
import { KeyValueStore } from './key_value_store.js';
|
|
10
10
|
import { purgeDefaultStorages } from './utils.js';
|
|
11
11
|
/** @internal */
|
|
12
|
-
const STATE_PERSISTENCE_KEY = '
|
|
12
|
+
const STATE_PERSISTENCE_KEY = 'SITEMAP_REQUEST_LOADER_STATE';
|
|
13
13
|
/**
|
|
14
14
|
* A list of URLs to crawl parsed from a sitemap.
|
|
15
15
|
*
|
|
16
16
|
* The loading of the sitemap is performed in the background so that crawling can start before the sitemap is fully loaded.
|
|
17
17
|
*/
|
|
18
|
-
export class
|
|
18
|
+
export class SitemapRequestLoader {
|
|
19
19
|
/**
|
|
20
20
|
* Set of URLs that were returned by `fetchNextRequest()` and not marked as handled yet.
|
|
21
21
|
* @internal
|
|
22
22
|
*/
|
|
23
23
|
inProgress = new Set();
|
|
24
|
-
/** Set of URLs for which `reclaimRequest()` was called. */
|
|
25
|
-
reclaimed = new Set();
|
|
26
24
|
/**
|
|
27
25
|
* Map of returned Request objects that have not been marked as handled yet.
|
|
28
26
|
*
|
|
29
|
-
* We use this to persist custom user fields on the in-progress
|
|
27
|
+
* We use this to persist custom user fields on the in-progress requests.
|
|
30
28
|
*/
|
|
31
|
-
requestData = new Map();
|
|
29
|
+
#requestData = new Map();
|
|
32
30
|
/**
|
|
33
31
|
* Object for keeping track of the sitemap parsing progress.
|
|
34
32
|
*/
|
|
35
|
-
sitemapParsingProgress = {
|
|
33
|
+
#sitemapParsingProgress = {
|
|
36
34
|
/**
|
|
37
35
|
* URL of the sitemap that is currently being parsed. `null` if no sitemap is being parsed.
|
|
38
36
|
*/
|
|
@@ -52,7 +50,7 @@ export class SitemapRequestList {
|
|
|
52
50
|
*
|
|
53
51
|
* Fetch the next URL to be processed using `fetchNextRequest()`.
|
|
54
52
|
*/
|
|
55
|
-
urlQueueStream;
|
|
53
|
+
#urlQueueStream;
|
|
56
54
|
/**
|
|
57
55
|
* Indicates whether the request list sitemap loading was aborted.
|
|
58
56
|
*
|
|
@@ -61,27 +59,28 @@ export class SitemapRequestList {
|
|
|
61
59
|
*
|
|
62
60
|
* If the loading is aborted and all the requests are handled, `isFinished()` will return `true`.
|
|
63
61
|
*/
|
|
64
|
-
abortLoading = false;
|
|
62
|
+
#abortLoading = false;
|
|
65
63
|
/** Number of URLs that were marked as handled */
|
|
66
|
-
handledUrlCount = 0;
|
|
67
|
-
persistStateKey;
|
|
68
|
-
store;
|
|
69
|
-
closed = false;
|
|
64
|
+
#handledUrlCount = 0;
|
|
65
|
+
#persistStateKey;
|
|
66
|
+
#store;
|
|
67
|
+
#closed = false;
|
|
70
68
|
/**
|
|
71
69
|
* Proxy URL to be used for sitemap loading.
|
|
72
70
|
*/
|
|
73
|
-
proxyUrl;
|
|
71
|
+
#proxyUrl;
|
|
74
72
|
/**
|
|
75
73
|
* Logger instance.
|
|
76
74
|
*/
|
|
77
|
-
log
|
|
78
|
-
urlExcludePatternObjects = [];
|
|
79
|
-
urlPatternObjects = [];
|
|
75
|
+
#log;
|
|
76
|
+
#urlExcludePatternObjects = [];
|
|
77
|
+
#urlPatternObjects = [];
|
|
80
78
|
/** EventManager used to handle persistence */
|
|
81
|
-
events;
|
|
82
|
-
persistenceOptions;
|
|
79
|
+
#events;
|
|
80
|
+
#persistenceOptions;
|
|
83
81
|
/** @internal */
|
|
84
82
|
constructor(options) {
|
|
83
|
+
const urlPatternValidator = ow.any(ow.string, ow.regExp, ow.object.hasKeys('glob'), ow.object.hasKeys('regexp'));
|
|
85
84
|
ow(options, ow.object.exactShape({
|
|
86
85
|
sitemapUrls: ow.array.ofType(ow.string),
|
|
87
86
|
proxyUrl: ow.optional.string,
|
|
@@ -90,35 +89,25 @@ export class SitemapRequestList {
|
|
|
90
89
|
timeoutMillis: ow.optional.number,
|
|
91
90
|
maxBufferSize: ow.optional.number,
|
|
92
91
|
parseSitemapOptions: ow.optional.object,
|
|
93
|
-
|
|
94
|
-
exclude: ow.optional.array.ofType(
|
|
95
|
-
regexps: ow.optional.array.ofType(ow.any(ow.regExp, ow.object.hasKeys('regexp'))),
|
|
96
|
-
config: ow.optional.object,
|
|
92
|
+
include: ow.optional.array.ofType(urlPatternValidator),
|
|
93
|
+
exclude: ow.optional.array.ofType(urlPatternValidator),
|
|
97
94
|
persistenceOptions: ow.optional.object,
|
|
95
|
+
httpClient: ow.optional.object,
|
|
98
96
|
}));
|
|
99
|
-
const {
|
|
97
|
+
const { include, exclude } = options;
|
|
98
|
+
this.#log = serviceLocator.getLogger().child({ prefix: 'SitemapRequestLoader' });
|
|
100
99
|
if (exclude?.length) {
|
|
101
|
-
|
|
102
|
-
if (typeof excl === 'string' || 'glob' in excl) {
|
|
103
|
-
this.urlExcludePatternObjects.push(...constructGlobObjectsFromGlobs([excl]));
|
|
104
|
-
}
|
|
105
|
-
else if (excl instanceof RegExp || 'regexp' in excl) {
|
|
106
|
-
this.urlExcludePatternObjects.push(...constructRegExpObjectsFromRegExps([excl]));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
100
|
+
this.#urlExcludePatternObjects.push(...constructUrlPatternObjects(exclude));
|
|
109
101
|
}
|
|
110
|
-
if (
|
|
111
|
-
this
|
|
102
|
+
if (include?.length) {
|
|
103
|
+
this.#urlPatternObjects.push(...constructUrlPatternObjects(include));
|
|
112
104
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
this
|
|
117
|
-
this.
|
|
118
|
-
this
|
|
119
|
-
this.urlQueueStream = this.createNewStream(options.maxBufferSize ?? 200);
|
|
120
|
-
this.sitemapParsingProgress.pendingSitemapUrls = new Set(options.sitemapUrls);
|
|
121
|
-
this.events = config.getEventManager();
|
|
105
|
+
this.#persistStateKey = options.persistStateKey;
|
|
106
|
+
this.#persistenceOptions = { enable: true, ...options.persistenceOptions };
|
|
107
|
+
this.#proxyUrl = options.proxyUrl;
|
|
108
|
+
this.#urlQueueStream = this.createNewStream(options.maxBufferSize ?? 200);
|
|
109
|
+
this.#sitemapParsingProgress.pendingSitemapUrls = new Set(options.sitemapUrls);
|
|
110
|
+
this.#events = serviceLocator.getEventManager();
|
|
122
111
|
this.persistState = this.persistState.bind(this);
|
|
123
112
|
}
|
|
124
113
|
/**
|
|
@@ -146,13 +135,13 @@ export class SitemapRequestList {
|
|
|
146
135
|
};
|
|
147
136
|
}
|
|
148
137
|
/**
|
|
149
|
-
* Checks whether the URL matches the `
|
|
138
|
+
* Checks whether the URL matches the `include` / `exclude` patterns provided in the `options`.
|
|
150
139
|
* @param url URL to be checked.
|
|
151
140
|
* @returns `true` if the URL matches the patterns, `false` otherwise.
|
|
152
141
|
*/
|
|
153
142
|
isUrlMatchingPatterns(url) {
|
|
154
|
-
return (!this
|
|
155
|
-
(this
|
|
143
|
+
return (!this.#urlExcludePatternObjects.some(this.matchesUrl(url)) &&
|
|
144
|
+
(this.#urlPatternObjects.length === 0 || this.#urlPatternObjects.some(this.matchesUrl(url))));
|
|
156
145
|
}
|
|
157
146
|
/**
|
|
158
147
|
* Adds a URL to the queue of parsed URLs.
|
|
@@ -161,13 +150,13 @@ export class SitemapRequestList {
|
|
|
161
150
|
*/
|
|
162
151
|
async pushNextUrl(url) {
|
|
163
152
|
return new Promise((resolve) => {
|
|
164
|
-
if (this
|
|
153
|
+
if (this.#closed || (url && !this.isUrlMatchingPatterns(url))) {
|
|
165
154
|
resolve();
|
|
166
155
|
return;
|
|
167
156
|
}
|
|
168
|
-
if (!this
|
|
157
|
+
if (!this.#urlQueueStream.push(url)) {
|
|
169
158
|
// This doesn't work with the 'drain' event (it's not emitted for some reason).
|
|
170
|
-
this
|
|
159
|
+
this.#urlQueueStream.once('readdata', () => {
|
|
171
160
|
resolve();
|
|
172
161
|
});
|
|
173
162
|
}
|
|
@@ -184,21 +173,21 @@ export class SitemapRequestList {
|
|
|
184
173
|
*/
|
|
185
174
|
async readNextUrl() {
|
|
186
175
|
return new Promise((resolve) => {
|
|
187
|
-
if (this
|
|
176
|
+
if (this.#closed) {
|
|
188
177
|
resolve(null);
|
|
189
178
|
return;
|
|
190
179
|
}
|
|
191
|
-
const result = this
|
|
180
|
+
const result = this.#urlQueueStream.read();
|
|
192
181
|
if (!result && !this.isSitemapFullyLoaded()) {
|
|
193
|
-
this
|
|
194
|
-
const nextUrl = this
|
|
182
|
+
this.#urlQueueStream.once('readable', () => {
|
|
183
|
+
const nextUrl = this.#urlQueueStream.read();
|
|
195
184
|
resolve(nextUrl);
|
|
196
185
|
});
|
|
197
186
|
}
|
|
198
187
|
else {
|
|
199
188
|
resolve(result);
|
|
200
189
|
}
|
|
201
|
-
this
|
|
190
|
+
this.#urlQueueStream.emit('readdata');
|
|
202
191
|
});
|
|
203
192
|
}
|
|
204
193
|
/**
|
|
@@ -207,8 +196,8 @@ export class SitemapRequestList {
|
|
|
207
196
|
* If this is `false`, the background processing is either still in progress or was aborted.
|
|
208
197
|
*/
|
|
209
198
|
isSitemapFullyLoaded() {
|
|
210
|
-
return (this
|
|
211
|
-
this
|
|
199
|
+
return (this.#sitemapParsingProgress.inProgressSitemapUrl === null &&
|
|
200
|
+
this.#sitemapParsingProgress.pendingSitemapUrls.size === 0);
|
|
212
201
|
}
|
|
213
202
|
/**
|
|
214
203
|
* Start processing the sitemaps and loading the URLs.
|
|
@@ -216,58 +205,61 @@ export class SitemapRequestList {
|
|
|
216
205
|
* Resolves once all the sitemaps URLs have been fully loaded (sets `isSitemapFullyLoaded` to `true`).
|
|
217
206
|
*/
|
|
218
207
|
async load({ parseSitemapOptions, }) {
|
|
219
|
-
while (!this.isSitemapFullyLoaded() && !this
|
|
220
|
-
const sitemapUrl = this
|
|
221
|
-
this
|
|
208
|
+
while (!this.isSitemapFullyLoaded() && !this.#abortLoading) {
|
|
209
|
+
const sitemapUrl = this.#sitemapParsingProgress.inProgressSitemapUrl ??
|
|
210
|
+
this.#sitemapParsingProgress.pendingSitemapUrls.values().next().value;
|
|
222
211
|
try {
|
|
223
|
-
for await (const item of parseSitemap([{ type: 'url', url: sitemapUrl }], this
|
|
212
|
+
for await (const item of parseSitemap([{ type: 'url', url: sitemapUrl }], this.#proxyUrl, {
|
|
224
213
|
...parseSitemapOptions,
|
|
225
214
|
maxDepth: 0,
|
|
226
215
|
emitNestedSitemaps: true,
|
|
227
216
|
})) {
|
|
228
217
|
if (!item.originSitemapUrl) {
|
|
229
218
|
// This is a nested sitemap
|
|
230
|
-
this
|
|
219
|
+
this.#sitemapParsingProgress.pendingSitemapUrls.add(item.loc);
|
|
231
220
|
continue;
|
|
232
221
|
}
|
|
233
|
-
if (!this
|
|
222
|
+
if (!this.#sitemapParsingProgress.inProgressEntries.has(item.loc)) {
|
|
234
223
|
await this.pushNextUrl(item.loc);
|
|
235
|
-
this
|
|
224
|
+
this.#sitemapParsingProgress.inProgressEntries.add(item.loc);
|
|
236
225
|
}
|
|
237
226
|
}
|
|
238
227
|
}
|
|
239
228
|
catch (e) {
|
|
240
|
-
this
|
|
229
|
+
this.#log.error('Error loading sitemap contents:', e);
|
|
241
230
|
}
|
|
242
|
-
this
|
|
243
|
-
this
|
|
244
|
-
this
|
|
231
|
+
this.#sitemapParsingProgress.pendingSitemapUrls.delete(sitemapUrl);
|
|
232
|
+
this.#sitemapParsingProgress.inProgressEntries.clear();
|
|
233
|
+
this.#sitemapParsingProgress.inProgressSitemapUrl = null;
|
|
245
234
|
}
|
|
246
|
-
this
|
|
235
|
+
this.#urlQueueStream.end();
|
|
247
236
|
}
|
|
248
237
|
/**
|
|
249
238
|
* Open a sitemap and start processing it.
|
|
250
239
|
*
|
|
251
|
-
* Resolves to a new instance of `
|
|
240
|
+
* Resolves to a new instance of `SitemapRequestLoader`, which **might not be fully loaded yet** - i.e. the sitemap might still be loading in the background.
|
|
252
241
|
*
|
|
253
242
|
* Track the loading progress using the `isSitemapFullyLoaded` property.
|
|
254
243
|
*/
|
|
255
244
|
static async open(options) {
|
|
256
|
-
const
|
|
257
|
-
|
|
245
|
+
const { httpClient, ...restOptions } = options;
|
|
246
|
+
const requestList = new SitemapRequestLoader({
|
|
247
|
+
...restOptions,
|
|
258
248
|
persistStateKey: options.persistStateKey ?? STATE_PERSISTENCE_KEY,
|
|
259
249
|
});
|
|
260
250
|
await requestList.restoreState();
|
|
261
|
-
void requestList.load({
|
|
262
|
-
|
|
263
|
-
|
|
251
|
+
void requestList.load({
|
|
252
|
+
parseSitemapOptions: { logger: serviceLocator.getLogger(), ...options.parseSitemapOptions, httpClient },
|
|
253
|
+
});
|
|
254
|
+
if (requestList.#persistenceOptions.enable) {
|
|
255
|
+
requestList.#events.on(EventType.PERSIST_STATE, requestList.persistState);
|
|
264
256
|
}
|
|
265
257
|
options?.signal?.addEventListener('abort', () => {
|
|
266
|
-
requestList
|
|
258
|
+
requestList.#abortLoading = true;
|
|
267
259
|
});
|
|
268
260
|
if (options.timeoutMillis) {
|
|
269
261
|
setTimeout(() => {
|
|
270
|
-
requestList
|
|
262
|
+
requestList.#abortLoading = true;
|
|
271
263
|
}, options.timeoutMillis);
|
|
272
264
|
}
|
|
273
265
|
return requestList;
|
|
@@ -275,38 +267,58 @@ export class SitemapRequestList {
|
|
|
275
267
|
/**
|
|
276
268
|
* @inheritDoc
|
|
277
269
|
*/
|
|
278
|
-
|
|
279
|
-
|
|
270
|
+
async getTotalCount() {
|
|
271
|
+
// Total known so far = not-yet-fetched (still buffered in the stream) + in-progress (fetched but not
|
|
272
|
+
// yet handled) + already handled.
|
|
273
|
+
return this.#urlQueueStream.readableLength + this.inProgress.size + this.#handledUrlCount;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* @inheritDoc
|
|
277
|
+
*/
|
|
278
|
+
async getPendingCount() {
|
|
279
|
+
// Pending = everything not yet handled = not-yet-fetched + in-progress.
|
|
280
|
+
return this.#urlQueueStream.readableLength + this.inProgress.size;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Combines this list with a request manager (a {@link RequestQueue} by default) into a
|
|
284
|
+
* {@link RequestManagerTandem}, allowing requests to be added and reclaimed while still
|
|
285
|
+
* being read from this list first.
|
|
286
|
+
*/
|
|
287
|
+
async toTandem(requestManager) {
|
|
288
|
+
// Import here to avoid circular imports.
|
|
289
|
+
const { RequestManagerTandem } = await import('./request_manager_tandem.js');
|
|
290
|
+
const { RequestQueue } = await import('./request_queue.js');
|
|
291
|
+
return new RequestManagerTandem(this, requestManager ?? (await RequestQueue.open()));
|
|
280
292
|
}
|
|
281
293
|
/**
|
|
282
294
|
* @inheritDoc
|
|
283
295
|
*/
|
|
284
296
|
async isFinished() {
|
|
285
|
-
return ((await this.isEmpty()) && this.inProgress.size === 0 && (this.isSitemapFullyLoaded() || this
|
|
297
|
+
return ((await this.isEmpty()) && this.inProgress.size === 0 && (this.isSitemapFullyLoaded() || this.#abortLoading));
|
|
286
298
|
}
|
|
287
299
|
/**
|
|
288
300
|
* @inheritDoc
|
|
289
301
|
*/
|
|
290
302
|
async isEmpty() {
|
|
291
|
-
return this
|
|
303
|
+
return this.#urlQueueStream.readableLength === 0;
|
|
292
304
|
}
|
|
293
305
|
/**
|
|
294
306
|
* @inheritDoc
|
|
295
307
|
*/
|
|
296
|
-
|
|
297
|
-
return this
|
|
308
|
+
async getHandledCount() {
|
|
309
|
+
return this.#handledUrlCount;
|
|
298
310
|
}
|
|
299
311
|
/**
|
|
300
312
|
* @inheritDoc
|
|
301
313
|
*/
|
|
302
314
|
async persistState() {
|
|
303
|
-
if (this
|
|
315
|
+
if (this.#persistStateKey === undefined) {
|
|
304
316
|
return;
|
|
305
317
|
}
|
|
306
|
-
this
|
|
318
|
+
this.#store ??= await KeyValueStore.open();
|
|
307
319
|
const urlQueue = [];
|
|
308
|
-
while (this
|
|
309
|
-
const url = this
|
|
320
|
+
while (this.#urlQueueStream.readableLength > 0) {
|
|
321
|
+
const url = this.#urlQueueStream.read();
|
|
310
322
|
if (url === null) {
|
|
311
323
|
break;
|
|
312
324
|
}
|
|
@@ -314,69 +326,63 @@ export class SitemapRequestList {
|
|
|
314
326
|
}
|
|
315
327
|
// Create a new stream, as we have read all the URLs from the current one.
|
|
316
328
|
// Pushing the urls back to the original stream might not be possible if it has been ended.
|
|
317
|
-
const newStream = this.createNewStream(this
|
|
329
|
+
const newStream = this.createNewStream(this.#urlQueueStream.readableHighWaterMark);
|
|
318
330
|
for (const url of urlQueue) {
|
|
319
331
|
newStream.push(url);
|
|
320
332
|
}
|
|
321
|
-
if (this
|
|
333
|
+
if (this.#urlQueueStream.writableEnded) {
|
|
322
334
|
newStream.end();
|
|
323
335
|
}
|
|
324
|
-
this
|
|
325
|
-
await this
|
|
336
|
+
this.#urlQueueStream = newStream;
|
|
337
|
+
await this.#store.setValue(this.#persistStateKey, {
|
|
326
338
|
sitemapParsingProgress: {
|
|
327
|
-
pendingSitemapUrls: Array.from(this
|
|
328
|
-
inProgressSitemapUrl: this
|
|
329
|
-
inProgressEntries: Array.from(this
|
|
339
|
+
pendingSitemapUrls: Array.from(this.#sitemapParsingProgress.pendingSitemapUrls),
|
|
340
|
+
inProgressSitemapUrl: this.#sitemapParsingProgress.inProgressSitemapUrl,
|
|
341
|
+
inProgressEntries: Array.from(this.#sitemapParsingProgress.inProgressEntries),
|
|
330
342
|
},
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
requestData: Array.from(this
|
|
334
|
-
abortLoading: this
|
|
335
|
-
closed: this
|
|
343
|
+
// Re-queue in-progress requests to the front so they are retried if the state is restored.
|
|
344
|
+
urlQueue: [...this.inProgress, ...urlQueue],
|
|
345
|
+
requestData: Array.from(this.#requestData.entries()),
|
|
346
|
+
abortLoading: this.#abortLoading,
|
|
347
|
+
closed: this.#closed,
|
|
336
348
|
});
|
|
337
349
|
}
|
|
338
350
|
async restoreState() {
|
|
339
351
|
await purgeDefaultStorages({ onlyPurgeOnce: true });
|
|
340
|
-
if (this
|
|
352
|
+
if (this.#persistStateKey === undefined) {
|
|
341
353
|
return;
|
|
342
354
|
}
|
|
343
|
-
this
|
|
344
|
-
const state = await this
|
|
355
|
+
this.#store ??= await KeyValueStore.open();
|
|
356
|
+
const state = await this.#store.getValue(this.#persistStateKey);
|
|
345
357
|
if (state === null) {
|
|
346
358
|
return;
|
|
347
359
|
}
|
|
348
|
-
this
|
|
349
|
-
this.sitemapParsingProgress = {
|
|
360
|
+
this.#sitemapParsingProgress = {
|
|
350
361
|
pendingSitemapUrls: new Set(state.sitemapParsingProgress.pendingSitemapUrls),
|
|
351
362
|
inProgressSitemapUrl: state.sitemapParsingProgress.inProgressSitemapUrl,
|
|
352
363
|
inProgressEntries: new Set(state.sitemapParsingProgress.inProgressEntries),
|
|
353
364
|
};
|
|
354
|
-
this
|
|
365
|
+
this.#requestData = new Map(state.requestData ?? []);
|
|
355
366
|
for (const url of state.urlQueue) {
|
|
356
|
-
this
|
|
367
|
+
this.#urlQueueStream.push(url);
|
|
357
368
|
}
|
|
358
|
-
this
|
|
359
|
-
this
|
|
369
|
+
this.#abortLoading = state.abortLoading;
|
|
370
|
+
this.#closed = state.closed;
|
|
360
371
|
}
|
|
361
372
|
/**
|
|
362
373
|
* @inheritDoc
|
|
363
374
|
*/
|
|
364
375
|
async fetchNextRequest() {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
this.reclaimed.delete(nextUrl);
|
|
376
|
+
const nextUrl = await this.readNextUrl();
|
|
377
|
+
if (!nextUrl) {
|
|
378
|
+
return null;
|
|
369
379
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
nextUrl
|
|
373
|
-
if (!nextUrl) {
|
|
374
|
-
return null;
|
|
375
|
-
}
|
|
376
|
-
this.requestData.set(nextUrl, new Request({ url: nextUrl }));
|
|
380
|
+
// A restored in-progress request already has its Request data; don't overwrite it.
|
|
381
|
+
if (!this.#requestData.has(nextUrl)) {
|
|
382
|
+
this.#requestData.set(nextUrl, new Request({ url: nextUrl }));
|
|
377
383
|
}
|
|
378
384
|
this.inProgress.add(nextUrl);
|
|
379
|
-
return this
|
|
385
|
+
return this.#requestData.get(nextUrl);
|
|
380
386
|
}
|
|
381
387
|
/**
|
|
382
388
|
* @inheritDoc
|
|
@@ -389,42 +395,30 @@ export class SitemapRequestList {
|
|
|
389
395
|
yield request;
|
|
390
396
|
}
|
|
391
397
|
}
|
|
392
|
-
/**
|
|
393
|
-
* @inheritDoc
|
|
394
|
-
*/
|
|
395
|
-
async reclaimRequest(request) {
|
|
396
|
-
this.ensureInProgressAndNotReclaimed(request.url);
|
|
397
|
-
this.reclaimed.add(request.url);
|
|
398
|
-
this.inProgress.delete(request.url);
|
|
399
|
-
}
|
|
400
398
|
/**
|
|
401
399
|
* Aborts the internal sitemap loading, stops the processing of the sitemap contents and drops all the pending URLs.
|
|
402
400
|
*
|
|
403
401
|
* Calling `fetchNextRequest()` after this method will always return `null`.
|
|
404
402
|
*/
|
|
405
403
|
async teardown() {
|
|
406
|
-
this
|
|
407
|
-
this
|
|
408
|
-
this
|
|
404
|
+
this.#closed = true;
|
|
405
|
+
this.#abortLoading = true;
|
|
406
|
+
this.#events.off(EventType.PERSIST_STATE, this.persistState);
|
|
409
407
|
await this.persistState();
|
|
410
|
-
this
|
|
408
|
+
this.#urlQueueStream.emit('readdata'); // unblocks the potentially waiting `pushNextUrl` call
|
|
411
409
|
}
|
|
412
410
|
/**
|
|
413
411
|
* @inheritDoc
|
|
414
412
|
*/
|
|
415
|
-
async
|
|
416
|
-
this
|
|
417
|
-
this.
|
|
413
|
+
async markRequestAsHandled(request) {
|
|
414
|
+
this.#handledUrlCount += 1;
|
|
415
|
+
this.ensureInProgress(request.url);
|
|
418
416
|
this.inProgress.delete(request.url);
|
|
419
|
-
this
|
|
417
|
+
this.#requestData.delete(request.url);
|
|
420
418
|
}
|
|
421
|
-
|
|
419
|
+
ensureInProgress(url) {
|
|
422
420
|
if (!this.inProgress.has(url)) {
|
|
423
421
|
throw new Error(`The request is not being processed (url: ${url})`);
|
|
424
422
|
}
|
|
425
|
-
if (this.reclaimed.has(url)) {
|
|
426
|
-
throw new Error(`The request was already reclaimed (url: ${url})`);
|
|
427
|
-
}
|
|
428
423
|
}
|
|
429
424
|
}
|
|
430
|
-
//# sourceMappingURL=sitemap_request_list.js.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { Constructor, DatasetBackend, KeyValueStoreBackend, RequestQueueBackend, StorageBackend, StorageIdentifier } from '@crawlee/types';
|
|
2
|
+
export type { StorageIdentifier } from '@crawlee/types';
|
|
3
|
+
/**
|
|
4
|
+
* Matches an `IStorage` – a storage "frontend" (Dataset, KeyValueStore, RequestQueue).
|
|
5
|
+
*/
|
|
6
|
+
export interface IStorage {
|
|
7
|
+
id: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
}
|
|
10
|
+
type Hashable = string;
|
|
11
|
+
/**
|
|
12
|
+
* Unified manager for opening and caching storage instances (Dataset, KeyValueStore, RequestQueue).
|
|
13
|
+
*
|
|
14
|
+
* A single instance manages all storage types. Instances are cached by
|
|
15
|
+
* `(storageClass, id/name/alias, backendCacheKey)` so the same storage is never opened twice.
|
|
16
|
+
*
|
|
17
|
+
* The manager itself does not resolve identifiers — callers pass explicit `id`, `name`, or `alias` (at most one),
|
|
18
|
+
* and a pre-bound `backendOpener` promise. When none of `id`, `name`, `alias` are provided, the manager automatically
|
|
19
|
+
* assigns a reserved default alias.
|
|
20
|
+
*/
|
|
21
|
+
export declare class StorageInstanceManager {
|
|
22
|
+
#private;
|
|
23
|
+
/**
|
|
24
|
+
* Open (or retrieve from cache) a storage instance.
|
|
25
|
+
*
|
|
26
|
+
* @param cls The storage class constructor (e.g. `Dataset`, `KeyValueStore`, `RequestQueue`).
|
|
27
|
+
* @param id Storage ID (mutually exclusive with `name` and `alias`).
|
|
28
|
+
* @param name Storage name (mutually exclusive with `id` and `alias`).
|
|
29
|
+
* @param alias Run-scoped alias (mutually exclusive with `id` and `name`).
|
|
30
|
+
* Automatically assigned when no identifier is provided.
|
|
31
|
+
* @param backendOpener A **lazy** factory that creates the sub-backend.
|
|
32
|
+
* Only called on a cache miss.
|
|
33
|
+
* @param backendCacheKey Opaque key identifying the storage backend, so that the same logical
|
|
34
|
+
* storage opened through different clients is cached separately.
|
|
35
|
+
*/
|
|
36
|
+
openStorage<TStorage extends IStorage>(cls: Constructor<TStorage>, { id, name, alias, backendOpener, backendCacheKey, }: (ExplicitStorageIdentifier | DefaultStorageIdentifier) & {
|
|
37
|
+
backendOpener: () => Promise<DatasetBackend | KeyValueStoreBackend | RequestQueueBackend>;
|
|
38
|
+
backendCacheKey: Hashable;
|
|
39
|
+
}): Promise<TStorage>;
|
|
40
|
+
/**
|
|
41
|
+
* Remove a storage instance from the cache (called from `storage.drop()`).
|
|
42
|
+
*/
|
|
43
|
+
removeFromCache(instance: IStorage): void;
|
|
44
|
+
/**
|
|
45
|
+
* Clear the entire cache. Also calls `clearCache()` on any cached KeyValueStore
|
|
46
|
+
* instances (duck-typed to avoid importing KeyValueStore and circular dependencies).
|
|
47
|
+
* Called during service locator reset.
|
|
48
|
+
*/
|
|
49
|
+
clearCache(): void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A storage identifier where exactly one of `id`, `name`, or `alias` is specified.
|
|
53
|
+
* Produced by {@link resolveStorageIdentifier} from ambiguous user input.
|
|
54
|
+
*/
|
|
55
|
+
export type ExplicitStorageIdentifier = {
|
|
56
|
+
id: string;
|
|
57
|
+
name?: never;
|
|
58
|
+
alias?: never;
|
|
59
|
+
} | {
|
|
60
|
+
id?: never;
|
|
61
|
+
name: string;
|
|
62
|
+
alias?: never;
|
|
63
|
+
} | {
|
|
64
|
+
id?: never;
|
|
65
|
+
name?: never;
|
|
66
|
+
alias: string;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Represents the case where no identifier was provided — the caller wants the default storage.
|
|
70
|
+
*/
|
|
71
|
+
export interface DefaultStorageIdentifier {
|
|
72
|
+
id?: never;
|
|
73
|
+
name?: never;
|
|
74
|
+
alias?: never;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Decompose a user-provided `identifier` (the `Dataset.open()` / `KeyValueStore.open()` /
|
|
78
|
+
* `RequestQueue.open()` argument) into separate `id`, `name`, and `alias` fields that
|
|
79
|
+
* the `StorageInstanceManager` and `StorageBackend.create*Client` expect.
|
|
80
|
+
*
|
|
81
|
+
* - `null` / `undefined` / `{}` → default storage alias
|
|
82
|
+
* - `string` → resolved via `storageExists` (ID-first, then name)
|
|
83
|
+
* - `{ id }` → `{ id }`
|
|
84
|
+
* - `{ name }` → `{ name }`
|
|
85
|
+
* - `{ alias }` → `{ alias }`
|
|
86
|
+
*/
|
|
87
|
+
export declare function resolveStorageIdentifier(identifier: string | StorageIdentifier | null | undefined, storageBackend: StorageBackend, storageType: 'Dataset' | 'KeyValueStore' | 'RequestQueue'): Promise<ExplicitStorageIdentifier>;
|