@crawlee/core 4.0.0-beta.11 → 4.0.0-beta.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/README.md +17 -13
  2. package/autoscaling/autoscaled_pool.d.ts +67 -172
  3. package/autoscaling/autoscaled_pool.js +165 -320
  4. package/autoscaling/client_load_signal.d.ts +55 -0
  5. package/autoscaling/client_load_signal.js +73 -0
  6. package/autoscaling/concurrency_system.d.ts +268 -0
  7. package/autoscaling/concurrency_system.js +351 -0
  8. package/autoscaling/cpu_load_signal.d.ts +43 -0
  9. package/autoscaling/cpu_load_signal.js +46 -0
  10. package/autoscaling/event_loop_load_signal.d.ts +51 -0
  11. package/autoscaling/event_loop_load_signal.js +60 -0
  12. package/autoscaling/index.d.ts +6 -1
  13. package/autoscaling/index.js +6 -1
  14. package/autoscaling/load_signal.d.ts +99 -0
  15. package/autoscaling/load_signal.js +104 -0
  16. package/autoscaling/memory_load_signal.d.ts +47 -0
  17. package/autoscaling/memory_load_signal.js +105 -0
  18. package/autoscaling/snapshotter.d.ts +58 -163
  19. package/autoscaling/snapshotter.js +45 -263
  20. package/autoscaling/system_status.d.ts +62 -84
  21. package/autoscaling/system_status.js +92 -122
  22. package/autoscaling/weighted_avg.d.ts +5 -0
  23. package/autoscaling/weighted_avg.js +14 -0
  24. package/byte_utils.d.ts +17 -0
  25. package/byte_utils.js +42 -0
  26. package/configuration.d.ts +96 -223
  27. package/configuration.js +170 -222
  28. package/cookie_utils.d.ts +4 -3
  29. package/cookie_utils.js +22 -13
  30. package/crawlers/context_pipeline.d.ts +10 -1
  31. package/crawlers/context_pipeline.js +31 -8
  32. package/crawlers/crawler_commons.d.ts +90 -83
  33. package/crawlers/crawler_commons.js +1 -116
  34. package/crawlers/error_snapshotter.d.ts +1 -4
  35. package/crawlers/error_snapshotter.js +3 -6
  36. package/crawlers/error_tracker.d.ts +0 -1
  37. package/crawlers/error_tracker.js +0 -1
  38. package/crawlers/index.d.ts +1 -3
  39. package/crawlers/index.js +0 -3
  40. package/crawlers/internals/types.d.ts +0 -1
  41. package/crawlers/internals/types.js +0 -1
  42. package/crawlers/statistics.d.ts +91 -41
  43. package/crawlers/statistics.js +83 -72
  44. package/debug.d.ts +36 -0
  45. package/debug.js +70 -0
  46. package/enqueue_links/enqueue_links.d.ts +59 -68
  47. package/enqueue_links/enqueue_links.js +57 -62
  48. package/enqueue_links/index.d.ts +0 -1
  49. package/enqueue_links/index.js +0 -1
  50. package/enqueue_links/shared.d.ts +39 -26
  51. package/enqueue_links/shared.js +89 -67
  52. package/errors.d.ts +41 -4
  53. package/errors.js +50 -5
  54. package/events/event_manager.d.ts +34 -8
  55. package/events/event_manager.js +8 -10
  56. package/events/index.d.ts +0 -1
  57. package/events/index.js +0 -1
  58. package/events/local_event_manager.d.ts +15 -3
  59. package/events/local_event_manager.js +37 -11
  60. package/index.d.ts +6 -4
  61. package/index.js +5 -3
  62. package/iterables.d.ts +79 -0
  63. package/iterables.js +134 -0
  64. package/log.d.ts +82 -3
  65. package/log.js +106 -1
  66. package/memory-storage/consts.d.ts +4 -0
  67. package/memory-storage/consts.js +4 -0
  68. package/memory-storage/index.d.ts +1 -0
  69. package/memory-storage/index.js +1 -0
  70. package/memory-storage/memory-storage.d.ts +42 -0
  71. package/memory-storage/memory-storage.js +136 -0
  72. package/memory-storage/resource-clients/common/base-client.d.ts +4 -0
  73. package/memory-storage/resource-clients/common/base-client.js +6 -0
  74. package/memory-storage/resource-clients/dataset.d.ts +40 -0
  75. package/memory-storage/resource-clients/dataset.js +114 -0
  76. package/memory-storage/resource-clients/key-value-store.d.ts +63 -0
  77. package/memory-storage/resource-clients/key-value-store.js +204 -0
  78. package/memory-storage/resource-clients/request-queue.d.ts +77 -0
  79. package/memory-storage/resource-clients/request-queue.js +422 -0
  80. package/memory-storage/utils.d.ts +16 -0
  81. package/memory-storage/utils.js +41 -0
  82. package/owned_or_injected.d.ts +58 -0
  83. package/owned_or_injected.js +98 -0
  84. package/package.json +13 -12
  85. package/proxy_configuration.d.ts +27 -152
  86. package/proxy_configuration.js +27 -179
  87. package/recoverable_state.d.ts +111 -0
  88. package/recoverable_state.js +143 -0
  89. package/request.d.ts +86 -17
  90. package/request.js +117 -41
  91. package/router.d.ts +193 -21
  92. package/router.js +188 -43
  93. package/serialization.d.ts +0 -1
  94. package/serialization.js +9 -11
  95. package/service_locator.d.ts +147 -0
  96. package/service_locator.js +244 -0
  97. package/session_pool/consts.d.ts +1 -2
  98. package/session_pool/consts.js +1 -2
  99. package/session_pool/errors.d.ts +0 -1
  100. package/session_pool/errors.js +0 -1
  101. package/session_pool/fingerprint.d.ts +9 -0
  102. package/session_pool/fingerprint.js +30 -0
  103. package/session_pool/index.d.ts +0 -2
  104. package/session_pool/index.js +0 -2
  105. package/session_pool/session.d.ts +37 -86
  106. package/session_pool/session.js +85 -138
  107. package/session_pool/session_pool.d.ts +77 -91
  108. package/session_pool/session_pool.js +159 -147
  109. package/storages/dataset.d.ts +114 -54
  110. package/storages/dataset.js +285 -144
  111. package/storages/index.d.ts +9 -8
  112. package/storages/index.js +7 -8
  113. package/storages/key_value_store.d.ts +185 -42
  114. package/storages/key_value_store.js +424 -151
  115. package/storages/key_value_store_codec.d.ts +32 -0
  116. package/storages/key_value_store_codec.js +113 -0
  117. package/storages/request_dedup_cache.d.ts +22 -0
  118. package/storages/request_dedup_cache.js +48 -0
  119. package/storages/request_list.d.ts +52 -116
  120. package/storages/request_list.js +158 -133
  121. package/storages/request_loader.d.ts +96 -0
  122. package/storages/request_loader.js +1 -0
  123. package/storages/request_manager.d.ts +33 -0
  124. package/storages/request_manager.js +1 -0
  125. package/storages/request_manager_tandem.d.ts +97 -0
  126. package/storages/request_manager_tandem.js +197 -0
  127. package/storages/request_queue.d.ts +290 -47
  128. package/storages/request_queue.js +824 -216
  129. package/storages/{sitemap_request_list.d.ts → sitemap_request_loader.d.ts} +37 -88
  130. package/storages/{sitemap_request_list.js → sitemap_request_loader.js} +136 -143
  131. package/storages/storage_instance_manager.d.ts +87 -0
  132. package/storages/storage_instance_manager.js +256 -0
  133. package/storages/storage_stats.d.ts +48 -0
  134. package/storages/storage_stats.js +29 -0
  135. package/storages/transaction.d.ts +252 -0
  136. package/storages/transaction.js +251 -0
  137. package/storages/utils.d.ts +54 -9
  138. package/storages/utils.js +64 -13
  139. package/system-info/cpu-info.d.ts +67 -0
  140. package/system-info/cpu-info.js +216 -0
  141. package/system-info/memory-info.d.ts +31 -0
  142. package/system-info/memory-info.js +115 -0
  143. package/system-info/ps-tree.d.ts +17 -0
  144. package/system-info/ps-tree.js +144 -0
  145. package/system-info/runtime.d.ts +14 -0
  146. package/system-info/runtime.js +80 -0
  147. package/typedefs.d.ts +0 -6
  148. package/typedefs.js +0 -1
  149. package/validators.d.ts +8 -1
  150. package/validators.js +10 -3
  151. package/autoscaling/autoscaled_pool.d.ts.map +0 -1
  152. package/autoscaling/autoscaled_pool.js.map +0 -1
  153. package/autoscaling/index.d.ts.map +0 -1
  154. package/autoscaling/index.js.map +0 -1
  155. package/autoscaling/snapshotter.d.ts.map +0 -1
  156. package/autoscaling/snapshotter.js.map +0 -1
  157. package/autoscaling/system_status.d.ts.map +0 -1
  158. package/autoscaling/system_status.js.map +0 -1
  159. package/configuration.d.ts.map +0 -1
  160. package/configuration.js.map +0 -1
  161. package/cookie_utils.d.ts.map +0 -1
  162. package/cookie_utils.js.map +0 -1
  163. package/crawlers/context_pipeline.d.ts.map +0 -1
  164. package/crawlers/context_pipeline.js.map +0 -1
  165. package/crawlers/crawler_commons.d.ts.map +0 -1
  166. package/crawlers/crawler_commons.js.map +0 -1
  167. package/crawlers/crawler_utils.d.ts +0 -10
  168. package/crawlers/crawler_utils.d.ts.map +0 -1
  169. package/crawlers/crawler_utils.js +0 -12
  170. package/crawlers/crawler_utils.js.map +0 -1
  171. package/crawlers/error_snapshotter.d.ts.map +0 -1
  172. package/crawlers/error_snapshotter.js.map +0 -1
  173. package/crawlers/error_tracker.d.ts.map +0 -1
  174. package/crawlers/error_tracker.js.map +0 -1
  175. package/crawlers/index.d.ts.map +0 -1
  176. package/crawlers/index.js.map +0 -1
  177. package/crawlers/internals/types.d.ts.map +0 -1
  178. package/crawlers/internals/types.js.map +0 -1
  179. package/crawlers/statistics.d.ts.map +0 -1
  180. package/crawlers/statistics.js.map +0 -1
  181. package/enqueue_links/enqueue_links.d.ts.map +0 -1
  182. package/enqueue_links/enqueue_links.js.map +0 -1
  183. package/enqueue_links/index.d.ts.map +0 -1
  184. package/enqueue_links/index.js.map +0 -1
  185. package/enqueue_links/shared.d.ts.map +0 -1
  186. package/enqueue_links/shared.js.map +0 -1
  187. package/errors.d.ts.map +0 -1
  188. package/errors.js.map +0 -1
  189. package/events/event_manager.d.ts.map +0 -1
  190. package/events/event_manager.js.map +0 -1
  191. package/events/index.d.ts.map +0 -1
  192. package/events/index.js.map +0 -1
  193. package/events/local_event_manager.d.ts.map +0 -1
  194. package/events/local_event_manager.js.map +0 -1
  195. package/http_clients/base-http-client.d.ts +0 -134
  196. package/http_clients/base-http-client.d.ts.map +0 -1
  197. package/http_clients/base-http-client.js +0 -33
  198. package/http_clients/base-http-client.js.map +0 -1
  199. package/http_clients/form-data-like.d.ts +0 -67
  200. package/http_clients/form-data-like.d.ts.map +0 -1
  201. package/http_clients/form-data-like.js +0 -5
  202. package/http_clients/form-data-like.js.map +0 -1
  203. package/http_clients/got-scraping-http-client.d.ts +0 -15
  204. package/http_clients/got-scraping-http-client.d.ts.map +0 -1
  205. package/http_clients/got-scraping-http-client.js +0 -69
  206. package/http_clients/got-scraping-http-client.js.map +0 -1
  207. package/http_clients/index.d.ts +0 -3
  208. package/http_clients/index.d.ts.map +0 -1
  209. package/http_clients/index.js +0 -3
  210. package/http_clients/index.js.map +0 -1
  211. package/index.d.ts.map +0 -1
  212. package/index.js.map +0 -1
  213. package/log.d.ts.map +0 -1
  214. package/log.js.map +0 -1
  215. package/proxy_configuration.d.ts.map +0 -1
  216. package/proxy_configuration.js.map +0 -1
  217. package/request.d.ts.map +0 -1
  218. package/request.js.map +0 -1
  219. package/router.d.ts.map +0 -1
  220. package/router.js.map +0 -1
  221. package/serialization.d.ts.map +0 -1
  222. package/serialization.js.map +0 -1
  223. package/session_pool/consts.d.ts.map +0 -1
  224. package/session_pool/consts.js.map +0 -1
  225. package/session_pool/errors.d.ts.map +0 -1
  226. package/session_pool/errors.js.map +0 -1
  227. package/session_pool/events.d.ts +0 -3
  228. package/session_pool/events.d.ts.map +0 -1
  229. package/session_pool/events.js +0 -3
  230. package/session_pool/events.js.map +0 -1
  231. package/session_pool/index.d.ts.map +0 -1
  232. package/session_pool/index.js.map +0 -1
  233. package/session_pool/session.d.ts.map +0 -1
  234. package/session_pool/session.js.map +0 -1
  235. package/session_pool/session_pool.d.ts.map +0 -1
  236. package/session_pool/session_pool.js.map +0 -1
  237. package/storages/access_checking.d.ts +0 -13
  238. package/storages/access_checking.d.ts.map +0 -1
  239. package/storages/access_checking.js +0 -14
  240. package/storages/access_checking.js.map +0 -1
  241. package/storages/dataset.d.ts.map +0 -1
  242. package/storages/dataset.js.map +0 -1
  243. package/storages/index.d.ts.map +0 -1
  244. package/storages/index.js.map +0 -1
  245. package/storages/key_value_store.d.ts.map +0 -1
  246. package/storages/key_value_store.js.map +0 -1
  247. package/storages/request_list.d.ts.map +0 -1
  248. package/storages/request_list.js.map +0 -1
  249. package/storages/request_provider.d.ts +0 -308
  250. package/storages/request_provider.d.ts.map +0 -1
  251. package/storages/request_provider.js +0 -555
  252. package/storages/request_provider.js.map +0 -1
  253. package/storages/request_queue.d.ts.map +0 -1
  254. package/storages/request_queue.js.map +0 -1
  255. package/storages/request_queue_v2.d.ts +0 -87
  256. package/storages/request_queue_v2.d.ts.map +0 -1
  257. package/storages/request_queue_v2.js +0 -438
  258. package/storages/request_queue_v2.js.map +0 -1
  259. package/storages/sitemap_request_list.d.ts.map +0 -1
  260. package/storages/sitemap_request_list.js.map +0 -1
  261. package/storages/storage_manager.d.ts +0 -58
  262. package/storages/storage_manager.d.ts.map +0 -1
  263. package/storages/storage_manager.js +0 -105
  264. package/storages/storage_manager.js.map +0 -1
  265. package/storages/utils.d.ts.map +0 -1
  266. package/storages/utils.js.map +0 -1
  267. package/tsconfig.build.tsbuildinfo +0 -1
  268. package/typedefs.d.ts.map +0 -1
  269. package/typedefs.js.map +0 -1
  270. package/validators.d.ts.map +0 -1
  271. 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 defaultLog from '@apify/log';
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 = 'SITEMAP_REQUEST_LIST_STATE';
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 SitemapRequestList {
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 (or reclaimed) requests.
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 = defaultLog.child({ prefix: 'SitemapRequestList' });
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
- globs: ow.optional.array.ofType(ow.any(ow.string, ow.object.hasKeys('glob'))),
94
- exclude: ow.optional.array.ofType(ow.any(ow.string, ow.regExp, ow.object.hasKeys('glob'), ow.object.hasKeys('regexp'))),
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 { globs, exclude, regexps, config = Configuration.getGlobalConfig() } = options;
96
+ const { include, exclude } = options;
97
+ this.#log = serviceLocator.getLogger().child({ prefix: 'SitemapRequestLoader' });
100
98
  if (exclude?.length) {
101
- for (const excl of exclude) {
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 (globs?.length) {
111
- this.urlPatternObjects.push(...constructGlobObjectsFromGlobs(globs));
101
+ if (include?.length) {
102
+ this.#urlPatternObjects.push(...constructUrlPatternObjects(include));
112
103
  }
113
- if (regexps?.length) {
114
- this.urlPatternObjects.push(...constructRegExpObjectsFromRegExps(regexps));
115
- }
116
- this.persistStateKey = options.persistStateKey;
117
- this.persistenceOptions = { enable: true, ...options.persistenceOptions };
118
- this.proxyUrl = options.proxyUrl;
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 `globs` / `regexps` / `exclude` provided in the `options`.
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.urlExcludePatternObjects.some(this.matchesUrl(url)) &&
155
- (this.urlPatternObjects.length === 0 || this.urlPatternObjects.some(this.matchesUrl(url))));
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.closed || (url && !this.isUrlMatchingPatterns(url))) {
152
+ if (this.#closed || (url && !this.isUrlMatchingPatterns(url))) {
165
153
  resolve();
166
154
  return;
167
155
  }
168
- if (!this.urlQueueStream.push(url)) {
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.urlQueueStream.once('readdata', () => {
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.closed) {
175
+ if (this.#closed) {
188
176
  resolve(null);
189
177
  return;
190
178
  }
191
- const result = this.urlQueueStream.read();
179
+ const result = this.#urlQueueStream.read();
192
180
  if (!result && !this.isSitemapFullyLoaded()) {
193
- this.urlQueueStream.once('readable', () => {
194
- const nextUrl = this.urlQueueStream.read();
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.urlQueueStream.emit('readdata');
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.sitemapParsingProgress.inProgressSitemapUrl === null &&
211
- this.sitemapParsingProgress.pendingSitemapUrls.size === 0);
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.abortLoading) {
220
- const sitemapUrl = this.sitemapParsingProgress.inProgressSitemapUrl ??
221
- this.sitemapParsingProgress.pendingSitemapUrls.values().next().value;
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.proxyUrl, {
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.sitemapParsingProgress.pendingSitemapUrls.add(item.loc);
218
+ this.#sitemapParsingProgress.pendingSitemapUrls.add(item.loc);
231
219
  continue;
232
220
  }
233
- if (!this.sitemapParsingProgress.inProgressEntries.has(item.loc)) {
221
+ if (!this.#sitemapParsingProgress.inProgressEntries.has(item.loc)) {
234
222
  await this.pushNextUrl(item.loc);
235
- this.sitemapParsingProgress.inProgressEntries.add(item.loc);
223
+ this.#sitemapParsingProgress.inProgressEntries.add(item.loc);
236
224
  }
237
225
  }
238
226
  }
239
227
  catch (e) {
240
- this.log.error('Error loading sitemap contents:', e);
228
+ this.#log.error('Error loading sitemap contents:', e);
241
229
  }
242
- this.sitemapParsingProgress.pendingSitemapUrls.delete(sitemapUrl);
243
- this.sitemapParsingProgress.inProgressEntries.clear();
244
- this.sitemapParsingProgress.inProgressSitemapUrl = null;
230
+ this.#sitemapParsingProgress.pendingSitemapUrls.delete(sitemapUrl);
231
+ this.#sitemapParsingProgress.inProgressEntries.clear();
232
+ this.#sitemapParsingProgress.inProgressSitemapUrl = null;
245
233
  }
246
- this.urlQueueStream.end();
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 `SitemapRequestList`, which **might not be fully loaded yet** - i.e. the sitemap might still be loading in the background.
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 requestList = new SitemapRequestList({
257
- ...options,
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({ parseSitemapOptions: options.parseSitemapOptions });
262
- if (requestList.persistenceOptions.enable) {
263
- requestList.events.on("persistState" /* EventType.PERSIST_STATE */, requestList.persistState);
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.abortLoading = true;
257
+ requestList.#abortLoading = true;
267
258
  });
268
259
  if (options.timeoutMillis) {
269
260
  setTimeout(() => {
270
- requestList.abortLoading = true;
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
- length() {
279
- return this.urlQueueStream.readableLength + this.handledUrlCount - this.inProgress.size - this.reclaimed.size;
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.abortLoading));
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.reclaimed.size === 0 && this.urlQueueStream.readableLength === 0;
302
+ return this.#urlQueueStream.readableLength === 0;
292
303
  }
293
304
  /**
294
305
  * @inheritDoc
295
306
  */
296
- handledCount() {
297
- return this.handledUrlCount;
307
+ async getHandledCount() {
308
+ return this.#handledUrlCount;
298
309
  }
299
310
  /**
300
311
  * @inheritDoc
301
312
  */
302
313
  async persistState() {
303
- if (this.persistStateKey === undefined) {
314
+ if (this.#persistStateKey === undefined) {
304
315
  return;
305
316
  }
306
- this.store ??= await KeyValueStore.open();
317
+ this.#store ??= await KeyValueStore.open();
307
318
  const urlQueue = [];
308
- while (this.urlQueueStream.readableLength > 0) {
309
- const url = this.urlQueueStream.read();
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.urlQueueStream.readableHighWaterMark);
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.urlQueueStream.writableEnded) {
332
+ if (this.#urlQueueStream.writableEnded) {
322
333
  newStream.end();
323
334
  }
324
- this.urlQueueStream = newStream;
325
- await this.store.setValue(this.persistStateKey, {
335
+ this.#urlQueueStream = newStream;
336
+ await this.#store.setValue(this.#persistStateKey, {
326
337
  sitemapParsingProgress: {
327
- pendingSitemapUrls: Array.from(this.sitemapParsingProgress.pendingSitemapUrls),
328
- inProgressSitemapUrl: this.sitemapParsingProgress.inProgressSitemapUrl,
329
- inProgressEntries: Array.from(this.sitemapParsingProgress.inProgressEntries),
338
+ pendingSitemapUrls: Array.from(this.#sitemapParsingProgress.pendingSitemapUrls),
339
+ inProgressSitemapUrl: this.#sitemapParsingProgress.inProgressSitemapUrl,
340
+ inProgressEntries: Array.from(this.#sitemapParsingProgress.inProgressEntries),
330
341
  },
331
- urlQueue,
332
- reclaimed: [...this.inProgress, ...this.reclaimed], // In-progress and reclaimed requests will be both retried if state is restored
333
- requestData: Array.from(this.requestData.entries()),
334
- abortLoading: this.abortLoading,
335
- closed: this.closed,
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.persistStateKey === undefined) {
351
+ if (this.#persistStateKey === undefined) {
341
352
  return;
342
353
  }
343
- this.store ??= await KeyValueStore.open();
344
- const state = await this.store.getValue(this.persistStateKey);
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.reclaimed = new Set(state.reclaimed);
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.requestData = new Map(state.requestData ?? []);
364
+ this.#requestData = new Map(state.requestData ?? []);
355
365
  for (const url of state.urlQueue) {
356
- this.urlQueueStream.push(url);
366
+ this.#urlQueueStream.push(url);
357
367
  }
358
- this.abortLoading = state.abortLoading;
359
- this.closed = state.closed;
368
+ this.#abortLoading = state.abortLoading;
369
+ this.#closed = state.closed;
360
370
  }
361
371
  /**
362
372
  * @inheritDoc
363
373
  */
364
374
  async fetchNextRequest() {
365
- // Try to return a reclaimed request first
366
- let nextUrl = this.reclaimed.values().next().value;
367
- if (nextUrl) {
368
- this.reclaimed.delete(nextUrl);
375
+ const nextUrl = await this.readNextUrl();
376
+ if (!nextUrl) {
377
+ return null;
369
378
  }
370
- else {
371
- // Otherwise read next url from the stream
372
- nextUrl = await this.readNextUrl();
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.requestData.get(nextUrl);
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.closed = true;
407
- this.abortLoading = true;
408
- this.events.off("persistState" /* EventType.PERSIST_STATE */, this.persistState);
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.urlQueueStream.emit('readdata'); // unblocks the potentially waiting `pushNextUrl` call
407
+ this.#urlQueueStream.emit('readdata'); // unblocks the potentially waiting `pushNextUrl` call
411
408
  }
412
409
  /**
413
410
  * @inheritDoc
414
411
  */
415
- async markRequestHandled(request) {
416
- this.handledUrlCount += 1;
417
- this.ensureInProgressAndNotReclaimed(request.url);
412
+ async markRequestAsHandled(request) {
413
+ this.#handledUrlCount += 1;
414
+ this.ensureInProgress(request.url);
418
415
  this.inProgress.delete(request.url);
419
- this.requestData.delete(request.url);
416
+ this.#requestData.delete(request.url);
420
417
  }
421
- ensureInProgressAndNotReclaimed(url) {
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>;