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.
Files changed (38) hide show
  1. package/README.md +94 -45
  2. package/lib/converters/lz4-value-converter.d.ts +7 -1
  3. package/lib/converters/lz4-value-converter.js +13 -0
  4. package/lib/converters/lz4-value-converter.js.map +1 -1
  5. package/lib/converters/prefix-key-converter.d.ts +9 -2
  6. package/lib/converters/prefix-key-converter.js +19 -0
  7. package/lib/converters/prefix-key-converter.js.map +1 -1
  8. package/lib/converters/zstandard-value-converter.d.ts +3 -3
  9. package/lib/converters/zstandard-value-converter.js +1 -1
  10. package/lib/converters/zstandard-value-converter.js.map +1 -1
  11. package/lib/disk-store-async-view.d.ts +15 -0
  12. package/lib/disk-store-async-view.js +38 -0
  13. package/lib/disk-store-async-view.js.map +1 -0
  14. package/lib/disk-store-view.d.ts +7 -16
  15. package/lib/disk-store-view.js +12 -15
  16. package/lib/disk-store-view.js.map +1 -1
  17. package/lib/disk-store-with-cache.d.ts +4 -4
  18. package/lib/disk-store-with-cache.js +8 -8
  19. package/lib/disk-store-with-cache.js.map +1 -1
  20. package/lib/disk-store.d.ts +11 -12
  21. package/lib/disk-store.js +83 -36
  22. package/lib/disk-store.js.map +1 -1
  23. package/lib/index.d.ts +4 -2
  24. package/lib/index.js +4 -2
  25. package/lib/index.js.map +1 -1
  26. package/lib/types.d.ts +9 -7
  27. package/migrations/001-initial.sql +19 -0
  28. package/migrations/schema.sql +7 -0
  29. package/package.json +34 -34
  30. package/src/converters/lz4-value-converter.ts +17 -1
  31. package/src/converters/prefix-key-converter.ts +23 -2
  32. package/src/converters/zstandard-value-converter.ts +3 -3
  33. package/src/disk-store-async-view.ts +53 -0
  34. package/src/disk-store-view.ts +18 -29
  35. package/src/disk-store-with-cache.ts +8 -8
  36. package/src/disk-store.ts +93 -42
  37. package/src/index.ts +4 -2
  38. 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 * as LMDB from 'lmdb';
