@crawlee/core 3.17.1-beta.60 → 3.17.1-beta.62

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.
@@ -35,7 +35,7 @@ export declare class Statistics {
35
35
  /**
36
36
  * Statistic instance id.
37
37
  */
38
- readonly id: number;
38
+ readonly id: string;
39
39
  /**
40
40
  * Current statistic state used for doing calculations on {@link Statistics.calculate} calls
41
41
  */
@@ -166,13 +166,22 @@ export interface StatisticsOptions {
166
166
  * @default false
167
167
  */
168
168
  saveErrorSnapshots?: boolean;
169
+ /**
170
+ * A unique identifier for this statistics instance. This ID is used for persistence
171
+ * to the key value store, ensuring the same statistics can be loaded after script restarts.
172
+ *
173
+ * If not provided, an auto-incremented ID will be used for backward compatibility.
174
+ * This means statistics may not persist correctly across script restarts
175
+ * if crawler creation order changes.
176
+ */
177
+ id?: string;
169
178
  }
170
179
  /**
171
180
  * Format of the persisted stats
172
181
  */
173
182
  export interface StatisticPersistedState extends Omit<StatisticState, 'statsPersistedAt'> {
174
183
  requestRetryHistogram: number[];
175
- statsId: number;
184
+ statsId: string;
176
185
  requestAvgFailedDurationMillis: number;
177
186
  requestAvgFinishedDurationMillis: number;
178
187
  requestTotalDurationMillis: number;
@@ -81,8 +81,8 @@ class Statistics {
81
81
  enumerable: true,
82
82
  configurable: true,
83
83
  writable: true,
84
- value: Statistics.id++
85
- }); // assign an id while incrementing so it can be saved/restored from KV
84
+ value: void 0
85
+ });
86
86
  /**
87
87
  * Current statistic state used for doing calculations on {@link Statistics.calculate} calls
88
88
  */
@@ -120,7 +120,7 @@ class Statistics {
120
120
  enumerable: true,
121
121
  configurable: true,
122
122
  writable: true,
123
- value: `SDK_CRAWLER_STATISTICS_${this.id}`
123
+ value: void 0
124
124
  });
125
125
  Object.defineProperty(this, "logIntervalMillis", {
126
126
  enumerable: true,
@@ -184,10 +184,13 @@ class Statistics {
184
184
  config: ow_1.default.optional.object,
185
185
  persistenceOptions: ow_1.default.optional.object,
186
186
  saveErrorSnapshots: ow_1.default.optional.boolean,
187
+ id: ow_1.default.optional.any(ow_1.default.number, ow_1.default.string),
187
188
  }));
188
189
  const { logIntervalSecs = 60, logMessage = 'Statistics', keyValueStore, config = configuration_1.Configuration.getGlobalConfig(), persistenceOptions = {
189
190
  enable: true,
190
- }, saveErrorSnapshots = false, } = options;
191
+ }, saveErrorSnapshots = false, id, } = options;
192
+ this.id = id ?? String(Statistics.id++);
193
+ this.persistStateKey = `SDK_CRAWLER_STATISTICS_${this.id}`;
191
194
  this.log = (options.log ?? log_1.log).child({ prefix: 'Statistics' });
192
195
  this.errorTracker = new error_tracker_1.ErrorTracker({ ...errorTrackerConfig, saveErrorSnapshots });
193
196
  this.errorTrackerRetry = new error_tracker_1.ErrorTracker({ ...errorTrackerConfig, saveErrorSnapshots });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crawlee/core",
3
- "version": "3.17.1-beta.60",
3
+ "version": "3.17.1-beta.62",
4
4
  "description": "The scalable web crawling and scraping library for JavaScript/Node.js. Enables development of data extraction and web automation jobs (not only) with headless Chrome and Puppeteer.",
5
5
  "engines": {
6
6
  "node": ">=16.0.0"
@@ -59,9 +59,9 @@
59
59
  "@apify/pseudo_url": "^2.0.30",
60
60
  "@apify/timeout": "^0.4.0",
61
61
  "@apify/utilities": "^2.7.10",
62
- "@crawlee/memory-storage": "3.17.1-beta.60",
63
- "@crawlee/types": "3.17.1-beta.60",
64
- "@crawlee/utils": "3.17.1-beta.60",
62
+ "@crawlee/memory-storage": "3.17.1-beta.62",
63
+ "@crawlee/types": "3.17.1-beta.62",
64
+ "@crawlee/utils": "3.17.1-beta.62",
65
65
  "@sapphire/async-queue": "^1.5.1",
66
66
  "@standard-schema/spec": "^1.0.0",
67
67
  "@vladfrangu/async_event_emitter": "^2.2.2",
@@ -84,5 +84,5 @@
84
84
  }
85
85
  }
86
86
  },
87
- "gitHead": "5ba2517b5f3bc6ef868b860c6c783a3e7e7d4a93"
87
+ "gitHead": "a4075c98770711a6fadb4e16d8aef644f8a96690"
88
88
  }
@@ -410,14 +410,21 @@ class SitemapRequestList {
410
410
  }
411
411
  // Create a new stream, as we have read all the URLs from the current one.
412
412
  // Pushing the urls back to the original stream might not be possible if it has been ended.
413
- const newStream = this.createNewStream(this.urlQueueStream.readableHighWaterMark);
413
+ const previousStream = this.urlQueueStream;
414
+ const newStream = this.createNewStream(previousStream.readableHighWaterMark);
414
415
  for (const url of urlQueue) {
415
416
  newStream.push(url);
416
417
  }
417
- if (this.urlQueueStream.writableEnded) {
418
+ if (previousStream.writableEnded) {
418
419
  newStream.end();
419
420
  }
420
421
  this.urlQueueStream = newStream;
422
+ // A `pushNextUrl()` call may be blocked on backpressure, waiting for a `readdata` event on the
423
+ // previous stream. That event is only ever emitted by `readNextUrl()` on the current stream, so
424
+ // after the swap the waiter would never be notified and the background sitemap loading would hang.
425
+ // Re-emit `readdata` on the previous stream to release any such pending waiter (its URL has already
426
+ // been transferred to the new stream above).
427
+ previousStream.emit('readdata');
421
428
  await this.store.setValue(this.persistStateKey, {
422
429
  sitemapParsingProgress: {
423
430
  pendingSitemapUrls: Array.from(this.sitemapParsingProgress.pendingSitemapUrls),