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