@crawlee/fs-storage 4.0.0-beta.197 → 4.0.0-beta.198
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/file-system-storage.d.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type * as storage from '@crawlee/types';
|
|
2
2
|
import type { CrawleeLogger } from '@crawlee/types';
|
|
3
|
+
import type { AdoptionCandidate } from '@crawlee/fs-storage-native';
|
|
4
|
+
/** What the key-value store hooks of {@link FileSystemStorageBackend} learn about the store at hand. */
|
|
5
|
+
export interface KeyValueStoreHookOptions {
|
|
6
|
+
/** Whether the store is the run's default key-value store. */
|
|
7
|
+
isDefaultStore: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** A file-system key-value store backend as seen by {@link FileSystemStorageBackend.purgeKeyValueStore}. */
|
|
10
|
+
export interface PurgeableKeyValueStoreBackend extends storage.KeyValueStoreBackend {
|
|
11
|
+
/** Remove every record from the store except the given keys. */
|
|
12
|
+
purgeExcept(keys: string[]): Promise<void>;
|
|
13
|
+
}
|
|
3
14
|
export interface FileSystemStorageOptions {
|
|
4
15
|
/**
|
|
5
16
|
* Path to directory where the data will be saved.
|
|
@@ -25,18 +36,6 @@ export interface FileSystemStorageOptions {
|
|
|
25
36
|
* @default 'single'
|
|
26
37
|
*/
|
|
27
38
|
requestQueueAccess?: 'single' | 'shared';
|
|
28
|
-
/**
|
|
29
|
-
* The key the run input is read from — Crawlee's `inputKey` (`CRAWLEE_INPUT_KEY`).
|
|
30
|
-
*
|
|
31
|
-
* Like the conventional `INPUT`, this key may arrive in the default key-value store as a bare value
|
|
32
|
-
* file with no metadata sidecar (e.g. the Apify CLI writes the effective input to `__CLI_INPUT.json`
|
|
33
|
-
* and points the run at that key). Such a file is adopted into a record under this key when the
|
|
34
|
-
* store is opened, and the key is preserved when the default store is purged — exactly like
|
|
35
|
-
* `INPUT`, which is always treated as an input key regardless of this setting.
|
|
36
|
-
*
|
|
37
|
-
* @default 'INPUT'
|
|
38
|
-
*/
|
|
39
|
-
inputKey?: string;
|
|
40
39
|
}
|
|
41
40
|
/**
|
|
42
41
|
* A file-system storage backend backed by the native `@crawlee/fs-storage-native` Rust extension.
|
|
@@ -64,6 +63,26 @@ export declare class FileSystemStorageBackend implements storage.StorageBackend
|
|
|
64
63
|
createDatasetBackend(options?: storage.StorageIdentifier): Promise<storage.DatasetBackend>;
|
|
65
64
|
createKeyValueStoreBackend(options?: storage.StorageIdentifier): Promise<storage.KeyValueStoreBackend>;
|
|
66
65
|
createRequestQueueBackend(options?: storage.StorageIdentifier): Promise<storage.RequestQueueBackend>;
|
|
66
|
+
/**
|
|
67
|
+
* What the native `open` may turn into records: value files sitting in the store directory with no
|
|
68
|
+
* metadata sidecar, written out-of-band by a CLI, a project template, a v3 Crawlee or a text editor.
|
|
69
|
+
* Each becomes a record keyed by its filename, with the content type derived from the extension alone.
|
|
70
|
+
*
|
|
71
|
+
* Subclasses may claim specific files under a key of their own by prepending candidates — the Apify
|
|
72
|
+
* SDK adopts a bare `INPUT` or `INPUT.json` in the default store as the record `INPUT` this way.
|
|
73
|
+
*
|
|
74
|
+
* Once adopted, the file is an ordinary record: readable, listed, deletable, and — in a run-scoped
|
|
75
|
+
* store — purged on start unless {@link purgeKeyValueStore} spares it. Files adoption skips (dotfiles)
|
|
76
|
+
* are purged all the same: the purge sweeps the directory and keeps only the store metadata and the
|
|
77
|
+
* spared keys.
|
|
78
|
+
*
|
|
79
|
+
*/
|
|
80
|
+
protected keyValueStoreAdoptionCandidates(_options: KeyValueStoreHookOptions): AdoptionCandidate[];
|
|
81
|
+
/**
|
|
82
|
+
* Empties one run-scoped key-value store during {@link purge}. Subclasses may spare some keys of the
|
|
83
|
+
* default store with {@link PurgeableKeyValueStoreBackend.purgeExcept} — the Apify SDK keeps its run input.
|
|
84
|
+
*/
|
|
85
|
+
protected purgeKeyValueStore(store: PurgeableKeyValueStoreBackend, _options: KeyValueStoreHookOptions): Promise<void>;
|
|
67
86
|
storageExists(id: string, type: 'Dataset' | 'KeyValueStore' | 'RequestQueue'): Promise<boolean>;
|
|
68
87
|
/**
|
|
69
88
|
* Cleans up the run-scoped storages before the run starts, sweeping the storage directories so that
|
package/file-system-storage.js
CHANGED
|
@@ -8,7 +8,6 @@ import { RequestQueueBackend } from './resource-clients/request-queue.js';
|
|
|
8
8
|
const fileSystemStorageOptionsSchema = z.object({
|
|
9
9
|
localDataDirectory: z.string(),
|
|
10
10
|
requestQueueAccess: z.enum(['single', 'shared']).default('single'),
|
|
11
|
-
inputKey: z.string().min(1).default('INPUT'),
|
|
12
11
|
logger: schemas.logger.optional(),
|
|
13
12
|
});
|
|
14
13
|
// The native package throws at load time on platforms without a published binary (e.g.
|
|
@@ -24,8 +23,6 @@ async function importNativeModule() {
|
|
|
24
23
|
const DEFAULT_STORAGE_ALIAS = '__default__';
|
|
25
24
|
/** The directory the default storage lives in, one level below `datasets` / `key_value_stores` / etc. */
|
|
26
25
|
const DEFAULT_STORAGE_DIRECTORY = 'default';
|
|
27
|
-
/** The conventional run-input key, always treated as one alongside the configured `inputKey`. */
|
|
28
|
-
const DEFAULT_INPUT_KEY = 'INPUT';
|
|
29
26
|
/**
|
|
30
27
|
* Content types declared for adopted files. The native client infers nothing from extensions, and
|
|
31
28
|
* neither do we beyond this: a `.json` file is JSON, anything else is bytes for the
|
|
@@ -48,16 +45,13 @@ export class FileSystemStorageBackend {
|
|
|
48
45
|
requestQueuesDirectory;
|
|
49
46
|
logger;
|
|
50
47
|
requestQueueAccess;
|
|
51
|
-
/** `INPUT` plus the configured `inputKey`, deduplicated. */
|
|
52
|
-
#inputKeys;
|
|
53
48
|
#keyValueStoreBackendCache = [];
|
|
54
49
|
#datasetBackendCache = [];
|
|
55
50
|
#requestQueueBackendCache = [];
|
|
56
51
|
constructor(options) {
|
|
57
|
-
const { logger, requestQueueAccess,
|
|
52
|
+
const { logger, requestQueueAccess, localDataDirectory } = parseArgument(options, fileSystemStorageOptionsSchema);
|
|
58
53
|
this.logger = logger;
|
|
59
54
|
this.requestQueueAccess = requestQueueAccess;
|
|
60
|
-
this.#inputKeys = [...new Set([DEFAULT_INPUT_KEY, inputKey])];
|
|
61
55
|
this.localDataDirectory = localDataDirectory;
|
|
62
56
|
this.datasetsDirectory = resolve(this.localDataDirectory, 'datasets');
|
|
63
57
|
this.keyValueStoresDirectory = resolve(this.localDataDirectory, 'key_value_stores');
|
|
@@ -112,13 +106,12 @@ export class FileSystemStorageBackend {
|
|
|
112
106
|
}
|
|
113
107
|
const nativeBackend = await (await importNativeModule()).FileSystemKeyValueStoreClient.open(id, name, alias, this.localDataDirectory,
|
|
114
108
|
// useTestClock — always real wall-clock outside of native tests.
|
|
115
|
-
undefined, this
|
|
109
|
+
undefined, this.keyValueStoreAdoptionCandidates({ isDefaultStore: cacheKey === DEFAULT_STORAGE_DIRECTORY }));
|
|
116
110
|
const newStore = await KeyValueStoreBackend.create({
|
|
117
111
|
name: alias ? undefined : (name ?? cacheKey),
|
|
118
112
|
cacheKey,
|
|
119
113
|
nativeBackend,
|
|
120
114
|
logger: this.logger,
|
|
121
|
-
inputKeys: this.#inputKeys,
|
|
122
115
|
});
|
|
123
116
|
this.#keyValueStoreBackendCache.push(newStore);
|
|
124
117
|
return newStore;
|
|
@@ -146,27 +139,19 @@ export class FileSystemStorageBackend {
|
|
|
146
139
|
/**
|
|
147
140
|
* What the native `open` may turn into records: value files sitting in the store directory with no
|
|
148
141
|
* metadata sidecar, written out-of-band by a CLI, a project template, a v3 Crawlee or a text editor.
|
|
142
|
+
* Each becomes a record keyed by its filename, with the content type derived from the extension alone.
|
|
149
143
|
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* the default store holds the run input, so elsewhere an `INPUT.json` is just a file named
|
|
153
|
-
* `INPUT.json`, adopted by the trailing sweep like every other one.
|
|
144
|
+
* Subclasses may claim specific files under a key of their own by prepending candidates — the Apify
|
|
145
|
+
* SDK adopts a bare `INPUT` or `INPUT.json` in the default store as the record `INPUT` this way.
|
|
154
146
|
*
|
|
155
147
|
* Once adopted, the file is an ordinary record: readable, listed, deletable, and — in a run-scoped
|
|
156
|
-
* store — purged on start unless
|
|
148
|
+
* store — purged on start unless {@link purgeKeyValueStore} spares it. Files adoption skips (dotfiles)
|
|
149
|
+
* are purged all the same: the purge sweeps the directory and keeps only the store metadata and the
|
|
150
|
+
* spared keys.
|
|
151
|
+
*
|
|
157
152
|
*/
|
|
158
|
-
|
|
159
|
-
const inputCandidates = isDefaultStore
|
|
160
|
-
? this.#inputKeys.map((key) => ({
|
|
161
|
-
key,
|
|
162
|
-
files: [
|
|
163
|
-
{ filename: key, contentType: ADOPTED_BINARY_CONTENT_TYPE },
|
|
164
|
-
{ filename: `${key}.json`, contentType: ADOPTED_JSON_CONTENT_TYPE },
|
|
165
|
-
],
|
|
166
|
-
}))
|
|
167
|
-
: [];
|
|
153
|
+
keyValueStoreAdoptionCandidates(_options) {
|
|
168
154
|
return [
|
|
169
|
-
...inputCandidates,
|
|
170
155
|
{
|
|
171
156
|
files: [
|
|
172
157
|
{ filename: '*.json', contentType: ADOPTED_JSON_CONTENT_TYPE },
|
|
@@ -175,6 +160,13 @@ export class FileSystemStorageBackend {
|
|
|
175
160
|
},
|
|
176
161
|
];
|
|
177
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Empties one run-scoped key-value store during {@link purge}. Subclasses may spare some keys of the
|
|
165
|
+
* default store with {@link PurgeableKeyValueStoreBackend.purgeExcept} — the Apify SDK keeps its run input.
|
|
166
|
+
*/
|
|
167
|
+
async purgeKeyValueStore(store, _options) {
|
|
168
|
+
await store.purge();
|
|
169
|
+
}
|
|
178
170
|
async storageExists(id, type) {
|
|
179
171
|
let backends;
|
|
180
172
|
let baseDir;
|
|
@@ -262,9 +254,7 @@ export class FileSystemStorageBackend {
|
|
|
262
254
|
*/
|
|
263
255
|
async purge() {
|
|
264
256
|
await Promise.all([
|
|
265
|
-
this.#purgeRunScopedStorages(this.keyValueStoresDirectory, async (alias) => this.createKeyValueStoreBackend({ alias }),
|
|
266
|
-
// Only the default store holds the run input, so it is the only one that keeps `INPUT`.
|
|
267
|
-
async (store, isDefault) => (isDefault ? store.purgeExceptInput() : store.purge())),
|
|
257
|
+
this.#purgeRunScopedStorages(this.keyValueStoresDirectory, async (alias) => this.createKeyValueStoreBackend({ alias }), async (store, isDefault) => this.purgeKeyValueStore(store, { isDefaultStore: isDefault })),
|
|
268
258
|
this.#purgeRunScopedStorages(this.datasetsDirectory, async (alias) => this.createDatasetBackend({ alias }), async (store) => store.purge()),
|
|
269
259
|
this.#purgeRunScopedStorages(this.requestQueuesDirectory, async (alias) => this.createRequestQueueBackend({ alias }), async (store) => store.purge()),
|
|
270
260
|
]);
|
package/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/fs-storage",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.198",
|
|
4
4
|
"description": "A file-system storage implementation of the Apify API",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.0.0"
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@crawlee/fs-storage-native": ">=0.2.2 <0.3",
|
|
46
|
-
"@crawlee/types": "4.0.0-beta.
|
|
47
|
-
"@crawlee/utils": "4.0.0-beta.
|
|
46
|
+
"@crawlee/types": "4.0.0-beta.198",
|
|
47
|
+
"@crawlee/utils": "4.0.0-beta.198",
|
|
48
48
|
"zod": "^4.5.4"
|
|
49
49
|
},
|
|
50
50
|
"lerna": {
|
|
@@ -54,5 +54,5 @@
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "889cfffcc20b2803f0d95d7cb006ad1e7f672452"
|
|
58
58
|
}
|
|
@@ -12,11 +12,6 @@ export interface KeyValueStoreBackendOptions {
|
|
|
12
12
|
cacheKey: string;
|
|
13
13
|
nativeBackend: NativeFileSystemKeyValueStoreBackend;
|
|
14
14
|
logger?: CrawleeLogger;
|
|
15
|
-
/**
|
|
16
|
-
* The run-input keys of this store, deduplicated — `INPUT` plus `FileSystemStorageOptions.inputKey`.
|
|
17
|
-
* All that is left of the run-input special case: the keys {@link purgeExceptInput} spares.
|
|
18
|
-
*/
|
|
19
|
-
inputKeys: string[];
|
|
20
15
|
}
|
|
21
16
|
/**
|
|
22
17
|
* A file-system key-value store backend backed by the native `@crawlee/fs-storage-native` Rust
|
|
@@ -37,11 +32,10 @@ export declare class KeyValueStoreBackend extends CachedIdClient implements stor
|
|
|
37
32
|
drop(): Promise<void>;
|
|
38
33
|
purge(): Promise<void>;
|
|
39
34
|
/**
|
|
40
|
-
* Remove every record from the store except the
|
|
41
|
-
*
|
|
42
|
-
* while preserving the run's input.
|
|
35
|
+
* Remove every record from the store except the given keys. For {@link FileSystemStorageBackend}
|
|
36
|
+
* subclasses that keep some of the default store's records across the purge on start.
|
|
43
37
|
*/
|
|
44
|
-
|
|
38
|
+
purgeExcept(keys: string[]): Promise<void>;
|
|
45
39
|
listKeys(options?: storage.KeyValueStoreListKeysOptions): Promise<storage.KeyValueStoreListKeysResult>;
|
|
46
40
|
/**
|
|
47
41
|
* Generates a public `file://` URL for accessing a specific record in the key-value store.
|
|
@@ -29,14 +29,11 @@ export class KeyValueStoreBackend extends CachedIdClient {
|
|
|
29
29
|
name;
|
|
30
30
|
cacheKey;
|
|
31
31
|
#nativeBackend;
|
|
32
|
-
/** See {@link KeyValueStoreBackendOptions.inputKeys}. */
|
|
33
|
-
#inputKeys;
|
|
34
32
|
constructor(options) {
|
|
35
33
|
super();
|
|
36
34
|
this.name = options.name;
|
|
37
35
|
this.cacheKey = options.cacheKey;
|
|
38
36
|
this.#nativeBackend = options.nativeBackend;
|
|
39
|
-
this.#inputKeys = options.inputKeys;
|
|
40
37
|
}
|
|
41
38
|
get keyValueStoreDirectory() {
|
|
42
39
|
return this.#nativeBackend.pathToKvs;
|
|
@@ -56,12 +53,11 @@ export class KeyValueStoreBackend extends CachedIdClient {
|
|
|
56
53
|
await this.#nativeBackend.purge();
|
|
57
54
|
}
|
|
58
55
|
/**
|
|
59
|
-
* Remove every record from the store except the
|
|
60
|
-
*
|
|
61
|
-
* while preserving the run's input.
|
|
56
|
+
* Remove every record from the store except the given keys. For {@link FileSystemStorageBackend}
|
|
57
|
+
* subclasses that keep some of the default store's records across the purge on start.
|
|
62
58
|
*/
|
|
63
|
-
async
|
|
64
|
-
await this.#nativeBackend.purge(
|
|
59
|
+
async purgeExcept(keys) {
|
|
60
|
+
await this.#nativeBackend.purge(keys);
|
|
65
61
|
}
|
|
66
62
|
async listKeys(options = {}) {
|
|
67
63
|
const { prefix, exclusiveStartKey, limit } = parseArgument(options, schemas.keyValueStoreListKeysOptions);
|