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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/README.md +17 -13
  2. package/autoscaling/autoscaled_pool.d.ts +67 -172
  3. package/autoscaling/autoscaled_pool.js +165 -320
  4. package/autoscaling/client_load_signal.d.ts +55 -0
  5. package/autoscaling/client_load_signal.js +73 -0
  6. package/autoscaling/concurrency_system.d.ts +268 -0
  7. package/autoscaling/concurrency_system.js +351 -0
  8. package/autoscaling/cpu_load_signal.d.ts +43 -0
  9. package/autoscaling/cpu_load_signal.js +46 -0
  10. package/autoscaling/event_loop_load_signal.d.ts +51 -0
  11. package/autoscaling/event_loop_load_signal.js +60 -0
  12. package/autoscaling/index.d.ts +6 -1
  13. package/autoscaling/index.js +6 -1
  14. package/autoscaling/load_signal.d.ts +99 -0
  15. package/autoscaling/load_signal.js +104 -0
  16. package/autoscaling/memory_load_signal.d.ts +47 -0
  17. package/autoscaling/memory_load_signal.js +105 -0
  18. package/autoscaling/snapshotter.d.ts +58 -163
  19. package/autoscaling/snapshotter.js +45 -263
  20. package/autoscaling/system_status.d.ts +62 -84
  21. package/autoscaling/system_status.js +92 -122
  22. package/autoscaling/weighted_avg.d.ts +5 -0
  23. package/autoscaling/weighted_avg.js +14 -0
  24. package/byte_utils.d.ts +17 -0
  25. package/byte_utils.js +42 -0
  26. package/configuration.d.ts +96 -223
  27. package/configuration.js +170 -222
  28. package/cookie_utils.d.ts +4 -3
  29. package/cookie_utils.js +22 -13
  30. package/crawlers/context_pipeline.d.ts +10 -1
  31. package/crawlers/context_pipeline.js +31 -8
  32. package/crawlers/crawler_commons.d.ts +90 -83
  33. package/crawlers/crawler_commons.js +1 -116
  34. package/crawlers/error_snapshotter.d.ts +1 -4
  35. package/crawlers/error_snapshotter.js +3 -6
  36. package/crawlers/error_tracker.d.ts +0 -1
  37. package/crawlers/error_tracker.js +0 -1
  38. package/crawlers/index.d.ts +1 -3
  39. package/crawlers/index.js +0 -3
  40. package/crawlers/internals/types.d.ts +0 -1
  41. package/crawlers/internals/types.js +0 -1
  42. package/crawlers/statistics.d.ts +91 -41
  43. package/crawlers/statistics.js +83 -72
  44. package/debug.d.ts +36 -0
  45. package/debug.js +70 -0
  46. package/enqueue_links/enqueue_links.d.ts +59 -68
  47. package/enqueue_links/enqueue_links.js +57 -62
  48. package/enqueue_links/index.d.ts +0 -1
  49. package/enqueue_links/index.js +0 -1
  50. package/enqueue_links/shared.d.ts +40 -27
  51. package/enqueue_links/shared.js +90 -68
  52. package/errors.d.ts +41 -4
  53. package/errors.js +50 -5
  54. package/events/event_manager.d.ts +34 -8
  55. package/events/event_manager.js +8 -10
  56. package/events/index.d.ts +0 -1
  57. package/events/index.js +0 -1
  58. package/events/local_event_manager.d.ts +15 -3
  59. package/events/local_event_manager.js +37 -11
  60. package/index.d.ts +6 -4
  61. package/index.js +5 -3
  62. package/iterables.d.ts +79 -0
  63. package/iterables.js +134 -0
  64. package/log.d.ts +82 -3
  65. package/log.js +106 -1
  66. package/memory-storage/consts.d.ts +4 -0
  67. package/memory-storage/consts.js +4 -0
  68. package/memory-storage/index.d.ts +1 -0
  69. package/memory-storage/index.js +1 -0
  70. package/memory-storage/memory-storage.d.ts +42 -0
  71. package/memory-storage/memory-storage.js +136 -0
  72. package/memory-storage/resource-clients/common/base-client.d.ts +4 -0
  73. package/memory-storage/resource-clients/common/base-client.js +6 -0
  74. package/memory-storage/resource-clients/dataset.d.ts +40 -0
  75. package/memory-storage/resource-clients/dataset.js +114 -0
  76. package/memory-storage/resource-clients/key-value-store.d.ts +63 -0
  77. package/memory-storage/resource-clients/key-value-store.js +204 -0
  78. package/memory-storage/resource-clients/request-queue.d.ts +77 -0
  79. package/memory-storage/resource-clients/request-queue.js +422 -0
  80. package/memory-storage/utils.d.ts +16 -0
  81. package/memory-storage/utils.js +41 -0
  82. package/owned_or_injected.d.ts +58 -0
  83. package/owned_or_injected.js +98 -0
  84. package/package.json +13 -12
  85. package/proxy_configuration.d.ts +27 -152
  86. package/proxy_configuration.js +27 -179
  87. package/recoverable_state.d.ts +111 -0
  88. package/recoverable_state.js +143 -0
  89. package/request.d.ts +86 -17
  90. package/request.js +117 -41
  91. package/router.d.ts +193 -21
  92. package/router.js +188 -43
  93. package/serialization.d.ts +0 -1
  94. package/serialization.js +9 -11
  95. package/service_locator.d.ts +147 -0
  96. package/service_locator.js +244 -0
  97. package/session_pool/consts.d.ts +1 -2
  98. package/session_pool/consts.js +1 -2
  99. package/session_pool/errors.d.ts +0 -1
  100. package/session_pool/errors.js +0 -1
  101. package/session_pool/fingerprint.d.ts +9 -0
  102. package/session_pool/fingerprint.js +30 -0
  103. package/session_pool/index.d.ts +0 -2
  104. package/session_pool/index.js +0 -2
  105. package/session_pool/session.d.ts +37 -86
  106. package/session_pool/session.js +85 -138
  107. package/session_pool/session_pool.d.ts +77 -91
  108. package/session_pool/session_pool.js +159 -147
  109. package/storages/dataset.d.ts +114 -54
  110. package/storages/dataset.js +285 -144
  111. package/storages/index.d.ts +9 -8
  112. package/storages/index.js +7 -8
  113. package/storages/key_value_store.d.ts +185 -42
  114. package/storages/key_value_store.js +424 -151
  115. package/storages/key_value_store_codec.d.ts +32 -0
  116. package/storages/key_value_store_codec.js +113 -0
  117. package/storages/request_dedup_cache.d.ts +22 -0
  118. package/storages/request_dedup_cache.js +48 -0
  119. package/storages/request_list.d.ts +52 -116
  120. package/storages/request_list.js +158 -133
  121. package/storages/request_loader.d.ts +96 -0
  122. package/storages/request_loader.js +1 -0
  123. package/storages/request_manager.d.ts +33 -0
  124. package/storages/request_manager.js +1 -0
  125. package/storages/request_manager_tandem.d.ts +97 -0
  126. package/storages/request_manager_tandem.js +197 -0
  127. package/storages/request_queue.d.ts +290 -47
  128. package/storages/request_queue.js +825 -216
  129. package/storages/{sitemap_request_list.d.ts → sitemap_request_loader.d.ts} +37 -88
  130. package/storages/{sitemap_request_list.js → sitemap_request_loader.js} +136 -143
  131. package/storages/storage_instance_manager.d.ts +87 -0
  132. package/storages/storage_instance_manager.js +256 -0
  133. package/storages/storage_stats.d.ts +48 -0
  134. package/storages/storage_stats.js +29 -0
  135. package/storages/transaction.d.ts +252 -0
  136. package/storages/transaction.js +251 -0
  137. package/storages/utils.d.ts +54 -9
  138. package/storages/utils.js +64 -13
  139. package/system-info/cpu-info.d.ts +67 -0
  140. package/system-info/cpu-info.js +216 -0
  141. package/system-info/memory-info.d.ts +31 -0
  142. package/system-info/memory-info.js +115 -0
  143. package/system-info/ps-tree.d.ts +17 -0
  144. package/system-info/ps-tree.js +144 -0
  145. package/system-info/runtime.d.ts +14 -0
  146. package/system-info/runtime.js +80 -0
  147. package/typedefs.d.ts +0 -6
  148. package/typedefs.js +0 -1
  149. package/validators.d.ts +8 -1
  150. package/validators.js +10 -3
  151. package/autoscaling/autoscaled_pool.d.ts.map +0 -1
  152. package/autoscaling/autoscaled_pool.js.map +0 -1
  153. package/autoscaling/index.d.ts.map +0 -1
  154. package/autoscaling/index.js.map +0 -1
  155. package/autoscaling/snapshotter.d.ts.map +0 -1
  156. package/autoscaling/snapshotter.js.map +0 -1
  157. package/autoscaling/system_status.d.ts.map +0 -1
  158. package/autoscaling/system_status.js.map +0 -1
  159. package/configuration.d.ts.map +0 -1
  160. package/configuration.js.map +0 -1
  161. package/cookie_utils.d.ts.map +0 -1
  162. package/cookie_utils.js.map +0 -1
  163. package/crawlers/context_pipeline.d.ts.map +0 -1
  164. package/crawlers/context_pipeline.js.map +0 -1
  165. package/crawlers/crawler_commons.d.ts.map +0 -1
  166. package/crawlers/crawler_commons.js.map +0 -1
  167. package/crawlers/crawler_utils.d.ts +0 -10
  168. package/crawlers/crawler_utils.d.ts.map +0 -1
  169. package/crawlers/crawler_utils.js +0 -12
  170. package/crawlers/crawler_utils.js.map +0 -1
  171. package/crawlers/error_snapshotter.d.ts.map +0 -1
  172. package/crawlers/error_snapshotter.js.map +0 -1
  173. package/crawlers/error_tracker.d.ts.map +0 -1
  174. package/crawlers/error_tracker.js.map +0 -1
  175. package/crawlers/index.d.ts.map +0 -1
  176. package/crawlers/index.js.map +0 -1
  177. package/crawlers/internals/types.d.ts.map +0 -1
  178. package/crawlers/internals/types.js.map +0 -1
  179. package/crawlers/statistics.d.ts.map +0 -1
  180. package/crawlers/statistics.js.map +0 -1
  181. package/enqueue_links/enqueue_links.d.ts.map +0 -1
  182. package/enqueue_links/enqueue_links.js.map +0 -1
  183. package/enqueue_links/index.d.ts.map +0 -1
  184. package/enqueue_links/index.js.map +0 -1
  185. package/enqueue_links/shared.d.ts.map +0 -1
  186. package/enqueue_links/shared.js.map +0 -1
  187. package/errors.d.ts.map +0 -1
  188. package/errors.js.map +0 -1
  189. package/events/event_manager.d.ts.map +0 -1
  190. package/events/event_manager.js.map +0 -1
  191. package/events/index.d.ts.map +0 -1
  192. package/events/index.js.map +0 -1
  193. package/events/local_event_manager.d.ts.map +0 -1
  194. package/events/local_event_manager.js.map +0 -1
  195. package/http_clients/base-http-client.d.ts +0 -134
  196. package/http_clients/base-http-client.d.ts.map +0 -1
  197. package/http_clients/base-http-client.js +0 -33
  198. package/http_clients/base-http-client.js.map +0 -1
  199. package/http_clients/form-data-like.d.ts +0 -67
  200. package/http_clients/form-data-like.d.ts.map +0 -1
  201. package/http_clients/form-data-like.js +0 -5
  202. package/http_clients/form-data-like.js.map +0 -1
  203. package/http_clients/got-scraping-http-client.d.ts +0 -15
  204. package/http_clients/got-scraping-http-client.d.ts.map +0 -1
  205. package/http_clients/got-scraping-http-client.js +0 -69
  206. package/http_clients/got-scraping-http-client.js.map +0 -1
  207. package/http_clients/index.d.ts +0 -3
  208. package/http_clients/index.d.ts.map +0 -1
  209. package/http_clients/index.js +0 -3
  210. package/http_clients/index.js.map +0 -1
  211. package/index.d.ts.map +0 -1
  212. package/index.js.map +0 -1
  213. package/log.d.ts.map +0 -1
  214. package/log.js.map +0 -1
  215. package/proxy_configuration.d.ts.map +0 -1
  216. package/proxy_configuration.js.map +0 -1
  217. package/request.d.ts.map +0 -1
  218. package/request.js.map +0 -1
  219. package/router.d.ts.map +0 -1
  220. package/router.js.map +0 -1
  221. package/serialization.d.ts.map +0 -1
  222. package/serialization.js.map +0 -1
  223. package/session_pool/consts.d.ts.map +0 -1
  224. package/session_pool/consts.js.map +0 -1
  225. package/session_pool/errors.d.ts.map +0 -1
  226. package/session_pool/errors.js.map +0 -1
  227. package/session_pool/events.d.ts +0 -3
  228. package/session_pool/events.d.ts.map +0 -1
  229. package/session_pool/events.js +0 -3
  230. package/session_pool/events.js.map +0 -1
  231. package/session_pool/index.d.ts.map +0 -1
  232. package/session_pool/index.js.map +0 -1
  233. package/session_pool/session.d.ts.map +0 -1
  234. package/session_pool/session.js.map +0 -1
  235. package/session_pool/session_pool.d.ts.map +0 -1
  236. package/session_pool/session_pool.js.map +0 -1
  237. package/storages/access_checking.d.ts +0 -13
  238. package/storages/access_checking.d.ts.map +0 -1
  239. package/storages/access_checking.js +0 -14
  240. package/storages/access_checking.js.map +0 -1
  241. package/storages/dataset.d.ts.map +0 -1
  242. package/storages/dataset.js.map +0 -1
  243. package/storages/index.d.ts.map +0 -1
  244. package/storages/index.js.map +0 -1
  245. package/storages/key_value_store.d.ts.map +0 -1
  246. package/storages/key_value_store.js.map +0 -1
  247. package/storages/request_list.d.ts.map +0 -1
  248. package/storages/request_list.js.map +0 -1
  249. package/storages/request_provider.d.ts +0 -308
  250. package/storages/request_provider.d.ts.map +0 -1
  251. package/storages/request_provider.js +0 -555
  252. package/storages/request_provider.js.map +0 -1
  253. package/storages/request_queue.d.ts.map +0 -1
  254. package/storages/request_queue.js.map +0 -1
  255. package/storages/request_queue_v2.d.ts +0 -87
  256. package/storages/request_queue_v2.d.ts.map +0 -1
  257. package/storages/request_queue_v2.js +0 -438
  258. package/storages/request_queue_v2.js.map +0 -1
  259. package/storages/sitemap_request_list.d.ts.map +0 -1
  260. package/storages/sitemap_request_list.js.map +0 -1
  261. package/storages/storage_manager.d.ts +0 -58
  262. package/storages/storage_manager.d.ts.map +0 -1
  263. package/storages/storage_manager.js +0 -105
  264. package/storages/storage_manager.js.map +0 -1
  265. package/storages/utils.d.ts.map +0 -1
  266. package/storages/utils.js.map +0 -1
  267. package/tsconfig.build.tsbuildinfo +0 -1
  268. package/typedefs.d.ts.map +0 -1
  269. package/typedefs.js.map +0 -1
  270. package/validators.d.ts.map +0 -1
  271. package/validators.js.map +0 -1
