extra-disk-store 0.5.1 → 0.7.0
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 +94 -45
- package/lib/converters/lz4-value-converter.d.ts +7 -1
- package/lib/converters/lz4-value-converter.js +13 -0
- package/lib/converters/lz4-value-converter.js.map +1 -1
- package/lib/converters/prefix-key-converter.d.ts +9 -2
- package/lib/converters/prefix-key-converter.js +19 -0
- package/lib/converters/prefix-key-converter.js.map +1 -1
- package/lib/converters/zstandard-value-converter.d.ts +3 -3
- package/lib/converters/zstandard-value-converter.js +1 -1
- package/lib/converters/zstandard-value-converter.js.map +1 -1
- package/lib/disk-store-async-view.d.ts +15 -0
- package/lib/disk-store-async-view.js +38 -0
- package/lib/disk-store-async-view.js.map +1 -0
- package/lib/disk-store-view.d.ts +7 -16
- package/lib/disk-store-view.js +12 -15
- package/lib/disk-store-view.js.map +1 -1
- package/lib/disk-store-with-cache.d.ts +4 -4
- package/lib/disk-store-with-cache.js +8 -8
- package/lib/disk-store-with-cache.js.map +1 -1
- package/lib/disk-store.d.ts +11 -12
- package/lib/disk-store.js +83 -36
- package/lib/disk-store.js.map +1 -1
- package/lib/index.d.ts +4 -2
- package/lib/index.js +4 -2
- package/lib/index.js.map +1 -1
- package/lib/types.d.ts +9 -7
- package/migrations/001-initial.sql +19 -0
- package/migrations/schema.sql +7 -0
- package/package.json +34 -34
- package/src/converters/lz4-value-converter.ts +17 -1
- package/src/converters/prefix-key-converter.ts +23 -2
- package/src/converters/zstandard-value-converter.ts +3 -3
- package/src/disk-store-async-view.ts +53 -0
- package/src/disk-store-view.ts +18 -29
- package/src/disk-store-with-cache.ts +8 -8
- package/src/disk-store.ts +93 -42
- package/src/index.ts +4 -2
- package/src/types.ts +10 -7
package/lib/disk-store.js
CHANGED
|
@@ -1,45 +1,92 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
import { findMigrationFilenames, readMigrationFile } from 'migration-files';
|
|
4
|
+
import { migrate } from '@blackglory/better-sqlite3-migrations';
|
|
5
|
+
import { go, assert } from '@blackglory/prelude';
|
|
6
|
+
import { map } from 'extra-promise';
|
|
7
|
+
import { findUpPackageFilename } from 'extra-filesystem';
|
|
1
8
|
import * as Iter from 'iterable-operator';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
9
|
+
import { withLazyStatic, lazyStatic } from 'extra-lazy';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
5
12
|
export class DiskStore {
|
|
6
|
-
constructor(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
constructor(_db) {
|
|
14
|
+
this._db = _db;
|
|
15
|
+
this.has = withLazyStatic((key) => {
|
|
16
|
+
const row = lazyStatic(() => this._db.prepare(`
|
|
17
|
+
SELECT EXISTS(
|
|
18
|
+
SELECT *
|
|
19
|
+
FROM store
|
|
20
|
+
WHERE key = $key
|
|
21
|
+
) AS item_exists
|
|
22
|
+
`), [this._db]).get({ key });
|
|
23
|
+
return row.item_exists === 1;
|
|
24
|
+
});
|
|
25
|
+
this.get = withLazyStatic((key) => {
|
|
26
|
+
const row = lazyStatic(() => this._db.prepare(`
|
|
27
|
+
SELECT value
|
|
28
|
+
FROM store
|
|
29
|
+
WHERE key = $key
|
|
30
|
+
`), [this._db]).get({ key });
|
|
31
|
+
return row === null || row === void 0 ? void 0 : row.value;
|
|
32
|
+
});
|
|
33
|
+
this.set = withLazyStatic((key, value) => {
|
|
34
|
+
lazyStatic(() => this._db.prepare(`
|
|
35
|
+
INSERT INTO store (
|
|
36
|
+
key
|
|
37
|
+
, value
|
|
38
|
+
)
|
|
39
|
+
VALUES ($key, $value)
|
|
40
|
+
ON CONFLICT(key)
|
|
41
|
+
DO UPDATE SET value = $value
|
|
42
|
+
`), [this._db]).run({
|
|
43
|
+
key,
|
|
44
|
+
value
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
this.delete = withLazyStatic((key) => {
|
|
48
|
+
lazyStatic(() => this._db.prepare(`
|
|
49
|
+
DELETE FROM store
|
|
50
|
+
WHERE key = $key
|
|
51
|
+
`), [this._db]).run({ key });
|
|
52
|
+
});
|
|
53
|
+
this.clear = withLazyStatic(() => {
|
|
54
|
+
lazyStatic(() => this._db.prepare(`
|
|
55
|
+
DELETE FROM store
|
|
56
|
+
`), [this._db]).run();
|
|
57
|
+
});
|
|
58
|
+
this.keys = withLazyStatic(() => {
|
|
59
|
+
const iter = lazyStatic(() => this._db.prepare(`
|
|
60
|
+
SELECT key
|
|
61
|
+
FROM store
|
|
62
|
+
`), [this._db]).iterate();
|
|
63
|
+
return Iter.map(iter, ({ key }) => key);
|
|
18
64
|
});
|
|
19
65
|
}
|
|
20
|
-
async
|
|
21
|
-
await
|
|
22
|
-
|
|
23
|
-
await
|
|
66
|
+
static async create(filename) {
|
|
67
|
+
const db = await go(async () => {
|
|
68
|
+
const db = new Database(filename !== null && filename !== void 0 ? filename : ':memory:');
|
|
69
|
+
await migrateDatabase(db);
|
|
70
|
+
db.unsafeMode(true);
|
|
71
|
+
return db;
|
|
72
|
+
});
|
|
73
|
+
return new this(db);
|
|
74
|
+
async function migrateDatabase(db) {
|
|
75
|
+
const packageFilename = await findUpPackageFilename(__dirname);
|
|
76
|
+
assert(packageFilename, 'package.json not found');
|
|
77
|
+
const packageRoot = path.dirname(packageFilename);
|
|
78
|
+
const migrationsPath = path.join(packageRoot, 'migrations');
|
|
79
|
+
const migrationFilenames = await findMigrationFilenames(migrationsPath);
|
|
80
|
+
const migrations = await map(migrationFilenames, readMigrationFile);
|
|
81
|
+
migrate(db, migrations);
|
|
24
82
|
}
|
|
25
83
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
async set(key, value) {
|
|
33
|
-
await this._db.put(key, value);
|
|
34
|
-
}
|
|
35
|
-
async delete(key) {
|
|
36
|
-
await this._db.remove(key);
|
|
37
|
-
}
|
|
38
|
-
async clear() {
|
|
39
|
-
await this._db.clearAsync();
|
|
40
|
-
}
|
|
41
|
-
keys() {
|
|
42
|
-
return Iter.map(this._db.getKeys(), key => key);
|
|
84
|
+
close() {
|
|
85
|
+
this._db.exec(`
|
|
86
|
+
PRAGMA analysis_limit=400;
|
|
87
|
+
PRAGMA optimize;
|
|
88
|
+
`);
|
|
89
|
+
this._db.close();
|
|
43
90
|
}
|
|
44
91
|
}
|
|
45
92
|
//# sourceMappingURL=disk-store.js.map
|
package/lib/disk-store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"disk-store.js","sourceRoot":"","sources":["../src/disk-store.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"disk-store.js","sourceRoot":"","sources":["../src/disk-store.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * from './disk-store.js';
|
|
2
|
-
export
|
|
3
|
-
export
|
|
2
|
+
export * from './disk-store-with-cache.js';
|
|
3
|
+
export * from './disk-store-view.js';
|
|
4
|
+
export * from './disk-store-async-view.js';
|
|
5
|
+
export * from './types.js';
|
|
4
6
|
export * from './converters/index.js';
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export * from './disk-store.js';
|
|
2
|
-
export
|
|
3
|
-
export
|
|
2
|
+
export * from './disk-store-with-cache.js';
|
|
3
|
+
export * from './disk-store-view.js';
|
|
4
|
+
export * from './disk-store-async-view.js';
|
|
5
|
+
export * from './types.js';
|
|
4
6
|
export * from './converters/index.js';
|
|
5
7
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,4BAA4B,CAAA;AAC1C,cAAc,sBAAsB,CAAA;AACpC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,YAAY,CAAA;AAC1B,cAAc,uBAAuB,CAAA"}
|
package/lib/types.d.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
2
|
import { Awaitable } from '@blackglory/prelude';
|
|
3
|
-
export interface ICache {
|
|
4
|
-
set(key: string, value: Buffer | boolean | undefined): void;
|
|
5
|
-
get(key: string): Buffer | boolean | undefined;
|
|
6
|
-
delete(key: string): void;
|
|
7
|
-
clear(): void;
|
|
8
|
-
}
|
|
9
3
|
export interface IKeyConverter<T> {
|
|
4
|
+
toString: (value: T) => string;
|
|
5
|
+
fromString: (value: string) => T | undefined;
|
|
6
|
+
}
|
|
7
|
+
export interface IValueConverter<T> {
|
|
8
|
+
toBuffer: (value: T) => Buffer;
|
|
9
|
+
fromBuffer: (value: Buffer) => T;
|
|
10
|
+
}
|
|
11
|
+
export interface IKeyAsyncConverter<T> {
|
|
10
12
|
toString: (value: T) => Awaitable<string>;
|
|
11
13
|
fromString: (value: string) => Awaitable<T | undefined>;
|
|
12
14
|
}
|
|
13
|
-
export interface
|
|
15
|
+
export interface IValueAsyncConverter<T> {
|
|
14
16
|
toBuffer: (value: T) => Awaitable<Buffer>;
|
|
15
17
|
fromBuffer: (value: Buffer) => Awaitable<T>;
|
|
16
18
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
--------------------------------------------------------------------------------
|
|
2
|
+
-- Up
|
|
3
|
+
--------------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
-- 在WAL模式下, better-sqlite3可充分发挥性能
|
|
6
|
+
PRAGMA journal_mode = WAL;
|
|
7
|
+
|
|
8
|
+
CREATE TABLE store (
|
|
9
|
+
key TEXT NOT NULL UNIQUE
|
|
10
|
+
, value BLOB NOT NULL
|
|
11
|
+
) STRICT;
|
|
12
|
+
|
|
13
|
+
--------------------------------------------------------------------------------
|
|
14
|
+
-- Down
|
|
15
|
+
--------------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
PRAGMA journal_mode = DELETE;
|
|
18
|
+
|
|
19
|
+
DROP TABLE store;
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "extra-disk-store",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"files": [
|
|
7
7
|
"lib",
|
|
8
|
-
"src"
|
|
8
|
+
"src",
|
|
9
|
+
"migrations"
|
|
9
10
|
],
|
|
10
11
|
"type": "module",
|
|
11
12
|
"main": "lib/index.js",
|
|
@@ -15,17 +16,16 @@
|
|
|
15
16
|
"license": "MIT",
|
|
16
17
|
"sideEffects": false,
|
|
17
18
|
"engines": {
|
|
18
|
-
"node": ">=
|
|
19
|
+
"node": ">=18.17.0"
|
|
19
20
|
},
|
|
20
21
|
"scripts": {
|
|
21
22
|
"prepare": "ts-patch install -s",
|
|
22
23
|
"lint": "eslint --ext .js,.jsx,.ts,.tsx --quiet src __tests__",
|
|
23
|
-
"test": "
|
|
24
|
-
"test:coverage": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage --config jest.config.cjs",
|
|
24
|
+
"test": "vitest --run",
|
|
25
25
|
"prepublishOnly": "run-s prepare clean build",
|
|
26
26
|
"clean": "rimraf lib",
|
|
27
27
|
"build": "tsc --project tsconfig.build.json",
|
|
28
|
-
"bench": "
|
|
28
|
+
"bench": "tsx benches/index.ts",
|
|
29
29
|
"release": "standard-version"
|
|
30
30
|
},
|
|
31
31
|
"husky": {
|
|
@@ -35,39 +35,39 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@blackglory/
|
|
39
|
-
"@
|
|
40
|
-
"@commitlint/
|
|
41
|
-
"@
|
|
42
|
-
"@types/
|
|
43
|
-
"@
|
|
44
|
-
"@typescript-eslint/
|
|
45
|
-
"@typescript-eslint/parser": "^5.54.1",
|
|
38
|
+
"@blackglory/structures": "^0.13.4",
|
|
39
|
+
"@commitlint/cli": "^18.6.1",
|
|
40
|
+
"@commitlint/config-conventional": "^18.6.2",
|
|
41
|
+
"@types/better-sqlite3": "^7.6.11",
|
|
42
|
+
"@types/node": "18",
|
|
43
|
+
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
|
44
|
+
"@typescript-eslint/parser": "^7.0.1",
|
|
46
45
|
"cross-env": "^7.0.3",
|
|
47
|
-
"eslint": "^8.
|
|
46
|
+
"eslint": "^8.56.0",
|
|
48
47
|
"husky": "^4.3.0",
|
|
49
|
-
"jest": "^29.5.0",
|
|
50
|
-
"jest-resolve": "^29.5.0",
|
|
51
48
|
"npm-run-all": "^4.1.5",
|
|
52
|
-
"
|
|
53
|
-
"rimraf": "^3.0.2",
|
|
49
|
+
"rimraf": "^5.0.5",
|
|
54
50
|
"standard-version": "^9.5.0",
|
|
55
|
-
"ts-
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
51
|
+
"ts-patch": "^3.1.2",
|
|
52
|
+
"tsx": "^4.7.1",
|
|
53
|
+
"typescript": "5.3.3",
|
|
54
|
+
"typescript-transform-paths": "^3.4.6",
|
|
55
|
+
"vite": "^5.4.7",
|
|
56
|
+
"vite-tsconfig-paths": "^5.0.1",
|
|
57
|
+
"vitest": "^2.1.1"
|
|
61
58
|
},
|
|
62
59
|
"dependencies": {
|
|
63
|
-
"@blackglory/
|
|
64
|
-
"@
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"extra-
|
|
68
|
-
"extra-
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
60
|
+
"@blackglory/better-sqlite3-migrations": "^0.1.18",
|
|
61
|
+
"@blackglory/prelude": "^0.3.4",
|
|
62
|
+
"@mongodb-js/zstd": "^1.2.0",
|
|
63
|
+
"better-sqlite3": "^11.3.0",
|
|
64
|
+
"extra-benchmark": "^0.2.3",
|
|
65
|
+
"extra-filesystem": "^0.5.1",
|
|
66
|
+
"extra-lazy": "^2.0.2",
|
|
67
|
+
"extra-promise": "^6.2.0",
|
|
68
|
+
"extra-utils": "^5.6.0",
|
|
69
|
+
"iterable-operator": "^5.0.0",
|
|
70
|
+
"lz4-wasm-nodejs": "^0.9.2",
|
|
71
|
+
"migration-files": "^0.4.3"
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import * as lz4 from 'lz4-wasm-nodejs'
|
|
2
|
-
import { IValueConverter } from '@src/types.js'
|
|
2
|
+
import { IValueConverter, IValueAsyncConverter } from '@src/types.js'
|
|
3
3
|
|
|
4
4
|
export class LZ4ValueConverter<T> implements IValueConverter<T> {
|
|
5
5
|
constructor(
|
|
6
6
|
private valueConverter: IValueConverter<T>
|
|
7
7
|
) {}
|
|
8
8
|
|
|
9
|
+
toBuffer(value: T): Buffer {
|
|
10
|
+
const buffer = this.valueConverter.toBuffer(value)
|
|
11
|
+
return Buffer.from(lz4.compress(buffer))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
fromBuffer(value: Buffer): T {
|
|
15
|
+
const buffer = Buffer.from(lz4.decompress(value))
|
|
16
|
+
return this.valueConverter.fromBuffer(buffer)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class LZ4ValueAsyncConverter<T> implements IValueAsyncConverter<T> {
|
|
21
|
+
constructor(
|
|
22
|
+
private valueConverter: IValueConverter<T> | IValueAsyncConverter<T>
|
|
23
|
+
) {}
|
|
24
|
+
|
|
9
25
|
async toBuffer(value: T): Promise<Buffer> {
|
|
10
26
|
const buffer = await this.valueConverter.toBuffer(value)
|
|
11
27
|
return Buffer.from(lz4.compress(buffer))
|
|
@@ -1,11 +1,32 @@
|
|
|
1
|
-
import { IKeyConverter } from '@src/types.js'
|
|
1
|
+
import { IKeyConverter, IKeyAsyncConverter } from '@src/types.js'
|
|
2
2
|
|
|
3
|
-
export class PrefixKeyConverter<T> implements IKeyConverter<T> {
|
|
3
|
+
export class PrefixKeyConverter<T> implements IKeyConverter<T>, IKeyAsyncConverter<T> {
|
|
4
4
|
constructor(
|
|
5
5
|
private keyConverter: IKeyConverter<T>
|
|
6
6
|
, private prefix: string
|
|
7
7
|
) {}
|
|
8
8
|
|
|
9
|
+
toString(value: T): string {
|
|
10
|
+
const key = this.keyConverter.toString(value)
|
|
11
|
+
return this.prefix + key
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
fromString(value: string): T | undefined {
|
|
15
|
+
if (value.startsWith(this.prefix)) {
|
|
16
|
+
const key = this.keyConverter.fromString(value.slice(this.prefix.length))
|
|
17
|
+
return key
|
|
18
|
+
} else {
|
|
19
|
+
return undefined
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class PrefixKeyAsyncConverter<T> implements IKeyAsyncConverter<T> {
|
|
25
|
+
constructor(
|
|
26
|
+
private keyConverter: IKeyConverter<T> | IKeyAsyncConverter<T>
|
|
27
|
+
, private prefix: string
|
|
28
|
+
) {}
|
|
29
|
+
|
|
9
30
|
async toString(value: T): Promise<string> {
|
|
10
31
|
const key = await this.keyConverter.toString(value)
|
|
11
32
|
return this.prefix + key
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as zstd from '@mongodb-js/zstd'
|
|
2
|
-
import { IValueConverter } from '@src/types.js'
|
|
2
|
+
import { IValueConverter, IValueAsyncConverter } from '@src/types.js'
|
|
3
3
|
|
|
4
|
-
export class
|
|
4
|
+
export class ZstandardValueAsyncConverter<T> implements IValueAsyncConverter<T> {
|
|
5
5
|
constructor(
|
|
6
|
-
private valueConverter: IValueConverter<T>
|
|
6
|
+
private valueConverter: IValueConverter<T> | IValueAsyncConverter<T>
|
|
7
7
|
, private level: number
|
|
8
8
|
) {}
|
|
9
9
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { DiskStore } from '@src/disk-store.js'
|
|
2
|
+
import { DiskStoreWithCache } from '@src/disk-store-with-cache.js'
|
|
3
|
+
import { pipe } from 'extra-utils'
|
|
4
|
+
import { mapAsync, filterAsync } from 'iterable-operator'
|
|
5
|
+
import { isntUndefined } from '@blackglory/prelude'
|
|
6
|
+
import { IKeyAsyncConverter, IValueAsyncConverter } from './types.js'
|
|
7
|
+
|
|
8
|
+
export class DiskStoreAsyncView<K, V> {
|
|
9
|
+
constructor(
|
|
10
|
+
private store: DiskStore | DiskStoreWithCache
|
|
11
|
+
, private keyConverter: IKeyAsyncConverter<K>
|
|
12
|
+
, private valueConverter: IValueAsyncConverter<V>
|
|
13
|
+
) {}
|
|
14
|
+
|
|
15
|
+
async has(key: K): Promise<boolean> {
|
|
16
|
+
return this.store.has(await this.keyConverter.toString(key))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async get(key: K): Promise<V | undefined> {
|
|
20
|
+
const buffer = this.store.get(await this.keyConverter.toString(key))
|
|
21
|
+
|
|
22
|
+
if (buffer) {
|
|
23
|
+
return this.valueConverter.fromBuffer(buffer)
|
|
24
|
+
} else {
|
|
25
|
+
return undefined
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async set(key: K, value: V): Promise<void> {
|
|
30
|
+
this.store.set(
|
|
31
|
+
...await Promise.all([
|
|
32
|
+
await this.keyConverter.toString(key)
|
|
33
|
+
, await this.valueConverter.toBuffer(value)
|
|
34
|
+
])
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async delete(key: K): Promise<void> {
|
|
39
|
+
this.store.delete(await this.keyConverter.toString(key))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
clear(): void {
|
|
43
|
+
this.store.clear()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
keys(): AsyncIterableIterator<K> {
|
|
47
|
+
return pipe(
|
|
48
|
+
this.store.keys()
|
|
49
|
+
, iter => mapAsync(iter, key => this.keyConverter.fromString(key))
|
|
50
|
+
, iter => filterAsync<Awaited<K> | undefined, Awaited<Awaited<K>>>(iter, isntUndefined)
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/disk-store-view.ts
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import { DiskStore } from '@src/disk-store.js'
|
|
2
2
|
import { DiskStoreWithCache } from '@src/disk-store-with-cache.js'
|
|
3
3
|
import { pipe } from 'extra-utils'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
export interface IKeyConverter<T> {
|
|
8
|
-
toString: (value: T) => Awaitable<string>
|
|
9
|
-
fromString: (value: string) => Awaitable<T | undefined>
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface IValueConverter<T> {
|
|
13
|
-
toBuffer: (value: T) => Awaitable<Buffer>
|
|
14
|
-
fromBuffer: (value: Buffer) => Awaitable<T>
|
|
15
|
-
}
|
|
4
|
+
import { map, filter } from 'iterable-operator'
|
|
5
|
+
import { isntUndefined } from '@blackglory/prelude'
|
|
6
|
+
import { IKeyConverter, IValueConverter } from './types.js'
|
|
16
7
|
|
|
17
8
|
export class DiskStoreView<K, V> {
|
|
18
9
|
constructor(
|
|
@@ -21,12 +12,12 @@ export class DiskStoreView<K, V> {
|
|
|
21
12
|
, private valueConverter: IValueConverter<V>
|
|
22
13
|
) {}
|
|
23
14
|
|
|
24
|
-
|
|
25
|
-
return this.store.has(
|
|
15
|
+
has(key: K): boolean {
|
|
16
|
+
return this.store.has(this.keyConverter.toString(key))
|
|
26
17
|
}
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
const buffer = this.store.get(
|
|
19
|
+
get(key: K): V | undefined {
|
|
20
|
+
const buffer = this.store.get(this.keyConverter.toString(key))
|
|
30
21
|
|
|
31
22
|
if (buffer) {
|
|
32
23
|
return this.valueConverter.fromBuffer(buffer)
|
|
@@ -35,28 +26,26 @@ export class DiskStoreView<K, V> {
|
|
|
35
26
|
}
|
|
36
27
|
}
|
|
37
28
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
, await this.valueConverter.toBuffer(value)
|
|
43
|
-
])
|
|
29
|
+
set(key: K, value: V): void {
|
|
30
|
+
this.store.set(
|
|
31
|
+
this.keyConverter.toString(key)
|
|
32
|
+
, this.valueConverter.toBuffer(value)
|
|
44
33
|
)
|
|
45
34
|
}
|
|
46
35
|
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
delete(key: K): void {
|
|
37
|
+
this.store.delete(this.keyConverter.toString(key))
|
|
49
38
|
}
|
|
50
39
|
|
|
51
|
-
|
|
52
|
-
|
|
40
|
+
clear(): void {
|
|
41
|
+
this.store.clear()
|
|
53
42
|
}
|
|
54
43
|
|
|
55
|
-
keys():
|
|
44
|
+
keys(): IterableIterator<K> {
|
|
56
45
|
return pipe(
|
|
57
46
|
this.store.keys()
|
|
58
|
-
, iter =>
|
|
59
|
-
, iter =>
|
|
47
|
+
, iter => map(iter, key => this.keyConverter.fromString(key))
|
|
48
|
+
, iter => filter<K | undefined, K>(iter, isntUndefined)
|
|
60
49
|
)
|
|
61
50
|
}
|
|
62
51
|
}
|
|
@@ -14,8 +14,8 @@ export class DiskStoreWithCache {
|
|
|
14
14
|
, private cache: ICache
|
|
15
15
|
) {}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
close(): void {
|
|
18
|
+
this.store.close()
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
has(key: string): boolean {
|
|
@@ -54,20 +54,20 @@ export class DiskStoreWithCache {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
set(key: string, value: Buffer): void {
|
|
58
|
+
this.store.set(key, value)
|
|
59
59
|
|
|
60
60
|
this.cache.delete(key)
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
delete(key: string): void {
|
|
64
|
+
this.store.delete(key)
|
|
65
65
|
|
|
66
66
|
this.cache.delete(key)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
clear(): void {
|
|
70
|
+
this.store.clear()
|
|
71
71
|
|
|
72
72
|
this.cache.clear()
|
|
73
73
|
}
|