@crawlee/core 4.0.0-beta.99 → 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.
- package/README.md +1 -1
- package/configuration.d.ts +16 -47
- package/configuration.js +13 -25
- package/debug.js +4 -4
- package/errors.d.ts +28 -38
- package/errors.js +33 -47
- package/events/event_manager.d.ts +2 -2
- package/events/event_manager.js +7 -6
- package/events/index.d.ts +1 -0
- package/events/local_event_manager.d.ts +1 -8
- package/events/local_event_manager.js +13 -13
- package/events/system_info.d.ts +38 -0
- package/index.d.ts +2 -8
- package/index.js +4 -8
- package/internal.d.ts +8 -0
- package/internal.js +9 -0
- package/log.d.ts +10 -11
- package/log.js +52 -20
- package/memory-storage/memory-storage.d.ts +15 -18
- package/memory-storage/memory-storage.js +80 -58
- package/memory-storage/resource-clients/dataset.d.ts +1 -6
- package/memory-storage/resource-clients/dataset.js +23 -31
- package/memory-storage/resource-clients/key-value-store.d.ts +1 -10
- package/memory-storage/resource-clients/key-value-store.js +43 -67
- package/memory-storage/resource-clients/request-queue.d.ts +1 -42
- package/memory-storage/resource-clients/request-queue.js +109 -117
- package/owned_or_injected.d.ts +1 -3
- package/owned_or_injected.js +17 -17
- package/package.json +17 -20
- package/proxy_configuration.d.ts +21 -26
- package/proxy_configuration.js +35 -25
- package/recoverable_state.d.ts +104 -47
- package/recoverable_state.js +199 -74
- package/request.d.ts +20 -107
- package/request.js +78 -244
- package/serialization.js +17 -16
- package/service_locator.d.ts +22 -10
- package/service_locator.js +59 -48
- package/storages/batched_adds.d.ts +37 -0
- package/storages/batched_adds.js +73 -0
- package/storages/dataset.d.ts +13 -8
- package/storages/dataset.js +149 -40
- package/storages/index.d.ts +4 -4
- package/storages/index.js +2 -4
- package/storages/key_value_store.d.ts +16 -35
- package/storages/key_value_store.js +223 -110
- package/storages/key_value_store_codec.js +6 -11
- package/storages/request_dedup_cache.d.ts +1 -4
- package/storages/request_dedup_cache.js +15 -15
- package/storages/request_list.d.ts +9 -104
- package/storages/request_list.js +236 -233
- package/storages/request_loader.d.ts +49 -18
- package/storages/request_loader.js +36 -1
- package/storages/request_manager.d.ts +86 -0
- package/storages/request_manager_tandem.d.ts +14 -38
- package/storages/request_manager_tandem.js +67 -64
- package/storages/request_queue.d.ts +23 -50
- package/storages/request_queue.js +371 -226
- package/storages/storage_instance_manager.d.ts +2 -4
- package/storages/storage_instance_manager.js +21 -21
- package/storages/storage_stats.d.ts +1 -1
- package/storages/storage_stats.js +4 -4
- package/storages/transaction.d.ts +270 -0
- package/storages/transaction.js +296 -0
- package/storages/utils.d.ts +6 -3
- package/storages/utils.js +11 -2
- package/system-info/runtime.js +7 -7
- package/url.d.ts +9 -0
- package/url.js +11 -0
- package/validators.d.ts +23 -25
- package/validators.js +14 -25
- package/autoscaling/autoscaled_pool.d.ts +0 -213
- package/autoscaling/autoscaled_pool.js +0 -378
- package/autoscaling/client_load_signal.d.ts +0 -59
- package/autoscaling/client_load_signal.js +0 -73
- package/autoscaling/concurrency_system.d.ts +0 -283
- package/autoscaling/concurrency_system.js +0 -350
- package/autoscaling/cpu_load_signal.d.ts +0 -44
- package/autoscaling/cpu_load_signal.js +0 -46
- package/autoscaling/event_loop_load_signal.d.ts +0 -54
- package/autoscaling/event_loop_load_signal.js +0 -60
- package/autoscaling/index.d.ts +0 -9
- package/autoscaling/index.js +0 -9
- package/autoscaling/load_signal.d.ts +0 -99
- package/autoscaling/load_signal.js +0 -103
- package/autoscaling/memory_load_signal.d.ts +0 -56
- package/autoscaling/memory_load_signal.js +0 -106
- package/autoscaling/snapshotter.d.ts +0 -87
- package/autoscaling/snapshotter.js +0 -67
- package/autoscaling/system_status.d.ts +0 -161
- package/autoscaling/system_status.js +0 -139
- package/autoscaling/weighted_avg.d.ts +0 -5
- package/autoscaling/weighted_avg.js +0 -14
- package/cookie_utils.d.ts +0 -44
- package/cookie_utils.js +0 -122
- package/crawlers/context_pipeline.d.ts +0 -70
- package/crawlers/context_pipeline.js +0 -122
- package/crawlers/crawler_commons.d.ts +0 -257
- package/crawlers/crawler_commons.js +0 -107
- package/crawlers/error_snapshotter.d.ts +0 -59
- package/crawlers/error_snapshotter.js +0 -117
- package/crawlers/error_tracker.d.ts +0 -54
- package/crawlers/error_tracker.js +0 -308
- package/crawlers/index.d.ts +0 -5
- package/crawlers/index.js +0 -5
- package/crawlers/internals/types.d.ts +0 -7
- package/crawlers/statistics.d.ts +0 -209
- package/crawlers/statistics.js +0 -350
- package/enqueue_links/enqueue_links.d.ts +0 -264
- package/enqueue_links/enqueue_links.js +0 -271
- package/enqueue_links/index.d.ts +0 -2
- package/enqueue_links/index.js +0 -2
- package/enqueue_links/shared.d.ts +0 -83
- package/enqueue_links/shared.js +0 -221
- package/router.d.ts +0 -309
- package/router.js +0 -309
- package/session_pool/consts.d.ts +0 -3
- package/session_pool/consts.js +0 -3
- package/session_pool/errors.d.ts +0 -7
- package/session_pool/errors.js +0 -11
- package/session_pool/fingerprint.d.ts +0 -9
- package/session_pool/fingerprint.js +0 -30
- package/session_pool/index.d.ts +0 -4
- package/session_pool/index.js +0 -4
- package/session_pool/session.d.ts +0 -161
- package/session_pool/session.js +0 -218
- package/session_pool/session_pool.d.ts +0 -246
- package/session_pool/session_pool.js +0 -386
- package/storages/access_checking.d.ts +0 -12
- package/storages/access_checking.js +0 -17
- package/storages/sitemap_request_loader.d.ts +0 -249
- package/storages/sitemap_request_loader.js +0 -432
- /package/{crawlers/internals/types.js → events/system_info.js} +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents the current status of the system.
|
|
3
|
+
*/
|
|
4
|
+
export interface SystemInfo {
|
|
5
|
+
/** If false, system is being overloaded. */
|
|
6
|
+
isSystemIdle: boolean;
|
|
7
|
+
memInfo: LoadSignalInfo;
|
|
8
|
+
eventLoopInfo: LoadSignalInfo;
|
|
9
|
+
cpuInfo: LoadSignalInfo;
|
|
10
|
+
storageBackendInfo: LoadSignalInfo;
|
|
11
|
+
memTotalBytes?: number;
|
|
12
|
+
memCurrentBytes?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Platform only property
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
cpuCurrentUsage?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Platform only property
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
isCpuOverloaded?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Platform only property
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
createdAt?: Date;
|
|
28
|
+
/**
|
|
29
|
+
* Status of additional load signals beyond the built-in four.
|
|
30
|
+
* Keys are `LoadSignal.name` values, values are overload info.
|
|
31
|
+
*/
|
|
32
|
+
loadSignalInfo?: Record<string, LoadSignalInfo>;
|
|
33
|
+
}
|
|
34
|
+
export interface LoadSignalInfo {
|
|
35
|
+
isOverloaded: boolean;
|
|
36
|
+
limitRatio: number;
|
|
37
|
+
actualRatio: number;
|
|
38
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
export * from './debug.js';
|
|
2
2
|
export * from './errors.js';
|
|
3
|
-
export * from './autoscaling/index.js';
|
|
4
3
|
export * from './configuration.js';
|
|
5
4
|
export * from './service_locator.js';
|
|
6
|
-
export * from './crawlers/index.js';
|
|
7
|
-
export * from './enqueue_links/index.js';
|
|
8
5
|
export * from './events/index.js';
|
|
9
6
|
export * from './log.js';
|
|
10
7
|
export * from './owned_or_injected.js';
|
|
11
8
|
export * from './proxy_configuration.js';
|
|
12
9
|
export * from './request.js';
|
|
13
|
-
export * from './router.js';
|
|
14
10
|
export * from './serialization.js';
|
|
15
|
-
export * from './session_pool/index.js';
|
|
16
11
|
export * from './storages/index.js';
|
|
17
12
|
export * from './memory-storage/index.js';
|
|
18
|
-
export
|
|
19
|
-
export * from './cookie_utils.js';
|
|
13
|
+
export { ArgumentValidationError, validators } from './validators.js';
|
|
20
14
|
export * from './recoverable_state.js';
|
|
21
|
-
export { PseudoUrl } from '@apify/pseudo_url';
|
|
22
15
|
export type { StorageBackend } from '@crawlee/types';
|
|
16
|
+
export { EnqueueStrategy } from '@crawlee/utils';
|
package/index.js
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
export * from './debug.js';
|
|
2
2
|
export * from './errors.js';
|
|
3
|
-
export * from './autoscaling/index.js';
|
|
4
3
|
export * from './configuration.js';
|
|
5
4
|
export * from './service_locator.js';
|
|
6
|
-
export * from './crawlers/index.js';
|
|
7
|
-
export * from './enqueue_links/index.js';
|
|
8
5
|
export * from './events/index.js';
|
|
9
6
|
export * from './log.js';
|
|
10
7
|
export * from './owned_or_injected.js';
|
|
11
8
|
export * from './proxy_configuration.js';
|
|
12
9
|
export * from './request.js';
|
|
13
|
-
export * from './router.js';
|
|
14
10
|
export * from './serialization.js';
|
|
15
|
-
export * from './session_pool/index.js';
|
|
16
11
|
export * from './storages/index.js';
|
|
17
12
|
export * from './memory-storage/index.js';
|
|
18
|
-
export
|
|
19
|
-
|
|
13
|
+
// Not `export *`: the rest of the module re-exports `@crawlee/utils/internal` symbols, which carry no
|
|
14
|
+
// semver guarantees and must not reach the public surface. Internal consumers import them directly.
|
|
15
|
+
export { ArgumentValidationError, validators } from './validators.js';
|
|
20
16
|
export * from './recoverable_state.js';
|
|
21
|
-
export {
|
|
17
|
+
export { EnqueueStrategy } from '@crawlee/utils';
|
package/internal.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './iterables.js';
|
|
2
|
+
export * from './storages/batched_adds.js';
|
|
3
|
+
export { joinRequestSourceStatuses } from './storages/request_loader.js';
|
|
4
|
+
export * from './system-info/cpu-info.js';
|
|
5
|
+
export * from './system-info/memory-info.js';
|
|
6
|
+
export * from './system-info/ps-tree.js';
|
|
7
|
+
export * from './system-info/runtime.js';
|
|
8
|
+
export * from './url.js';
|
package/internal.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Not part of the public API: internals that other Crawlee packages need but that carry no semver guarantees.
|
|
2
|
+
export * from './iterables.js';
|
|
3
|
+
export * from './storages/batched_adds.js';
|
|
4
|
+
export { joinRequestSourceStatuses } from './storages/request_loader.js';
|
|
5
|
+
export * from './system-info/cpu-info.js';
|
|
6
|
+
export * from './system-info/memory-info.js';
|
|
7
|
+
export * from './system-info/ps-tree.js';
|
|
8
|
+
export * from './system-info/runtime.js';
|
|
9
|
+
export * from './url.js';
|
package/log.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { CrawleeLogger, CrawleeLoggerOptions } from '@crawlee/types';
|
|
1
|
+
import type { CrawleeLogger, CrawleeLoggerOptions, LogOptions } from '@crawlee/types';
|
|
2
2
|
import type { LoggerOptions } from '@apify/log';
|
|
3
3
|
import log, { Log, Logger, LoggerJson, LoggerText, LogLevel } from '@apify/log';
|
|
4
|
-
export type { CrawleeLogger, CrawleeLoggerOptions };
|
|
4
|
+
export type { CrawleeLogger, CrawleeLoggerOptions, LogOptions };
|
|
5
5
|
/**
|
|
6
6
|
* Abstract base class for custom Crawlee logger implementations.
|
|
7
7
|
*
|
|
@@ -33,8 +33,7 @@ export type { CrawleeLogger, CrawleeLoggerOptions };
|
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
35
|
export declare abstract class BaseCrawleeLogger implements CrawleeLogger {
|
|
36
|
-
private
|
|
37
|
-
private readonly warningsLogged;
|
|
36
|
+
#private;
|
|
38
37
|
constructor(options?: Partial<CrawleeLoggerOptions>);
|
|
39
38
|
/**
|
|
40
39
|
* Core logging method. Subclasses must implement this to dispatch log messages
|
|
@@ -56,14 +55,14 @@ export declare abstract class BaseCrawleeLogger implements CrawleeLogger {
|
|
|
56
55
|
getOptions(): CrawleeLoggerOptions;
|
|
57
56
|
setOptions(options: Partial<CrawleeLoggerOptions>): void;
|
|
58
57
|
child(options: Partial<CrawleeLoggerOptions>): CrawleeLogger;
|
|
59
|
-
error(message: string, data?: Record<string, unknown
|
|
58
|
+
error(message: string, data?: Record<string, unknown>, options?: LogOptions): void;
|
|
60
59
|
exception(exception: Error, message: string, data?: Record<string, unknown>): void;
|
|
61
|
-
softFail(message: string, data?: Record<string, unknown
|
|
62
|
-
warning(message: string, data?: Record<string, unknown
|
|
60
|
+
softFail(message: string, data?: Record<string, unknown>, options?: LogOptions): void;
|
|
61
|
+
warning(message: string, data?: Record<string, unknown>, options?: LogOptions): void;
|
|
63
62
|
warningOnce(message: string): void;
|
|
64
|
-
info(message: string, data?: Record<string, unknown
|
|
65
|
-
debug(message: string, data?: Record<string, unknown
|
|
66
|
-
perf(message: string, data?: Record<string, unknown
|
|
63
|
+
info(message: string, data?: Record<string, unknown>, options?: LogOptions): void;
|
|
64
|
+
debug(message: string, data?: Record<string, unknown>, options?: LogOptions): void;
|
|
65
|
+
perf(message: string, data?: Record<string, unknown>, options?: LogOptions): void;
|
|
67
66
|
deprecated(message: string): void;
|
|
68
67
|
}
|
|
69
68
|
/**
|
|
@@ -73,7 +72,7 @@ export declare abstract class BaseCrawleeLogger implements CrawleeLogger {
|
|
|
73
72
|
* Users who want to use a different logging library should implement {@link BaseCrawleeLogger} directly.
|
|
74
73
|
*/
|
|
75
74
|
export declare class ApifyLogAdapter extends BaseCrawleeLogger {
|
|
76
|
-
private
|
|
75
|
+
#private;
|
|
77
76
|
constructor(apifyLog: Log, options?: Partial<CrawleeLoggerOptions>);
|
|
78
77
|
logWithLevel(level: number, message: string, data?: Record<string, unknown>): void;
|
|
79
78
|
protected createChild(options: Partial<CrawleeLoggerOptions>): CrawleeLogger;
|
package/log.js
CHANGED
|
@@ -30,21 +30,43 @@ import log, { Log, Logger, LoggerJson, LoggerText, LogLevel } from '@apify/log';
|
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
32
|
export class BaseCrawleeLogger {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
// Note: If wrapping logger in a Proxy, unbound methods calling #-fields throw TypeError
|
|
34
|
+
// unless bound to the target (see createLogProxy in adaptive-playwright-crawler.ts).
|
|
35
|
+
#options;
|
|
36
|
+
#loggedOnce = new Set();
|
|
35
37
|
constructor(options = {}) {
|
|
36
|
-
this
|
|
38
|
+
this.#options = options;
|
|
37
39
|
}
|
|
38
40
|
getOptions() {
|
|
39
|
-
return this
|
|
41
|
+
return this.#options;
|
|
40
42
|
}
|
|
41
43
|
setOptions(options) {
|
|
42
|
-
this
|
|
44
|
+
this.#options = { ...this.#options, ...options };
|
|
43
45
|
}
|
|
44
46
|
child(options) {
|
|
45
47
|
return this.createChild(options);
|
|
46
48
|
}
|
|
47
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Whether `once` suppresses this message. Keyed by level as well as text, so that a message logged once as
|
|
51
|
+
* `info` does not swallow the same text logged as a `warning`.
|
|
52
|
+
*
|
|
53
|
+
* Handled here rather than in {@link BaseCrawleeLogger.logWithLevel} so that every logger gets it, instead
|
|
54
|
+
* of each implementation having to dedupe for itself.
|
|
55
|
+
*/
|
|
56
|
+
#suppressedAsRepeat(level, message, options) {
|
|
57
|
+
if (!options?.once) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const key = `${level}:${message}`;
|
|
61
|
+
if (this.#loggedOnce.has(key)) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
this.#loggedOnce.add(key);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
error(message, data, options) {
|
|
68
|
+
if (this.#suppressedAsRepeat(LogLevel.ERROR, message, options))
|
|
69
|
+
return;
|
|
48
70
|
this.logWithLevel(LogLevel.ERROR, message, data);
|
|
49
71
|
}
|
|
50
72
|
exception(exception, message, data) {
|
|
@@ -54,25 +76,35 @@ export class BaseCrawleeLogger {
|
|
|
54
76
|
exception,
|
|
55
77
|
});
|
|
56
78
|
}
|
|
57
|
-
softFail(message, data) {
|
|
79
|
+
softFail(message, data, options) {
|
|
80
|
+
if (this.#suppressedAsRepeat(LogLevel.SOFT_FAIL, message, options))
|
|
81
|
+
return;
|
|
58
82
|
this.logWithLevel(LogLevel.SOFT_FAIL, message, data);
|
|
59
83
|
}
|
|
60
|
-
warning(message, data) {
|
|
84
|
+
warning(message, data, options) {
|
|
85
|
+
if (this.#suppressedAsRepeat(LogLevel.WARNING, message, options))
|
|
86
|
+
return;
|
|
61
87
|
this.logWithLevel(LogLevel.WARNING, message, data);
|
|
62
88
|
}
|
|
63
89
|
warningOnce(message) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
info(message, data) {
|
|
90
|
+
// Gates before delegating, so that a suppressed repeat is not even a `warning()` call.
|
|
91
|
+
if (this.#suppressedAsRepeat(LogLevel.WARNING, message, { once: true }))
|
|
92
|
+
return;
|
|
93
|
+
this.warning(message);
|
|
94
|
+
}
|
|
95
|
+
info(message, data, options) {
|
|
96
|
+
if (this.#suppressedAsRepeat(LogLevel.INFO, message, options))
|
|
97
|
+
return;
|
|
70
98
|
this.logWithLevel(LogLevel.INFO, message, data);
|
|
71
99
|
}
|
|
72
|
-
debug(message, data) {
|
|
100
|
+
debug(message, data, options) {
|
|
101
|
+
if (this.#suppressedAsRepeat(LogLevel.DEBUG, message, options))
|
|
102
|
+
return;
|
|
73
103
|
this.logWithLevel(LogLevel.DEBUG, message, data);
|
|
74
104
|
}
|
|
75
|
-
perf(message, data) {
|
|
105
|
+
perf(message, data, options) {
|
|
106
|
+
if (this.#suppressedAsRepeat(LogLevel.PERF, message, options))
|
|
107
|
+
return;
|
|
76
108
|
this.logWithLevel(LogLevel.PERF, `[PERF] ${message}`, data);
|
|
77
109
|
}
|
|
78
110
|
deprecated(message) {
|
|
@@ -86,16 +118,16 @@ export class BaseCrawleeLogger {
|
|
|
86
118
|
* Users who want to use a different logging library should implement {@link BaseCrawleeLogger} directly.
|
|
87
119
|
*/
|
|
88
120
|
export class ApifyLogAdapter extends BaseCrawleeLogger {
|
|
89
|
-
apifyLog;
|
|
121
|
+
#apifyLog;
|
|
90
122
|
constructor(apifyLog, options) {
|
|
91
123
|
super(options ?? {});
|
|
92
|
-
this
|
|
124
|
+
this.#apifyLog = apifyLog;
|
|
93
125
|
}
|
|
94
126
|
logWithLevel(level, message, data) {
|
|
95
|
-
this
|
|
127
|
+
this.#apifyLog.internal(level, message, data);
|
|
96
128
|
}
|
|
97
129
|
createChild(options) {
|
|
98
|
-
return new ApifyLogAdapter(this
|
|
130
|
+
return new ApifyLogAdapter(this.#apifyLog.child({ prefix: options.prefix ?? null }), {
|
|
99
131
|
...this.getOptions(),
|
|
100
132
|
...options,
|
|
101
133
|
});
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type * as storage from '@crawlee/types';
|
|
2
2
|
import type { CrawleeLogger } from '@crawlee/types';
|
|
3
|
-
import { DatasetBackend } from './resource-clients/dataset.js';
|
|
4
|
-
import { KeyValueStoreBackend } from './resource-clients/key-value-store.js';
|
|
5
|
-
import { RequestQueueBackend } from './resource-clients/request-queue.js';
|
|
6
3
|
export interface MemoryStorageOptions {
|
|
7
4
|
/**
|
|
8
5
|
* Optional logger for MemoryStorageBackend warnings.
|
|
@@ -10,32 +7,32 @@ export interface MemoryStorageOptions {
|
|
|
10
7
|
logger?: CrawleeLogger;
|
|
11
8
|
}
|
|
12
9
|
export declare class MemoryStorageBackend implements storage.StorageBackend {
|
|
10
|
+
#private;
|
|
13
11
|
readonly logger?: CrawleeLogger;
|
|
14
|
-
/**
|
|
15
|
-
* Unique per-instance cache partition key. Mirrors the way `FileSystemStorageBackend` partitions its
|
|
16
|
-
* cache by storage directory: two distinct `MemoryStorageBackend` instances must not share cached backends.
|
|
17
|
-
*/
|
|
18
|
-
private readonly instanceCacheKey;
|
|
19
|
-
readonly keyValueStoreBackendCache: KeyValueStoreBackend[];
|
|
20
|
-
readonly datasetBackendCache: DatasetBackend[];
|
|
21
|
-
readonly requestQueueBackendCache: RequestQueueBackend[];
|
|
22
12
|
constructor(options?: MemoryStorageOptions);
|
|
23
13
|
/**
|
|
24
14
|
* Return a per-instance unique cache key so that distinct `MemoryStorageBackend` instances get separate
|
|
25
15
|
* cache partitions in the storage backend cache.
|
|
26
16
|
*/
|
|
27
17
|
getStorageBackendCacheKey(): string;
|
|
28
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Evict a cached backend so that a dropped storage is no longer resolved by `createXBackend`,
|
|
20
|
+
* reported by `storageExists` or visited by `purge`. Returns whether the backend was cached, which
|
|
21
|
+
* tells the caller whether it still owns in-memory state worth clearing.
|
|
22
|
+
*
|
|
23
|
+
* The resource clients own their own entry's lifetime but must not reach into the caches directly.
|
|
24
|
+
* Because a client is only ever constructed by `createXBackend`, which caches it immediately, the
|
|
25
|
+
* entry matching `id` is always the caller itself.
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
evictBackend(type: 'Dataset' | 'KeyValueStore' | 'RequestQueue', id: string): boolean;
|
|
29
29
|
createDatasetBackend(options?: storage.StorageIdentifier): Promise<storage.DatasetBackend>;
|
|
30
30
|
createKeyValueStoreBackend(options?: storage.StorageIdentifier): Promise<storage.KeyValueStoreBackend>;
|
|
31
|
-
createRequestQueueBackend(options?: storage.StorageIdentifier): Promise<RequestQueueBackend>;
|
|
31
|
+
createRequestQueueBackend(options?: storage.StorageIdentifier): Promise<storage.RequestQueueBackend>;
|
|
32
32
|
storageExists(id: string, type: 'Dataset' | 'KeyValueStore' | 'RequestQueue'): Promise<boolean>;
|
|
33
33
|
/**
|
|
34
|
-
* Cleans up the
|
|
35
|
-
* resets the in-memory state of the cached
|
|
36
|
-
*
|
|
37
|
-
* As with `FileSystemStorageBackend`, the run's input (the `INPUT` key in the default key-value
|
|
38
|
-
* store) is preserved — only the rest of the default storages is cleared.
|
|
34
|
+
* Cleans up the run-scoped storages before the run starts. For the in-memory storage this simply
|
|
35
|
+
* resets the in-memory state of the cached backends.
|
|
39
36
|
*/
|
|
40
37
|
purge(): Promise<void>;
|
|
41
38
|
/**
|
|
@@ -2,16 +2,18 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { DatasetBackend } from './resource-clients/dataset.js';
|
|
3
3
|
import { KeyValueStoreBackend } from './resource-clients/key-value-store.js';
|
|
4
4
|
import { RequestQueueBackend } from './resource-clients/request-queue.js';
|
|
5
|
+
/** The alias the default (unnamed) storage is opened under. */
|
|
6
|
+
const DEFAULT_STORAGE_ALIAS = '__default__';
|
|
5
7
|
export class MemoryStorageBackend {
|
|
6
8
|
logger;
|
|
7
9
|
/**
|
|
8
10
|
* Unique per-instance cache partition key. Mirrors the way `FileSystemStorageBackend` partitions its
|
|
9
11
|
* cache by storage directory: two distinct `MemoryStorageBackend` instances must not share cached backends.
|
|
10
12
|
*/
|
|
11
|
-
instanceCacheKey = `MemoryStorageBackend:${randomUUID()}`;
|
|
12
|
-
keyValueStoreBackendCache = [];
|
|
13
|
-
datasetBackendCache = [];
|
|
14
|
-
requestQueueBackendCache = [];
|
|
13
|
+
#instanceCacheKey = `MemoryStorageBackend:${randomUUID()}`;
|
|
14
|
+
#keyValueStoreBackendCache = [];
|
|
15
|
+
#datasetBackendCache = [];
|
|
16
|
+
#requestQueueBackendCache = [];
|
|
15
17
|
constructor(options = {}) {
|
|
16
18
|
this.logger = options.logger;
|
|
17
19
|
}
|
|
@@ -20,80 +22,108 @@ export class MemoryStorageBackend {
|
|
|
20
22
|
* cache partitions in the storage backend cache.
|
|
21
23
|
*/
|
|
22
24
|
getStorageBackendCacheKey() {
|
|
23
|
-
return this
|
|
25
|
+
return this.#instanceCacheKey;
|
|
24
26
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Evict a cached backend so that a dropped storage is no longer resolved by `createXBackend`,
|
|
29
|
+
* reported by `storageExists` or visited by `purge`. Returns whether the backend was cached, which
|
|
30
|
+
* tells the caller whether it still owns in-memory state worth clearing.
|
|
31
|
+
*
|
|
32
|
+
* The resource clients own their own entry's lifetime but must not reach into the caches directly.
|
|
33
|
+
* Because a client is only ever constructed by `createXBackend`, which caches it immediately, the
|
|
34
|
+
* entry matching `id` is always the caller itself.
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
evictBackend(type, id) {
|
|
38
|
+
let cache;
|
|
39
|
+
switch (type) {
|
|
40
|
+
case 'Dataset':
|
|
41
|
+
cache = this.#datasetBackendCache;
|
|
42
|
+
break;
|
|
43
|
+
case 'KeyValueStore':
|
|
44
|
+
cache = this.#keyValueStoreBackendCache;
|
|
45
|
+
break;
|
|
46
|
+
case 'RequestQueue':
|
|
47
|
+
cache = this.#requestQueueBackendCache;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
const index = cache.findIndex((entry) => entry.id === id);
|
|
51
|
+
if (index === -1) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
cache.splice(index, 1);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
static #resolveStorageKey(options) {
|
|
58
|
+
// No identifier at all means the default storage, which is opened under the reserved alias —
|
|
59
|
+
// same rule as `resolveStorageIdentifier` in the storage frontends, so that a backend used
|
|
60
|
+
// directly lands on the very storage the frontends would have opened.
|
|
61
|
+
const alias = options.alias || (!options.id && !options.name ? DEFAULT_STORAGE_ALIAS : undefined);
|
|
62
|
+
// `alias` covers the identifier-less case, so one of the three is always set.
|
|
63
|
+
const rawKey = alias ?? options.name ?? options.id;
|
|
28
64
|
// Normalize the internal __default__ alias to the user-facing 'default' name.
|
|
29
|
-
const cacheKey = rawKey ===
|
|
30
|
-
return { isAlias, cacheKey };
|
|
65
|
+
const cacheKey = rawKey === DEFAULT_STORAGE_ALIAS ? 'default' : rawKey;
|
|
66
|
+
return { isAlias: alias !== undefined, cacheKey };
|
|
31
67
|
}
|
|
32
68
|
async createDatasetBackend(options = {}) {
|
|
33
|
-
const { isAlias, cacheKey } = MemoryStorageBackend
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return found;
|
|
40
|
-
}
|
|
69
|
+
const { isAlias, cacheKey } = MemoryStorageBackend.#resolveStorageKey(options);
|
|
70
|
+
const found = this.#datasetBackendCache.find((store) => store.id === cacheKey ||
|
|
71
|
+
store.name?.toLowerCase() === cacheKey.toLowerCase() ||
|
|
72
|
+
store.cacheKey.toLowerCase() === cacheKey.toLowerCase());
|
|
73
|
+
if (found) {
|
|
74
|
+
return found;
|
|
41
75
|
}
|
|
42
76
|
const newStore = new DatasetBackend({
|
|
43
77
|
name: isAlias ? undefined : cacheKey,
|
|
44
78
|
cacheKey,
|
|
45
79
|
storageBackend: this,
|
|
46
80
|
});
|
|
47
|
-
this
|
|
81
|
+
this.#datasetBackendCache.push(newStore);
|
|
48
82
|
return newStore;
|
|
49
83
|
}
|
|
50
84
|
async createKeyValueStoreBackend(options = {}) {
|
|
51
|
-
const { isAlias, cacheKey } = MemoryStorageBackend
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return found;
|
|
58
|
-
}
|
|
85
|
+
const { isAlias, cacheKey } = MemoryStorageBackend.#resolveStorageKey(options);
|
|
86
|
+
const found = this.#keyValueStoreBackendCache.find((store) => store.id === cacheKey ||
|
|
87
|
+
store.name?.toLowerCase() === cacheKey.toLowerCase() ||
|
|
88
|
+
store.cacheKey.toLowerCase() === cacheKey.toLowerCase());
|
|
89
|
+
if (found) {
|
|
90
|
+
return found;
|
|
59
91
|
}
|
|
60
92
|
const newStore = new KeyValueStoreBackend({
|
|
61
93
|
name: isAlias ? undefined : cacheKey,
|
|
62
94
|
cacheKey,
|
|
63
95
|
storageBackend: this,
|
|
64
96
|
});
|
|
65
|
-
this
|
|
97
|
+
this.#keyValueStoreBackendCache.push(newStore);
|
|
66
98
|
return newStore;
|
|
67
99
|
}
|
|
68
100
|
async createRequestQueueBackend(options = {}) {
|
|
69
|
-
const { isAlias, cacheKey } = MemoryStorageBackend
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return found;
|
|
76
|
-
}
|
|
101
|
+
const { isAlias, cacheKey } = MemoryStorageBackend.#resolveStorageKey(options);
|
|
102
|
+
const found = this.#requestQueueBackendCache.find((queue) => queue.id === cacheKey ||
|
|
103
|
+
queue.name?.toLowerCase() === cacheKey.toLowerCase() ||
|
|
104
|
+
queue.cacheKey.toLowerCase() === cacheKey.toLowerCase());
|
|
105
|
+
if (found) {
|
|
106
|
+
return found;
|
|
77
107
|
}
|
|
78
108
|
const newStore = new RequestQueueBackend({
|
|
79
109
|
name: isAlias ? undefined : cacheKey,
|
|
80
110
|
cacheKey,
|
|
81
111
|
storageBackend: this,
|
|
82
112
|
});
|
|
83
|
-
this
|
|
113
|
+
this.#requestQueueBackendCache.push(newStore);
|
|
84
114
|
return newStore;
|
|
85
115
|
}
|
|
86
116
|
async storageExists(id, type) {
|
|
87
117
|
let backends;
|
|
88
118
|
switch (type) {
|
|
89
119
|
case 'Dataset':
|
|
90
|
-
backends = this
|
|
120
|
+
backends = this.#datasetBackendCache;
|
|
91
121
|
break;
|
|
92
122
|
case 'KeyValueStore':
|
|
93
|
-
backends = this
|
|
123
|
+
backends = this.#keyValueStoreBackendCache;
|
|
94
124
|
break;
|
|
95
125
|
case 'RequestQueue':
|
|
96
|
-
backends = this
|
|
126
|
+
backends = this.#requestQueueBackendCache;
|
|
97
127
|
break;
|
|
98
128
|
default:
|
|
99
129
|
return false;
|
|
@@ -102,28 +132,20 @@ export class MemoryStorageBackend {
|
|
|
102
132
|
return backends.some((store) => store.id === id);
|
|
103
133
|
}
|
|
104
134
|
/**
|
|
105
|
-
* Cleans up the
|
|
106
|
-
* resets the in-memory state of the cached
|
|
107
|
-
*
|
|
108
|
-
* As with `FileSystemStorageBackend`, the run's input (the `INPUT` key in the default key-value
|
|
109
|
-
* store) is preserved — only the rest of the default storages is cleared.
|
|
135
|
+
* Cleans up the run-scoped storages before the run starts. For the in-memory storage this simply
|
|
136
|
+
* resets the in-memory state of the cached backends.
|
|
110
137
|
*/
|
|
111
138
|
async purge() {
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const isDefault = (store) => store.name === 'default' || store.cacheKey === 'default';
|
|
118
|
-
const purgeDefaults = async (cache, purgeStore) => {
|
|
119
|
-
await Promise.all(cache.filter(isDefault).map(async (store) => purgeStore(store)));
|
|
139
|
+
// `#resolveStorageKey` leaves `name` unset for the default and alias-keyed storages, which is what
|
|
140
|
+
// marks them as run-scoped. `'default'` is the exception — it collapses onto the default storage.
|
|
141
|
+
const isRunScoped = (store) => store.name === undefined || store.name === 'default';
|
|
142
|
+
const purgeRunScoped = async (cache, purgeStore) => {
|
|
143
|
+
await Promise.all(cache.filter(isRunScoped).map(async (store) => purgeStore(store)));
|
|
120
144
|
};
|
|
121
145
|
await Promise.all([
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
purgeDefaults(this.datasetBackendCache, async (store) => store.purge()),
|
|
126
|
-
purgeDefaults(this.requestQueueBackendCache, async (store) => store.purge()),
|
|
146
|
+
purgeRunScoped(this.#keyValueStoreBackendCache, async (store) => store.purge()),
|
|
147
|
+
purgeRunScoped(this.#datasetBackendCache, async (store) => store.purge()),
|
|
148
|
+
purgeRunScoped(this.#requestQueueBackendCache, async (store) => store.purge()),
|
|
127
149
|
]);
|
|
128
150
|
}
|
|
129
151
|
/**
|
|
@@ -14,6 +14,7 @@ export interface DatasetBackendOptions {
|
|
|
14
14
|
storageBackend: MemoryStorageBackend;
|
|
15
15
|
}
|
|
16
16
|
export declare class DatasetBackend<Data extends Dictionary = Dictionary> extends BaseClient implements storage.DatasetBackend<Data> {
|
|
17
|
+
#private;
|
|
17
18
|
name?: string;
|
|
18
19
|
/**
|
|
19
20
|
* The key used for cache lookup. For named storages, this equals the name. For alias (unnamed)
|
|
@@ -24,17 +25,11 @@ export declare class DatasetBackend<Data extends Dictionary = Dictionary> extend
|
|
|
24
25
|
accessedAt: Date;
|
|
25
26
|
modifiedAt: Date;
|
|
26
27
|
itemCount: number;
|
|
27
|
-
private readonly datasetEntries;
|
|
28
|
-
private readonly storageBackend;
|
|
29
28
|
constructor(options: DatasetBackendOptions);
|
|
30
29
|
getMetadata(): Promise<storage.DatasetInfo>;
|
|
31
30
|
drop(): Promise<void>;
|
|
32
31
|
purge(): Promise<void>;
|
|
33
32
|
getData(options?: storage.DatasetBackendListOptions): Promise<storage.PaginatedList<Data>>;
|
|
34
|
-
private getDataPage;
|
|
35
33
|
pushData(items: Data[]): Promise<void>;
|
|
36
34
|
toDatasetInfo(): storage.DatasetInfo;
|
|
37
|
-
private generateLocalEntryName;
|
|
38
|
-
private getStartAndEndIndexes;
|
|
39
|
-
private updateTimestamps;
|
|
40
35
|
}
|