@@ -1,31 +1,19 @@
1
- import { EventEmitter } from 'node:events';
2
1
  import { AsyncQueue } from '@sapphire/async-queue';
3
2
  import ow from 'ow';
4
- import { Configuration } from '../configuration.js';
5
- import { log as defaultLog } from '../log.js';
3
+ import { serviceLocator } from '../service_locator.js';
6
4
  import { KeyValueStore } from '../storages/key_value_store.js';
7
- import { BLOCKED_STATUS_CODES, MAX_POOL_SIZE, PERSIST_STATE_KEY } from './consts.js';
5
+ import { MAX_POOL_SIZE, PERSIST_STATE_KEY } from './consts.js';
6
+ import { createDefaultSessionFingerprint } from './fingerprint.js';
8
7
  import { Session } from './session.js';
8
+ const SESSION_REUSE_STRATEGIES = ['random', 'round-robin', 'use-until-failure'];
9
9
  /**
10
10
  * Handles the rotation, creation and persistence of user-like sessions.
11
11
  * Creates a pool of {@link Session} instances, that are randomly rotated.
12
12
  * When some session is marked as blocked, it is removed and new one is created instead (the pool never returns an unusable session).
13
13
  * Learn more in the {@doclink guides/session-management | Session management guide}.
14
14
  *
15
- * You can create one by calling the {@link SessionPool.open} function.
16
- *
17
- * Session pool is already integrated into crawlers, and it can significantly improve your scraper
18
- * performance with just 2 lines of code.
19
- *
20
- * **Example usage:**
21
- *
22
- * ```javascript
23
- * const crawler = new CheerioCrawler({
24
- * useSessionPool: true,
25
- * persistCookiesPerSession: true,
26
- * // ...
27
- * })
28
- * ```
15
+ * Session pool is already integrated into crawlers and is always active.
16
+ * All public methods are lazy-initialized — the pool initializes itself on first use.
29
17
  *
30
18
  * You can configure the pool with many options. See the {@link SessionPoolOptions}.
31
19
  * Session pool is by default persisted in default {@link KeyValueStore}.
@@ -35,7 +23,7 @@ import { Session } from './session.js';
35
23
  * **Advanced usage:**
36
24
  *
37
25
  * ```javascript
38
- * const sessionPool = await SessionPool.open({
26
+ * const sessionPool = new SessionPool({
39
27
  * maxPoolSize: 25,
40
28
  * sessionOptions:{
41
29
  * maxAgeSecs: 10,
@@ -71,94 +59,98 @@ import { Session } from './session.js';
71
59
  *
72
60
  * @category Scaling
73
61
  */
