@crawlee/core 4.0.0-beta.13 → 4.0.0-beta.131

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 (280) hide show
  1. package/README.md +17 -13
  2. package/autoscaling/autoscaled_pool.d.ts +67 -172
  3. package/autoscaling/autoscaled_pool.js +182 -329
  4. package/autoscaling/concurrency_system.d.ts +268 -0
  5. package/autoscaling/concurrency_system.js +362 -0
  6. package/autoscaling/cpu_load_signal.d.ts +43 -0
  7. package/autoscaling/cpu_load_signal.js +47 -0
  8. package/autoscaling/event_loop_load_signal.d.ts +51 -0
  9. package/autoscaling/event_loop_load_signal.js +60 -0
  10. package/autoscaling/index.d.ts +6 -1
  11. package/autoscaling/index.js +6 -1
  12. package/autoscaling/load_signal.d.ts +100 -0
  13. package/autoscaling/load_signal.js +105 -0
  14. package/autoscaling/memory_load_signal.d.ts +47 -0
  15. package/autoscaling/memory_load_signal.js +106 -0
  16. package/autoscaling/snapshotter.d.ts +58 -163
  17. package/autoscaling/snapshotter.js +45 -263
  18. package/autoscaling/storage_backend_load_signal.d.ts +56 -0
  19. package/autoscaling/storage_backend_load_signal.js +73 -0
  20. package/autoscaling/system_status.d.ts +67 -89
  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 +3 -2
  29. package/cookie_utils.js +18 -7
  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 -129
  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 +187 -63
  43. package/crawlers/statistics.js +354 -164
  44. package/debug.d.ts +36 -0
  45. package/debug.js +70 -0
  46. package/enqueue_links/enqueue_links.d.ts +68 -154
  47. package/enqueue_links/enqueue_links.js +38 -210
  48. package/enqueue_links/index.d.ts +0 -1
  49. package/enqueue_links/index.js +0 -1
  50. package/enqueue_links/shared.d.ts +52 -26
  51. package/enqueue_links/shared.js +115 -66
  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 +12 -13
  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 +108 -0
  78. package/memory-storage/resource-clients/key-value-store.d.ts +63 -0
  79. package/memory-storage/resource-clients/key-value-store.js +201 -0
  80. package/memory-storage/resource-clients/request-queue.d.ts +77 -0
  81. package/memory-storage/resource-clients/request-queue.js +409 -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 +14 -13
  87. package/proxy_configuration.d.ts +24 -132
  88. package/proxy_configuration.js +32 -147
  89. package/recoverable_state.d.ts +155 -0
  90. package/recoverable_state.js +231 -0
  91. package/request.d.ts +80 -16
  92. package/request.js +147 -68
  93. package/router.d.ts +193 -21
  94. package/router.js +188 -43
  95. package/serialization.d.ts +0 -1
  96. package/serialization.js +15 -15
  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 -88
  108. package/session_pool/session.js +101 -159
  109. package/session_pool/session_pool.d.ts +73 -91
  110. package/session_pool/session_pool.js +172 -165
  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 +291 -148
  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 +445 -169
  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 -115
  124. package/storages/request_list.js +183 -152
  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 +762 -216
  133. package/storages/{sitemap_request_list.d.ts → sitemap_request_loader.d.ts} +45 -89
  134. package/storages/sitemap_request_loader.js +438 -0
  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 +239 -0
  140. package/storages/throttling_request_manager.js +646 -0
  141. package/storages/transaction.d.ts +252 -0
  142. package/storages/transaction.js +251 -0
  143. package/storages/utils.d.ts +59 -11
  144. package/storages/utils.js +75 -15
  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 +22 -18
  158. package/validators.js +13 -18
  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 -140
  204. package/http_clients/base-http-client.d.ts.map +0 -1
  205. package/http_clients/base-http-client.js +0 -40
  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 -20
  212. package/http_clients/got-scraping-http-client.d.ts.map +0 -1
  213. package/http_clients/got-scraping-http-client.js +0 -82
  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 +0 -430
  269. package/storages/sitemap_request_list.js.map +0 -1
  270. package/storages/storage_manager.d.ts +0 -58
  271. package/storages/storage_manager.d.ts.map +0 -1
  272. package/storages/storage_manager.js +0 -105
  273. package/storages/storage_manager.js.map +0 -1
  274. package/storages/utils.d.ts.map +0 -1
  275. package/storages/utils.js.map +0 -1
  276. package/tsconfig.build.tsbuildinfo +0 -1
  277. package/typedefs.d.ts.map +0 -1
  278. package/typedefs.js.map +0 -1
  279. package/validators.d.ts.map +0 -1
  280. package/validators.js.map +0 -1
