@crawlee/core 4.0.0-rc.0 → 4.0.0-rc.1

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 (122) hide show
  1. package/README.md +1 -1
  2. package/configuration.d.ts +15 -46
  3. package/configuration.js +8 -20
  4. package/errors.d.ts +12 -53
  5. package/errors.js +13 -66
  6. package/events/index.d.ts +1 -0
  7. package/events/local_event_manager.d.ts +0 -7
  8. package/events/local_event_manager.js +8 -8
  9. package/events/system_info.d.ts +38 -0
  10. package/index.d.ts +2 -8
  11. package/index.js +4 -8
  12. package/internal.d.ts +8 -0
  13. package/internal.js +9 -0
  14. package/log.d.ts +10 -11
  15. package/log.js +53 -25
  16. package/memory-storage/memory-storage.d.ts +12 -7
  17. package/memory-storage/memory-storage.js +45 -17
  18. package/memory-storage/resource-clients/dataset.d.ts +0 -5
  19. package/memory-storage/resource-clients/dataset.js +17 -20
  20. package/memory-storage/resource-clients/key-value-store.d.ts +0 -9
  21. package/memory-storage/resource-clients/key-value-store.js +11 -33
  22. package/memory-storage/resource-clients/request-queue.d.ts +0 -22
  23. package/memory-storage/resource-clients/request-queue.js +57 -53
  24. package/package.json +16 -18
  25. package/proxy_configuration.d.ts +20 -23
  26. package/proxy_configuration.js +18 -12
  27. package/recoverable_state.d.ts +28 -6
  28. package/recoverable_state.js +51 -14
  29. package/request.d.ts +18 -104
  30. package/request.js +41 -220
  31. package/serialization.js +3 -3
  32. package/service_locator.d.ts +3 -0
  33. package/service_locator.js +2 -0
  34. package/storages/dataset.d.ts +9 -15
  35. package/storages/dataset.js +22 -14
  36. package/storages/index.d.ts +3 -4
  37. package/storages/index.js +1 -4
  38. package/storages/key_value_store.d.ts +12 -46
  39. package/storages/key_value_store.js +31 -47
  40. package/storages/key_value_store_codec.js +6 -11
  41. package/storages/request_dedup_cache.d.ts +0 -2
  42. package/storages/request_dedup_cache.js +8 -8
  43. package/storages/request_list.d.ts +6 -82
  44. package/storages/request_list.js +175 -179
  45. package/storages/request_loader.d.ts +48 -22
  46. package/storages/request_loader.js +36 -1
  47. package/storages/request_manager.d.ts +86 -0
  48. package/storages/request_manager_tandem.d.ts +13 -28
  49. package/storages/request_manager_tandem.js +46 -43
  50. package/storages/request_queue.d.ts +19 -49
  51. package/storages/request_queue.js +92 -88
  52. package/storages/storage_instance_manager.d.ts +1 -2
  53. package/storages/storage_instance_manager.js +4 -4
  54. package/storages/transaction.d.ts +27 -9
  55. package/storages/transaction.js +56 -11
  56. package/storages/utils.d.ts +2 -2
  57. package/validators.d.ts +3 -2
  58. package/validators.js +3 -2
  59. package/autoscaling/autoscaled_pool.d.ts +0 -195
  60. package/autoscaling/autoscaled_pool.js +0 -386
  61. package/autoscaling/concurrency_system.d.ts +0 -268
  62. package/autoscaling/concurrency_system.js +0 -362
  63. package/autoscaling/cpu_load_signal.d.ts +0 -43
  64. package/autoscaling/cpu_load_signal.js +0 -47
  65. package/autoscaling/event_loop_load_signal.d.ts +0 -51
  66. package/autoscaling/event_loop_load_signal.js +0 -60
  67. package/autoscaling/index.d.ts +0 -9
  68. package/autoscaling/index.js +0 -9
  69. package/autoscaling/load_signal.d.ts +0 -100
  70. package/autoscaling/load_signal.js +0 -105
  71. package/autoscaling/memory_load_signal.d.ts +0 -47
  72. package/autoscaling/memory_load_signal.js +0 -106
  73. package/autoscaling/snapshotter.d.ts +0 -84
  74. package/autoscaling/snapshotter.js +0 -67
  75. package/autoscaling/storage_backend_load_signal.d.ts +0 -56
  76. package/autoscaling/storage_backend_load_signal.js +0 -73
  77. package/autoscaling/system_status.d.ts +0 -159
  78. package/autoscaling/system_status.js +0 -139
  79. package/autoscaling/weighted_avg.d.ts +0 -5
  80. package/autoscaling/weighted_avg.js +0 -14
  81. package/cookie_utils.d.ts +0 -44
  82. package/cookie_utils.js +0 -122
  83. package/crawlers/context_pipeline.d.ts +0 -70
  84. package/crawlers/context_pipeline.js +0 -122
  85. package/crawlers/crawler_commons.d.ts +0 -159
  86. package/crawlers/error_snapshotter.d.ts +0 -57
  87. package/crawlers/error_snapshotter.js +0 -117
  88. package/crawlers/error_tracker.d.ts +0 -54
  89. package/crawlers/error_tracker.js +0 -308
  90. package/crawlers/index.d.ts +0 -5
  91. package/crawlers/index.js +0 -4
  92. package/crawlers/internals/types.d.ts +0 -7
  93. package/crawlers/internals/types.js +0 -1
  94. package/crawlers/statistics.d.ts +0 -328
  95. package/crawlers/statistics.js +0 -536
  96. package/enqueue_links/enqueue_links.d.ts +0 -156
  97. package/enqueue_links/enqueue_links.js +0 -78
  98. package/enqueue_links/index.d.ts +0 -2
  99. package/enqueue_links/index.js +0 -2
  100. package/enqueue_links/shared.d.ts +0 -93
  101. package/enqueue_links/shared.js +0 -239
  102. package/http.d.ts +0 -9
  103. package/http.js +0 -28
  104. package/router.d.ts +0 -306
  105. package/router.js +0 -309
  106. package/session_pool/consts.d.ts +0 -3
  107. package/session_pool/consts.js +0 -3
  108. package/session_pool/errors.d.ts +0 -7
  109. package/session_pool/errors.js +0 -11
  110. package/session_pool/fingerprint.d.ts +0 -9
  111. package/session_pool/fingerprint.js +0 -30
  112. package/session_pool/index.d.ts +0 -4
  113. package/session_pool/index.js +0 -4
  114. package/session_pool/session.d.ts +0 -150
  115. package/session_pool/session.js +0 -220
  116. package/session_pool/session_pool.d.ts +0 -240
  117. package/session_pool/session_pool.js +0 -394
  118. package/storages/sitemap_request_loader.d.ts +0 -201
  119. package/storages/sitemap_request_loader.js +0 -438
  120. package/storages/throttling_request_manager.d.ts +0 -239
  121. package/storages/throttling_request_manager.js +0 -646
  122. /package/{crawlers/crawler_commons.js → events/system_info.js} +0 -0
