@crawlee/core 4.0.0-beta.23 → 4.0.0-beta.230

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 (265) hide show
  1. package/README.md +15 -15
  2. package/byte-utils.d.ts +17 -0
  3. package/byte-utils.js +42 -0
  4. package/configuration.d.ts +77 -235
  5. package/configuration.js +157 -227
  6. package/debug.d.ts +36 -0
  7. package/debug.js +70 -0
  8. package/errors.d.ts +45 -18
  9. package/errors.js +58 -27
  10. package/events/event-manager.d.ts +78 -0
  11. package/events/{event_manager.js → event-manager.js} +12 -13
  12. package/events/index.d.ts +3 -3
  13. package/events/index.js +2 -3
  14. package/events/local-event-manager.d.ts +32 -0
  15. package/events/local-event-manager.js +101 -0
  16. package/events/system-info.d.ts +38 -0
  17. package/events/system-info.js +1 -0
  18. package/index.d.ts +9 -12
  19. package/index.js +10 -11
  20. package/internal.d.ts +8 -0
  21. package/internal.js +9 -0
  22. package/iterables.d.ts +79 -0
  23. package/iterables.js +134 -0
  24. package/log.d.ts +77 -1
  25. package/log.js +134 -1
  26. package/memory-storage/consts.d.ts +4 -0
  27. package/memory-storage/consts.js +4 -0
  28. package/memory-storage/index.d.ts +1 -0
  29. package/memory-storage/index.js +1 -0
  30. package/memory-storage/memory-storage.d.ts +43 -0
  31. package/memory-storage/memory-storage.js +158 -0
  32. package/memory-storage/resource-clients/common/base-client.d.ts +4 -0
  33. package/memory-storage/resource-clients/common/base-client.js +6 -0
  34. package/memory-storage/resource-clients/dataset.d.ts +35 -0
  35. package/memory-storage/resource-clients/dataset.js +105 -0
  36. package/memory-storage/resource-clients/key-value-store.d.ts +54 -0
  37. package/memory-storage/resource-clients/key-value-store.js +179 -0
  38. package/memory-storage/resource-clients/request-queue.d.ts +55 -0
  39. package/memory-storage/resource-clients/request-queue.js +413 -0
  40. package/memory-storage/utils.d.ts +16 -0
  41. package/memory-storage/utils.js +41 -0
  42. package/owned-or-injected.d.ts +58 -0
  43. package/owned-or-injected.js +98 -0
  44. package/package.json +20 -20
  45. package/proxy-configuration.d.ts +118 -0
  46. package/proxy-configuration.js +141 -0
  47. package/recoverable-state.d.ts +177 -0
  48. package/recoverable-state.js +268 -0
  49. package/request.d.ts +48 -93
  50. package/request.js +112 -230
  51. package/serialization.d.ts +0 -1
  52. package/serialization.js +18 -18
  53. package/service-locator.d.ts +168 -0
  54. package/service-locator.js +255 -0
  55. package/storages/batched-adds.d.ts +37 -0
  56. package/storages/batched-adds.js +73 -0
  57. package/storages/dataset.d.ts +105 -58
  58. package/storages/dataset.js +292 -150
  59. package/storages/index.d.ts +11 -12
  60. package/storages/index.js +7 -12
  61. package/storages/key-value-store-codec.d.ts +32 -0
  62. package/storages/key-value-store-codec.js +108 -0
  63. package/storages/{key_value_store.d.ts → key-value-store.d.ts} +169 -72
  64. package/storages/key-value-store.js +767 -0
  65. package/storages/request-dedup-cache.d.ts +20 -0
  66. package/storages/request-dedup-cache.js +48 -0
  67. package/storages/{request_list.d.ts → request-list.d.ts} +33 -173
  68. package/storages/{request_list.js → request-list.js} +276 -255
  69. package/storages/request-loader.d.ts +127 -0
  70. package/storages/request-loader.js +36 -0
  71. package/storages/request-manager-tandem.d.ts +82 -0
  72. package/storages/request-manager-tandem.js +200 -0
  73. package/storages/request-manager.d.ts +119 -0
  74. package/storages/request-manager.js +1 -0
  75. package/storages/{request_provider.d.ts → request-queue.d.ts} +128 -159
  76. package/storages/request-queue.js +870 -0
  77. package/storages/storage-instance-manager.d.ts +86 -0
  78. package/storages/storage-instance-manager.js +256 -0
  79. package/storages/storage-stats.d.ts +48 -0
  80. package/storages/storage-stats.js +29 -0
  81. package/storages/transaction.d.ts +270 -0
  82. package/storages/transaction.js +296 -0
  83. package/storages/utils.d.ts +59 -11
  84. package/storages/utils.js +76 -16
  85. package/system-info/cpu-info.d.ts +67 -0
  86. package/system-info/cpu-info.js +216 -0
  87. package/system-info/memory-info.d.ts +31 -0
  88. package/system-info/memory-info.js +115 -0
  89. package/system-info/ps-tree.d.ts +17 -0
  90. package/system-info/ps-tree.js +144 -0
  91. package/system-info/runtime.d.ts +14 -0
  92. package/system-info/runtime.js +80 -0
  93. package/typedefs.d.ts +0 -6
  94. package/typedefs.js +0 -1
  95. package/url.d.ts +9 -0
  96. package/url.js +11 -0
  97. package/validators.d.ts +23 -18
  98. package/validators.js +14 -18
  99. package/autoscaling/autoscaled_pool.d.ts +0 -300
  100. package/autoscaling/autoscaled_pool.d.ts.map +0 -1
  101. package/autoscaling/autoscaled_pool.js +0 -533
  102. package/autoscaling/autoscaled_pool.js.map +0 -1
  103. package/autoscaling/index.d.ts +0 -4
  104. package/autoscaling/index.d.ts.map +0 -1
  105. package/autoscaling/index.js +0 -4
  106. package/autoscaling/index.js.map +0 -1
  107. package/autoscaling/snapshotter.d.ts +0 -189
  108. package/autoscaling/snapshotter.d.ts.map +0 -1
  109. package/autoscaling/snapshotter.js +0 -285
  110. package/autoscaling/snapshotter.js.map +0 -1
  111. package/autoscaling/system_status.d.ts +0 -181
  112. package/autoscaling/system_status.d.ts.map +0 -1
  113. package/autoscaling/system_status.js +0 -169
  114. package/autoscaling/system_status.js.map +0 -1
  115. package/configuration.d.ts.map +0 -1
  116. package/configuration.js.map +0 -1
  117. package/cookie_utils.d.ts +0 -43
  118. package/cookie_utils.d.ts.map +0 -1
  119. package/cookie_utils.js +0 -111
  120. package/cookie_utils.js.map +0 -1
  121. package/crawlers/context_pipeline.d.ts +0 -61
  122. package/crawlers/context_pipeline.d.ts.map +0 -1
  123. package/crawlers/context_pipeline.js +0 -99
  124. package/crawlers/context_pipeline.js.map +0 -1
  125. package/crawlers/crawler_commons.d.ts +0 -191
  126. package/crawlers/crawler_commons.d.ts.map +0 -1
  127. package/crawlers/crawler_commons.js +0 -108
  128. package/crawlers/crawler_commons.js.map +0 -1
  129. package/crawlers/crawler_utils.d.ts +0 -10
  130. package/crawlers/crawler_utils.d.ts.map +0 -1
  131. package/crawlers/crawler_utils.js +0 -12
  132. package/crawlers/crawler_utils.js.map +0 -1
  133. package/crawlers/error_snapshotter.d.ts +0 -60
  134. package/crawlers/error_snapshotter.d.ts.map +0 -1
  135. package/crawlers/error_snapshotter.js +0 -118
  136. package/crawlers/error_snapshotter.js.map +0 -1
  137. package/crawlers/error_tracker.d.ts +0 -55
  138. package/crawlers/error_tracker.d.ts.map +0 -1
  139. package/crawlers/error_tracker.js +0 -309
  140. package/crawlers/error_tracker.js.map +0 -1
  141. package/crawlers/index.d.ts +0 -7
  142. package/crawlers/index.d.ts.map +0 -1
  143. package/crawlers/index.js +0 -7
  144. package/crawlers/index.js.map +0 -1
  145. package/crawlers/internals/types.d.ts +0 -8
  146. package/crawlers/internals/types.d.ts.map +0 -1
  147. package/crawlers/internals/types.js +0 -2
  148. package/crawlers/internals/types.js.map +0 -1
  149. package/crawlers/statistics.d.ts +0 -213
  150. package/crawlers/statistics.d.ts.map +0 -1
  151. package/crawlers/statistics.js +0 -349
  152. package/crawlers/statistics.js.map +0 -1
  153. package/enqueue_links/enqueue_links.d.ts +0 -249
  154. package/enqueue_links/enqueue_links.d.ts.map +0 -1
  155. package/enqueue_links/enqueue_links.js +0 -269
  156. package/enqueue_links/enqueue_links.js.map +0 -1
  157. package/enqueue_links/index.d.ts +0 -3
  158. package/enqueue_links/index.d.ts.map +0 -1
  159. package/enqueue_links/index.js +0 -3
  160. package/enqueue_links/index.js.map +0 -1
  161. package/enqueue_links/shared.d.ts +0 -73
  162. package/enqueue_links/shared.d.ts.map +0 -1
  163. package/enqueue_links/shared.js +0 -212
  164. package/enqueue_links/shared.js.map +0 -1
  165. package/errors.d.ts.map +0 -1
  166. package/errors.js.map +0 -1
  167. package/events/event_manager.d.ts +0 -52
  168. package/events/event_manager.d.ts.map +0 -1
  169. package/events/event_manager.js.map +0 -1
  170. package/events/index.d.ts.map +0 -1
  171. package/events/index.js.map +0 -1
  172. package/events/local_event_manager.d.ts +0 -27
  173. package/events/local_event_manager.d.ts.map +0 -1
  174. package/events/local_event_manager.js +0 -75
  175. package/events/local_event_manager.js.map +0 -1
  176. package/index.d.ts.map +0 -1
  177. package/index.js.map +0 -1
  178. package/log.d.ts.map +0 -1
  179. package/log.js.map +0 -1
  180. package/proxy_configuration.d.ts +0 -169
  181. package/proxy_configuration.d.ts.map +0 -1
  182. package/proxy_configuration.js +0 -247
  183. package/proxy_configuration.js.map +0 -1
  184. package/recoverable_state.d.ts +0 -123
  185. package/recoverable_state.d.ts.map +0 -1
  186. package/recoverable_state.js +0 -140
  187. package/recoverable_state.js.map +0 -1
  188. package/request.d.ts.map +0 -1
  189. package/request.js.map +0 -1
  190. package/router.d.ts +0 -134
  191. package/router.d.ts.map +0 -1
  192. package/router.js +0 -164
  193. package/router.js.map +0 -1
  194. package/serialization.d.ts.map +0 -1
  195. package/serialization.js.map +0 -1
  196. package/session_pool/consts.d.ts +0 -4
  197. package/session_pool/consts.d.ts.map +0 -1
  198. package/session_pool/consts.js +0 -4
  199. package/session_pool/consts.js.map +0 -1
  200. package/session_pool/errors.d.ts +0 -8
  201. package/session_pool/errors.d.ts.map +0 -1
  202. package/session_pool/errors.js +0 -12
  203. package/session_pool/errors.js.map +0 -1
  204. package/session_pool/events.d.ts +0 -3
  205. package/session_pool/events.d.ts.map +0 -1
  206. package/session_pool/events.js +0 -3
  207. package/session_pool/events.js.map +0 -1
  208. package/session_pool/index.d.ts +0 -6
  209. package/session_pool/index.d.ts.map +0 -1
  210. package/session_pool/index.js +0 -6
  211. package/session_pool/index.js.map +0 -1
  212. package/session_pool/session.d.ts +0 -186
  213. package/session_pool/session.d.ts.map +0 -1
  214. package/session_pool/session.js +0 -278
  215. package/session_pool/session.js.map +0 -1
  216. package/session_pool/session_pool.d.ts +0 -258
  217. package/session_pool/session_pool.d.ts.map +0 -1
  218. package/session_pool/session_pool.js +0 -387
  219. package/session_pool/session_pool.js.map +0 -1
  220. package/storages/access_checking.d.ts +0 -13
  221. package/storages/access_checking.d.ts.map +0 -1
  222. package/storages/access_checking.js +0 -14
  223. package/storages/access_checking.js.map +0 -1
  224. package/storages/dataset.d.ts.map +0 -1
  225. package/storages/dataset.js.map +0 -1
  226. package/storages/index.d.ts.map +0 -1
  227. package/storages/index.js.map +0 -1
  228. package/storages/key_value_store.d.ts.map +0 -1
  229. package/storages/key_value_store.js +0 -512
  230. package/storages/key_value_store.js.map +0 -1
  231. package/storages/request_list.d.ts.map +0 -1
  232. package/storages/request_list.js.map +0 -1
  233. package/storages/request_list_adapter.d.ts +0 -58
  234. package/storages/request_list_adapter.d.ts.map +0 -1
  235. package/storages/request_list_adapter.js +0 -81
  236. package/storages/request_list_adapter.js.map +0 -1
  237. package/storages/request_manager_tandem.d.ts +0 -68
  238. package/storages/request_manager_tandem.d.ts.map +0 -1
  239. package/storages/request_manager_tandem.js +0 -124
  240. package/storages/request_manager_tandem.js.map +0 -1
  241. package/storages/request_provider.d.ts.map +0 -1
  242. package/storages/request_provider.js +0 -591
  243. package/storages/request_provider.js.map +0 -1
  244. package/storages/request_queue.d.ts +0 -128
  245. package/storages/request_queue.d.ts.map +0 -1
  246. package/storages/request_queue.js +0 -320
  247. package/storages/request_queue.js.map +0 -1
  248. package/storages/request_queue_v2.d.ts +0 -87
  249. package/storages/request_queue_v2.d.ts.map +0 -1
  250. package/storages/request_queue_v2.js +0 -438
  251. package/storages/request_queue_v2.js.map +0 -1
  252. package/storages/sitemap_request_list.d.ts +0 -250
  253. package/storages/sitemap_request_list.d.ts.map +0 -1
  254. package/storages/sitemap_request_list.js +0 -431
  255. package/storages/sitemap_request_list.js.map +0 -1
  256. package/storages/storage_manager.d.ts +0 -62
  257. package/storages/storage_manager.d.ts.map +0 -1
  258. package/storages/storage_manager.js +0 -105
  259. package/storages/storage_manager.js.map +0 -1
  260. package/storages/utils.d.ts.map +0 -1
  261. package/storages/utils.js.map +0 -1
  262. package/typedefs.d.ts.map +0 -1
  263. package/typedefs.js.map +0 -1
  264. package/validators.d.ts.map +0 -1
  265. package/validators.js.map +0 -1
