@crawlee/fs-storage 4.0.0-beta.67
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 +153 -0
- package/file-system-storage.d.ts +88 -0
- package/file-system-storage.d.ts.map +1 -0
- package/file-system-storage.js +230 -0
- package/file-system-storage.js.map +1 -0
- package/index.d.ts +2 -0
- package/index.d.ts.map +1 -0
- package/index.js +2 -0
- package/index.js.map +1 -0
- package/package.json +57 -0
- package/resource-clients/cached-id-client.d.ts +12 -0
- package/resource-clients/cached-id-client.d.ts.map +1 -0
- package/resource-clients/cached-id-client.js +14 -0
- package/resource-clients/cached-id-client.js.map +1 -0
- package/resource-clients/dataset.d.ts +37 -0
- package/resource-clients/dataset.d.ts.map +1 -0
- package/resource-clients/dataset.js +76 -0
- package/resource-clients/dataset.js.map +1 -0
- package/resource-clients/key-value-store.d.ts +82 -0
- package/resource-clients/key-value-store.d.ts.map +1 -0
- package/resource-clients/key-value-store.js +240 -0
- package/resource-clients/key-value-store.js.map +1 -0
- package/resource-clients/request-queue.d.ts +52 -0
- package/resource-clients/request-queue.d.ts.map +1 -0
- package/resource-clients/request-queue.js +120 -0
- package/resource-clients/request-queue.js.map +1 -0
- package/utils.d.ts +2 -0
- package/utils.d.ts.map +1 -0
- package/utils.js +6 -0
- package/utils.js.map +1 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { s } from '@sapphire/shapeshift';
|
|
2
|
+
import { CachedIdClient } from './cached-id-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* `getData` options accepted by the high-level `Dataset` frontend but not supported by the native
|
|
5
|
+
* file-system backend (it can only paginate raw items by `offset`/`limit`/`desc`). They are silently
|
|
6
|
+
* ignored, so we warn once if a caller passes any of them.
|
|
7
|
+
*
|
|
8
|
+
* Implementing these in the native client is tracked in
|
|
9
|
+
* https://github.com/apify/crawlee-storage/issues/8.
|
|
10
|
+
*/
|
|
11
|
+
const UNSUPPORTED_GET_DATA_OPTIONS = ['clean', 'fields', 'omit', 'skipHidden', 'skipEmpty'];
|
|
12
|
+
/**
|
|
13
|
+
* A file-system dataset backend backed by the native `@crawlee/fs-storage-native` Rust extension.
|
|
14
|
+
*
|
|
15
|
+
* This class is a thin adapter: it forwards each operation to the native client (which owns the
|
|
16
|
+
* on-disk format, timestamps and item counting) and converts results into the shapes expected by
|
|
17
|
+
* the `@crawlee/types` interfaces.
|
|
18
|
+
*/
|
|
19
|
+
export class DatasetBackend extends CachedIdClient {
|
|
20
|
+
name;
|
|
21
|
+
cacheKey;
|
|
22
|
+
nativeBackend;
|
|
23
|
+
logger;
|
|
24
|
+
constructor(options) {
|
|
25
|
+
super();
|
|
26
|
+
this.name = options.name;
|
|
27
|
+
this.cacheKey = options.cacheKey;
|
|
28
|
+
this.nativeBackend = options.nativeBackend;
|
|
29
|
+
this.logger = options.logger;
|
|
30
|
+
}
|
|
31
|
+
get datasetDirectory() {
|
|
32
|
+
return this.nativeBackend.pathToDataset;
|
|
33
|
+
}
|
|
34
|
+
static async create(options) {
|
|
35
|
+
const backend = new DatasetBackend(options);
|
|
36
|
+
backend._cachedId = (await options.nativeBackend.getMetadata()).id;
|
|
37
|
+
return backend;
|
|
38
|
+
}
|
|
39
|
+
async getMetadata() {
|
|
40
|
+
return this.nativeBackend.getMetadata();
|
|
41
|
+
}
|
|
42
|
+
async drop() {
|
|
43
|
+
await this.nativeBackend.dropStorage();
|
|
44
|
+
}
|
|
45
|
+
async purge() {
|
|
46
|
+
await this.nativeBackend.purge();
|
|
47
|
+
}
|
|
48
|
+
async pushData(items) {
|
|
49
|
+
await this.nativeBackend.pushData(items);
|
|
50
|
+
}
|
|
51
|
+
async getData(options = {}) {
|
|
52
|
+
const passedOptions = options;
|
|
53
|
+
const ignored = UNSUPPORTED_GET_DATA_OPTIONS.filter((key) => passedOptions[key] !== undefined);
|
|
54
|
+
if (ignored.length > 0) {
|
|
55
|
+
this.logger?.warning?.(`getData() options [${ignored.join(', ')}] are not supported by the file-system dataset ` +
|
|
56
|
+
`and were ignored. Only "offset", "limit" and "desc" are honored.`);
|
|
57
|
+
}
|
|
58
|
+
const { desc, limit, offset } = s
|
|
59
|
+
.object({
|
|
60
|
+
desc: s.boolean().optional(),
|
|
61
|
+
limit: s.number().int().optional(),
|
|
62
|
+
offset: s.number().int().optional(),
|
|
63
|
+
})
|
|
64
|
+
.parse(options);
|
|
65
|
+
const page = await this.nativeBackend.getData(offset ?? 0, limit, desc ?? false, false);
|
|
66
|
+
return {
|
|
67
|
+
count: page.count,
|
|
68
|
+
desc: page.desc,
|
|
69
|
+
items: page.items,
|
|
70
|
+
limit: page.limit,
|
|
71
|
+
offset: page.offset,
|
|
72
|
+
total: page.total,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=dataset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataset.js","sourceRoot":"","sources":["../../src/resource-clients/dataset.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,sBAAsB,CAAC;AAIzC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,4BAA4B,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,CAAU,CAAC;AAcrG;;;;;;GAMG;AACH,MAAM,OAAO,cACT,SAAQ,cAAc;IAGb,IAAI,CAAU;IACd,QAAQ,CAAS;IAET,aAAa,CAAiC;IAC9C,MAAM,CAAiB;IAExC,YAAY,OAA8B;QACtC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QAC3C,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IACjC,CAAC;IAED,IAAI,gBAAgB;QAChB,OAAO,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC;IAC5C,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,MAAM,CACf,OAA8B;QAE9B,MAAM,OAAO,GAAG,IAAI,cAAc,CAAO,OAAO,CAAC,CAAC;QAClD,OAAO,CAAC,SAAS,GAAG,CAAC,MAAM,OAAO,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC;QACnE,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,WAAW;QACb,OAAO,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,IAAI;QACN,MAAM,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,KAAK;QACP,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAa;QACxB,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,UAA6C,EAAE;QACzD,MAAM,aAAa,GAAG,OAAkC,CAAC;QACzD,MAAM,OAAO,GAAG,4BAA4B,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC;QAC/F,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAClB,sBAAsB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,iDAAiD;gBACrF,kEAAkE,CACzE,CAAC;QACN,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC;aAC5B,MAAM,CAAC;YACJ,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;YAC5B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;YAClC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;SACtC,CAAC;aACD,KAAK,CAAC,OAAO,CAAC,CAAC;QAEpB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,IAAI,KAAK,EAAE,KAAK,CAAC,CAAC;QAExF,OAAO;YACH,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAe;YAC3B,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK,EAAE,IAAI,CAAC,KAAK;SACpB,CAAC;IACN,CAAC;CACJ"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type * as storage from '@crawlee/types';
|
|
2
|
+
import type { CrawleeLogger } from '@crawlee/types';
|
|
3
|
+
import type { FileSystemKeyValueStoreClient as NativeFileSystemKeyValueStoreBackend } from '@crawlee/fs-storage-native';
|
|
4
|
+
import { CachedIdClient } from './cached-id-client.js';
|
|
5
|
+
export interface KeyValueStoreBackendOptions {
|
|
6
|
+
/** The user-facing storage name, or `undefined` for unnamed (alias / default) storages. */
|
|
7
|
+
name?: string;
|
|
8
|
+
/**
|
|
9
|
+
* The key used for cache lookup in {@link FileSystemStorageBackend}. For named storages this equals
|
|
10
|
+
* the name; for alias (unnamed) storages it is the alias string. Falls back to the storage id.
|
|
11
|
+
*/
|
|
12
|
+
cacheKey: string;
|
|
13
|
+
nativeBackend: NativeFileSystemKeyValueStoreBackend;
|
|
14
|
+
logger?: CrawleeLogger;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A file-system key-value store backend backed by the native `@crawlee/fs-storage-native` Rust
|
|
18
|
+
* extension.
|
|
19
|
+
*
|
|
20
|
+
* This adapter is a plain byte transport: values are written and read verbatim as `Buffer`s with a
|
|
21
|
+
* content type carried alongside them. Serializing arbitrary values into bytes and parsing them back
|
|
22
|
+
* is the {@link KeyValueStore} frontend codec's job, not this backend's.
|
|
23
|
+
*/
|
|
24
|
+
export declare class KeyValueStoreBackend extends CachedIdClient implements storage.KeyValueStoreBackend {
|
|
25
|
+
readonly name?: string;
|
|
26
|
+
readonly cacheKey: string;
|
|
27
|
+
private readonly nativeBackend;
|
|
28
|
+
constructor(options: KeyValueStoreBackendOptions);
|
|
29
|
+
get keyValueStoreDirectory(): string;
|
|
30
|
+
static create(options: KeyValueStoreBackendOptions): Promise<KeyValueStoreBackend>;
|
|
31
|
+
getMetadata(): Promise<storage.KeyValueStoreInfo>;
|
|
32
|
+
drop(): Promise<void>;
|
|
33
|
+
purge(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Remove every record from the store except the run input. Used by
|
|
36
|
+
* {@link FileSystemStorageBackend.purge} to clean the default key-value store at the start of a run
|
|
37
|
+
* while preserving the run's input, matching the historical file-system storage behavior.
|
|
38
|
+
*
|
|
39
|
+
* The native `purge` keep-list matches by exact key with no extension globbing, so we pass every
|
|
40
|
+
* filename the input might live under (`INPUT`, `INPUT.json`, `INPUT.txt`, `INPUT.bin`).
|
|
41
|
+
*/
|
|
42
|
+
purgeExceptInput(): Promise<void>;
|
|
43
|
+
listKeys(options?: storage.KeyValueStoreListKeysOptions): Promise<storage.KeyValueStoreListKeysResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Generates a public `file://` URL for accessing a specific record in the key-value store.
|
|
46
|
+
*
|
|
47
|
+
* Returns `undefined` if the record does not exist.
|
|
48
|
+
* @param key The key of the record to generate the public URL for.
|
|
49
|
+
*/
|
|
50
|
+
getPublicUrl(key: string): Promise<string | undefined>;
|
|
51
|
+
/**
|
|
52
|
+
* Tests whether a record with the given key exists without retrieving its value.
|
|
53
|
+
*
|
|
54
|
+
* @param key The queried record key.
|
|
55
|
+
* @returns `true` if the record exists, `false` otherwise.
|
|
56
|
+
*/
|
|
57
|
+
recordExists(key: string): Promise<boolean>;
|
|
58
|
+
getValue(key: string): Promise<storage.KeyValueStoreRecord | undefined>;
|
|
59
|
+
setValue(record: storage.KeyValueStoreInputRecord): Promise<void>;
|
|
60
|
+
deleteValue(key: string): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Resolve `key` to the on-disk key that actually exists, or `undefined` if nothing does. Every
|
|
63
|
+
* key is checked against its tracked record; the run-input keys additionally fall back to
|
|
64
|
+
* out-of-band bare files, in which case the matched on-disk key is returned so callers like
|
|
65
|
+
* `getPublicUrl` point at the file that exists. Two run-input shapes are handled (see
|
|
66
|
+
* {@link bareFallbacksFor}): the logical `INPUT`, which probes the conventional extensions, and a
|
|
67
|
+
* literal bare filename such as `INPUT.json` as listed by `listKeys`, which resolves itself.
|
|
68
|
+
*/
|
|
69
|
+
private resolveExistingKey;
|
|
70
|
+
/**
|
|
71
|
+
* The native `resolveValue`/`resolveExistingKey` bare-file fallbacks to use for `key`, or
|
|
72
|
+
* `undefined` if `key` is a plain tracked-record lookup with no bare-file probing.
|
|
73
|
+
*
|
|
74
|
+
* - The logical run-input key (`INPUT`) probes the full extension ladder (`INPUT`, `INPUT.json`,
|
|
75
|
+
* `INPUT.txt`, `INPUT.bin`), matching how Crawlee reads run input.
|
|
76
|
+
* - A literal bare filename as surfaced by `listKeys` (`INPUT.json`/`.txt`/`.bin`) resolves itself:
|
|
77
|
+
* the tracked record first, then the bare file at that exact name (a single empty-extension
|
|
78
|
+
* fallback), so a listed key round-trips through `getValue`/`recordExists`.
|
|
79
|
+
*/
|
|
80
|
+
private bareFallbacksFor;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=key-value-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-value-store.d.ts","sourceRoot":"","sources":["../../src/resource-clients/key-value-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpD,OAAO,KAAK,EACR,6BAA6B,IAAI,oCAAoC,EAExE,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAoDvD,MAAM,WAAW,2BAA2B;IACxC,2FAA2F;IAC3F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,oCAAoC,CAAC;IACpD,MAAM,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,qBAAa,oBAAqB,SAAQ,cAAe,YAAW,OAAO,CAAC,oBAAoB;IAC5F,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAuC;gBAEzD,OAAO,EAAE,2BAA2B;IAOhD,IAAI,sBAAsB,IAAI,MAAM,CAEnC;WAEY,MAAM,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAMlF,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC;IAIjD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;;;;;OAOG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC,QAAQ,CAAC,OAAO,GAAE,OAAO,CAAC,4BAAiC,GAAG,OAAO,CAAC,OAAO,CAAC,2BAA2B,CAAC;IAuChH;;;;;OAKG;IACG,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAa5D;;;;;OAKG;IACG,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK3C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAmBvE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IA4CjE,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7C;;;;;;;OAOG;YACW,kBAAkB;IAahC;;;;;;;;;OASG;IAEH,OAAO,CAAC,gBAAgB;CAU3B"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import { s } from '@sapphire/shapeshift';
|
|
3
|
+
import { isStream } from '../utils.js';
|
|
4
|
+
import { CachedIdClient } from './cached-id-client.js';
|
|
5
|
+
/**
|
|
6
|
+
* Out-of-band ("bare") value-file fallbacks tried when the {@link ALLOWED_BARE_FILES} lookup misses the tracked
|
|
7
|
+
* record, so a lookup for `INPUT` also matches a hand-placed `INPUT.json`/`.txt`/`.bin`. Passed to the
|
|
8
|
+
* native `resolveValue`/`resolveExistingKey`, which do the probing and re-keying.
|
|
9
|
+
*
|
|
10
|
+
* Each entry declares the content type to report on a match — the native client does no MIME
|
|
11
|
+
* inference. An empty `contentType` is its sentinel for "keep the synthesized
|
|
12
|
+
* `application/octet-stream`", used for the extensionless key and `.bin`.
|
|
13
|
+
*/
|
|
14
|
+
const BARE_FILE_FALLBACKS = [
|
|
15
|
+
{ extension: '', contentType: '' },
|
|
16
|
+
{ extension: '.json', contentType: 'application/json; charset=utf-8' },
|
|
17
|
+
{ extension: '.txt', contentType: 'text/plain; charset=utf-8' },
|
|
18
|
+
{ extension: '.bin', contentType: '' },
|
|
19
|
+
];
|
|
20
|
+
const ALLOWED_BARE_FILES = ['INPUT'];
|
|
21
|
+
/**
|
|
22
|
+
* The out-of-band ("bare") files to surface from the native `listKeys`, derived from
|
|
23
|
+
* {@link ALLOWED_BARE_FILES} × {@link BARE_FILE_FALLBACKS}. Each native {@link ListBareFallback}
|
|
24
|
+
* `name` is the literal on-disk filename to probe (e.g. `INPUT.json`), and the native lists a match
|
|
25
|
+
* under that same `name` — which is exactly the key we return, so a listed bare file round-trips
|
|
26
|
+
* through `getValue`/`recordExists` (see {@link BARE_FILE_CONTENT_TYPES}).
|
|
27
|
+
*/
|
|
28
|
+
const LIST_BARE_FALLBACKS = ALLOWED_BARE_FILES.flatMap((key) => BARE_FILE_FALLBACKS.map(({ extension, contentType }) => ({ name: `${key}${extension}`, contentType })));
|
|
29
|
+
/**
|
|
30
|
+
* Lookup from a bare file's literal on-disk name (e.g. `INPUT.json`) to the content type to report
|
|
31
|
+
* for it, used to read a listed bare key back directly (`getValue('INPUT.json')`). The empty-extension
|
|
32
|
+
* entry (`INPUT`) is intentionally excluded: an extensionless `INPUT` lookup goes through the
|
|
33
|
+
* `resolveValue` fallback probing instead, which already covers the extensionless file.
|
|
34
|
+
*/
|
|
35
|
+
const BARE_FILE_CONTENT_TYPES = new Map(ALLOWED_BARE_FILES.flatMap((key) => BARE_FILE_FALLBACKS.filter(({ extension }) => extension !== '').map(({ extension, contentType }) => [`${key}${extension}`, contentType])));
|
|
36
|
+
/** Maps a bare file's on-disk name (e.g. `INPUT.json`) to its logical key (e.g. `INPUT`), for dedup. */
|
|
37
|
+
const BARE_FILE_LOGICAL_KEYS = new Map(ALLOWED_BARE_FILES.flatMap((key) => BARE_FILE_FALLBACKS.map(({ extension }) => [`${key}${extension}`, key])));
|
|
38
|
+
/**
|
|
39
|
+
* A file-system key-value store backend backed by the native `@crawlee/fs-storage-native` Rust
|
|
40
|
+
* extension.
|
|
41
|
+
*
|
|
42
|
+
* This adapter is a plain byte transport: values are written and read verbatim as `Buffer`s with a
|
|
43
|
+
* content type carried alongside them. Serializing arbitrary values into bytes and parsing them back
|
|
44
|
+
* is the {@link KeyValueStore} frontend codec's job, not this backend's.
|
|
45
|
+
*/
|
|
46
|
+
export class KeyValueStoreBackend extends CachedIdClient {
|
|
47
|
+
name;
|
|
48
|
+
cacheKey;
|
|
49
|
+
nativeBackend;
|
|
50
|
+
constructor(options) {
|
|
51
|
+
super();
|
|
52
|
+
this.name = options.name;
|
|
53
|
+
this.cacheKey = options.cacheKey;
|
|
54
|
+
this.nativeBackend = options.nativeBackend;
|
|
55
|
+
}
|
|
56
|
+
get keyValueStoreDirectory() {
|
|
57
|
+
return this.nativeBackend.pathToKvs;
|
|
58
|
+
}
|
|
59
|
+
static async create(options) {
|
|
60
|
+
const backend = new KeyValueStoreBackend(options);
|
|
61
|
+
backend._cachedId = (await options.nativeBackend.getMetadata()).id;
|
|
62
|
+
return backend;
|
|
63
|
+
}
|
|
64
|
+
async getMetadata() {
|
|
65
|
+
return this.nativeBackend.getMetadata();
|
|
66
|
+
}
|
|
67
|
+
async drop() {
|
|
68
|
+
await this.nativeBackend.dropStorage();
|
|
69
|
+
}
|
|
70
|
+
async purge() {
|
|
71
|
+
await this.nativeBackend.purge();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Remove every record from the store except the run input. Used by
|
|
75
|
+
* {@link FileSystemStorageBackend.purge} to clean the default key-value store at the start of a run
|
|
76
|
+
* while preserving the run's input, matching the historical file-system storage behavior.
|
|
77
|
+
*
|
|
78
|
+
* The native `purge` keep-list matches by exact key with no extension globbing, so we pass every
|
|
79
|
+
* filename the input might live under (`INPUT`, `INPUT.json`, `INPUT.txt`, `INPUT.bin`).
|
|
80
|
+
*/
|
|
81
|
+
async purgeExceptInput() {
|
|
82
|
+
await this.nativeBackend.purge(BARE_FILE_FALLBACKS.flatMap(({ extension }) => `INPUT${extension}`));
|
|
83
|
+
}
|
|
84
|
+
async listKeys(options = {}) {
|
|
85
|
+
const { prefix, exclusiveStartKey, limit } = s
|
|
86
|
+
.object({
|
|
87
|
+
prefix: s.string().optional(),
|
|
88
|
+
exclusiveStartKey: s.string().optional(),
|
|
89
|
+
limit: s.number().int().greaterThan(0).optional(),
|
|
90
|
+
})
|
|
91
|
+
.parse(options);
|
|
92
|
+
// Pass the bare-file fallbacks so out-of-band value files (e.g. a hand-placed `INPUT.json`)
|
|
93
|
+
// are enumerated alongside tracked records, under their actual on-disk name. The native reads
|
|
94
|
+
// everything it needs off the filesystem index — no per-file reads — so this stays cheap.
|
|
95
|
+
// The native `listKeys` already returns a self-describing page (items + pagination cursors)
|
|
96
|
+
// matching the `KeyValueStoreListKeysResult` contract, so we only post-process the items.
|
|
97
|
+
const page = await this.nativeBackend.listKeys(exclusiveStartKey, limit, prefix, LIST_BARE_FALLBACKS);
|
|
98
|
+
const presentKeys = new Set(page.items.map((record) => record.key));
|
|
99
|
+
// A bare value file is listed under its actual name (`INPUT.json`), which already round-trips
|
|
100
|
+
// through `getValue`/`recordExists`. The only collision is a tracked record occupying the
|
|
101
|
+
// logical key itself (`INPUT`): it shadows the extension-bearing bare variants (`INPUT.json`
|
|
102
|
+
// etc.) for the same logical key, so drop those. The extensionless bare file *is* the logical
|
|
103
|
+
// key, so it is never a separate duplicate.
|
|
104
|
+
const items = page.items.filter((record) => {
|
|
105
|
+
const logicalKey = BARE_FILE_LOGICAL_KEYS.get(record.key);
|
|
106
|
+
const isExtensionBearingBareFile = logicalKey !== undefined && logicalKey !== record.key;
|
|
107
|
+
return !(isExtensionBearingBareFile && presentKeys.has(logicalKey));
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
items,
|
|
111
|
+
count: items.length,
|
|
112
|
+
limit: page.limit,
|
|
113
|
+
exclusiveStartKey: page.exclusiveStartKey,
|
|
114
|
+
isTruncated: page.isTruncated,
|
|
115
|
+
nextExclusiveStartKey: page.nextExclusiveStartKey,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Generates a public `file://` URL for accessing a specific record in the key-value store.
|
|
120
|
+
*
|
|
121
|
+
* Returns `undefined` if the record does not exist.
|
|
122
|
+
* @param key The key of the record to generate the public URL for.
|
|
123
|
+
*/
|
|
124
|
+
async getPublicUrl(key) {
|
|
125
|
+
s.string().parse(key);
|
|
126
|
+
// The native `getPublicUrl` stats the encoded path but does not probe bare-file extensions,
|
|
127
|
+
// so we resolve the on-disk key first (handling e.g. `INPUT` -> `INPUT.json`) and normalize
|
|
128
|
+
// the native `null`-for-missing result to the historical `undefined` contract.
|
|
129
|
+
const resolvedKey = await this.resolveExistingKey(key);
|
|
130
|
+
if (resolvedKey === undefined) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
return (await this.nativeBackend.getPublicUrl(resolvedKey)) ?? undefined;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Tests whether a record with the given key exists without retrieving its value.
|
|
137
|
+
*
|
|
138
|
+
* @param key The queried record key.
|
|
139
|
+
* @returns `true` if the record exists, `false` otherwise.
|
|
140
|
+
*/
|
|
141
|
+
async recordExists(key) {
|
|
142
|
+
s.string().parse(key);
|
|
143
|
+
return (await this.resolveExistingKey(key)) !== undefined;
|
|
144
|
+
}
|
|
145
|
+
async getValue(key) {
|
|
146
|
+
s.string().parse(key);
|
|
147
|
+
const fallbacks = this.bareFallbacksFor(key);
|
|
148
|
+
const record = fallbacks
|
|
149
|
+
? await this.nativeBackend.resolveValue(key, fallbacks)
|
|
150
|
+
: await this.nativeBackend.getValue(key);
|
|
151
|
+
if (record) {
|
|
152
|
+
return {
|
|
153
|
+
key: record.key,
|
|
154
|
+
value: record.value,
|
|
155
|
+
contentType: record.contentType,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
async setValue(record) {
|
|
161
|
+
// By the time a value reaches the backend the frontend (KeyValueStore codec) has already
|
|
162
|
+
// serialized it: non-bytes become a `string`, everything else is a `Buffer`/typed array or a
|
|
163
|
+
// stream. So we only accept those shapes here — there is no JSON inference or `String(value)`
|
|
164
|
+
// coercion left to do.
|
|
165
|
+
s.object({
|
|
166
|
+
key: s.string().lengthGreaterThan(0),
|
|
167
|
+
value: s.union([
|
|
168
|
+
s.string(),
|
|
169
|
+
s.instance(Buffer),
|
|
170
|
+
s.instance(ArrayBuffer),
|
|
171
|
+
s.typedArray(),
|
|
172
|
+
// A stream is an object; disabling validation makes shapeshift only check it is a
|
|
173
|
+
// non-null, non-array object (the stream guard below does the real check).
|
|
174
|
+
s.object({}).setValidationEnabled(false),
|
|
175
|
+
]),
|
|
176
|
+
contentType: s.string().lengthGreaterThan(0).optional(),
|
|
177
|
+
}).parse(record);
|
|
178
|
+
const { key, value } = record;
|
|
179
|
+
// The frontend resolves the content type before it reaches the backend; this backend is a plain
|
|
180
|
+
// byte transport and does not infer content types.
|
|
181
|
+
const contentType = record.contentType ?? 'application/octet-stream';
|
|
182
|
+
// Stream the value straight to disk without buffering it all into memory. The native client
|
|
183
|
+
// consumes a Web `ReadableStream`, so convert the Node `Readable` we get from the frontend.
|
|
184
|
+
if (isStream(value)) {
|
|
185
|
+
const webStream = Readable.toWeb(value);
|
|
186
|
+
await this.nativeBackend.setValueStream(key, webStream, contentType);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
// Normalize the remaining (already-serialized) value into a Buffer for the native client.
|
|
190
|
+
const buffer = Buffer.isBuffer(value)
|
|
191
|
+
? value
|
|
192
|
+
: value instanceof ArrayBuffer
|
|
193
|
+
? Buffer.from(value)
|
|
194
|
+
: ArrayBuffer.isView(value)
|
|
195
|
+
? Buffer.from(value.buffer, value.byteOffset, value.byteLength)
|
|
196
|
+
: Buffer.from(value);
|
|
197
|
+
await this.nativeBackend.setValue(key, buffer, contentType);
|
|
198
|
+
}
|
|
199
|
+
async deleteValue(key) {
|
|
200
|
+
s.string().parse(key);
|
|
201
|
+
await this.nativeBackend.deleteValue(key);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Resolve `key` to the on-disk key that actually exists, or `undefined` if nothing does. Every
|
|
205
|
+
* key is checked against its tracked record; the run-input keys additionally fall back to
|
|
206
|
+
* out-of-band bare files, in which case the matched on-disk key is returned so callers like
|
|
207
|
+
* `getPublicUrl` point at the file that exists. Two run-input shapes are handled (see
|
|
208
|
+
* {@link bareFallbacksFor}): the logical `INPUT`, which probes the conventional extensions, and a
|
|
209
|
+
* literal bare filename such as `INPUT.json` as listed by `listKeys`, which resolves itself.
|
|
210
|
+
*/
|
|
211
|
+
async resolveExistingKey(key) {
|
|
212
|
+
const fallbacks = this.bareFallbacksFor(key);
|
|
213
|
+
if (fallbacks) {
|
|
214
|
+
return ((await this.nativeBackend.resolveExistingKey(key, fallbacks.map(({ extension }) => extension))) ?? undefined);
|
|
215
|
+
}
|
|
216
|
+
return (await this.nativeBackend.recordExists(key)) ? key : undefined;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The native `resolveValue`/`resolveExistingKey` bare-file fallbacks to use for `key`, or
|
|
220
|
+
* `undefined` if `key` is a plain tracked-record lookup with no bare-file probing.
|
|
221
|
+
*
|
|
222
|
+
* - The logical run-input key (`INPUT`) probes the full extension ladder (`INPUT`, `INPUT.json`,
|
|
223
|
+
* `INPUT.txt`, `INPUT.bin`), matching how Crawlee reads run input.
|
|
224
|
+
* - A literal bare filename as surfaced by `listKeys` (`INPUT.json`/`.txt`/`.bin`) resolves itself:
|
|
225
|
+
* the tracked record first, then the bare file at that exact name (a single empty-extension
|
|
226
|
+
* fallback), so a listed key round-trips through `getValue`/`recordExists`.
|
|
227
|
+
*/
|
|
228
|
+
// eslint-disable-next-line class-methods-use-this
|
|
229
|
+
bareFallbacksFor(key) {
|
|
230
|
+
if (ALLOWED_BARE_FILES.includes(key)) {
|
|
231
|
+
return BARE_FILE_FALLBACKS;
|
|
232
|
+
}
|
|
233
|
+
const contentType = BARE_FILE_CONTENT_TYPES.get(key);
|
|
234
|
+
if (contentType !== undefined) {
|
|
235
|
+
return [{ extension: '', contentType }];
|
|
236
|
+
}
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=key-value-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-value-store.js","sourceRoot":"","sources":["../../src/resource-clients/key-value-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type * as storage from '@crawlee/types';
|
|
2
|
+
import type { CrawleeLogger } from '@crawlee/types';
|
|
3
|
+
import type { FileSystemRequestQueueClient as NativeFileSystemRequestQueueBackend } from '@crawlee/fs-storage-native';
|
|
4
|
+
import { CachedIdClient } from './cached-id-client.js';
|
|
5
|
+
export interface RequestQueueBackendOptions {
|
|
6
|
+
/** The user-facing storage name, or `undefined` for unnamed (alias / default) storages. */
|
|
7
|
+
name?: string;
|
|
8
|
+
/**
|
|
9
|
+
* The key used for cache lookup in {@link FileSystemStorageBackend}. For named storages this equals
|
|
10
|
+
* the name; for alias (unnamed) storages it is the alias string. Falls back to the storage id.
|
|
11
|
+
*/
|
|
12
|
+
cacheKey: string;
|
|
13
|
+
nativeBackend: NativeFileSystemRequestQueueBackend;
|
|
14
|
+
logger?: CrawleeLogger;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A file-system request queue backend backed by the native `@crawlee/fs-storage-native` Rust
|
|
18
|
+
* extension.
|
|
19
|
+
*
|
|
20
|
+
* Request ordering, in-progress locking and state persistence are all owned by the native client.
|
|
21
|
+
* This adapter forwards each operation and converts result shapes to the `@crawlee/types` interfaces.
|
|
22
|
+
*/
|
|
23
|
+
export declare class RequestQueueBackend extends CachedIdClient implements storage.RequestQueueBackend {
|
|
24
|
+
readonly name?: string;
|
|
25
|
+
readonly cacheKey: string;
|
|
26
|
+
private readonly nativeBackend;
|
|
27
|
+
constructor(options: RequestQueueBackendOptions);
|
|
28
|
+
get requestQueueDirectory(): string;
|
|
29
|
+
static create(options: RequestQueueBackendOptions): Promise<RequestQueueBackend>;
|
|
30
|
+
/**
|
|
31
|
+
* Tells the native client how long (in seconds) a fetched request stays locked before it becomes
|
|
32
|
+
* available again.
|
|
33
|
+
*/
|
|
34
|
+
setExpectedRequestProcessingTimeSecs(secs: number): Promise<void>;
|
|
35
|
+
getMetadata(): Promise<storage.RequestQueueInfo>;
|
|
36
|
+
drop(): Promise<void>;
|
|
37
|
+
purge(): Promise<void>;
|
|
38
|
+
addBatchOfRequests(requests: storage.RequestSchema[], options?: storage.RequestQueueOperationOptions): Promise<storage.BatchAddRequestsResult>;
|
|
39
|
+
getRequest(uniqueKey: string): Promise<storage.UpdateRequestSchema | undefined>;
|
|
40
|
+
fetchNextRequest(): Promise<storage.UpdateRequestSchema | undefined>;
|
|
41
|
+
markRequestAsHandled(request: storage.UpdateRequestSchema): Promise<storage.QueueOperationInfo | undefined>;
|
|
42
|
+
reclaimRequest(request: storage.UpdateRequestSchema, options?: storage.RequestQueueOperationOptions): Promise<storage.QueueOperationInfo | undefined>;
|
|
43
|
+
isEmpty(): Promise<boolean>;
|
|
44
|
+
isFinished(): Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Persist the native client's in-memory state to disk. Called by
|
|
47
|
+
* {@link FileSystemStorageBackend.teardown} so that fetched-but-unhandled requests are not stuck
|
|
48
|
+
* for the next consumer of the same on-disk queue.
|
|
49
|
+
*/
|
|
50
|
+
persistState(): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=request-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-queue.d.ts","sourceRoot":"","sources":["../../src/resource-clients/request-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpD,OAAO,KAAK,EAAE,4BAA4B,IAAI,mCAAmC,EAAE,MAAM,4BAA4B,CAAC;AAEtH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAoCvD,MAAM,WAAW,0BAA0B;IACvC,2FAA2F;IAC3F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,mCAAmC,CAAC;IACnD,MAAM,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,cAAe,YAAW,OAAO,CAAC,mBAAmB;IAC1F,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsC;gBAExD,OAAO,EAAE,0BAA0B;IAO/C,IAAI,qBAAqB,IAAI,MAAM,CAElC;WAEY,MAAM,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAMtF;;;OAGG;IACG,oCAAoC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjE,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC;IAIhD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,kBAAkB,CACpB,QAAQ,EAAE,OAAO,CAAC,aAAa,EAAE,EACjC,OAAO,GAAE,OAAO,CAAC,4BAAiC,GACnD,OAAO,CAAC,OAAO,CAAC,sBAAsB,CAAC;IAkBpC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAQ/E,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAIpE,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAK3G,cAAc,CAChB,OAAO,EAAE,OAAO,CAAC,mBAAmB,EACpC,OAAO,GAAE,OAAO,CAAC,4BAAiC,GACnD,OAAO,CAAC,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAQ5C,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAI3B,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAIpC;;;;OAIG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CAGtC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { s } from '@sapphire/shapeshift';
|
|
2
|
+
import { CachedIdClient } from './cached-id-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Convert a request (either a Crawlee `Request` instance or a plain schema object) into a plain object
|
|
5
|
+
* whose properties are all enumerable.
|
|
6
|
+
*
|
|
7
|
+
* Crawlee's `Request` stores internal metadata (crawl depth, enqueue strategy, session id, ...) in a
|
|
8
|
+
* *non-enumerable* `userData.__crawlee` bag. The native `@crawlee/fs-storage-native` client reads
|
|
9
|
+
* request properties directly over the N-API boundary, which only exposes enumerable own properties
|
|
10
|
+
* and does not honor `toJSON`. Passing a `Request` straight through would therefore silently drop the
|
|
11
|
+
* `__crawlee` metadata, resetting `crawlDepth` to 0 on the next `fetchNextRequest` (breaking e.g.
|
|
12
|
+
* `maxCrawlDepth` and enqueue-strategy handling). Round-tripping through JSON invokes the request's
|
|
13
|
+
* `toJSON`, flattening everything into enumerable properties the native client can persist.
|
|
14
|
+
*/
|
|
15
|
+
function plainifyRequest(request) {
|
|
16
|
+
return JSON.parse(JSON.stringify(request));
|
|
17
|
+
}
|
|
18
|
+
const requestShape = s
|
|
19
|
+
.object({
|
|
20
|
+
id: s.string(),
|
|
21
|
+
url: s.string().url({ allowedProtocols: ['http:', 'https:'] }),
|
|
22
|
+
uniqueKey: s.string(),
|
|
23
|
+
method: s.string().optional(),
|
|
24
|
+
retryCount: s.number().int().optional(),
|
|
25
|
+
handledAt: s.union([s.string(), s.date().valid()]).optional(),
|
|
26
|
+
})
|
|
27
|
+
.passthrough();
|
|
28
|
+
const requestShapeWithoutId = requestShape.omit(['id']);
|
|
29
|
+
const batchRequestShapeWithoutId = requestShapeWithoutId.array();
|
|
30
|
+
const requestOptionsShape = s.object({
|
|
31
|
+
forefront: s.boolean().optional(),
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* A file-system request queue backend backed by the native `@crawlee/fs-storage-native` Rust
|
|
35
|
+
* extension.
|
|
36
|
+
*
|
|
37
|
+
* Request ordering, in-progress locking and state persistence are all owned by the native client.
|
|
38
|
+
* This adapter forwards each operation and converts result shapes to the `@crawlee/types` interfaces.
|
|
39
|
+
*/
|
|
40
|
+
export class RequestQueueBackend extends CachedIdClient {
|
|
41
|
+
name;
|
|
42
|
+
cacheKey;
|
|
43
|
+
nativeBackend;
|
|
44
|
+
constructor(options) {
|
|
45
|
+
super();
|
|
46
|
+
this.name = options.name;
|
|
47
|
+
this.cacheKey = options.cacheKey;
|
|
48
|
+
this.nativeBackend = options.nativeBackend;
|
|
49
|
+
}
|
|
50
|
+
get requestQueueDirectory() {
|
|
51
|
+
return this.nativeBackend.pathToRq;
|
|
52
|
+
}
|
|
53
|
+
static async create(options) {
|
|
54
|
+
const backend = new RequestQueueBackend(options);
|
|
55
|
+
backend._cachedId = (await options.nativeBackend.getMetadata()).id;
|
|
56
|
+
return backend;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Tells the native client how long (in seconds) a fetched request stays locked before it becomes
|
|
60
|
+
* available again.
|
|
61
|
+
*/
|
|
62
|
+
async setExpectedRequestProcessingTimeSecs(secs) {
|
|
63
|
+
await this.nativeBackend.setExpectedRequestProcessingTime(secs);
|
|
64
|
+
}
|
|
65
|
+
async getMetadata() {
|
|
66
|
+
return this.nativeBackend.getMetadata();
|
|
67
|
+
}
|
|
68
|
+
async drop() {
|
|
69
|
+
await this.nativeBackend.dropStorage();
|
|
70
|
+
}
|
|
71
|
+
async purge() {
|
|
72
|
+
await this.nativeBackend.purge();
|
|
73
|
+
}
|
|
74
|
+
async addBatchOfRequests(requests, options = {}) {
|
|
75
|
+
batchRequestShapeWithoutId.parse(requests);
|
|
76
|
+
requestOptionsShape.parse(options);
|
|
77
|
+
const response = await this.nativeBackend.addBatchOfRequests(requests.map((request) => plainifyRequest(request)), options.forefront ?? false);
|
|
78
|
+
// `processedRequests` is structurally identical between the native and `storage` types, so it
|
|
79
|
+
// passes through unchanged. `unprocessedRequests` only differs in that the native `method` is
|
|
80
|
+
// a plain `string`, hence the cast to the narrower `AllowedHttpMethods` union.
|
|
81
|
+
return {
|
|
82
|
+
processedRequests: response.processedRequests,
|
|
83
|
+
unprocessedRequests: response.unprocessedRequests,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
async getRequest(uniqueKey) {
|
|
87
|
+
s.string().parse(uniqueKey);
|
|
88
|
+
// The native client tags requests with an internal `orderNo`; it's harmless to leak, so we
|
|
89
|
+
// hand the request back as-is rather than copying it just to drop one undeclared property.
|
|
90
|
+
// The native client already returns `undefined` for a missing request, matching this contract.
|
|
91
|
+
return (await this.nativeBackend.getRequest(uniqueKey));
|
|
92
|
+
}
|
|
93
|
+
async fetchNextRequest() {
|
|
94
|
+
return (await this.nativeBackend.fetchNextRequest());
|
|
95
|
+
}
|
|
96
|
+
async markRequestAsHandled(request) {
|
|
97
|
+
requestShape.parse(request);
|
|
98
|
+
return (await this.nativeBackend.markRequestAsHandled(plainifyRequest(request))) ?? undefined;
|
|
99
|
+
}
|
|
100
|
+
async reclaimRequest(request, options = {}) {
|
|
101
|
+
requestShape.parse(request);
|
|
102
|
+
requestOptionsShape.parse(options);
|
|
103
|
+
return ((await this.nativeBackend.reclaimRequest(plainifyRequest(request), options.forefront ?? false)) ?? undefined);
|
|
104
|
+
}
|
|
105
|
+
async isEmpty() {
|
|
106
|
+
return this.nativeBackend.isEmpty();
|
|
107
|
+
}
|
|
108
|
+
async isFinished() {
|
|
109
|
+
return this.nativeBackend.isFinished();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Persist the native client's in-memory state to disk. Called by
|
|
113
|
+
* {@link FileSystemStorageBackend.teardown} so that fetched-but-unhandled requests are not stuck
|
|
114
|
+
* for the next consumer of the same on-disk queue.
|
|
115
|
+
*/
|
|
116
|
+
async persistState() {
|
|
117
|
+
await this.nativeBackend.persistState();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=request-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-queue.js","sourceRoot":"","sources":["../../src/resource-clients/request-queue.ts"],"names":[],"mappings":"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"}
|
package/utils.d.ts
ADDED
package/utils.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAM5C"}
|
package/utils.js
ADDED
package/utils.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,QAAQ,CAAC,KAAU;IAC/B,OAAO,CACH,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK;QACL,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,UAAU,CAAC,CAClF,CAAC;AACN,CAAC"}
|