@@ -1,44 +1,33 @@
1
- import { type ParseSitemapOptions } from '@crawlee/utils';
2
- import { Configuration } from '../configuration.js';
3
- import type { GlobInput, RegExpInput } from '../enqueue_links/shared.js';
1
+ import type { BaseHttpClient } from '@crawlee/http-client';
2
+ import { EnqueueStrategy, type ParseSitemapOptions } from '@crawlee/utils';
3
+ import type { UrlPatternInput } from '../enqueue_links/shared.js';
4
4
  import { Request } from '../request.js';
5
- import type { IRequestList } from './request_list.js';
5
+ import type { IRequestLoader } from './request_loader.js';
6
+ import type { IRequestManager } from './request_manager.js';
6
7
  interface UrlConstraints {
7
8
  /**
8
- * An array of glob pattern strings or plain objects
9
- * containing glob pattern strings matching the URLs to be enqueued.
9
+ * An array of URL patterns that URLs must match to be included.
10
10
  *
11
- * The plain objects must include at least the `glob` property, which holds the glob pattern string.
11
+ * Accepts glob pattern strings, `{ glob: string }` objects, `RegExp` instances, or `{ regexp: RegExp }` objects.
12
12
  *
13
- * The matching is always case-insensitive.
14
- * If you need case-sensitive matching, use `regexps` property directly.
13
+ * Glob matching is always case-insensitive.
14
+ * If you need case-sensitive matching, use a `RegExp`.
15
15
  *
16
- * If `globs` is an empty array or `undefined`, and `regexps` are also not defined, then the `SitemapRequestList`
16
+ * If `include` is an empty array or `undefined`, then the `SitemapRequestLoader`
17
17
  * includes all the URLs from the sitemap.
18
18
  */
19
- globs?: readonly GlobInput[];
19
+ include?: readonly UrlPatternInput[];
20
20
  /**
21
- * An array of glob pattern strings, regexp patterns or plain objects
22
- * containing patterns matching URLs that will **never** be included.
21
+ * An array of URL patterns. Matching URLs will **not** be included.
23
22
  *
24
- * The plain objects must include either the `glob` property or the `regexp` property.
23
+ * Accepts glob pattern strings, `{ glob: string }` objects, `RegExp` instances, or `{ regexp: RegExp }` objects.
25
24
  *
26
25
  * Glob matching is always case-insensitive.
27
- * If you need case-sensitive matching, provide a regexp.
26
+ * If you need case-sensitive matching, use a `RegExp`.
28
27
  */
29
- exclude?: readonly (GlobInput | RegExp)[];
30
- /**
31
- * An array of regular expressions or plain objects
32
- * containing regular expressions matching the URLs to be enqueued.
33
- *
34
- * The plain objects must include at least the `regexp` property, which holds the regular expression.
35
- *
36
- * If `regexps` is an empty array or `undefined`, and `globs` are also not defined, then the `SitemapRequestList`
37
- * includes all the URLs from the sitemap.
38
- */
39
- regexps?: readonly RegExpInput[];
28
+ exclude?: readonly UrlPatternInput[];
40
29
  }