@@ -0,0 +1,168 @@
1
+ import type { StorageBackend } from '@crawlee/types';
2
+ import { Configuration } from './configuration.js';
3
+ import type { EventManager } from './events/event-manager.js';
4
+ import type { CrawleeLogger } from './log.js';
5
+ import { StorageInstanceManager } from './storages/storage-instance-manager.js';
6
+ interface ServiceLocatorInterface {
7
+ /**
8
+ * Get the configuration.
9
+ * Creates a default Configuration instance if none has been set.
10
+ */
11
+ getConfiguration(): Configuration;
12
+ /**
13
+ * Set the configuration.
14
+ *
15
+ * @param configuration The configuration to set
16
+ * @throws {ServiceConflictError} If a different configuration has already been retrieved
17
+ */
18
+ setConfiguration(configuration: Configuration): void;
19
+ /**
20
+ * Get the event manager.
21
+ * Creates a default LocalEventManager instance if none has been set.
22
+ */
23
+ getEventManager(): EventManager;
24
+ /**
25
+ * Set the event manager.
26
+ *
27
+ * @param eventManager The event manager to set
28
+ * @throws {ServiceConflictError} If a different event manager has already been retrieved
29
+ */
30
+ setEventManager(eventManager: EventManager): void;
31
+ /**
32
+ * Get the storage backend.
33
+ * Creates a default storage backend if none has been set — `FileSystemStorageBackend` when
34
+ * `persistStorage` is enabled (the default), `MemoryStorageBackend` otherwise.
35
+ */
36
+ getStorageBackend(): StorageBackend;
37
+ /**
38
+ * Set the storage backend.
39
+ *
40
+ * @param storageBackend The storage backend to set
41
+ * @throws {ServiceConflictError} If a different storage backend has already been retrieved
42
+ */
43
+ setStorageBackend(storageBackend: StorageBackend): void;
44
+ /**
45
+ * Get the logger.
46
+ * Returns the default `@apify/log` logger if none has been set.
47
+ */
48
+ getLogger(): CrawleeLogger;
49
+ /**
50
+ * Set the logger.
51
+ *
52
+ * @param logger The logger to set
53
+ * @throws {ServiceConflictError} If a different logger has already been retrieved
54
+ */
55
+ setLogger(logger: CrawleeLogger): void;
56
+ /**
57
+ * Get a child logger with the given prefix.
58
+ * Equivalent to `getLogger().child({ prefix })`.
59
+ */
60
+ getChildLog(prefix: string): CrawleeLogger;
61
+ /**
62
+ * Get the storage instance manager (shared across all storage types).
63
+ * @internal
64
+ */
65
+ getStorageInstanceManager(): StorageInstanceManager;
66
+ /**
67
+ * Returns the currently set services without triggering the implicit creation of defaults.
68
+ * Used to inherit already-materialized services into crawler-scoped service locators.
69
+ * @internal
70
+ */
71
+ getServicesIfSet(): {
72
+ configuration?: Configuration;
73
+ eventManager?: EventManager;
74
+ storageBackend?: StorageBackend;
75
+ logger?: CrawleeLogger;
76
+ };
77
+ /**
78
+ * Resets the service locator to its initial state.
79
+ * Used mainly for testing purposes.
80
+ * @internal
81
+ */
82
+ reset(): void;
83
+ }
84
+ /**
85
+ * Service locator for managing the services used by Crawlee.
86
+ *
87
+ * All services are initialized to their default value lazily.
88
+ *
89
+ * There are two primary usage patterns:
90
+ *
91
+ * **1. Global service locator (for default services):**
92
+ * ```typescript
93
+ * import { serviceLocator, BasicCrawler } from 'crawlee';
94
+ *
95
+ * // Optionally configure global services before creating crawlers
96
+ * serviceLocator.setStorageBackend(myCustomClient);
97
+ *
98
+ * // Crawler uses global services
99
+ * const crawler = new BasicCrawler({ ... });
100
+ * ```
101
+ *
102
+ * **2. Per-crawler services (recommended for isolation):**
103
+ * ```typescript
104
+ * import { BasicCrawler, Configuration, LocalEventManager, MemoryStorageBackend } from 'crawlee';
105
+ *
106
+ * const crawler = new BasicCrawler({
107
+ * requestHandler: async ({ request }) => { ... },
108
+ * configuration: new Configuration({ ... }), // custom configuration
109
+ * storageBackend: new MemoryStorageBackend(), // custom storage
110
+ * eventManager: LocalEventManager.fromConfiguration(), // custom events
111
+ * });
112
+ * // Crawler has its own isolated ServiceLocator instance
113
+ * ```
114
+ */
115
+ export declare class ServiceLocator implements ServiceLocatorInterface {
116
+ #private;
117
+ /**
118
+ * Creates a new ServiceLocator instance.
119
+ *
120
+ * @param configuration Optional configuration instance to use
121
+ * @param eventManager Optional event manager instance to use
122
+ * @param storageBackend Optional storage backend instance to use
123
+ * @param logger Optional logger instance to use
124
+ */
125
+ constructor(configuration?: Configuration, eventManager?: EventManager, storageBackend?: StorageBackend, logger?: CrawleeLogger);
126
+ /** @internal */
127
+ getServicesIfSet(): {
128
+ configuration?: Configuration;
129
+ eventManager?: EventManager;
130
+ storageBackend?: StorageBackend;
131
+ logger?: CrawleeLogger;
132
+ };
133
+ getConfiguration(): Configuration;
134
+ setConfiguration(configuration: Configuration): void;
135
+ getEventManager(): EventManager;
136
+ setEventManager(eventManager: EventManager): void;
137
+ getStorageBackend(): StorageBackend;
138
+ setStorageBackend(storageBackend: StorageBackend): void;
139
+ getLogger(): CrawleeLogger;
140
+ setLogger(logger: CrawleeLogger): void;
141
+ getChildLog(prefix: string): CrawleeLogger;
142
+ /** @internal */
143
+ getStorageInstanceManager(): StorageInstanceManager;
144
+ /** @internal */
145
+ reset(): void;
146
+ }
147
+ /**
148
+ * Wraps all methods on `target` so that any code they invoke will see the given
149
+ * `serviceLocator` via `AsyncLocalStorage`, rather than the global one.
150
+ *
151
+ * Walks the prototype chain and replaces each method on the *instance* (not the prototype)
152
+ * with a wrapper that calls `serviceLocatorStorage.run(serviceLocator, originalMethod)`.
153
+ *
154
+ * The `AsyncLocalStorage` context propagates through the entire sync/async call tree of each
155
+ * wrapped method — including `super` calls, since the prototype methods execute within the
156
+ * context established by the instance-level wrapper.
157
+ *
158
+ * @internal
159
+ * @returns Scope control functions: `run` executes a callback within the scoped context,
160
+ * `enterScope`/`exitScope` allow entering/leaving the scope imperatively (e.g., for constructor bodies).
161
+ */
162
+ export declare function bindMethodsToServiceLocator(serviceLocator: ServiceLocator, target: {}): {
163
+ run: <T>(fn: () => T) => T;
164
+ enterScope: () => void;
165
+ exitScope: () => void;
166
+ };
167
+ export declare const serviceLocator: ServiceLocatorInterface;
168
+ export {};
@@ -0,0 +1,255 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { FileSystemStorageBackend } from '@crawlee/fs-storage';
3
+ import log from '@apify/log';
4
+ import { Configuration } from './configuration.js';
5
+ import { ServiceConflictError } from './errors.js';
6
+ import { LocalEventManager } from './events/local-event-manager.js';
7
+ import { ApifyLogAdapter } from './log.js';
8
+ import { MemoryStorageBackend } from './memory-storage/index.js';
9
+ import { StorageInstanceManager } from './storages/storage-instance-manager.js';
10
+ /**
11
+ * Service locator for managing the services used by Crawlee.
12
+ *
13
+ * All services are initialized to their default value lazily.
14
+ *
15
+ * There are two primary usage patterns:
16
+ *
17
+ * **1. Global service locator (for default services):**
18
+ * ```typescript
19
+ * import { serviceLocator, BasicCrawler } from 'crawlee';
20
+ *
21
+ * // Optionally configure global services before creating crawlers
22
+ * serviceLocator.setStorageBackend(myCustomClient);
23
+ *
24
+ * // Crawler uses global services
25
+ * const crawler = new BasicCrawler({ ... });
26
+ * ```
27
+ *
28
+ * **2. Per-crawler services (recommended for isolation):**
29
+ * ```typescript
30
+ * import { BasicCrawler, Configuration, LocalEventManager, MemoryStorageBackend } from 'crawlee';
31
+ *
32
+ * const crawler = new BasicCrawler({
33
+ * requestHandler: async ({ request }) => { ... },
34
+ * configuration: new Configuration({ ... }), // custom configuration
35
+ * storageBackend: new MemoryStorageBackend(), // custom storage
36
+ * eventManager: LocalEventManager.fromConfiguration(), // custom events
37
+ * });
38
+ * // Crawler has its own isolated ServiceLocator instance
39
+ * ```
40
+ */
41
+ export class ServiceLocator {
42
+ #configuration;
43
+ #eventManager;
44
+ #storageBackend;
45
+ #logger;
46
+ /**
47
+ * Unified storage instance manager for Dataset, KeyValueStore, and RequestQueue.
48
+ * Shared across all ServiceLocator instances (global singleton), matching crawlee-python.
49
+ * Per-crawler isolation is achieved via `clientCacheKey`, not separate manager instances.
50
+ */
51
+ static #storageInstanceManager;
52
+ /**
53
+ * Creates a new ServiceLocator instance.
54
+ *
55
+ * @param configuration Optional configuration instance to use
56
+ * @param eventManager Optional event manager instance to use
57
+ * @param storageBackend Optional storage backend instance to use
58
+ * @param logger Optional logger instance to use
59
+ */
60
+ constructor(configuration, eventManager, storageBackend, logger) {
61
+ this.#configuration = configuration;
62
+ this.#eventManager = eventManager;
63
+ this.#storageBackend = storageBackend;
64
+ this.#logger = logger;
65
+ }
66
+ /** @internal */
67
+ getServicesIfSet() {
68
+ return {
69
+ configuration: this.#configuration,
70
+ eventManager: this.#eventManager,
71
+ storageBackend: this.#storageBackend,
72
+ logger: this.#logger,
73
+ };
74
+ }
75
+ getConfiguration() {
76
+ if (!this.#configuration) {
77
+ this.getLogger().debug('No configuration set, implicitly creating and using default Configuration.');
78
+ this.#configuration = new Configuration();
79
+ }
80
+ return this.#configuration;
81
+ }
82
+ setConfiguration(configuration) {
83
+ // Same instance, no need to do anything
84
+ if (this.#configuration === configuration) {
85
+ return;
86
+ }
87
+ // Already have a different configuration that was retrieved
88
+ if (this.#configuration) {
89
+ throw new ServiceConflictError('Configuration', configuration, this.#configuration);
90
+ }
91
+ this.#configuration = configuration;
92
+ }
93
+ getEventManager() {
94
+ if (!this.#eventManager) {
95
+ this.getLogger().debug('No event manager set, implicitly creating and using default LocalEventManager.');
96
+ if (!this.#configuration) {
97
+ this.getLogger().warning('Implicit creation of event manager will implicitly set configuration as side effect. ' +
98
+ 'It is advised to explicitly first set the configuration instead.');
99
+ }
100
+ this.#eventManager = LocalEventManager.fromConfiguration(this.getConfiguration());
101
+ }
102
+ return this.#eventManager;
103
+ }
104
+ setEventManager(eventManager) {
105
+ // Same instance, no need to do anything
106
+ if (this.#eventManager === eventManager) {
107
+ return;
108
+ }
109
+ // Already have a different event manager that was retrieved
110
+ if (this.#eventManager) {
111
+ throw new ServiceConflictError('EventManager', eventManager, this.#eventManager);
112
+ }
113
+ this.#eventManager = eventManager;
114
+ }
115
+ getStorageBackend() {
116
+ if (!this.#storageBackend) {
117
+ this.getLogger().debug('No storage backend set, implicitly creating and using the default storage backend ' +
118
+ '(FileSystemStorageBackend when persistStorage is enabled, MemoryStorageBackend otherwise).');
119
+ if (!this.#configuration) {
120
+ this.getLogger().warning('Implicit creation of storage backend will implicitly set configuration as side effect. ' +
121
+ 'It is advised to explicitly first set the configuration instead.');
122
+ }
123
+ const configuration = this.getConfiguration();
124
+ this.#storageBackend = configuration.persistStorage
125
+ ? new FileSystemStorageBackend({
126
+ localDataDirectory: configuration.storageDir,
127
+ logger: this.getLogger().child({ prefix: 'FileSystemStorageBackend' }),
128
+ })
129
+ : new MemoryStorageBackend({
130
+ logger: this.getLogger().child({ prefix: 'MemoryStorageBackend' }),
131
+ });
132
+ }
133
+ return this.#storageBackend;
134
+ }
135
+ setStorageBackend(storageBackend) {
136
+ // Same instance, no need to do anything
137
+ if (this.#storageBackend === storageBackend) {
138
+ return;
139
+ }
140
+ // Already have a different storage backend that was retrieved
141
+ if (this.#storageBackend) {
142
+ throw new ServiceConflictError('StorageBackend', storageBackend, this.#storageBackend);
143
+ }
144
+ this.#storageBackend = storageBackend;
145
+ }
146
+ getLogger() {
147
+ if (!this.#logger) {
148
+ this.#logger = new ApifyLogAdapter(log);
149
+ }
150
+ return this.#logger;
151
+ }
152
+ setLogger(logger) {
153
+ if (this.#logger === logger) {
154
+ return;
155
+ }
156
+ if (this.#logger) {
157
+ throw new ServiceConflictError('Logger', logger, this.#logger);
158
+ }
159
+ this.#logger = logger;
160
+ }
161
+ getChildLog(prefix) {
162
+ return this.getLogger().child({ prefix });
163
+ }
164
+ /** @internal */
165
+ getStorageInstanceManager() {
166
+ if (!ServiceLocator.#storageInstanceManager) {
167
+ ServiceLocator.#storageInstanceManager = new StorageInstanceManager();
168
+ }
169
+ return ServiceLocator.#storageInstanceManager;
170
+ }
171
+ /** @internal */
172
+ reset() {
173
+ this.#configuration = undefined;
174
+ this.#eventManager = undefined;
175
+ this.#storageBackend = undefined;
176
+ this.#logger = undefined;
177
+ ServiceLocator.#storageInstanceManager?.clearCache();
178
+ ServiceLocator.#storageInstanceManager = undefined;
179
+ }
180
+ }
181
+ /**
182
+ * Used as the default service provider when crawlers don't specify custom services.
183
+ */
184
+ const globalServiceLocator = new ServiceLocator();
185
+ const serviceLocatorStorage = new AsyncLocalStorage();
186
+ /**
187
+ * Wraps all methods on `target` so that any code they invoke will see the given
188
+ * `serviceLocator` via `AsyncLocalStorage`, rather than the global one.
189
+ *
190
+ * Walks the prototype chain and replaces each method on the *instance* (not the prototype)
191
+ * with a wrapper that calls `serviceLocatorStorage.run(serviceLocator, originalMethod)`.
192
+ *
193
+ * The `AsyncLocalStorage` context propagates through the entire sync/async call tree of each
194
+ * wrapped method — including `super` calls, since the prototype methods execute within the
195
+ * context established by the instance-level wrapper.
196
+ *
197
+ * @internal
198
+ * @returns Scope control functions: `run` executes a callback within the scoped context,
199
+ * `enterScope`/`exitScope` allow entering/leaving the scope imperatively (e.g., for constructor bodies).
200
+ */
201
+ export function bindMethodsToServiceLocator(serviceLocator, target) {
202
+ let proto = Object.getPrototypeOf(target);
203
+ const seenKeys = new Set();
204
+ while (proto !== null && proto !== Object.prototype) {
205
+ const propertyKeys = [...Object.getOwnPropertyNames(proto), ...Object.getOwnPropertySymbols(proto)];
206
+ for (const propertyKey of propertyKeys) {
207
+ // The chain is walked derived-first, so the first occurrence of a key is the one dynamic
208
+ // dispatch would pick — a subclass override must not be clobbered by its base version.
209
+ if (seenKeys.has(propertyKey))
210
+ continue;
211
+ seenKeys.add(propertyKey);
212
+ const descriptor = Object.getOwnPropertyDescriptor(proto, propertyKey);
213
+ // We use property descriptors rather than accessing target[propertyKey] directly,
214
+ // because that would trigger getters and cause unwanted side effects.
215
+ // Skip getters, setters, and constructors — only wrap regular methods.
216
+ if (propertyKey === 'constructor' ||
217
+ !descriptor ||
218
+ descriptor.get ||
219
+ descriptor.set ||
220
+ typeof descriptor.value !== 'function')
221
+ continue;
222
+ const original = descriptor.value;
223
+ target[propertyKey] = (...args) => {
224
+ return serviceLocatorStorage.run(serviceLocator, () => {
225
+ return original.apply(target, args);
226
+ });
227
+ };
228
+ }
229
+ proto = Object.getPrototypeOf(proto);
230
+ }
231
+ let previousStore;
232
+ return {
233
+ run: (fn) => serviceLocatorStorage.run(serviceLocator, fn),
234
+ enterScope: () => {
235
+ previousStore = serviceLocatorStorage.getStore();
236
+ serviceLocatorStorage.enterWith(serviceLocator);
237
+ },
238
+ exitScope: () => {
239
+ serviceLocatorStorage.enterWith(previousStore); // casting to any so that `undefined` is accepted - this "unsets" the AsyncLocalStorage
240
+ },
241
+ };
242
+ }
243
+ export const serviceLocator = new Proxy({}, {
244
+ get(_target, prop) {
245
+ const active = serviceLocatorStorage.getStore() ?? globalServiceLocator;
246
+ const value = Reflect.get(active, prop, active);
247
+ if (typeof value === 'function') {
248
+ return value.bind(active);
249
+ }
250
+ return value;
251
+ },
252
+ set(_target, prop) {
253
+ throw new TypeError(`Cannot set property '${String(prop)}' on serviceLocator directly. Use the setter methods (e.g. setConfiguration(), setStorageBackend()) instead.`);
254
+ },
255
+ });
@@ -0,0 +1,37 @@
1
+ import type { ProcessedRequest } from '@crawlee/types';
2
+ import type { Source } from '../request.js';
3
+ import type { AddRequestsBatchedResult } from './request-queue.js';
4
+ export interface DrainRequestBatchesOptions<TItem extends Source> {
5
+ /**
6
+ * The requests to add, already normalized by the caller. Consumed lazily: an unbounded or expensive
7
+ * iterable is only pulled from as far as the batching (and any `maxNewRequests` budget) requires.
8
+ */
9
+ items: AsyncGenerator<TItem>;
10
+ batchSize: number;
11
+ waitBetweenBatchesMillis: number;
12
+ waitForAllRequestsToBeAdded: boolean;
13
+ maxNewRequests?: number;
14
+ /**
15
+ * Adds a single chunk and reports what it processed.
16
+ *
17
+ * @param isInitial Whether this is the first chunk, which is added before this function returns. Later
18
+ * chunks land in the background, which is why some callers cache only the first.
19
+ */
20
+ processChunk: (chunk: TItem[], isInitial: boolean) => Promise<ProcessedRequest[]>;
21
+ /**
22
+ * Called with the promise covering every chunk after the first, so the caller can keep its own
23
+ * `isFinished` honest while batches are still landing.
24
+ */
25
+ trackBackgroundBatches?: (batches: Promise<unknown>) => void;
26
+ }
27
+ /**
28
+ * Drives the chunk-by-chunk half of `addRequestsBatched`: the first chunk is added before returning and the
29
+ * rest continue in the background, paced by `waitBetweenBatchesMillis`.
30
+ *
31
+ * Callers differ only in how a chunk is added and how the input is normalized, so that is all
32
+ * {@link DrainRequestBatchesOptions} asks for - the budget arithmetic, the lazy chunking, the
33
+ * over-limit reporting and the transaction handling are identical for everyone and live here. In
34
+ * particular, every caller has to keep its background chunks out of a transaction they will outlive,
35
+ * so that is read from the ambient transaction rather than asked of the caller.
36
+ */
37
+ export declare function drainRequestBatches<TItem extends Source>(options: DrainRequestBatchesOptions<TItem>): Promise<AddRequestsBatchedResult>;
@@ -0,0 +1,73 @@
1
+ import { setTimeout as sleep } from 'node:timers/promises';
2
+ import { chunkedAsyncIterable, peekableAsyncIterable } from '../iterables.js';
3
+ import { activeStorageTransaction, withDirectStorageAccess } from './transaction.js';
4
+ /**
5
+ * Drives the chunk-by-chunk half of `addRequestsBatched`: the first chunk is added before returning and the
6
+ * rest continue in the background, paced by `waitBetweenBatchesMillis`.
7
+ *
8
+ * Callers differ only in how a chunk is added and how the input is normalized, so that is all
9
+ * {@link DrainRequestBatchesOptions} asks for - the budget arithmetic, the lazy chunking, the
10
+ * over-limit reporting and the transaction handling are identical for everyone and live here. In
11
+ * particular, every caller has to keep its background chunks out of a transaction they will outlive,
12
+ * so that is read from the ambient transaction rather than asked of the caller.
13
+ */
14
+ export async function drainRequestBatches(options) {
15
+ const { items, batchSize, waitBetweenBatchesMillis, waitForAllRequestsToBeAdded, maxNewRequests, processChunk, trackBackgroundBatches, } = options;
16
+ const deferred = activeStorageTransaction()?.policy.requestQueue === 'deferred';
17
+ let remainingBudget = maxNewRequests ?? Infinity;
18
+ const requestsOverLimit = [];
19
+ // Never hand a chunk more than the budget allows, so an over-large final batch cannot overshoot.
20
+ const effectiveChunkSize = maxNewRequests !== undefined ? () => Math.min(batchSize, remainingBudget) : batchSize;
21
+ const chunks = peekableAsyncIterable(chunkedAsyncIterable(items, effectiveChunkSize));
22
+ const chunksIterator = chunks[Symbol.asyncIterator]();
23
+ const addChunk = async (chunk, isInitial) => {
24
+ const processedRequests = await processChunk(chunk, isInitial);
25
+ if (maxNewRequests !== undefined) {
26
+ remainingBudget -= processedRequests.filter((request) => !request.wasAlreadyPresent).length;
27
+ }
28
+ return processedRequests;
29
+ };
30
+ const buildResult = async (addedRequests, waitForAll) => {
31
+ if (maxNewRequests !== undefined) {
32
+ // `chunkedAsyncIterable` stops pulling once the budget-derived chunk size hits zero, so whatever
33
+ // is left is still sitting in `items` rather than in a chunk we have seen.
34
+ for await (const item of items) {
35
+ requestsOverLimit.push(item);
36
+ }
37
+ }
38
+ return { addedRequests, waitForAllRequestsToBeAdded: waitForAll, requestsOverLimit };
39
+ };
40
+ const initialChunk = await chunksIterator.peek();
41
+ if (initialChunk === undefined) {
42
+ return buildResult([], Promise.resolve([]));
43
+ }
44
+ const addedRequests = await addChunk(initialChunk, true);
45
+ await chunksIterator.next();
46
+ if ((await chunksIterator.peek()) === undefined) {
47
+ return buildResult(addedRequests, Promise.resolve([]));
48
+ }
49
+ const processRemainingChunks = async () => {
50
+ const added = [];
51
+ for await (const chunk of chunks) {
52
+ added.push(...(await addChunk(chunk, false)));
53
+ // Under `deferred` no chunk performs backend I/O, so pacing them would only stall the handler.
54
+ await sleep(deferred ? 0 : waitBetweenBatchesMillis);
55
+ }
56
+ return added;
57
+ };
58
+ // With a budget we must drain everything before we can report what went over it; under `deferred` a
59
+ // writer that finishes after commit would have nowhere to put its journal entries.
60
+ const awaitsRemainder = waitForAllRequestsToBeAdded || maxNewRequests !== undefined || deferred;
61
+ // An un-awaited writer outlives the transaction scope it inherits, so it must not record into a
62
+ // transaction that may already be closed. It writes directly - its write-through additions were never
63
+ // going to be rolled back anyway - which means the requests it adds are not journaled.
64
+ // See `StorageTransactionView.enqueuedUrls`.
65
+ const remainder = awaitsRemainder ? processRemainingChunks() : withDirectStorageAccess(processRemainingChunks);
66
+ // The caller is not obliged to await `remainder`, so give it a handler of its own - an unhandled
67
+ // rejection here would otherwise take the process down.
68
+ trackBackgroundBatches?.(remainder.catch(() => { }));
69
+ if (awaitsRemainder) {
70
+ addedRequests.push(...(await remainder));
71
+ }
72
+ return buildResult(addedRequests, remainder);
73
+ }