74
- export class SessionPool extends EventEmitter {
75
- config;
76
- log;
62
+ export class SessionPool {
63
+ static #nextId = 0;
64
+ id;
65
+ #log;
66
+ #sessions = [];
67
+ // kept as TS-private: session_pool tests read/override the members below directly
77
68
  maxPoolSize;
78
69
  createSessionFunction;
79
70
  keyValueStore;
80
- sessions = [];
81
71
  sessionMap = new Map();
82
72
  sessionOptions;
83
73
  persistStateKeyValueStoreId;
84
74
  persistStateKey;
85
- _listener;
86
- events;
87
- blockedStatusCodes;
88
- persistenceOptions;
89
- isInitialized = false;
90
- queue = new AsyncQueue();
91
- /**
92
- * @internal
93
- */
94
- constructor(options = {}, config = Configuration.getGlobalConfig()) {
95
- super();
96
- this.config = config;
75
+ #listener;
76
+ #events;
77
+ #persistenceOptions;
78
+ #sessionReuseStrategy;
79
+ #initPromise;
80
+ #queue = new AsyncQueue();
81
+ #roundRobinIndex = 0;
82
+ constructor(options = {}) {
97
83
  ow(options, ow.object.exactShape({
84
+ id: ow.optional.any(ow.number, ow.string),
98
85
  maxPoolSize: ow.optional.number,
99
86
  persistStateKeyValueStoreId: ow.optional.string,
100
87
  persistStateKey: ow.optional.string,
101
88
  createSessionFunction: ow.optional.function,
102
89
  sessionOptions: ow.optional.object,
103
- blockedStatusCodes: ow.optional.array.ofType(ow.number),
104
90
  log: ow.optional.object,
105
91
  persistenceOptions: ow.optional.object,
92
+ sessionReuseStrategy: ow.optional.string.oneOf([...SESSION_REUSE_STRATEGIES]),
106
93
  }));
107
- const { maxPoolSize = MAX_POOL_SIZE, persistStateKeyValueStoreId, persistStateKey = PERSIST_STATE_KEY, createSessionFunction, sessionOptions = {}, blockedStatusCodes = BLOCKED_STATUS_CODES, log = defaultLog, persistenceOptions = {
94
+ const { id, maxPoolSize = MAX_POOL_SIZE, persistStateKeyValueStoreId, persistStateKey, createSessionFunction, sessionOptions = {}, log = serviceLocator.getLogger(), persistenceOptions = {
108
95
  enable: true,
109
- }, } = options;
110
- this.config = config;
111
- this.blockedStatusCodes = blockedStatusCodes;
112
- this.events = config.getEventManager();
113
- this.log = log.child({ prefix: 'SessionPool' });
114
- this.persistenceOptions = persistenceOptions;
96
+ }, sessionReuseStrategy = 'random', } = options;
97
+ this.id = id != null ? String(id) : String(SessionPool.#nextId++);
98
+ this.#sessionReuseStrategy = sessionReuseStrategy;
99
+ this.#events = serviceLocator.getEventManager();
100
+ this.#log = log.child({ prefix: 'SessionPool' });
101
+ this.#persistenceOptions = persistenceOptions;
115
102
  // Pool Configuration
116
103
  this.maxPoolSize = maxPoolSize;
117
- this.createSessionFunction = createSessionFunction || this._defaultCreateSessionFunction;
118
- // Session configuration
104
+ this.createSessionFunction = createSessionFunction || this.defaultCreateSessionFunction;
105
+ // Session configuration. The pool-scoped logger is merged into per-call sessionOptions inside
106
+ // `invokeCreateSessionFunction`, so every Session inherits it without custom createSessionFunctions
107
+ // having to know about it.
119
108
  this.sessionOptions = {
120
109
  ...sessionOptions,
121
- // the log needs to propagate to createSessionFunction as in "new Session({ ...sessionPool.sessionOptions })"
122
- // and can't go inside _defaultCreateSessionFunction
123
- log: this.log,
110
+ log: this.#log,
124
111
  };
125
112
  // Session keyValueStore
126
113
  this.persistStateKeyValueStoreId = persistStateKeyValueStoreId;
127
- this.persistStateKey = persistStateKey;
114
+ this.persistStateKey = persistStateKey ?? `${PERSIST_STATE_KEY}_${this.id}`;
128
115
  }
129
116
  /**
130
117
  * Gets count of usable sessions in the pool.
131
118
  */
132
- get usableSessionsCount() {
133
- return this.sessions.filter((session) => session.isUsable()).length;
119
+ async usableSessionsCount() {
120
+ await this.ensureInitialized();
121
+ return this.#sessions.filter((session) => session.isUsable()).length;
134
122
  }
135
123
  /**
136
124
  * Gets count of retired sessions in the pool.
137
125
  */
138
- get retiredSessionsCount() {
139
- return this.sessions.filter((session) => !session.isUsable()).length;
126
+ async retiredSessionsCount() {
127
+ await this.ensureInitialized();
128
+ return this.#sessions.filter((session) => !session.isUsable()).length;
140
129
  }
141
130
  /**
142
131
  * Starts periodic state persistence and potentially loads SessionPool state from {@link KeyValueStore}.
143
- * It is called automatically by the {@link SessionPool.open} function.
132
+ * Called automatically on first use of any public method.
144
133
  */
145
- async initialize() {
146
- if (this.isInitialized) {
147
- return;
134
+ async ensureInitialized() {
135
+ if (!this.#initPromise) {
136
+ this.#initPromise = this.setupPool();
148
137
  }
149
- this.keyValueStore = await KeyValueStore.open(this.persistStateKeyValueStoreId, { config: this.config });
150
- if (!this.persistenceOptions.enable) {
151
- this.isInitialized = true;
138
+ return this.#initPromise;
139
+ }
140
+ async setupPool() {
141
+ if (!this.#persistenceOptions.enable) {
152
142
  return;
153
143
  }
144
+ this.keyValueStore = await KeyValueStore.open(this.persistStateKeyValueStoreId ? { id: this.persistStateKeyValueStoreId } : null, {
145
+ configuration: serviceLocator.getConfiguration(),
146
+ });
154
147
  if (!this.persistStateKeyValueStoreId) {
155
- this.log.debug(`No 'persistStateKeyValueStoreId' options specified, this session pool's data has been saved in the KeyValueStore with the id: ${this.keyValueStore.id}`);
148
+ this.#log.debug(`No 'persistStateKeyValueStoreId' options specified, this session pool's data has been saved in the KeyValueStore with the id: ${this.keyValueStore.id}`);
156
149
  }
157
150
  // in case of migration happened and SessionPool state should be restored from the keyValueStore.
158
- await this._maybeLoadSessionPool();
159
- this._listener = this.persistState.bind(this);
160
- this.events.on("persistState" /* EventType.PERSIST_STATE */, this._listener);
161
- this.isInitialized = true;
151
+ await this.maybeLoadSessionPool();
152
+ this.#listener = this.persistState.bind(this);
153
+ this.#events.on("persistState" /* EventType.PERSIST_STATE */, this.#listener);
162
154
  }
163
155
  /**
164
156
  * Adds a new session to the session pool. The pool automatically creates sessions up to the maximum size of the pool,
@@ -167,7 +159,7 @@ export class SessionPool extends EventEmitter {
167
159
  * @param [options] The configuration options for the session being added to the session pool.
168
160
  */
169
161
  async addSession(options = {}) {
170
- this._throwIfNotInitialized();
162
+ await this.ensureInitialized();
171
163
  const { id } = options;
172
164
  if (id) {
173
165
  const sessionExists = this.sessionMap.has(id);
@@ -175,12 +167,24 @@ export class SessionPool extends EventEmitter {
175
167
  throw new Error(`Cannot add session with id '${id}' as it already exists in the pool`);
176
168
  }
177
169
  }
178
- if (!this._hasSpaceForSession()) {
179
- this._removeRetiredSessions();
170
+ if (!this.hasSpaceForSession()) {
171
+ this.removeRetiredSessions();
180
172
  }
181
- const newSession = options instanceof Session ? options : await this.createSessionFunction(this, { sessionOptions: options });
182
- this.log.debug(`Adding new Session - ${newSession.id}`);
183
- this._addSession(newSession);
173
+ const newSession = options instanceof Session ? options : await this.invokeCreateSessionFunction(options);
174
+ this.#log.debug(`Adding new Session - ${newSession.id}`);
175
+ this.registerSession(newSession);
176
+ }
177
+ /**
178
+ * Adds a new session to the session pool. The pool automatically creates sessions up to the maximum size of the pool,
179
+ * but this allows you to add more sessions once the max pool size is reached.
180
+ * This also allows you to add session with overridden session options (e.g. with specific session id).
181
+ * @param [options] The configuration options for the session being added to the session pool.
182
+ */
183
+ async newSession(sessionOptions) {
184
+ await this.ensureInitialized();
185
+ const newSession = await this.invokeCreateSessionFunction(sessionOptions);
186
+ this.registerSession(newSession);
187
+ return newSession;
184
188
  }
185
189
  /**
186
190
  * Gets session.
@@ -190,47 +194,48 @@ export class SessionPool extends EventEmitter {
190
194
  * @param [sessionId] If provided, it returns the usable session with this id, `undefined` otherwise.
191
195
  */
192
196
  async getSession(sessionId) {
193
- await this.queue.wait();
197
+ await this.ensureInitialized();
198
+ await this.#queue.wait();
194
199
  try {
195
- this._throwIfNotInitialized();
196
200
  if (sessionId) {
197
201
  const session = this.sessionMap.get(sessionId);
198
- if (session && session.isUsable())
202
+ if (session?.isUsable())
199
203
  return session;
200
204
  return undefined;
201
205
  }
202
- if (this._hasSpaceForSession()) {
203
- return await this._createSession();
204
- }
205
- const pickedSession = this._pickSession();
206
- if (pickedSession.isUsable()) {
206
+ const pickedSession = this.pickSession();
207
+ if (pickedSession)
207
208
  return pickedSession;
209
+ if (this.hasSpaceForSession()) {
210
+ return await this.createSession();
208
211
  }
209
- this._removeRetiredSessions();
210
- return await this._createSession();
212
+ this.removeRetiredSessions();
213
+ return await this.createSession();
211
214
  }
212
215
  finally {
213
- this.queue.shift();
216
+ this.#queue.shift();
214
217
  }
215
218
  }
216
219
  /**
217
220
  * @param options - Override the persistence options provided in the constructor
218
221
  */
219
222
  async resetStore(options) {
220
- if (!this.persistenceOptions.enable && !options?.enable) {
223
+ if (!this.#persistenceOptions.enable && !options?.enable) {
221
224
  return;
222
225
  }
226
+ await this.ensureInitialized();
223
227
  await this.keyValueStore?.setValue(this.persistStateKey, null);
224
228
  }
225
229
  /**
226
230
  * Returns an object representing the internal state of the `SessionPool` instance.
227
231
  * Note that the object's fields can change in future releases.
228
232
  */
229
- getState() {
233
+ async getState() {
234
+ await this.ensureInitialized();
230
235
  return {
231
- usableSessionsCount: this.usableSessionsCount,
232
- retiredSessionsCount: this.retiredSessionsCount,
233
- sessions: this.sessions.map((session) => session.getState()),
236
+ usableSessionsCount: await this.usableSessionsCount(),
237
+ retiredSessionsCount: await this.retiredSessionsCount(),
238
+ sessions: this.#sessions.map((session) => session.getState()),
234
239
  };
235
240
  }
236
241
  /**
@@ -239,47 +244,40 @@ export class SessionPool extends EventEmitter {
239
244
  * @param options - Override the persistence options provided in the constructor
240
245
  */
241
246
  async persistState(options) {
242
- if (!this.persistenceOptions.enable && !options?.enable) {
247
+ if (!this.#persistenceOptions.enable && !options?.enable) {
243
248
  return;
244
249
  }
245
- this.log.debug('Persisting state', {
250
+ await this.ensureInitialized();
251
+ this.#log.debug('Persisting state', {
246
252
  persistStateKeyValueStoreId: this.persistStateKeyValueStoreId,
247
253
  persistStateKey: this.persistStateKey,
248
254
  });
249
- // use half the interval of `persistState` to avoid race conditions
250
- const persistStateIntervalMillis = this.config.get('persistStateIntervalMillis');
251
- const timeoutSecs = persistStateIntervalMillis / 2_000;
252
255
  await this.keyValueStore
253
- .setValue(this.persistStateKey, this.getState(), {
254
- timeoutSecs,
255
- doNotRetryTimeouts: true,
256
- })
257
- .catch((error) => this.log.warning(`Failed to persist the session pool stats to ${this.persistStateKey}`, { error }));
256
+ ?.setValue(this.persistStateKey, await this.getState())
257
+ .catch((error) => this.#log.warning(`Failed to persist the session pool stats to ${this.persistStateKey}`, { error }));
258
258
  }
259
259
  /**
260
260
  * Removes listener from `persistState` event.
261
261
  * This function should be called after you are done with using the `SessionPool` instance.
262
262
  */
263
263
  async teardown() {
264
- this.events.off("persistState" /* EventType.PERSIST_STATE */, this._listener);
264
+ if (!this.#initPromise)
265
+ return;
266
+ await this.ensureInitialized();
267
+ if (this.#listener) {
268
+ this.#events.off("persistState" /* EventType.PERSIST_STATE */, this.#listener);
269
+ }
265
270
  await this.persistState();
266
271
  }
267
- /**
268
- * SessionPool should not work before initialization.
269
- */
270
- _throwIfNotInitialized() {
271
- if (!this.isInitialized)
272
- throw new Error('SessionPool is not initialized.');
273
- }
274
272
  /**
275
273
  * Removes retired `Session` instances from `SessionPool`.
276
274
  */
277
- _removeRetiredSessions() {
278
- this.sessions = this.sessions.filter((storedSession) => {
275
+ removeRetiredSessions() {
276
+ this.#sessions = this.#sessions.filter((storedSession) => {
279
277
  if (storedSession.isUsable())
280
278
  return true;
281
279
  this.sessionMap.delete(storedSession.id);
282
- this.log.debug(`Removed Session - ${storedSession.id}`);
280
+ this.#log.debug(`Removed Session - ${storedSession.id}`);
283
281
  return false;
284
282
  });
285
283
  }
@@ -287,89 +285,103 @@ export class SessionPool extends EventEmitter {
287
285
  * Adds `Session` instance to `SessionPool`.
288
286
  * @param newSession `Session` instance to be added.
289
287
  */
290
- _addSession(newSession) {
291
- this.sessions.push(newSession);
288
+ registerSession(newSession) {
289
+ this.#sessions.push(newSession);
292
290
  this.sessionMap.set(newSession.id, newSession);
293
291
  }
294
292
  /**
295
293
  * Gets random index.
296
294
  */
297
- _getRandomIndex() {
298
- return Math.floor(Math.random() * this.sessions.length);
295
+ getRandomIndex() {
296
+ return Math.floor(Math.random() * this.#sessions.length);
299
297
  }
300
298
  /**
301
299
  * Creates new session without any extra behavior.
302
- * @param sessionPool
303
300
  * @param [options]
304
301
  * @param [options.sessionOptions] The configuration options for the session being created.
305
302
  * @returns New session.
306
303
  */
307
- _defaultCreateSessionFunction(sessionPool, options = {}) {
304
+ async defaultCreateSessionFunction(options = {}) {
308
305
  ow(options, ow.object.exactShape({ sessionOptions: ow.optional.object }));
309
306
  const { sessionOptions = {} } = options;
310
- return new Session({
307
+ return new Session(sessionOptions);
308
+ }
309
+ /**
310
+ * Invokes `createSessionFunction` with `sessionOptions` already merged from pool-wide defaults and
311
+ * the supplied per-call overrides, so custom implementations don't need to spread `pool.sessionOptions` themselves.
312
+ *
313
+ * A default {@link SessionFingerprint} is generated up front (host OS as
314
+ * `platform`, a random valid `browser`/`device` for that platform). Pool-wide
315
+ * and per-call options override it, and a persisted fingerprint coming
316
+ * through `maybeLoadSessionPool` naturally wins because it arrives in
317
+ * `perCallOptions`.
318
+ */
319
+ async invokeCreateSessionFunction(perCallOptions) {
320
+ const sessionOptions = {
321
+ fingerprint: createDefaultSessionFingerprint(),
311
322
  ...this.sessionOptions,
312
- ...sessionOptions,
313
- sessionPool,
314
- });
323
+ ...perCallOptions,
324
+ };
325
+ return this.createSessionFunction({ sessionOptions });
315
326
  }
316
327
  /**
317
328
  * Creates new session and adds it to the pool.
318
329
  * @returns Newly created `Session` instance.
319
330
  */
320
- async _createSession() {
321
- const newSession = await this.createSessionFunction(this);
322
- this._addSession(newSession);
323
- this.log.debug(`Created new Session - ${newSession.id}`);
331
+ async createSession() {
332
+ const newSession = await this.invokeCreateSessionFunction();
333
+ this.registerSession(newSession);
334
+ this.#log.debug(`Created new Session - ${newSession.id}`);
324
335
  return newSession;
325
336
  }
326
337
  /**
327
338
  * Decides whether there is enough space for creating new session.
328
339
  */
329
- _hasSpaceForSession() {
330
- return this.sessions.length < this.maxPoolSize;
340
+ hasSpaceForSession() {
341
+ return this.#sessions.length < this.maxPoolSize;
331
342
  }
332
343
  /**
333
- * Picks random session from the `SessionPool`.
334
- * @returns Picked `Session`.
344
+ * Picks a session from the `SessionPool` according to the configured `sessionReuseStrategy`.
345
+ * Returns `undefined` when no session should be reused and a new one should be created instead.
335
346
  */
336
- _pickSession() {
337
- return this.sessions[this._getRandomIndex()]; // Or maybe we should let the developer to customize the picking algorithm
347
+ pickSession() {
348
+ if (this.#sessionReuseStrategy !== 'use-until-failure' && this.hasSpaceForSession())
349
+ return undefined;
350
+ if (this.#sessionReuseStrategy === 'use-until-failure') {
351
+ return this.#sessions.find((session) => session.isUsable());
352
+ }
353
+ let picked;
354
+ if (this.#sessionReuseStrategy === 'round-robin') {
355
+ const index = this.#roundRobinIndex % this.#sessions.length;
356
+ this.#roundRobinIndex = index + 1;
357
+ picked = this.#sessions[index];
358
+ }
359
+ else {
360
+ picked = this.#sessions[this.getRandomIndex()];
361
+ }
362
+ return picked.isUsable() ? picked : undefined;
338
363
  }
339
364
  /**
340
365
  * Potentially loads `SessionPool`.
341
366
  * If the state was persisted it loads the `SessionPool` from the persisted state.
342
367
  */
343
- async _maybeLoadSessionPool() {
344
- const loadedSessionPool = await this.keyValueStore.getValue(this.persistStateKey);
368
+ async maybeLoadSessionPool() {
369
+ const loadedSessionPool = await this.keyValueStore?.getValue(this.persistStateKey);
345
370
  if (!loadedSessionPool)
346
371
  return;
347
372
  // Invalidate old sessions and load active sessions only
348
- this.log.debug('Recreating state from KeyValueStore', {
373
+ this.#log.debug('Recreating state from KeyValueStore', {
349
374
  persistStateKeyValueStoreId: this.persistStateKeyValueStoreId,
350
375
  persistStateKey: this.persistStateKey,
351
376
  });
352
377
  for (const sessionObject of loadedSessionPool.sessions) {
353
- sessionObject.sessionPool = this;
354
378
  sessionObject.createdAt = new Date(sessionObject.createdAt);
355
379
  sessionObject.expiresAt = new Date(sessionObject.expiresAt);
356
- const recreatedSession = await this.createSessionFunction(this, { sessionOptions: sessionObject });
380
+ const recreatedSession = await this.invokeCreateSessionFunction(sessionObject);
357
381
  if (recreatedSession.isUsable()) {
358
- this._addSession(recreatedSession);
382
+ this.registerSession(recreatedSession);
359
383
  }
360
384
  }
361
- this.log.debug(`${this.usableSessionsCount} active sessions loaded from KeyValueStore`);
362
- }
363
- /**
364
- * Opens a SessionPool and returns a promise resolving to an instance
365
- * of the {@link SessionPool} class that is already initialized.
366
- *
367
- * For more details and code examples, see the {@link SessionPool} class.
368
- */
369
- static async open(options, config) {
370
- const sessionPool = new SessionPool(options, config);
371
- await sessionPool.initialize();
372
- return sessionPool;
385
+ this.#log.debug(`${this.#sessions.length} active sessions loaded from KeyValueStore`);
373
386
  }
374
387
  }
375
- //# sourceMappingURL=session_pool.js.map