41
- export interface SitemapRequestListOptions extends UrlConstraints {
30
+ export interface SitemapRequestLoaderOptions extends UrlConstraints {
42
31
  /**
43
32
  * List of sitemap URLs to parse.
44
33
  */
@@ -76,72 +65,34 @@ export interface SitemapRequestListOptions extends UrlConstraints {
76
65
  * @default 200
77
66
  */
78
67
  maxBufferSize?: number;
68
+ /**
69
+ * Keep only sitemap-derived URLs matching this strategy relative to the parent sitemap URL; non-`http(s)`
70
+ * schemes are always dropped. The filtering stays enforced after navigation (e.g. across redirects).
71
+ * Pass `'all'` to disable host filtering.
72
+ * @default EnqueueStrategy.SameHostname
73
+ */
74
+ enqueueStrategy?: EnqueueStrategy | `${EnqueueStrategy}`;
79
75
  /**
80
76
  * Advanced options for the underlying `parseSitemap` call.
81
77
  */
82
78
  parseSitemapOptions?: Omit<ParseSitemapOptions, 'emitNestedSitemaps' | 'maxDepth'>;
83
79
  /**
84
- * Crawlee configuration
80
+ * Custom HTTP client to be used for sitemap loading.
85
81
  */
86
- config?: Configuration;
82
+ httpClient?: BaseHttpClient;
87
83
  }
88
84
  /**
89
85
  * A list of URLs to crawl parsed from a sitemap.
90
86
  *
91
87
  * The loading of the sitemap is performed in the background so that crawling can start before the sitemap is fully loaded.
92
88
  */
93
- export declare class SitemapRequestList implements IRequestList {
89
+ export declare class SitemapRequestLoader implements IRequestLoader {
90
+ #private;
94
91
  /**
95
92
  * Set of URLs that were returned by `fetchNextRequest()` and not marked as handled yet.
96
93
  * @internal
97
94
  */
98
95
  inProgress: Set<string>;
99
- /** Set of URLs for which `reclaimRequest()` was called. */
100
- private reclaimed;
101
- /**
102
- * Map of returned Request objects that have not been marked as handled yet.
103
- *
104
- * We use this to persist custom user fields on the in-progress (or reclaimed) requests.
105
- */
106
- private requestData;
107
- /**
108
- * Object for keeping track of the sitemap parsing progress.
109
- */
110
- private sitemapParsingProgress;
111
- /**
112
- * Object stream of URLs parsed from the sitemaps.
113
- * Using `highWaterMark`, this can manage the speed of the sitemap loading.
114
- *
115
- * Fetch the next URL to be processed using `fetchNextRequest()`.
116
- */
117
- private urlQueueStream;
118
- /**
119
- * Indicates whether the request list sitemap loading was aborted.
120
- *
121
- * If the loading was aborted before the sitemaps were fully loaded, the request list might be missing some URLs.
122
- * The `isSitemapFullyLoaded` method can be used to check if the sitemaps were fully loaded.
123
- *
124
- * If the loading is aborted and all the requests are handled, `isFinished()` will return `true`.
125
- */
126
- private abortLoading;
127
- /** Number of URLs that were marked as handled */
128
- private handledUrlCount;
129
- private persistStateKey?;
130
- private store?;
131
- private closed;
132
- /**
133
- * Proxy URL to be used for sitemap loading.
134
- */
135
- private proxyUrl;
136
- /**
137
- * Logger instance.
138
- */
139
- private log;
140
- private urlExcludePatternObjects;
141
- private urlPatternObjects;
142
- /** EventManager used to handle persistence */
143
- private events;
144
- private persistenceOptions;
145
96
  /** @internal */
146
97
  private constructor();
147
98
  /**
@@ -157,7 +108,7 @@ export declare class SitemapRequestList implements IRequestList {
157
108
  */
158
109
  private matchesUrl;
159
110
  /**
160
- * Checks whether the URL matches the `globs` / `regexps` / `exclude` provided in the `options`.
111
+ * Checks whether the URL matches the `include` / `exclude` patterns provided in the `options`.
161
112
  * @param url URL to be checked.
162
113
  * @returns `true` if the URL matches the patterns, `false` otherwise.
163
114
  */
@@ -190,15 +141,25 @@ export declare class SitemapRequestList implements IRequestList {
190
141
  /**
191
142
  * Open a sitemap and start processing it.
192
143
  *
193
- * 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.
144
+ * 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.
194
145
  *
195
146
  * Track the loading progress using the `isSitemapFullyLoaded` property.
196
147
  */
197
- static open(options: SitemapRequestListOptions): Promise<SitemapRequestList>;
148
+ static open(options: SitemapRequestLoaderOptions): Promise<SitemapRequestLoader>;
198
149
  /**
199
150
  * @inheritDoc
200
151
  */
201
- length(): number;
152
+ getTotalCount(): Promise<number>;
153
+ /**
154
+ * @inheritDoc
155
+ */
156
+ getPendingCount(): Promise<number>;
157
+ /**
158
+ * Combines this list with a request manager (a {@link RequestQueue} by default) into a
159
+ * {@link RequestManagerTandem}, allowing requests to be added and reclaimed while still
160
+ * being read from this list first.
161
+ */
162
+ toTandem(requestManager?: IRequestManager): Promise<IRequestManager>;
202
163
  /**
203
164
  * @inheritDoc
204
165
  */
@@ -210,7 +171,7 @@ export declare class SitemapRequestList implements IRequestList {
210
171
  /**
211
172
  * @inheritDoc
212
173
  */
213
- handledCount(): number;
174
+ getHandledCount(): Promise<number>;
214
175
  /**
215
176
  * @inheritDoc
216
177
  */
@@ -224,11 +185,7 @@ export declare class SitemapRequestList implements IRequestList {
224
185
  * @inheritDoc
225
186
  */
226
187
  // @ts-ignore optional peer dependency or compatibility with es2022
227
- [Symbol.asyncIterator](): AsyncGenerator<Request<import("@crawlee/utils").Dictionary>, void, unknown>;
228
- /**
229
- * @inheritDoc
230
- */
231
- reclaimRequest(request: Request): Promise<void>;
188
+ [Symbol.asyncIterator](): AsyncGenerator<Request<import("@crawlee/types").Dictionary>, void, unknown>;
232
189
  /**
233
190
  * Aborts the internal sitemap loading, stops the processing of the sitemap contents and drops all the pending URLs.
234
191
  *
@@ -238,8 +195,7 @@ export declare class SitemapRequestList implements IRequestList {
238
195
  /**
239
196
  * @inheritDoc
240
197
  */
241
- markRequestHandled(request: Request): Promise<void>;
242
- private ensureInProgressAndNotReclaimed;
198
+ markRequestAsHandled(request: Request): Promise<void>;
199
+ private ensureInProgress;
243
200
  }
244
201
  export {};
245
- //# sourceMappingURL=sitemap_request_list.d.ts.map
@@ -0,0 +1,438 @@
1
+ import { Transform } from 'node:stream';
2
+ import { EnqueueStrategy, parseSitemap } from '@crawlee/utils';
3
+ import { minimatch } from 'minimatch';
4
+ import { z } from 'zod';
5
+ import { constructUrlPatternObjects, urlPatternSchema } from '../enqueue_links/shared.js';
6
+ import { EventType } from '../events/event_manager.js';
7
+ import { Request } from '../request.js';
8
+ import { serviceLocator } from '../service_locator.js';
9
+ import { parseArgument, schemas } from '../validators.js';
10
+ import { KeyValueStore } from './key_value_store.js';
11
+ import { purgeDefaultStorages } from './utils.js';
12
+ const sitemapRequestLoaderOptionsSchema = z.strictObject({
13
+ sitemapUrls: schemas.arrayOf(z.string(), 'strings'),
14
+ proxyUrl: z.string().optional(),
15
+ persistStateKey: z.string().optional(),
16
+ signal: z.unknown().optional(),
17
+ timeoutMillis: schemas.anyNumber.optional(),
18
+ maxBufferSize: schemas.anyNumber.default(200),
19
+ enqueueStrategy: z.enum(EnqueueStrategy).default(EnqueueStrategy.SameHostname),
20
+ parseSitemapOptions: z.looseObject({}).optional(),
21
+ include: schemas.arrayOf(urlPatternSchema, 'URL patterns').optional(),
22
+ exclude: schemas.arrayOf(urlPatternSchema, 'URL patterns').optional(),
23
+ persistenceOptions: z.looseObject({}).optional(),
24
+ httpClient: schemas.httpClient.optional(),
25
+ });
26
+ /** @internal */
27
+ const STATE_PERSISTENCE_KEY = 'SITEMAP_REQUEST_LOADER_STATE';
28
+ /**
29
+ * A list of URLs to crawl parsed from a sitemap.
30
+ *
31
+ * The loading of the sitemap is performed in the background so that crawling can start before the sitemap is fully loaded.
32
+ */
33
+ export class SitemapRequestLoader {
34
+ /**
35
+ * Set of URLs that were returned by `fetchNextRequest()` and not marked as handled yet.
36
+ * @internal
37
+ */
38
+ inProgress = new Set();
39
+ /**
40
+ * Map of returned Request objects that have not been marked as handled yet.
41
+ *
42
+ * We use this to persist custom user fields on the in-progress requests.
43
+ */
44
+ #requestData = new Map();
45
+ /**
46
+ * Object for keeping track of the sitemap parsing progress.
47
+ */
48
+ #sitemapParsingProgress = {
49
+ /**
50
+ * URL of the sitemap that is currently being parsed. `null` if no sitemap is being parsed.
51
+ */
52
+ inProgressSitemapUrl: null,
53
+ /**
54
+ * Buffer for URLs from the currently parsed sitemap. Used for tracking partially loaded sitemaps across migrations.
55
+ */
56
+ inProgressEntries: new Set(),
57
+ /**
58
+ * Set of sitemap URLs that have not been parsed yet. If the set is empty and `inProgressSitemapUrl` is `null`, the sitemap loading is finished.
59
+ */
60
+ pendingSitemapUrls: new Set(),
61
+ };
62
+ /**
63
+ * Object stream of URLs parsed from the sitemaps.
64
+ * Using `highWaterMark`, this can manage the speed of the sitemap loading.
65
+ *
66
+ * Fetch the next URL to be processed using `fetchNextRequest()`.
67
+ */
68
+ #urlQueueStream;
69
+ /**
70
+ * Indicates whether the request list sitemap loading was aborted.
71
+ *
72
+ * If the loading was aborted before the sitemaps were fully loaded, the request list might be missing some URLs.
73
+ * The `isSitemapFullyLoaded` method can be used to check if the sitemaps were fully loaded.
74
+ *
75
+ * If the loading is aborted and all the requests are handled, `isFinished()` will return `true`.
76
+ */
77
+ #abortLoading = false;
78
+ /** Number of URLs that were marked as handled */
79
+ #handledUrlCount = 0;
80
+ #persistStateKey;
81
+ #store;
82
+ #closed = false;
83
+ /**
84
+ * Proxy URL to be used for sitemap loading.
85
+ */
86
+ #proxyUrl;
87
+ /**
88
+ * Enqueue strategy applied to sitemap-derived URLs and stamped onto the emitted `Request` objects.
89
+ */
90
+ #enqueueStrategy;
91
+ /**
92
+ * Logger instance.
93
+ */
94
+ #log;
95
+ #urlExcludePatternObjects = [];
96
+ #urlPatternObjects = [];
97
+ /** EventManager used to handle persistence */
98
+ #events;
99
+ #persistenceOptions;
100
+ /** @internal */
101
+ constructor(options) {
102
+ const { include, exclude, persistStateKey, persistenceOptions, proxyUrl, maxBufferSize, sitemapUrls, enqueueStrategy, } = parseArgument(options, sitemapRequestLoaderOptionsSchema, 'SitemapRequestLoaderOptions');
103
+ this.#log = serviceLocator.getLogger().child({ prefix: 'SitemapRequestLoader' });
104
+ if (exclude?.length) {
105
+ this.#urlExcludePatternObjects.push(...constructUrlPatternObjects(exclude));
106
+ }
107
+ if (include?.length) {
108
+ this.#urlPatternObjects.push(...constructUrlPatternObjects(include));
109
+ }
110
+ this.#persistStateKey = persistStateKey;
111
+ this.#persistenceOptions = { enable: true, ...persistenceOptions };
112
+ this.#proxyUrl = proxyUrl;
113
+ this.#enqueueStrategy = enqueueStrategy;
114
+ this.#urlQueueStream = this.createNewStream(maxBufferSize);
115
+ this.#sitemapParsingProgress.pendingSitemapUrls = new Set(sitemapUrls);
116
+ this.#events = serviceLocator.getEventManager();
117
+ this.persistState = this.persistState.bind(this);
118
+ }
119
+ /**
120
+ * Creates a new object stream with the specified highWaterMark.
121
+ * @param highWaterMark High water mark for the stream (the maximum number of objects the stream will buffer).
122
+ * @returns A new object stream.
123
+ */
124
+ createNewStream(highWaterMark) {
125
+ return new Transform({
126
+ objectMode: true,
127
+ highWaterMark,
128
+ }).pause();
129
+ }
130
+ /**
131
+ * Returns a function that checks whether the provided pattern matches the closure URL.
132
+ * @param url URL to be checked.
133
+ * @returns A matcher function that checks whether the pattern matches the closure URL.
134
+ */
135
+ matchesUrl(url) {
136
+ return (patternObject) => {
137
+ const { regexp, glob } = patternObject;
138
+ const matchesRegex = (regexp && url.match(regexp)) || false;
139
+ const matchesGlob = (glob && minimatch(url, glob, { nocase: true })) || false;
140
+ return Boolean(matchesRegex || matchesGlob);
141
+ };
142
+ }
143
+ /**
144
+ * Checks whether the URL matches the `include` / `exclude` patterns provided in the `options`.
145
+ * @param url URL to be checked.
146
+ * @returns `true` if the URL matches the patterns, `false` otherwise.
147
+ */
148
+ isUrlMatchingPatterns(url) {
149
+ return (!this.#urlExcludePatternObjects.some(this.matchesUrl(url)) &&
150
+ (this.#urlPatternObjects.length === 0 || this.#urlPatternObjects.some(this.matchesUrl(url))));
151
+ }
152
+ /**
153
+ * Adds a URL to the queue of parsed URLs.
154
+ *
155
+ * Blocks if the stream is full until it is drained.
156
+ */
157
+ async pushNextUrl(url) {
158
+ return new Promise((resolve) => {
159
+ if (this.#closed || (url && !this.isUrlMatchingPatterns(url))) {
160
+ resolve();
161
+ return;
162
+ }
163
+ if (!this.#urlQueueStream.push(url)) {
164
+ // This doesn't work with the 'drain' event (it's not emitted for some reason).
165
+ this.#urlQueueStream.once('readdata', () => {
166
+ resolve();
167
+ });
168
+ }
169
+ else {
170
+ resolve();
171
+ }
172
+ });
173
+ }
174
+ /**
175
+ * Reads the next URL from the queue of parsed URLs.
176
+ *
177
+ * If the stream is empty, blocks until a new URL is pushed.
178
+ * @returns The next URL from the queue or `null` if we have read all URLs.
179
+ */
180
+ async readNextUrl() {
181
+ return new Promise((resolve) => {
182
+ if (this.#closed) {
183
+ resolve(null);
184
+ return;
185
+ }
186
+ const result = this.#urlQueueStream.read();
187
+ if (!result && !this.isSitemapFullyLoaded()) {
188
+ this.#urlQueueStream.once('readable', () => {
189
+ const nextUrl = this.#urlQueueStream.read();
190
+ resolve(nextUrl);
191
+ });
192
+ }
193
+ else {
194
+ resolve(result);
195
+ }
196
+ this.#urlQueueStream.emit('readdata');
197
+ });
198
+ }
199
+ /**
200
+ * Indicates whether the background processing of sitemap contents has successfully finished.
201
+ *
202
+ * If this is `false`, the background processing is either still in progress or was aborted.
203
+ */
204
+ isSitemapFullyLoaded() {
205
+ return (this.#sitemapParsingProgress.inProgressSitemapUrl === null &&
206
+ this.#sitemapParsingProgress.pendingSitemapUrls.size === 0);
207
+ }
208
+ /**
209
+ * Start processing the sitemaps and loading the URLs.
210
+ *
211
+ * Resolves once all the sitemaps URLs have been fully loaded (sets `isSitemapFullyLoaded` to `true`).
212
+ */
213
+ async load({ parseSitemapOptions, }) {
214
+ while (!this.isSitemapFullyLoaded() && !this.#abortLoading) {
215
+ const sitemapUrl = this.#sitemapParsingProgress.inProgressSitemapUrl ??
216
+ this.#sitemapParsingProgress.pendingSitemapUrls.values().next().value;
217
+ try {
218
+ for await (const item of parseSitemap([{ type: 'url', url: sitemapUrl }], this.#proxyUrl, {
219
+ ...parseSitemapOptions,
220
+ maxDepth: 0,
221
+ emitNestedSitemaps: true,
222
+ enqueueStrategy: this.#enqueueStrategy,
223
+ })) {
224
+ if (!item.originSitemapUrl) {
225
+ // This is a nested sitemap
226
+ this.#sitemapParsingProgress.pendingSitemapUrls.add(item.loc);
227
+ continue;
228
+ }
229
+ if (!this.#sitemapParsingProgress.inProgressEntries.has(item.loc)) {
230
+ await this.pushNextUrl(item.loc);
231
+ this.#sitemapParsingProgress.inProgressEntries.add(item.loc);
232
+ }
233
+ }
234
+ }
235
+ catch (e) {
236
+ this.#log.error('Error loading sitemap contents:', e);
237
+ }
238
+ this.#sitemapParsingProgress.pendingSitemapUrls.delete(sitemapUrl);
239
+ this.#sitemapParsingProgress.inProgressEntries.clear();
240
+ this.#sitemapParsingProgress.inProgressSitemapUrl = null;
241
+ }
242
+ this.#urlQueueStream.end();
243
+ }
244
+ /**
245
+ * Open a sitemap and start processing it.
246
+ *
247
+ * 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.
248
+ *
249
+ * Track the loading progress using the `isSitemapFullyLoaded` property.
250
+ */
251
+ static async open(options) {
252
+ const { httpClient, ...restOptions } = options;
253
+ const requestList = new SitemapRequestLoader({
254
+ ...restOptions,
255
+ persistStateKey: options.persistStateKey ?? STATE_PERSISTENCE_KEY,
256
+ });
257
+ await requestList.restoreState();
258
+ void requestList.load({
259
+ parseSitemapOptions: { logger: serviceLocator.getLogger(), ...options.parseSitemapOptions, httpClient },
260
+ });
261
+ if (requestList.#persistenceOptions.enable) {
262
+ requestList.#events.on(EventType.PERSIST_STATE, requestList.persistState);
263
+ }
264
+ options?.signal?.addEventListener('abort', () => {
265
+ requestList.#abortLoading = true;
266
+ });
267
+ if (options.timeoutMillis) {
268
+ setTimeout(() => {
269
+ requestList.#abortLoading = true;
270
+ }, options.timeoutMillis);
271
+ }
272
+ return requestList;
273
+ }
274
+ /**
275
+ * @inheritDoc
276
+ */
277
+ async getTotalCount() {
278
+ // Total known so far = not-yet-fetched (still buffered in the stream) + in-progress (fetched but not
279
+ // yet handled) + already handled.
280
+ return this.#urlQueueStream.readableLength + this.inProgress.size + this.#handledUrlCount;
281
+ }
282
+ /**
283
+ * @inheritDoc
284
+ */
285
+ async getPendingCount() {
286
+ // Pending = everything not yet handled = not-yet-fetched + in-progress.
287
+ return this.#urlQueueStream.readableLength + this.inProgress.size;
288
+ }
289
+ /**
290
+ * Combines this list with a request manager (a {@link RequestQueue} by default) into a
291
+ * {@link RequestManagerTandem}, allowing requests to be added and reclaimed while still
292
+ * being read from this list first.
293
+ */
294
+ async toTandem(requestManager) {
295
+ // Import here to avoid circular imports.
296
+ const { RequestManagerTandem } = await import('./request_manager_tandem.js');
297
+ const { RequestQueue } = await import('./request_queue.js');
298
+ return new RequestManagerTandem(this, requestManager ?? (await RequestQueue.open()));
299
+ }
300
+ /**
301
+ * @inheritDoc
302
+ */
303
+ async isFinished() {
304
+ return ((await this.isEmpty()) && this.inProgress.size === 0 && (this.isSitemapFullyLoaded() || this.#abortLoading));
305
+ }
306
+ /**
307
+ * @inheritDoc
308
+ */
309
+ async isEmpty() {
310
+ return this.#urlQueueStream.readableLength === 0;
311
+ }
312
+ /**
313
+ * @inheritDoc
314
+ */
315
+ async getHandledCount() {
316
+ return this.#handledUrlCount;
317
+ }
318
+ /**
319
+ * @inheritDoc
320
+ */
321
+ async persistState() {
322
+ if (this.#persistStateKey === undefined) {
323
+ return;
324
+ }
325
+ this.#store ??= await KeyValueStore.open();
326
+ const urlQueue = [];
327
+ while (this.#urlQueueStream.readableLength > 0) {
328
+ const url = this.#urlQueueStream.read();
329
+ if (url === null) {
330
+ break;
331
+ }
332
+ urlQueue.push(url);
333
+ }
334
+ // Create a new stream, as we have read all the URLs from the current one.
335
+ // Pushing the urls back to the original stream might not be possible if it has been ended.
336
+ const previousStream = this.#urlQueueStream;
337
+ const newStream = this.createNewStream(previousStream.readableHighWaterMark);
338
+ for (const url of urlQueue) {
339
+ newStream.push(url);
340
+ }
341
+ if (previousStream.writableEnded) {
342
+ newStream.end();
343
+ }
344
+ this.#urlQueueStream = newStream;
345
+ // A `pushNextUrl()` call may be blocked on backpressure, waiting for a `readdata` event on the
346
+ // previous stream. That event is only ever emitted by `readNextUrl()` on the current stream, so
347
+ // after the swap the waiter would never be notified and the background sitemap loading would hang.
348
+ // Re-emit `readdata` on the previous stream to release any such pending waiter (its URL has already
349
+ // been transferred to the new stream above).
350
+ previousStream.emit('readdata');
351
+ await this.#store.setValue(this.#persistStateKey, {
352
+ sitemapParsingProgress: {
353
+ pendingSitemapUrls: Array.from(this.#sitemapParsingProgress.pendingSitemapUrls),
354
+ inProgressSitemapUrl: this.#sitemapParsingProgress.inProgressSitemapUrl,
355
+ inProgressEntries: Array.from(this.#sitemapParsingProgress.inProgressEntries),
356
+ },
357
+ // Re-queue in-progress requests to the front so they are retried if the state is restored.
358
+ urlQueue: [...this.inProgress, ...urlQueue],
359
+ requestData: Array.from(this.#requestData.entries()),
360
+ abortLoading: this.#abortLoading,
361
+ closed: this.#closed,
362
+ });
363
+ }
364
+ async restoreState() {
365
+ await purgeDefaultStorages({ onlyPurgeOnce: true });
366
+ if (this.#persistStateKey === undefined) {
367
+ return;
368
+ }
369
+ this.#store ??= await KeyValueStore.open();
370
+ const state = await this.#store.getValue(this.#persistStateKey);
371
+ if (state === null) {
372
+ return;
373
+ }
374
+ this.#sitemapParsingProgress = {
375
+ pendingSitemapUrls: new Set(state.sitemapParsingProgress.pendingSitemapUrls),
376
+ inProgressSitemapUrl: state.sitemapParsingProgress.inProgressSitemapUrl,
377
+ inProgressEntries: new Set(state.sitemapParsingProgress.inProgressEntries),
378
+ };
379
+ this.#requestData = new Map(state.requestData ?? []);
380
+ for (const url of state.urlQueue) {
381
+ this.#urlQueueStream.push(url);
382
+ }
383
+ this.#abortLoading = state.abortLoading;
384
+ this.#closed = state.closed;
385
+ }
386
+ /**
387
+ * @inheritDoc
388
+ */
389
+ async fetchNextRequest() {
390
+ const nextUrl = await this.readNextUrl();
391
+ if (!nextUrl) {
392
+ return null;
393
+ }
394
+ // A restored in-progress request already has its Request data; don't overwrite it.
395
+ if (!this.#requestData.has(nextUrl)) {
396
+ this.#requestData.set(nextUrl, new Request({ url: nextUrl, enqueueStrategy: this.#enqueueStrategy }));
397
+ }
398
+ this.inProgress.add(nextUrl);
399
+ return this.#requestData.get(nextUrl);
400
+ }
401
+ /**
402
+ * @inheritDoc
403
+ */
404
+ async *[Symbol.asyncIterator]() {
405
+ while (!(await this.isFinished())) {
406
+ const request = await this.fetchNextRequest();
407
+ if (!request)
408
+ break;
409
+ yield request;
410
+ }
411
+ }
412
+ /**
413
+ * Aborts the internal sitemap loading, stops the processing of the sitemap contents and drops all the pending URLs.
414
+ *
415
+ * Calling `fetchNextRequest()` after this method will always return `null`.
416
+ */
417
+ async teardown() {
418
+ this.#closed = true;
419
+ this.#abortLoading = true;
420
+ this.#events.off(EventType.PERSIST_STATE, this.persistState);
421
+ await this.persistState();
422
+ this.#urlQueueStream.emit('readdata'); // unblocks the potentially waiting `pushNextUrl` call
423
+ }
424
+ /**
425
+ * @inheritDoc
426
+ */
427
+ async markRequestAsHandled(request) {
428
+ this.#handledUrlCount += 1;
429
+ this.ensureInProgress(request.url);
430
+ this.inProgress.delete(request.url);
431
+ this.#requestData.delete(request.url);
432
+ }
433
+ ensureInProgress(url) {
434
+ if (!this.inProgress.has(url)) {
435
+ throw new Error(`The request is not being processed (url: ${url})`);
436
+ }
437
+ }
438
+ }