3
- import { createTempNameSync, remove } from 'extra-filesystem';
4
- import { isUndefined } from '@blackglory/prelude';
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(dirname) {
7
- if (isUndefined(dirname)) {
8
- this._dirname = createTempNameSync();
9
- this.isTempPathname = true;
10
- }
11
- else {
12
- this._dirname = dirname;
13
- this.isTempPathname = false;
14
- }
15
- this._db = LMDB.open(this._dirname, {
16
- encoding: 'binary',
17
- compression: false
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 close() {
21
- await this._db.close();
22
- if (this.isTempPathname) {
23
- await remove(this._dirname);
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
- has(key) {
27
- return this._db.doesExist(key);
28
- }
29
- get(key) {
30
- return this._db.getBinary(key);
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
@@ -1 +1 @@
1
- {"version":3,"file":"disk-store.js","sourceRoot":"","sources":["../src/disk-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAA;AACzC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEjD,MAAM,OAAO,SAAS;IAKpB,YAAY,OAAgB;QAC1B,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE;YACxB,IAAI,CAAC,QAAQ,GAAG,kBAAkB,EAAE,CAAA;YACpC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;SAC3B;aAAM;YACL,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAA;YACvB,IAAI,CAAC,cAAc,GAAG,KAAK,CAAA;SAC5B;QAED,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,IAAI,CAAiB,IAAI,CAAC,QAAQ,EAAE;YAGlD,QAAQ,EAAE,QAAQ;YAClB,WAAW,EAAE,KAAK;SACnB,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAA;QAEtB,IAAI,IAAI,CAAC,cAAc,EAAE;YACvB,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;SAC5B;IACH,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;IAChC,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;IAChC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAa;QAClC,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAChC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,CAAA;IAC7B,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,GAAa,CAAC,CAAA;IAC3D,CAAC;CACF"}
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 { DiskStoreWithCache, ICache } from './disk-store-with-cache.js';
3
- export { DiskStoreView, IKeyConverter, IValueConverter } from './disk-store-view.js';
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 { DiskStoreWithCache } from './disk-store-with-cache.js';
3
- export { DiskStoreView } from './disk-store-view.js';
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,OAAO,EAAE,kBAAkB,EAAU,MAAM,4BAA4B,CAAA;AACvE,OAAO,EAAE,aAAa,EAAkC,MAAM,sBAAsB,CAAA;AACpF,cAAc,uBAAuB,CAAA"}
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 IValueConverter<T> {
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;
@@ -0,0 +1,7 @@
1
+ -- 在WAL模式下, better-sqlite3可充分发挥性能
2
+ PRAGMA journal_mode = WAL;
3
+
4
+ CREATE TABLE store (
5
+ key TEXT NOT NULL UNIQUE
6
+ , value BLOB NOT NULL
7
+ ) STRICT;
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "extra-disk-store",
3
- "version": "0.5.1",
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": ">=16"
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": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --config jest.config.cjs",
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": "ts-node --esm --require tsconfig-paths/register benches/index.ts",
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/jest-resolver": "^0.3.0",
39
- "@blackglory/structures": "^0.13.1",
40
- "@commitlint/cli": "^17.4.4",
41
- "@commitlint/config-conventional": "^17.4.4",
42
- "@types/jest": "^29.4.0",
43
- "@types/node": "16",
44
- "@typescript-eslint/eslint-plugin": "^5.54.1",
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.36.0",
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
- "pretty-bytes": "6.1.0",
53
- "rimraf": "^3.0.2",
49
+ "rimraf": "^5.0.5",
54
50
  "standard-version": "^9.5.0",
55
- "ts-jest": "^29.0.5",
56
- "ts-node": "^10.9.1",
57
- "ts-patch": "^2.1.0",
58
- "tsconfig-paths": "^4.1.2",
59
- "typescript": "4.8",
60
- "typescript-transform-paths": "^3.4.6"
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/prelude": "^0.3.1",
64
- "@mongodb-js/zstd": "^1.1.0",
65
- "extra-benchmark": "^0.2.2",
66
- "extra-filesystem": "^0.4.10",
67
- "extra-promise": "^6.0.3",
68
- "extra-utils": "^5.1.0",
69
- "iterable-operator": "^4.0.3",
70
- "lmdb": "^2.7.10",
71
- "lz4-wasm-nodejs": "^0.9.2"
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 ZstandardValueConverter<T> implements IValueConverter<T> {
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
+ }
@@ -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 { mapAsync, filterAsync } from 'iterable-operator'
5
- import { Awaitable, isntUndefined } from '@blackglory/prelude'
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
- async has(key: K): Promise<boolean> {
25
- return this.store.has(await this.keyConverter.toString(key))
15
+ has(key: K): boolean {
16
+ return this.store.has(this.keyConverter.toString(key))
26
17
  }
27
18
 
28
- async get(key: K): Promise<V | undefined> {
29
- const buffer = this.store.get(await this.keyConverter.toString(key))
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
- async set(key: K, value: V): Promise<void> {
39
- await this.store.set(
40
- ...await Promise.all([
41
- await this.keyConverter.toString(key)
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
- async delete(key: K): Promise<void> {
48
- await this.store.delete(await this.keyConverter.toString(key))
36
+ delete(key: K): void {
37
+ this.store.delete(this.keyConverter.toString(key))
49
38
  }
50
39
 
51
- async clear(): Promise<void> {
52
- await this.store.clear()
40
+ clear(): void {
41
+ this.store.clear()
53
42
  }
54
43
 
55
- keys(): AsyncIterableIterator<K> {
44
+ keys(): IterableIterator<K> {
56
45
  return pipe(
57
46
  this.store.keys()
58
- , iter => mapAsync(iter, async key => await this.keyConverter.fromString(key))
59
- , iter => filterAsync(iter, isntUndefined)
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
- async close(): Promise<void> {
18
- await this.store.close()
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
- async set(key: string, value: Buffer): Promise<void> {
58
- await this.store.set(key, value)
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
- async delete(key: string): Promise<void> {
64
- await this.store.delete(key)
63
+ delete(key: string): void {
64
+ this.store.delete(key)
65
65
 
66
66
  this.cache.delete(key)
67
67
  }
68
68
 
69
- async clear(): Promise<void> {
70
- await this.store.clear()
69
+ clear(): void {
70
+ this.store.clear()
71
71
 
72
72
  this.cache.clear()
73
73
  }