@crawlee/core 4.0.0-beta.11 → 4.0.0-beta.111
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 +46 -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 +105 -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 +1 -4
- package/crawlers/error_snapshotter.js +3 -6
- 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 +91 -41
- package/crawlers/statistics.js +83 -72
- 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 +41 -4
- package/errors.js +50 -5
- package/events/event_manager.d.ts +34 -8
- package/events/event_manager.js +8 -10
- 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 +37 -11
- package/index.d.ts +6 -4
- package/index.js +5 -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 +42 -0
- package/memory-storage/memory-storage.js +136 -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 +27 -152
- package/proxy_configuration.js +27 -179
- package/recoverable_state.d.ts +111 -0
- package/recoverable_state.js +143 -0
- package/request.d.ts +86 -17
- package/request.js +117 -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 +147 -0
- package/service_locator.js +244 -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 +37 -86
- package/session_pool/session.js +85 -138
- package/session_pool/session_pool.d.ts +77 -91
- package/session_pool/session_pool.js +159 -147
- package/storages/dataset.d.ts +114 -54
- package/storages/dataset.js +285 -144
- package/storages/index.d.ts +9 -8
- package/storages/index.js +7 -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 +158 -133
- package/storages/request_loader.d.ts +96 -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 +825 -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} +136 -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/transaction.d.ts +252 -0
- package/storages/transaction.js +251 -0
- package/storages/utils.d.ts +54 -9
- 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/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,34 @@ 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 { Configuration } from '../configuration.js';
|
|
7
|
-
import { constructGlobObjectsFromGlobs, constructRegExpObjectsFromRegExps } from '../enqueue_links/shared.js';
|
|
5
|
+
import { constructUrlPatternObjects } from '../enqueue_links/shared.js';
|
|
8
6
|
import { Request } from '../request.js';
|
|
7
|
+
import { serviceLocator } from '../service_locator.js';
|
|
9
8
|
import { KeyValueStore } from './key_value_store.js';
|
|
10
9
|
import { purgeDefaultStorages } from './utils.js';
|
|
11
10
|
/** @internal */
|
|
12
|
-
const STATE_PERSISTENCE_KEY = '
|
|
11
|
+
const STATE_PERSISTENCE_KEY = 'SITEMAP_REQUEST_LOADER_STATE';
|
|
13
12
|
/**
|
|
14
13
|
* A list of URLs to crawl parsed from a sitemap.
|
|
15
14
|
*
|
|
16
15
|
* The loading of the sitemap is performed in the background so that crawling can start before the sitemap is fully loaded.
|
|
17
16
|
*/
|
|
18
|
-
export class
|
|
17
|
+
export class SitemapRequestLoader {
|
|
19
18
|
/**
|
|
20
19
|
* Set of URLs that were returned by `fetchNextRequest()` and not marked as handled yet.
|
|
21
20
|
* @internal
|
|
22
21
|
*/
|
|
23
22
|
inProgress = new Set();
|
|
24
|
-
/** Set of URLs for which `reclaimRequest()` was called. */
|
|
25
|
-
reclaimed = new Set();
|
|
26
23
|
/**
|
|
27
24
|
* Map of returned Request objects that have not been marked as handled yet.
|
|
28
25
|
*
|
|
29
|
-
* We use this to persist custom user fields on the in-progress
|
|
26
|
+
* We use this to persist custom user fields on the in-progress requests.
|
|
30
27
|
*/
|
|
31
|
-
requestData = new Map();
|
|
28
|
+
#requestData = new Map();
|
|
32
29
|
/**
|
|
33
30
|
* Object for keeping track of the sitemap parsing progress.
|
|
34
31
|
*/
|
|
35
|
-
sitemapParsingProgress = {
|
|
32
|
+
#sitemapParsingProgress = {
|
|
36
33
|
/**
|
|
37
34
|
* URL of the sitemap that is currently being parsed. `null` if no sitemap is being parsed.
|
|
38
35
|
*/
|
|
@@ -52,7 +49,7 @@ export class SitemapRequestList {
|
|
|
52
49
|
*
|
|
53
50
|
* Fetch the next URL to be processed using `fetchNextRequest()`.
|
|
54
51
|
*/
|
|
55
|
-
urlQueueStream;
|
|
52
|
+
#urlQueueStream;
|
|
56
53
|
/**
|
|
57
54
|
* Indicates whether the request list sitemap loading was aborted.
|
|
58
55
|
*
|
|
@@ -61,27 +58,28 @@ export class SitemapRequestList {
|
|
|
61
58
|
*
|
|
62
59
|
* If the loading is aborted and all the requests are handled, `isFinished()` will return `true`.
|
|
63
60
|
*/
|
|
64
|
-
abortLoading = false;
|
|
61
|
+
#abortLoading = false;
|
|
65
62
|
/** Number of URLs that were marked as handled */
|
|
66
|
-
handledUrlCount = 0;
|
|
67
|
-
persistStateKey;
|
|
68
|
-
store;
|
|
69
|
-
closed = false;
|
|
63
|
+
#handledUrlCount = 0;
|
|
64
|
+
#persistStateKey;
|
|
65
|
+
#store;
|
|
66
|
+
#closed = false;
|
|
70
67
|
/**
|
|
71
68
|
* Proxy URL to be used for sitemap loading.
|
|
72
69
|
*/
|
|
73
|
-
proxyUrl;
|
|
70
|
+
#proxyUrl;
|
|
74
71
|
/**
|
|
75
72
|
* Logger instance.
|
|
76
73
|
*/
|
|
77
|
-
log
|
|
78
|
-
urlExcludePatternObjects = [];
|
|
79
|
-
urlPatternObjects = [];
|
|
74
|
+
#log;
|
|
75
|
+
#urlExcludePatternObjects = [];
|
|
76
|
+
#urlPatternObjects = [];
|
|
80
77
|
/** EventManager used to handle persistence */
|
|
81
|
-
events;
|
|
82
|
-
persistenceOptions;
|
|
78
|
+
#events;
|
|
79
|
+
#persistenceOptions;
|
|
83
80
|
/** @internal */
|
|
84
81
|
constructor(options) {
|
|
82
|
+
const urlPatternValidator = ow.any(ow.string, ow.regExp, ow.object.hasKeys('glob'), ow.object.hasKeys('regexp'));
|
|
85
83
|
ow(options, ow.object.exactShape({
|
|
86
84
|
sitemapUrls: ow.array.ofType(ow.string),
|
|
87
85
|
proxyUrl: ow.optional.string,
|
|
@@ -90,35 +88,25 @@ export class SitemapRequestList {
|
|
|
90
88
|
timeoutMillis: ow.optional.number,
|
|
91
89
|
maxBufferSize: ow.optional.number,
|
|
92
90
|
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,
|
|
91
|
+
include: ow.optional.array.ofType(urlPatternValidator),
|
|
92
|
+
exclude: ow.optional.array.ofType(urlPatternValidator),
|
|
97
93
|
persistenceOptions: ow.optional.object,
|
|
94
|
+
httpClient: ow.optional.object,
|
|
98
95
|
}));
|
|
99
|
-
const {
|
|
96
|
+
const { include, exclude } = options;
|
|
97
|
+
this.#log = serviceLocator.getLogger().child({ prefix: 'SitemapRequestLoader' });
|
|
100
98
|
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
|
-
}
|
|
99
|
+
this.#urlExcludePatternObjects.push(...constructUrlPatternObjects(exclude));
|
|
109
100
|
}
|
|
110
|
-
if (
|
|
111
|
-
this
|
|
101
|
+
if (include?.length) {
|
|
102
|
+
this.#urlPatternObjects.push(...constructUrlPatternObjects(include));
|
|
112
103
|
}
|
|
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();
|
|
104
|
+
this.#persistStateKey = options.persistStateKey;
|
|
105
|
+
this.#persistenceOptions = { enable: true, ...options.persistenceOptions };
|
|
106
|
+
this.#proxyUrl = options.proxyUrl;
|
|
107
|
+
this.#urlQueueStream = this.createNewStream(options.maxBufferSize ?? 200);
|
|
108
|
+
this.#sitemapParsingProgress.pendingSitemapUrls = new Set(options.sitemapUrls);
|
|
109
|
+
this.#events = serviceLocator.getEventManager();
|
|
122
110
|
this.persistState = this.persistState.bind(this);
|
|
123
111
|
}
|
|
124
112
|
/**
|
|
@@ -146,13 +134,13 @@ export class SitemapRequestList {
|
|
|
146
134
|
};
|
|
147
135
|
}
|
|
148
136
|
/**
|
|
149
|
-
* Checks whether the URL matches the `
|
|
137
|
+
* Checks whether the URL matches the `include` / `exclude` patterns provided in the `options`.
|
|
150
138
|
* @param url URL to be checked.
|
|
151
139
|
* @returns `true` if the URL matches the patterns, `false` otherwise.
|
|
152
140
|
*/
|
|
153
141
|
isUrlMatchingPatterns(url) {
|
|
154
|
-
return (!this
|
|
155
|
-
(this
|
|
142
|
+
return (!this.#urlExcludePatternObjects.some(this.matchesUrl(url)) &&
|
|
143
|
+
(this.#urlPatternObjects.length === 0 || this.#urlPatternObjects.some(this.matchesUrl(url))));
|
|
156
144
|
}
|
|
157
145
|
/**
|
|
158
146
|
* Adds a URL to the queue of parsed URLs.
|
|
@@ -161,13 +149,13 @@ export class SitemapRequestList {
|
|
|
161
149
|
*/
|
|
162
150
|
async pushNextUrl(url) {
|
|
163
151
|
return new Promise((resolve) => {
|
|
164
|
-
if (this
|
|
152
|
+
if (this.#closed || (url && !this.isUrlMatchingPatterns(url))) {
|
|
165
153
|
resolve();
|
|
166
154
|
return;
|
|
167
155
|
}
|
|
168
|
-
if (!this
|
|
156
|
+
if (!this.#urlQueueStream.push(url)) {
|
|
169
157
|
// This doesn't work with the 'drain' event (it's not emitted for some reason).
|
|
170
|
-
this
|
|
158
|
+
this.#urlQueueStream.once('readdata', () => {
|
|
171
159
|
resolve();
|
|
172
160
|
});
|
|
173
161
|
}
|
|
@@ -184,21 +172,21 @@ export class SitemapRequestList {
|
|
|
184
172
|
*/
|
|
185
173
|
async readNextUrl() {
|
|
186
174
|
return new Promise((resolve) => {
|
|
187
|
-
if (this
|
|
175
|
+
if (this.#closed) {
|
|
188
176
|
resolve(null);
|
|
189
177
|
return;
|
|
190
178
|
}
|
|
191
|
-
const result = this
|
|
179
|
+
const result = this.#urlQueueStream.read();
|
|
192
180
|
if (!result && !this.isSitemapFullyLoaded()) {
|
|
193
|
-
this
|
|
194
|
-
const nextUrl = this
|
|
181
|
+
this.#urlQueueStream.once('readable', () => {
|
|
182
|
+
const nextUrl = this.#urlQueueStream.read();
|
|
195
183
|
resolve(nextUrl);
|
|
196
184
|
});
|
|
197
185
|
}
|
|
198
186
|
else {
|
|
199
187
|
resolve(result);
|
|
200
188
|
}
|
|
201
|
-
this
|
|
189
|
+
this.#urlQueueStream.emit('readdata');
|
|
202
190
|
});
|
|
203
191
|
}
|
|
204
192
|
/**
|
|
@@ -207,8 +195,8 @@ export class SitemapRequestList {
|
|
|
207
195
|
* If this is `false`, the background processing is either still in progress or was aborted.
|
|
208
196
|
*/
|
|
209
197
|
isSitemapFullyLoaded() {
|
|
210
|
-
return (this
|
|
211
|
-
this
|
|
198
|
+
return (this.#sitemapParsingProgress.inProgressSitemapUrl === null &&
|
|
199
|
+
this.#sitemapParsingProgress.pendingSitemapUrls.size === 0);
|
|
212
200
|
}
|
|
213
201
|
/**
|
|
214
202
|
* Start processing the sitemaps and loading the URLs.
|
|
@@ -216,58 +204,61 @@ export class SitemapRequestList {
|
|
|
216
204
|
* Resolves once all the sitemaps URLs have been fully loaded (sets `isSitemapFullyLoaded` to `true`).
|
|
217
205
|
*/
|
|
218
206
|
async load({ parseSitemapOptions, }) {
|
|
219
|
-
while (!this.isSitemapFullyLoaded() && !this
|
|
220
|
-
const sitemapUrl = this
|
|
221
|
-
this
|
|
207
|
+
while (!this.isSitemapFullyLoaded() && !this.#abortLoading) {
|
|
208
|
+
const sitemapUrl = this.#sitemapParsingProgress.inProgressSitemapUrl ??
|
|
209
|
+
this.#sitemapParsingProgress.pendingSitemapUrls.values().next().value;
|
|
222
210
|
try {
|
|
223
|
-
for await (const item of parseSitemap([{ type: 'url', url: sitemapUrl }], this
|
|
211
|
+
for await (const item of parseSitemap([{ type: 'url', url: sitemapUrl }], this.#proxyUrl, {
|
|
224
212
|
...parseSitemapOptions,
|
|
225
213
|
maxDepth: 0,
|
|
226
214
|
emitNestedSitemaps: true,
|
|
227
215
|
})) {
|
|
228
216
|
if (!item.originSitemapUrl) {
|
|
229
217
|
// This is a nested sitemap
|
|
230
|
-
this
|
|
218
|
+
this.#sitemapParsingProgress.pendingSitemapUrls.add(item.loc);
|
|
231
219
|
continue;
|
|
232
220
|
}
|
|
233
|
-
if (!this
|
|
221
|
+
if (!this.#sitemapParsingProgress.inProgressEntries.has(item.loc)) {
|
|
234
222
|
await this.pushNextUrl(item.loc);
|
|
235
|
-
this
|
|
223
|
+
this.#sitemapParsingProgress.inProgressEntries.add(item.loc);
|
|
236
224
|
}
|
|
237
225
|
}
|
|
238
226
|
}
|
|
239
227
|
catch (e) {
|
|
240
|
-
this
|
|
228
|
+
this.#log.error('Error loading sitemap contents:', e);
|
|
241
229
|
}
|
|
242
|
-
this
|
|
243
|
-
this
|
|
244
|
-
this
|
|
230
|
+
this.#sitemapParsingProgress.pendingSitemapUrls.delete(sitemapUrl);
|
|
231
|
+
this.#sitemapParsingProgress.inProgressEntries.clear();
|
|
232
|
+
this.#sitemapParsingProgress.inProgressSitemapUrl = null;
|
|
245
233
|
}
|
|
246
|
-
this
|
|
234
|
+
this.#urlQueueStream.end();
|
|
247
235
|
}
|
|
248
236
|
/**
|
|
249
237
|
* Open a sitemap and start processing it.
|
|
250
238
|
*
|
|
251
|
-
* Resolves to a new instance of `
|
|
239
|
+
* 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
240
|
*
|
|
253
241
|
* Track the loading progress using the `isSitemapFullyLoaded` property.
|
|
254
242
|
*/
|
|
255
243
|
static async open(options) {
|
|
256
|
-
const
|
|
257
|
-
|
|
244
|
+
const { httpClient, ...restOptions } = options;
|
|
245
|
+
const requestList = new SitemapRequestLoader({
|
|
246
|
+
...restOptions,
|
|
258
247
|
persistStateKey: options.persistStateKey ?? STATE_PERSISTENCE_KEY,
|
|
259
248
|
});
|
|
260
249
|
await requestList.restoreState();
|
|
261
|
-
void requestList.load({
|
|
262
|
-
|
|
263
|
-
|
|
250
|
+
void requestList.load({
|
|
251
|
+
parseSitemapOptions: { logger: serviceLocator.getLogger(), ...options.parseSitemapOptions, httpClient },
|
|
252
|
+
});
|
|
253
|
+
if (requestList.#persistenceOptions.enable) {
|
|
254
|
+
requestList.#events.on("persistState" /* EventType.PERSIST_STATE */, requestList.persistState);
|
|
264
255
|
}
|
|
265
256
|
options?.signal?.addEventListener('abort', () => {
|
|
266
|
-
requestList
|
|
257
|
+
requestList.#abortLoading = true;
|
|
267
258
|
});
|
|
268
259
|
if (options.timeoutMillis) {
|
|
269
260
|
setTimeout(() => {
|
|
270
|
-
requestList
|
|
261
|
+
requestList.#abortLoading = true;
|
|
271
262
|
}, options.timeoutMillis);
|
|
272
263
|
}
|
|
273
264
|
return requestList;
|
|
@@ -275,38 +266,58 @@ export class SitemapRequestList {
|
|
|
275
266
|
/**
|
|
276
267
|
* @inheritDoc
|
|
277
268
|
*/
|
|
278
|
-
|
|
279
|
-
|
|
269
|
+
async getTotalCount() {
|
|
270
|
+
// Total known so far = not-yet-fetched (still buffered in the stream) + in-progress (fetched but not
|
|
271
|
+
// yet handled) + already handled.
|
|
272
|
+
return this.#urlQueueStream.readableLength + this.inProgress.size + this.#handledUrlCount;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* @inheritDoc
|
|
276
|
+
*/
|
|
277
|
+
async getPendingCount() {
|
|
278
|
+
// Pending = everything not yet handled = not-yet-fetched + in-progress.
|
|
279
|
+
return this.#urlQueueStream.readableLength + this.inProgress.size;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Combines this list with a request manager (a {@link RequestQueue} by default) into a
|
|
283
|
+
* {@link RequestManagerTandem}, allowing requests to be added and reclaimed while still
|
|
284
|
+
* being read from this list first.
|
|
285
|
+
*/
|
|
286
|
+
async toTandem(requestManager) {
|
|
287
|
+
// Import here to avoid circular imports.
|
|
288
|
+
const { RequestManagerTandem } = await import('./request_manager_tandem.js');
|
|
289
|
+
const { RequestQueue } = await import('./request_queue.js');
|
|
290
|
+
return new RequestManagerTandem(this, requestManager ?? (await RequestQueue.open()));
|
|
280
291
|
}
|
|
281
292
|
/**
|
|
282
293
|
* @inheritDoc
|
|
283
294
|
*/
|
|
284
295
|
async isFinished() {
|
|
285
|
-
return ((await this.isEmpty()) && this.inProgress.size === 0 && (this.isSitemapFullyLoaded() || this
|
|
296
|
+
return ((await this.isEmpty()) && this.inProgress.size === 0 && (this.isSitemapFullyLoaded() || this.#abortLoading));
|
|
286
297
|
}
|
|
287
298
|
/**
|
|
288
299
|
* @inheritDoc
|
|
289
300
|
*/
|
|
290
301
|
async isEmpty() {
|
|
291
|
-
return this
|
|
302
|
+
return this.#urlQueueStream.readableLength === 0;
|
|
292
303
|
}
|
|
293
304
|
/**
|
|
294
305
|
* @inheritDoc
|
|
295
306
|
*/
|
|
296
|
-
|
|
297
|
-
return this
|
|
307
|
+
async getHandledCount() {
|
|
308
|
+
return this.#handledUrlCount;
|
|
298
309
|
}
|
|
299
310
|
/**
|
|
300
311
|
* @inheritDoc
|
|
301
312
|
*/
|
|
302
313
|
async persistState() {
|
|
303
|
-
if (this
|
|
314
|
+
if (this.#persistStateKey === undefined) {
|
|
304
315
|
return;
|
|
305
316
|
}
|
|
306
|
-
this
|
|
317
|
+
this.#store ??= await KeyValueStore.open();
|
|
307
318
|
const urlQueue = [];
|
|
308
|
-
while (this
|
|
309
|
-
const url = this
|
|
319
|
+
while (this.#urlQueueStream.readableLength > 0) {
|
|
320
|
+
const url = this.#urlQueueStream.read();
|
|
310
321
|
if (url === null) {
|
|
311
322
|
break;
|
|
312
323
|
}
|
|
@@ -314,69 +325,63 @@ export class SitemapRequestList {
|
|
|
314
325
|
}
|
|
315
326
|
// Create a new stream, as we have read all the URLs from the current one.
|
|
316
327
|
// Pushing the urls back to the original stream might not be possible if it has been ended.
|
|
317
|
-
const newStream = this.createNewStream(this
|
|
328
|
+
const newStream = this.createNewStream(this.#urlQueueStream.readableHighWaterMark);
|
|
318
329
|
for (const url of urlQueue) {
|
|
319
330
|
newStream.push(url);
|
|
320
331
|
}
|
|
321
|
-
if (this
|
|
332
|
+
if (this.#urlQueueStream.writableEnded) {
|
|
322
333
|
newStream.end();
|
|
323
334
|
}
|
|
324
|
-
this
|
|
325
|
-
await this
|
|
335
|
+
this.#urlQueueStream = newStream;
|
|
336
|
+
await this.#store.setValue(this.#persistStateKey, {
|
|
326
337
|
sitemapParsingProgress: {
|
|
327
|
-
pendingSitemapUrls: Array.from(this
|
|
328
|
-
inProgressSitemapUrl: this
|
|
329
|
-
inProgressEntries: Array.from(this
|
|
338
|
+
pendingSitemapUrls: Array.from(this.#sitemapParsingProgress.pendingSitemapUrls),
|
|
339
|
+
inProgressSitemapUrl: this.#sitemapParsingProgress.inProgressSitemapUrl,
|
|
340
|
+
inProgressEntries: Array.from(this.#sitemapParsingProgress.inProgressEntries),
|
|
330
341
|
},
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
requestData: Array.from(this
|
|
334
|
-
abortLoading: this
|
|
335
|
-
closed: this
|
|
342
|
+
// Re-queue in-progress requests to the front so they are retried if the state is restored.
|
|
343
|
+
urlQueue: [...this.inProgress, ...urlQueue],
|
|
344
|
+
requestData: Array.from(this.#requestData.entries()),
|
|
345
|
+
abortLoading: this.#abortLoading,
|
|
346
|
+
closed: this.#closed,
|
|
336
347
|
});
|
|
337
348
|
}
|
|
338
349
|
async restoreState() {
|
|
339
350
|
await purgeDefaultStorages({ onlyPurgeOnce: true });
|
|
340
|
-
if (this
|
|
351
|
+
if (this.#persistStateKey === undefined) {
|
|
341
352
|
return;
|
|
342
353
|
}
|
|
343
|
-
this
|
|
344
|
-
const state = await this
|
|
354
|
+
this.#store ??= await KeyValueStore.open();
|
|
355
|
+
const state = await this.#store.getValue(this.#persistStateKey);
|
|
345
356
|
if (state === null) {
|
|
346
357
|
return;
|
|
347
358
|
}
|
|
348
|
-
this
|
|
349
|
-
this.sitemapParsingProgress = {
|
|
359
|
+
this.#sitemapParsingProgress = {
|
|
350
360
|
pendingSitemapUrls: new Set(state.sitemapParsingProgress.pendingSitemapUrls),
|
|
351
361
|
inProgressSitemapUrl: state.sitemapParsingProgress.inProgressSitemapUrl,
|
|
352
362
|
inProgressEntries: new Set(state.sitemapParsingProgress.inProgressEntries),
|
|
353
363
|
};
|
|
354
|
-
this
|
|
364
|
+
this.#requestData = new Map(state.requestData ?? []);
|
|
355
365
|
for (const url of state.urlQueue) {
|
|
356
|
-
this
|
|
366
|
+
this.#urlQueueStream.push(url);
|
|
357
367
|
}
|
|
358
|
-
this
|
|
359
|
-
this
|
|
368
|
+
this.#abortLoading = state.abortLoading;
|
|
369
|
+
this.#closed = state.closed;
|
|
360
370
|
}
|
|
361
371
|
/**
|
|
362
372
|
* @inheritDoc
|
|
363
373
|
*/
|
|
364
374
|
async fetchNextRequest() {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
this.reclaimed.delete(nextUrl);
|
|
375
|
+
const nextUrl = await this.readNextUrl();
|
|
376
|
+
if (!nextUrl) {
|
|
377
|
+
return null;
|
|
369
378
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
nextUrl
|
|
373
|
-
if (!nextUrl) {
|
|
374
|
-
return null;
|
|
375
|
-
}
|
|
376
|
-
this.requestData.set(nextUrl, new Request({ url: nextUrl }));
|
|
379
|
+
// A restored in-progress request already has its Request data; don't overwrite it.
|
|
380
|
+
if (!this.#requestData.has(nextUrl)) {
|
|
381
|
+
this.#requestData.set(nextUrl, new Request({ url: nextUrl }));
|
|
377
382
|
}
|
|
378
383
|
this.inProgress.add(nextUrl);
|
|
379
|
-
return this
|
|
384
|
+
return this.#requestData.get(nextUrl);
|
|
380
385
|
}
|
|
381
386
|
/**
|
|
382
387
|
* @inheritDoc
|
|
@@ -389,42 +394,30 @@ export class SitemapRequestList {
|
|
|
389
394
|
yield request;
|
|
390
395
|
}
|
|
391
396
|
}
|
|
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
397
|
/**
|
|
401
398
|
* Aborts the internal sitemap loading, stops the processing of the sitemap contents and drops all the pending URLs.
|
|
402
399
|
*
|
|
403
400
|
* Calling `fetchNextRequest()` after this method will always return `null`.
|
|
404
401
|
*/
|
|
405
402
|
async teardown() {
|
|
406
|
-
this
|
|
407
|
-
this
|
|
408
|
-
this
|
|
403
|
+
this.#closed = true;
|
|
404
|
+
this.#abortLoading = true;
|
|
405
|
+
this.#events.off("persistState" /* EventType.PERSIST_STATE */, this.persistState);
|
|
409
406
|
await this.persistState();
|
|
410
|
-
this
|
|
407
|
+
this.#urlQueueStream.emit('readdata'); // unblocks the potentially waiting `pushNextUrl` call
|
|
411
408
|
}
|
|
412
409
|
/**
|
|
413
410
|
* @inheritDoc
|
|
414
411
|
*/
|
|
415
|
-
async
|
|
416
|
-
this
|
|
417
|
-
this.
|
|
412
|
+
async markRequestAsHandled(request) {
|
|
413
|
+
this.#handledUrlCount += 1;
|
|
414
|
+
this.ensureInProgress(request.url);
|
|
418
415
|
this.inProgress.delete(request.url);
|
|
419
|
-
this
|
|
416
|
+
this.#requestData.delete(request.url);
|
|
420
417
|
}
|
|
421
|
-
|
|
418
|
+
ensureInProgress(url) {
|
|
422
419
|
if (!this.inProgress.has(url)) {
|
|
423
420
|
throw new Error(`The request is not being processed (url: ${url})`);
|
|
424
421
|
}
|
|
425
|
-
if (this.reclaimed.has(url)) {
|
|
426
|
-
throw new Error(`The request was already reclaimed (url: ${url})`);
|
|
427
|
-
}
|
|
428
422
|
}
|
|
429
423
|
}
|
|
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>;
|