package/serialization.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { pipeline as streamPipeline, Readable, Writable } from 'node:stream';
2
2
  import util from 'node:util';
3
3
  import zlib from 'node:zlib';
4
- import StreamArray from 'stream-json/streamers/StreamArray.js';
4
+ import StreamArray from 'stream-json/streamers/stream-array.js';
5
5
  import { z } from 'zod';
6
6
  import { parseArgument, schemas } from './validators.js';
7
7
  const pipeline = util.promisify(streamPipeline);
@@ -76,7 +76,7 @@ export async function serializeArray(data) {
76
76
  export async function deserializeArray(compressedData) {
77
77
  parseArgument(compressedData, uint8ArraySchema);
78
78
  const { chunks, collector } = createChunkCollector({ fromValuesStream: true });
79
- await pipeline(Readable.from([compressedData]), zlib.createGunzip(), StreamArray.withParser(), collector);
79
+ await pipeline(Readable.from([compressedData]), zlib.createGunzip(), StreamArray.withParserAsStream(), collector);
80
80
  return chunks;
81
81
  }
82
82
  /**
@@ -90,7 +90,7 @@ export async function deserializeArray(compressedData) {
90
90
  */
91
91
  export function createDeserialize(compressedData) {
92
92
  parseArgument(compressedData, uint8ArraySchema);
93
- const streamArray = StreamArray.withParser();
93
+ const streamArray = StreamArray.withParserAsStream();
94
94
  const destination = pluckValue(streamArray);
95
95
  streamPipeline(Readable.from([compressedData]), zlib.createGunzip(), destination, (err) => destination.emit(err));
96
96
  return destination;
@@ -60,6 +60,7 @@ interface ServiceLocatorInterface {
60
60
  getChildLog(prefix: string): CrawleeLogger;
61
61
  /**
62
62
  * Get the storage instance manager (shared across all storage types).
63
+ * @internal
63
64
  */
64
65
  getStorageInstanceManager(): StorageInstanceManager;
65
66
  /**
@@ -138,7 +139,9 @@ export declare class ServiceLocator implements ServiceLocatorInterface {
138
139
  getLogger(): CrawleeLogger;
139
140
  setLogger(logger: CrawleeLogger): void;
140
141
  getChildLog(prefix: string): CrawleeLogger;
142
+ /** @internal */
141
143
  getStorageInstanceManager(): StorageInstanceManager;
144
+ /** @internal */
142
145
  reset(): void;
143
146
  }
144
147
  /**
@@ -161,12 +161,14 @@ export class ServiceLocator {
161
161
  getChildLog(prefix) {
162
162
  return this.getLogger().child({ prefix });
163
163
  }
164
+ /** @internal */
164
165
  getStorageInstanceManager() {
165
166
  if (!ServiceLocator.#storageInstanceManager) {
166
167
  ServiceLocator.#storageInstanceManager = new StorageInstanceManager();
167
168
  }
168
169
  return ServiceLocator.#storageInstanceManager;
169
170
  }
171
+ /** @internal */
170
172
  reset() {
171
173
  this.#configuration = undefined;
172
174
  this.#eventManager = undefined;
@@ -1,6 +1,5 @@
1
1
  import type { Awaitable, DatasetBackend, DatasetInfo, Dictionary } from '@crawlee/types';
2
2
  import { Configuration } from '../configuration.js';
3
- import type { CrawleeLogger } from '../log.js';
4
3
  import type { JournalEntry } from './transaction.js';
5
4
  import type { DatasetStats } from './storage_stats.js';
6
5
  import type { StorageOpenOptions } from './utils.js';
@@ -142,10 +141,9 @@ export interface DatasetExportToOptions extends DatasetExportOptions {
142
141
  export declare class Dataset<Data extends Dictionary = Dictionary> {
143
142
  #private;
144
143
  readonly configuration: Configuration;
145
- id: string;
146
- name?: string;
147
- backend: DatasetBackend<Data>;
148
- log: CrawleeLogger;
144
+ readonly id: string;
145
+ readonly name?: string;
146
+ private readonly backend;
149
147
  /**
150
148
  * @internal
151
149
  */
@@ -171,14 +169,6 @@ export declare class Dataset<Data extends Dictionary = Dictionary> {
171
169
  * Returns {@link DatasetContent} object holding the items in the dataset based on the provided parameters.
172
170
  */
173
171
  getData(options?: DatasetDataOptions): Promise<DatasetContent<Data>>;
174
- /**
175
- * The single transaction-aware page read all dataset read paths go through — both `getData()` and
176
- * the private `fetchPages()`. Returns the real page concatenated with the current transaction's
177
- * buffered items, with `offset` / `limit` / `desc` windowing applied across the concatenation.
178
- */
179
- private readPage;
180
- /** The active transaction's buffered writes to this dataset, derived from its journal. */
181
- private bufferedJournalEntries;
182
172
  /** @internal */
183
173
  commitJournalEntries(entries: JournalEntry[]): Promise<void>;
184
174
  /**
@@ -323,8 +313,6 @@ export declare class Dataset<Data extends Dictionary = Dictionary> {
323
313
  * @param [options] An object containing extra options for `reduce()`
324
314
  */
325
315
  reduce<T>(iteratee: DatasetReducer<T, Data>, memo: T, options?: DatasetIteratorOptions): Promise<T>;
326
- private fetchEntryPages;
327
- private fetchPages;
328
316
  /**
329
317
  * Returns dataset items.
330
318
  *
@@ -391,6 +379,11 @@ export declare class Dataset<Data extends Dictionary = Dictionary> {
391
379
  * depending on the mode of operation.
392
380
  */
393
381
  drop(): Promise<void>;
382
+ /**
383
+ * Removes all items from the dataset but keeps the dataset itself, along with its
384
+ * {@link Dataset.id|`id`} and {@link Dataset.name|`name`}.
385
+ */
386
+ purge(): Promise<void>;
394
387
  /**
395
388
  * Opens a dataset and returns a promise resolving to an instance of the {@link Dataset} class.
396
389
  *
@@ -469,6 +462,7 @@ export interface DatasetReducer<T, Data> {
469
462
  */
470
463
  (memo: T, item: Data, index: number): Awaitable<T>;
471
464
  }
465
+ /** @internal */
472
466
  export interface DatasetOptions {
473
467
  /** Resolved metadata for the dataset, as returned by the backend's `getMetadata()`. */
474
468
  metadata: DatasetInfo;
@@ -91,8 +91,8 @@ export class Dataset {
91
91
  configuration;
92
92
  id;
93
93
  name;
94
+ // oxlint-disable-next-line crawlee/prefer-private-fields -- tests spy on the backend directly
94
95
  backend;
95
- log;
96
96
  #statsTracker = new StorageStatsTracker({
97
97
  readCount: 0,
98
98
  writeCount: 0,
@@ -105,7 +105,6 @@ export class Dataset {
105
105
  this.id = options.metadata.id;
106
106
  this.name = options.metadata.name;
107
107
  this.backend = options.backend;
108
- this.log = serviceLocator.getLogger().child({ prefix: 'Dataset' });
109
108
  }
110
109
  /**
111
110
  * Backend-independent usage counters tracked for this dataset (read / write operations issued to
@@ -126,6 +125,7 @@ export class Dataset {
126
125
  * The objects must be serializable to JSON.
127
126
  */
128
127
  async pushData(data) {
128
+ tryCancel();
129
129
  const transaction = activeStorageTransaction();
130
130
  parseArgument(data, schemas.anyObject);
131
131
  // Normalize to array and validate each item
@@ -152,7 +152,7 @@ export class Dataset {
152
152
  */
153
153
  async getData(options = {}) {
154
154
  try {
155
- return await this.readPage(options);
155
+ return await this.#readPage(options);
156
156
  }
157
157
  catch (e) {
158
158
  const error = e;
@@ -167,8 +167,8 @@ export class Dataset {
167
167
  * the private `fetchPages()`. Returns the real page concatenated with the current transaction's
168
168
  * buffered items, with `offset` / `limit` / `desc` windowing applied across the concatenation.
169
169
  */
170
- async readPage(options) {
171
- const buffered = this.bufferedJournalEntries()?.flatMap((entry) => entry.items);
170
+ async #readPage(options) {
171
+ const buffered = this.#bufferedJournalEntries()?.flatMap((entry) => entry.items);
172
172
  // Every branch below hits the backend exactly once.
173
173
  this.#statsTracker.add('readCount');
174
174
  if (!buffered?.length) {
@@ -224,7 +224,7 @@ export class Dataset {
224
224
  };
225
225
  }
226
226
  /** The active transaction's buffered writes to this dataset, derived from its journal. */
227
- bufferedJournalEntries() {
227
+ #bufferedJournalEntries() {
228
228
  const transaction = activeStorageTransaction();
229
229
  return transaction?.journal.filter((entry) => entry.type === 'dataset' && entry.participant === this);
230
230
  }
@@ -250,7 +250,7 @@ export class Dataset {
250
250
  async export(options = {}) {
251
251
  tryCancel();
252
252
  const items = [];
253
- for await (const page of this.fetchPages(options)) {
253
+ for await (const page of this.#fetchPages(options)) {
254
254
  items.push(...page.items);
255
255
  }
256
256
  return items;
@@ -348,7 +348,7 @@ export class Dataset {
348
348
  * @throws If the underlying storage no longer exists (e.g. it was deleted externally).
349
349
  */
350
350
  async getInfo() {
351
- const buffered = this.bufferedJournalEntries();
351
+ const buffered = this.#bufferedJournalEntries();
352
352
  const metadata = await this.backend.getMetadata();
353
353
  if (buffered?.length) {
354
354
  const lastWriteAt = buffered[buffered.length - 1].recordedAt;
@@ -432,16 +432,16 @@ export class Dataset {
432
432
  await this.forEach(wrappedFunc, options);
433
433
  return currentMemo;
434
434
  }
435
- async *fetchEntryPages(options) {
435
+ async *#fetchEntryPages(options) {
436
436
  let index = options.offset ?? 0;
437
- for await (const page of this.fetchPages(options)) {
437
+ for await (const page of this.#fetchPages(options)) {
438
438
  yield {
439
439
  ...page,
440
440
  items: page.items.map((item) => [index++, item]),
441
441
  };
442
442
  }
443
443
  }
444
- async *fetchPages(options, pageSize = DATASET_ITERATORS_DEFAULT_LIMIT) {
444
+ async *#fetchPages(options, pageSize = DATASET_ITERATORS_DEFAULT_LIMIT) {
445
445
  let offset = options.offset ?? 0;
446
446
  const totalLimit = options.limit;
447
447
  let yielded = 0;
@@ -449,7 +449,7 @@ export class Dataset {
449
449
  const fetchLimit = totalLimit !== undefined ? Math.min(pageSize, totalLimit - yielded) : pageSize;
450
450
  if (fetchLimit <= 0)
451
451
  break;
452
- const page = await this.readPage({ ...options, offset, limit: fetchLimit });
452
+ const page = await this.#readPage({ ...options, offset, limit: fetchLimit });
453
453
  yield page;
454
454
  yielded += page.items.length;
455
455
  if (page.items.length < fetchLimit || offset + page.items.length >= page.total)
@@ -483,7 +483,7 @@ export class Dataset {
483
483
  values(options = {}) {
484
484
  tryCancel();
485
485
  return createDualIterable({
486
- createPages: () => this.fetchPages(options),
486
+ createPages: () => this.#fetchPages(options),
487
487
  extractItems: (page) => page.items,
488
488
  });
489
489
  }
@@ -513,7 +513,7 @@ export class Dataset {
513
513
  entries(options = {}) {
514
514
  tryCancel();
515
515
  return createDualIterable({
516
- createPages: () => this.fetchEntryPages(options),
516
+ createPages: () => this.#fetchEntryPages(options),
517
517
  extractItems: (page) => page.items,
518
518
  });
519
519
  }
@@ -541,6 +541,14 @@ export class Dataset {
541
541
  await this.backend.drop();
542
542
  serviceLocator.getStorageInstanceManager().removeFromCache(this);
543
543
  }
544
+ /**
545
+ * Removes all items from the dataset but keeps the dataset itself, along with its
546
+ * {@link Dataset.id|`id`} and {@link Dataset.name|`name`}.
547
+ */
548
+ async purge() {
549
+ rejectOperationInTransaction('Dataset.purge()');
550
+ await this.backend.purge();
551
+ }
544
552
  /**
545
553
  * Opens a dataset and returns a promise resolving to an instance of the {@link Dataset} class.
546
554
  *
@@ -5,10 +5,9 @@ export * from './request_list.js';
5
5
  export type * from './request_loader.js';
6
6
  export type * from './request_manager.js';
7
7
  export * from './request_queue.js';
8
- export * from './storage_instance_manager.js';
9
- export * from './storage_stats.js';
8
+ export type { DefaultStorageIdentifier, ExplicitStorageIdentifier, IStorage, StorageIdentifier, } from './storage_instance_manager.js';
9
+ export { StorageInstanceManager } from './storage_instance_manager.js';
10
+ export type { DatasetStats, KeyValueStoreStats, RequestQueueStats } from './storage_stats.js';
10
11
  export * from './utils.js';
11
12
  export * from './transaction.js';
12
- export * from './sitemap_request_loader.js';
13
13
  export * from './request_manager_tandem.js';
14
- export * from './throttling_request_manager.js';
package/storages/index.js CHANGED
@@ -3,10 +3,7 @@ export * from './key_value_store.js';
3
3
  export * from './key_value_store_codec.js';
4
4
  export * from './request_list.js';
5
5
  export * from './request_queue.js';
6
- export * from './storage_instance_manager.js';
7
- export * from './storage_stats.js';
6
+ export { StorageInstanceManager } from './storage_instance_manager.js';
8
7
  export * from './utils.js';
9
8
  export * from './transaction.js';
10
- export * from './sitemap_request_loader.js';
11
9
  export * from './request_manager_tandem.js';
12
- export * from './throttling_request_manager.js';
@@ -20,8 +20,6 @@ import type { StorageIdentifier } from './storage_instance_manager.js';
20
20
  * To access the default key-value store directly, you can use the
21
21
  * {@link KeyValueStore.getValue} and {@link KeyValueStore.setValue} convenience functions.
22
22
  *
23
- * To access the input, you can also use the {@link KeyValueStore.getInput} convenience function.
24
- *
25
23
  * `KeyValueStore` stores its data on a local disk.
26
24
  *
27
25
  * If the `CRAWLEE_STORAGE_DIR` environment variable is set, the data is stored in
@@ -29,15 +27,14 @@ import type { StorageIdentifier } from './storage_instance_manager.js';
29
27
  * ```
30
28
  * {CRAWLEE_STORAGE_DIR}/key_value_stores/{STORE_ID}/{INDEX}.{EXT}
31
29
  * ```
32
- * Note that `{STORE_ID}` is the name or ID of the key-value store. The default key-value store has ID: `default`,
33
- * unless you override it by setting the `CRAWLEE_DEFAULT_KEY_VALUE_STORE_ID` environment variable.
30
+ * Note that `{STORE_ID}` is the name or ID of the key-value store. The default key-value store has ID: `default`.
34
31
  * The `{KEY}` is the key of the record and `{EXT}` corresponds to the MIME content type of the data value.
35
32
  *
36
33
  * **Example usage:**
37
34
  *
38
35
  * ```javascript
39
36
  * // Get crawler input from the default key-value store.
40
- * const input = await KeyValueStore.getInput();
37
+ * const input = await KeyValueStore.getValue('INPUT');
41
38
  * // Get some value from the default key-value store.
42
39
  * const otherValue = await KeyValueStore.getValue('my-key');
43
40
  *
@@ -65,7 +62,7 @@ export declare class KeyValueStore {
65
62
  readonly configuration: Configuration;
66
63
  readonly id: string;
67
64
  readonly name?: string;
68
- private readonly backend;
65
+ readonly backend: KeyValueStoreBackend;
69
66
  /**
70
67
  * @internal
71
68
  */
@@ -139,21 +136,6 @@ export declare class KeyValueStore {
139
136
  * on the MIME content type of the record, or the default value if the key is missing from the store.
140
137
  */
141
138
  getValue<T = unknown>(key: string, defaultValue: T): Promise<T>;
142
- /**
143
- * The active transaction's last buffered write per key for this store, derived from its journal.
144
- * An entry with a `null` value is a tombstone (an in-transaction deletion).
145
- */
146
- private bufferedJournalEntries;
147
- /**
148
- * The single transaction-aware record read shared by `getValue`, `getRecord`, `recordExists` and the
149
- * listing paths: buffered key → serialized through the standard codec (same fidelity as a real
150
- * round-trip); tombstoned key → `null`; otherwise the backend.
151
- *
152
- * The per-key buffered lookup requires the whole journal to be reduced to a last-write-per-key map,
153
- * which is O(journal). Single-record callers let it default (rebuilt per call); the listing paths,
154
- * which read many keys, pass a map built once so the read stays O(1) per key instead of O(journal).
155
- */
156
- private readRecord;
157
139
  /**
158
140
  * Reads a record from the key-value store without parsing the value.
159
141
  *
@@ -189,9 +171,6 @@ export declare class KeyValueStore {
189
171
  */
190
172
  recordExists(key: string): Promise<boolean>;
191
173
  getAutoSavedValue<T extends Dictionary = Dictionary>(key: string, defaultValue?: T): Promise<T>;
192
- private ensurePersistStateEvent;
193
- private fetchKeyValuePages;
194
- private fetchKeyPages;
195
174
  /**
196
175
  * Saves or deletes a record in the key-value store.
197
176
  * The function returns a promise that resolves once the record has been saved or deleted.
@@ -243,6 +222,11 @@ export declare class KeyValueStore {
243
222
  * depending on the mode of operation.
244
223
  */
245
224
  drop(): Promise<void>;
225
+ /**
226
+ * Removes all records from the store but keeps the store itself, along with its
227
+ * {@link KeyValueStore.id|`id`} and {@link KeyValueStore.name|`name`}.
228
+ */
229
+ purge(): Promise<void>;
246
230
  /** @internal */
247
231
  clearCache(): void;
248
232
  /**
@@ -355,7 +339,9 @@ export declare class KeyValueStore {
355
339
  /**
356
340
  * Returns a file URL for the given key.
357
341
  *
358
- * If the record does not exist or has no associated file path (i.e., it is not stored as a file), returns `undefined`.
342
+ * The URL is derived from the key, so it is also returned for a record that does not exist (yet) —
343
+ * including one written earlier in an uncommitted storage transaction. Returns `undefined` only when
344
+ * the storage has no file URLs at all (e.g. the in-memory storage).
359
345
  *
360
346
  * @param key The key of the record to generate the public URL for.
361
347
  */
@@ -486,27 +472,6 @@ export declare class KeyValueStore {
486
472
  * @ignore
487
473
  */
488
474
  static setValue<T>(key: string, value: T | null, options?: RecordOptions): Promise<void>;
489
- /**
490
- * Gets the crawler input value from the default {@link KeyValueStore} associated with the current crawler run.
491
- *
492
- * The input is read from the default {@link KeyValueStore} under the configured input key
493
- * (`CRAWLEE_INPUT_KEY`, default `INPUT`).
494
- *
495
- * Note that the `getInput()` function does not cache the value read from the key-value store.
496
- * If you need to use the input multiple times in your crawler,
497
- * it is far more efficient to read it once and store it locally.
498
- *
499
- * For more information, see {@link KeyValueStore.open}
500
- * and {@link KeyValueStore.getValue}.
501
- *
502
- * @returns
503
- * Returns a promise that resolves to an object, string
504
- * or [`Buffer`](https://nodejs.org/api/buffer.html), depending
505
- * on the MIME content type of the record, or `null`
506
- * if the record is missing.
507
- * @ignore
508
- */
509
- static getInput<T = Dictionary | string | Buffer>(): Promise<T | null>;
510
475
  }
511
476
  /**
512
477
  * User-function used in the {@link KeyValueStore.forEachKey} method.
@@ -522,6 +487,7 @@ export interface KeyConsumer {
522
487
  size: number;
523
488
  }): Awaitable<void>;
524
489
  }
490
+ /** @internal */
525
491
  export interface KeyValueStoreOptions {
526
492
  /** Resolved metadata for the key-value store, as returned by the backend's `getMetadata()`. */
527
493
  metadata: KeyValueStoreInfo;
@@ -42,8 +42,6 @@ const openOptionsSchema = z.strictObject({
42
42
  * To access the default key-value store directly, you can use the
43
43
  * {@link KeyValueStore.getValue} and {@link KeyValueStore.setValue} convenience functions.
44
44
  *
45
- * To access the input, you can also use the {@link KeyValueStore.getInput} convenience function.
46
- *
47
45
  * `KeyValueStore` stores its data on a local disk.
48
46
  *
49
47
  * If the `CRAWLEE_STORAGE_DIR` environment variable is set, the data is stored in
@@ -51,15 +49,14 @@ const openOptionsSchema = z.strictObject({
51
49
  * ```
52
50
  * {CRAWLEE_STORAGE_DIR}/key_value_stores/{STORE_ID}/{INDEX}.{EXT}
53
51
  * ```
54
- * Note that `{STORE_ID}` is the name or ID of the key-value store. The default key-value store has ID: `default`,
55
- * unless you override it by setting the `CRAWLEE_DEFAULT_KEY_VALUE_STORE_ID` environment variable.
52
+ * Note that `{STORE_ID}` is the name or ID of the key-value store. The default key-value store has ID: `default`.
56
53
  * The `{KEY}` is the key of the record and `{EXT}` corresponds to the MIME content type of the data value.
57
54
  *
58
55
  * **Example usage:**
59
56
  *
60
57
  * ```javascript
61
58
  * // Get crawler input from the default key-value store.
62
- * const input = await KeyValueStore.getInput();
59
+ * const input = await KeyValueStore.getValue('INPUT');
63
60
  * // Get some value from the default key-value store.
64
61
  * const otherValue = await KeyValueStore.getValue('my-key');
65
62
  *
@@ -86,7 +83,6 @@ export class KeyValueStore {
86
83
  configuration;
87
84
  id;
88
85
  name;
89
- // kept as TS-private: key_value_store tests spy on the backend directly
90
86
  backend;
91
87
  #persistStateEventStarted = false;
92
88
  /** Cache for persistent (auto-saved) values. When we try to set such value, the cache will be updated automatically. */
@@ -148,7 +144,7 @@ export class KeyValueStore {
148
144
  async getValue(key, defaultValue) {
149
145
  tryCancel();
150
146
  parseArgument(key, keySchema);
151
- const record = await this.readRecord(key);
147
+ const record = await this.#readRecord(key);
152
148
  // A missing record falls back to the default; a record that parses to a falsy value (including
153
149
  // a stored literal `null`) is returned verbatim, so callers can tell "stored null" from "absent".
154
150
  if (!record) {
@@ -161,7 +157,7 @@ export class KeyValueStore {
161
157
  * The active transaction's last buffered write per key for this store, derived from its journal.
162
158
  * An entry with a `null` value is a tombstone (an in-transaction deletion).
163
159
  */
164
- bufferedJournalEntries() {
160
+ #bufferedJournalEntries() {
165
161
  const transaction = activeStorageTransaction();
166
162
  if (!transaction)
167
163
  return undefined;
@@ -182,7 +178,7 @@ export class KeyValueStore {
182
178
  * which is O(journal). Single-record callers let it default (rebuilt per call); the listing paths,
183
179
  * which read many keys, pass a map built once so the read stays O(1) per key instead of O(journal).
184
180
  */
185
- async readRecord(key, buffered = this.bufferedJournalEntries()) {
181
+ async #readRecord(key, buffered = this.#bufferedJournalEntries()) {
186
182
  const entry = buffered?.get(key);
187
183
  if (entry) {
188
184
  if (entry.value === null) {
@@ -232,7 +228,7 @@ export class KeyValueStore {
232
228
  async getRecord(key) {
233
229
  tryCancel();
234
230
  parseArgument(key, keySchema);
235
- return this.readRecord(key);
231
+ return this.#readRecord(key);
236
232
  }
237
233
  /**
238
234
  * Tests whether a record with the given key exists in the key-value store without retrieving its value.
@@ -243,7 +239,7 @@ export class KeyValueStore {
243
239
  async recordExists(key) {
244
240
  tryCancel();
245
241
  parseArgument(key, keySchema);
246
- const entry = this.bufferedJournalEntries()?.get(key);
242
+ const entry = this.#bufferedJournalEntries()?.get(key);
247
243
  if (entry) {
248
244
  return entry.value !== null;
249
245
  }
@@ -265,10 +261,10 @@ export class KeyValueStore {
265
261
  return this.#cache.get(key);
266
262
  }
267
263
  this.#cache.set(key, value);
268
- this.ensurePersistStateEvent();
264
+ this.#ensurePersistStateEvent();
269
265
  return value;
270
266
  }
271
- ensurePersistStateEvent() {
267
+ #ensurePersistStateEvent() {
272
268
  if (this.#persistStateEventStarted) {
273
269
  return;
274
270
  }
@@ -281,15 +277,15 @@ export class KeyValueStore {
281
277
  });
282
278
  this.#persistStateEventStarted = true;
283
279
  }
284
- async *fetchKeyValuePages(options, mapRecord) {
280
+ async *#fetchKeyValuePages(options, mapRecord) {
285
281
  // Reduce the journal once for the whole iteration, not once per key inside `readRecord`.
286
- const buffered = this.bufferedJournalEntries();
287
- for await (const page of this.fetchKeyPages(options, buffered)) {
282
+ const buffered = this.#bufferedJournalEntries();
283
+ for await (const page of this.#fetchKeyPages(options, buffered)) {
288
284
  const results = [];
289
285
  for (const item of page) {
290
286
  // The shared transaction-aware read, so a key that exists only in the transaction resolves
291
287
  // here instead of being dropped (`values()` would disagree with `keys()` on length).
292
- const record = await this.readRecord(item.key, buffered);
288
+ const record = await this.#readRecord(item.key, buffered);
293
289
  if (record) {
294
290
  const parsed = parseValue(record.value, record.contentType ?? null);
295
291
  results.push(mapRecord(item.key, parsed));
@@ -298,7 +294,7 @@ export class KeyValueStore {
298
294
  yield results;
299
295
  }
300
296
  }
301
- async *fetchKeyPages(options, buffered = this.bufferedJournalEntries(), limit = KVS_KEYS_DEFAULT_LIMIT) {
297
+ async *#fetchKeyPages(options, buffered = this.#bufferedJournalEntries(), limit = KVS_KEYS_DEFAULT_LIMIT) {
302
298
  // Buffered keys are emitted first, then the real pages with any buffered (or tombstoned) key
303
299
  // skipped - a merge-join is not an option, since `listKeys` promises no sort order.
304
300
  const shadowedKeys = new Set();
@@ -460,6 +456,16 @@ export class KeyValueStore {
460
456
  await this.backend.drop();
461
457
  serviceLocator.getStorageInstanceManager().removeFromCache(this);
462
458
  }
459
+ /**
460
+ * Removes all records from the store but keeps the store itself, along with its
461
+ * {@link KeyValueStore.id|`id`} and {@link KeyValueStore.name|`name`}.
462
+ */
463
+ async purge() {
464
+ rejectOperationInTransaction('KeyValueStore.purge()');
465
+ await this.backend.purge();
466
+ // The auto-saved values this cache holds are no longer in the store.
467
+ this.#cache.clear();
468
+ }
463
469
  /** @internal */
464
470
  clearCache() {
465
471
  rejectOperationInTransaction('KeyValueStore.clearCache()');
@@ -491,7 +497,7 @@ export class KeyValueStore {
491
497
  parseArgument(iteratee, schemas.anyFunction);
492
498
  const parsedOptions = parseArgument(options, iteratorOptionsSchema);
493
499
  let index = 0;
494
- for await (const page of this.fetchKeyPages(parsedOptions)) {
500
+ for await (const page of this.#fetchKeyPages(parsedOptions)) {
495
501
  for (const item of page) {
496
502
  await iteratee(item.key, index++, { size: item.size });
497
503
  }
@@ -523,7 +529,7 @@ export class KeyValueStore {
523
529
  keys(options = {}) {
524
530
  tryCancel();
525
531
  return createDualIterable({
526
- createPages: () => this.fetchKeyPages(options),
532
+ createPages: () => this.#fetchKeyPages(options),
527
533
  extractItems: (page) => page.map((item) => item.key),
528
534
  });
529
535
  }
@@ -553,7 +559,7 @@ export class KeyValueStore {
553
559
  values(options = {}) {
554
560
  tryCancel();
555
561
  return createDualIterable({
556
- createPages: () => this.fetchKeyValuePages(options, (_key, value) => value),
562
+ createPages: () => this.#fetchKeyValuePages(options, (_key, value) => value),
557
563
  extractItems: (page) => page,
558
564
  });
559
565
  }
@@ -583,7 +589,7 @@ export class KeyValueStore {
583
589
  entries(options = {}) {
584
590
  tryCancel();
585
591
  return createDualIterable({
586
- createPages: () => this.fetchKeyValuePages(options, (key, value) => [key, value]),
592
+ createPages: () => this.#fetchKeyValuePages(options, (key, value) => [key, value]),
587
593
  extractItems: (page) => page,
588
594
  });
589
595
  }
@@ -605,7 +611,9 @@ export class KeyValueStore {
605
611
  /**
606
612
  * Returns a file URL for the given key.
607
613
  *
608
- * If the record does not exist or has no associated file path (i.e., it is not stored as a file), returns `undefined`.
614
+ * The URL is derived from the key, so it is also returned for a record that does not exist (yet) —
615
+ * including one written earlier in an uncommitted storage transaction. Returns `undefined` only when
616
+ * the storage has no file URLs at all (e.g. the in-memory storage).
609
617
  *
610
618
  * @param key The key of the record to generate the public URL for.
611
619
  */
@@ -737,30 +745,6 @@ export class KeyValueStore {
737
745
  const store = await this.open();
738
746
  return store.setValue(key, value, options);
739
747
  }
740
- /**
741
- * Gets the crawler input value from the default {@link KeyValueStore} associated with the current crawler run.
742
- *
743
- * The input is read from the default {@link KeyValueStore} under the configured input key
744
- * (`CRAWLEE_INPUT_KEY`, default `INPUT`).
745
- *
746
- * Note that the `getInput()` function does not cache the value read from the key-value store.
747
- * If you need to use the input multiple times in your crawler,
748
- * it is far more efficient to read it once and store it locally.
749
- *
750
- * For more information, see {@link KeyValueStore.open}
751
- * and {@link KeyValueStore.getValue}.
752
- *
753
- * @returns
754
- * Returns a promise that resolves to an object, string
755
- * or [`Buffer`](https://nodejs.org/api/buffer.html), depending
756
- * on the MIME content type of the record, or `null`
757
- * if the record is missing.
758
- * @ignore
759
- */
760
- static async getInput() {
761
- const store = await this.open();
762
- return store.getValue(store.configuration.inputKey);
763
- }
764
748
  }
765
749
  /** Normalizes a codec-serialized value into the `Buffer | ArrayBuffer` shape raw record reads promise. */
766
750
  function normalizeSerializedValue(value) {
@@ -1,4 +1,4 @@
1
- import contentTypeParser from 'content-type';
1
+ import { isTypeValid, parse as parseContentType } from 'content-type';
2
2
  import JSON5 from 'json5';
3
3
  import { jsonStringifyExtended } from '@apify/utilities';
4
4
  import { isBuffer, isStream } from '../byte_utils.js';
@@ -67,17 +67,12 @@ export function parseValue(body, contentTypeHeader) {
67
67
  // No content type at all → we have no basis for interpretation; hand back the raw value.
68
68
  if (contentTypeHeader === null)
69
69
  return body;
70
- let contentType;
71
- let charset;
72
- try {
73
- const result = contentTypeParser.parse(contentTypeHeader);
74
- contentType = result.type;
75
- charset = result.parameters.charset;
76
- }
77
- catch {
78
- // Unparseable header → keep the original value rather than a mangled string.
70
+ const result = parseContentType(contentTypeHeader);
71
+ // Unparseable header → keep the original value rather than a mangled string.
72
+ if (!isTypeValid(result.type))
79
73
  return body;
80
- }
74
+ const contentType = result.type;
75
+ const charset = result.parameters.charset;
81
76
  // If we can't successfully interpret it, we return the original value rather than mangling it.
82
77
  if (!areDataStringifiable(contentType, charset))
83
78
  return body;
@@ -13,10 +13,8 @@
13
13
  */
14
14
  export declare class RequestDeduplicationCache {
15
15
  #private;
16
- private readonly size;
17
16
  constructor(size?: number);
18
17
  get(cacheKey: string): string | null;
19
18
  add(cacheKey: string, requestId: string): void;
20
19
  clear(): void;
21
- private indexOf;
22
20
  }