@sphereon/ssi-sdk.presentation-exchange 0.11.1-next.65
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/.yalc/@veramo/kv-store/LICENSE +201 -0
- package/.yalc/@veramo/kv-store/README.md +132 -0
- package/.yalc/@veramo/kv-store/build/index.d.ts +11 -0
- package/.yalc/@veramo/kv-store/build/index.js +45 -0
- package/.yalc/@veramo/kv-store/build/key-value-store.d.ts +27 -0
- package/.yalc/@veramo/kv-store/build/key-value-store.js +143 -0
- package/.yalc/@veramo/kv-store/build/key-value-types.d.ts +51 -0
- package/.yalc/@veramo/kv-store/build/key-value-types.js +3 -0
- package/.yalc/@veramo/kv-store/build/keyv/keyv-types.d.ts +62 -0
- package/.yalc/@veramo/kv-store/build/keyv/keyv-types.js +3 -0
- package/.yalc/@veramo/kv-store/build/keyv/keyv.d.ts +53 -0
- package/.yalc/@veramo/kv-store/build/keyv/keyv.js +385 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/index.d.ts +3 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/index.js +30 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/tiered/index.d.ts +32 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/tiered/index.js +301 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/tiered/types.d.ts +14 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/tiered/types.js +3 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/entities/keyValueStoreEntity.d.ts +11 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/entities/keyValueStoreEntity.js +50 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/index.d.ts +41 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/index.js +186 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/1.createKVDatabase.d.ts +14 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/1.createKVDatabase.js +90 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/index.d.ts +10 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/index.js +13 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/types.d.ts +15 -0
- package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/types.js +3 -0
- package/.yalc/@veramo/kv-store/build/tsdoc-metadata.json +11 -0
- package/.yalc/@veramo/kv-store/package.json +39 -0
- package/.yalc/@veramo/kv-store/src/__tests__/keyv.test.ts +204 -0
- package/.yalc/@veramo/kv-store/src/__tests__/kvstore.test.ts +93 -0
- package/.yalc/@veramo/kv-store/src/index.ts +10 -0
- package/.yalc/@veramo/kv-store/src/key-value-store.ts +109 -0
- package/.yalc/@veramo/kv-store/src/key-value-types.ts +97 -0
- package/.yalc/@veramo/kv-store/src/keyv/keyv-types.ts +71 -0
- package/.yalc/@veramo/kv-store/src/keyv/keyv.ts +282 -0
- package/.yalc/@veramo/kv-store/src/store-adapters/index.ts +2 -0
- package/.yalc/@veramo/kv-store/src/store-adapters/tiered/index.ts +158 -0
- package/.yalc/@veramo/kv-store/src/store-adapters/tiered/types.ts +16 -0
- package/.yalc/@veramo/kv-store/src/store-adapters/typeorm/entities/keyValueStoreEntity.ts +20 -0
- package/.yalc/@veramo/kv-store/src/store-adapters/typeorm/index.ts +122 -0
- package/.yalc/@veramo/kv-store/src/store-adapters/typeorm/migrations/1.createKVDatabase.ts +47 -0
- package/.yalc/@veramo/kv-store/src/store-adapters/typeorm/migrations/index.ts +10 -0
- package/.yalc/@veramo/kv-store/src/store-adapters/typeorm/types.ts +18 -0
- package/.yalc/@veramo/kv-store/yalc.sig +1 -0
- package/LICENSE +201 -0
- package/README.md +56 -0
- package/dist/agent/PresentationExchange.d.ts +25 -0
- package/dist/agent/PresentationExchange.d.ts.map +1 -0
- package/dist/agent/PresentationExchange.js +182 -0
- package/dist/agent/PresentationExchange.js.map +1 -0
- package/dist/functions.d.ts +12 -0
- package/dist/functions.d.ts.map +1 -0
- package/dist/functions.js +41 -0
- package/dist/functions.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/types/IPresentationExchange.d.ts +81 -0
- package/dist/types/IPresentationExchange.d.ts.map +1 -0
- package/dist/types/IPresentationExchange.js +3 -0
- package/dist/types/IPresentationExchange.js.map +1 -0
- package/package.json +67 -0
- package/plugin.schema.json +329 -0
- package/src/agent/PresentationExchange.ts +204 -0
- package/src/functions.ts +56 -0
- package/src/index.ts +8 -0
- package/src/types/IPresentationExchange.ts +114 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is how the store will actually store the value.
|
|
3
|
+
* It contains an optional `expires` property, which indicates when the value would expire
|
|
4
|
+
*/
|
|
5
|
+
export interface IValueData<ValueType> {
|
|
6
|
+
value: ValueType | undefined
|
|
7
|
+
expires: number | undefined
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface IKeyValueStoreOnArgs {
|
|
11
|
+
eventName: string | symbol
|
|
12
|
+
listener: (...args: any[]) => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/*export interface IKeyValueStoreGetArgs {
|
|
16
|
+
key: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IKeyValueStoreGetManyArgs {
|
|
20
|
+
keys: string[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface IKeyValueStoreHasArgs {
|
|
24
|
+
key: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface IKeyValueStoreDeleteArgs {
|
|
28
|
+
key: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IKeyValueStoreDeleteManyArgs {
|
|
32
|
+
keys: string[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface IKeyValueStoreSetArgs<ValueType> {
|
|
36
|
+
key: string
|
|
37
|
+
value: ValueType
|
|
38
|
+
ttl?: number
|
|
39
|
+
}*/
|
|
40
|
+
|
|
41
|
+
export interface IKeyValueStoreOptions<ValueType> {
|
|
42
|
+
[key: string]: any
|
|
43
|
+
|
|
44
|
+
/** Namespace for the current instance. */
|
|
45
|
+
namespace?: string | undefined
|
|
46
|
+
|
|
47
|
+
/** A custom serialization function. */
|
|
48
|
+
/*serialize?: ((data: KeyvDeserializedData<ValueType>) => OrPromise<string | undefined>)
|
|
49
|
+
/!** A custom deserialization function. *!/
|
|
50
|
+
deserialize?: ((data: any) => OrPromise<KeyvDeserializedData<ValueType> | undefined>);*/
|
|
51
|
+
/** The connection string URI. */
|
|
52
|
+
uri?: string | undefined
|
|
53
|
+
/** The storage adapter instance to be used by Keyv. or any other implementation */
|
|
54
|
+
store: IKeyValueStoreAdapter<ValueType> | Map<string, ValueType>
|
|
55
|
+
/** Default TTL. Can be overridden by specifying a TTL on `.set()`. */
|
|
56
|
+
ttl?: number | undefined
|
|
57
|
+
|
|
58
|
+
/** Enable compression option **/
|
|
59
|
+
/*compression?: KeyvCompressionAdapter | undefined;*/
|
|
60
|
+
|
|
61
|
+
emitErrors?: boolean
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface IKeyValueStoreAdapter<ValueType> {
|
|
65
|
+
namespace?: string | undefined
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface IKeyValueStore<ValueType> {
|
|
69
|
+
/**
|
|
70
|
+
* Get a single value by key. Can be undefined as the underlying store typically will not throw an error for a non existing key
|
|
71
|
+
*
|
|
72
|
+
* @param key Contains the key to search for
|
|
73
|
+
*/
|
|
74
|
+
get(key: string): Promise<ValueType | undefined>
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get a single item as Value Data from the store. Will always return a Value Data Object, but the value in it can be undefined in case the actual store does not contain the value
|
|
78
|
+
* @param key Contains the key to search for
|
|
79
|
+
*/
|
|
80
|
+
getAsValueData(key: string): Promise<IValueData<ValueType>>
|
|
81
|
+
|
|
82
|
+
getMany(keys: string[]): Promise<Array<ValueType | undefined>>
|
|
83
|
+
|
|
84
|
+
getManyAsValueData(keys: string[]): Promise<Array<IValueData<ValueType>>>
|
|
85
|
+
|
|
86
|
+
set(key: string, value: ValueType, ttl?: number): Promise<IValueData<ValueType>>
|
|
87
|
+
|
|
88
|
+
delete(key: string): Promise<boolean>
|
|
89
|
+
|
|
90
|
+
deleteMany(keys: string[]): Promise<boolean[]>
|
|
91
|
+
|
|
92
|
+
clear(): Promise<IKeyValueStore<ValueType>>
|
|
93
|
+
|
|
94
|
+
has(key: string): Promise<boolean>
|
|
95
|
+
|
|
96
|
+
disconnect(): Promise<void>
|
|
97
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Please be aware these types are compatible with the keyv package, to ensure we can use its store-adapters for free.
|
|
3
|
+
*
|
|
4
|
+
* Be very careful when extending/changing!. Normally you will want to create an adapter or decorator for your additional behaviour or requirements
|
|
5
|
+
*/
|
|
6
|
+
import { OrPromise } from '@veramo/utils'
|
|
7
|
+
|
|
8
|
+
export interface KeyvOptions<Value> {
|
|
9
|
+
[key: string]: any
|
|
10
|
+
|
|
11
|
+
/** Namespace for the current instance. */
|
|
12
|
+
namespace?: string | undefined
|
|
13
|
+
/** A custom serialization function. */
|
|
14
|
+
serialize?: (data: KeyvDeserializedData<Value>) => OrPromise<string | undefined>
|
|
15
|
+
/** A custom deserialization function. */
|
|
16
|
+
deserialize?: (data: any) => OrPromise<KeyvDeserializedData<Value> | undefined>
|
|
17
|
+
/** The connection string URI. */
|
|
18
|
+
uri?: string | undefined
|
|
19
|
+
/** The storage adapter instance to be used by Keyv. */
|
|
20
|
+
store?: KeyvStore<Value | undefined> | undefined
|
|
21
|
+
/** Default TTL. Can be overridden by specififying a TTL on `.set()`. */
|
|
22
|
+
ttl?: number | undefined
|
|
23
|
+
/** Specify an adapter to use. e.g `'redis'` or `'mongodb'`. */
|
|
24
|
+
adapter?: 'redis' | 'mongodb' | 'mongo' | 'sqlite' | 'postgresql' | 'postgres' | 'mysql' | object | string | undefined
|
|
25
|
+
/** Enable compression option **/
|
|
26
|
+
compression?: KeyvCompressionAdapter | undefined
|
|
27
|
+
|
|
28
|
+
emitErrors?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface KeyvCompressionAdapter {
|
|
32
|
+
compress(value: any, options?: any): OrPromise<any>
|
|
33
|
+
|
|
34
|
+
decompress(value: any, options?: any): OrPromise<any>
|
|
35
|
+
|
|
36
|
+
serialize(value: any): OrPromise<string>
|
|
37
|
+
|
|
38
|
+
deserialize(value: any): OrPromise<any>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface KeyvDeserializedData<Value> {
|
|
42
|
+
value: Value
|
|
43
|
+
expires: number | undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type KeyvStoredData<Value> = KeyvDeserializedData<Value> | string | Value | undefined
|
|
47
|
+
|
|
48
|
+
export interface KeyvStore<Value> {
|
|
49
|
+
namespace?: string | undefined
|
|
50
|
+
|
|
51
|
+
opts: KeyvOptions<Value>
|
|
52
|
+
|
|
53
|
+
on?(eventName: string | symbol, listener: (...args: any[]) => void): void
|
|
54
|
+
get(key: string | string[], options?: { raw?: boolean }): OrPromise<KeyvStoredData<Value> | Array<KeyvStoredData<Value>>>
|
|
55
|
+
|
|
56
|
+
getMany?(keys: string[], options?: { raw?: boolean }): OrPromise<Array<KeyvStoredData<Value>>> | undefined
|
|
57
|
+
|
|
58
|
+
iterator?(namespace?: string | undefined): AsyncGenerator<any, void, any>
|
|
59
|
+
|
|
60
|
+
set(key: string, value: Value, ttl?: number): any
|
|
61
|
+
|
|
62
|
+
delete(key: string | string[]): OrPromise<boolean>
|
|
63
|
+
|
|
64
|
+
deleteMany?(keys: string[]): OrPromise<boolean>
|
|
65
|
+
|
|
66
|
+
clear(): OrPromise<void>
|
|
67
|
+
|
|
68
|
+
has?(key: string): OrPromise<boolean>
|
|
69
|
+
|
|
70
|
+
disconnect?(): void
|
|
71
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { EventEmitter } from 'events'
|
|
2
|
+
import JSONB from 'json-buffer'
|
|
3
|
+
import { KeyvDeserializedData, KeyvOptions, KeyvStore, KeyvStoredData } from './keyv-types'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Please note that this is code adapted from @link https://github.com/jaredwray/keyv to support Typescript and ESM in Veramo
|
|
7
|
+
*
|
|
8
|
+
* The code should support the storage plugins available for the keyv project.
|
|
9
|
+
* Veramo itself supports NodeJS, Browser and React-Native environment.
|
|
10
|
+
* Please be aware that these requirements probably aren't true for any keyv storage plugins.
|
|
11
|
+
*
|
|
12
|
+
* One of the big changes compared to the upstream project is that this port does not have dynamic loading of store-adapters based on URIs.
|
|
13
|
+
* We believe that any Veramo Key Value store should use explicitly defined store-adapters.
|
|
14
|
+
*
|
|
15
|
+
* The port is part of the Veramo Key Value Store module, as we do not want to make an official maintained port out of it.
|
|
16
|
+
* Veramo exposes its own API/interfaces for the Key Value store, meaning we could also support any other implementation in the future
|
|
17
|
+
*
|
|
18
|
+
* The Veramo kv-store module provides out of the box support for in memory/maps, sqlite and typeorm implementations,
|
|
19
|
+
* including a tiered local/remote implementation that support all environments.
|
|
20
|
+
*
|
|
21
|
+
* We welcome any new storage modules
|
|
22
|
+
*/
|
|
23
|
+
export class Keyv<Value = any> extends EventEmitter implements KeyvStore<Value> {
|
|
24
|
+
readonly opts: KeyvOptions<Value>
|
|
25
|
+
readonly namespace: string
|
|
26
|
+
iterator?: (namespace?: string) => AsyncGenerator<any, void>
|
|
27
|
+
|
|
28
|
+
constructor(uri?: string | Map<string, Value | undefined> | KeyvStore<Value> | undefined, options?: KeyvOptions<Value>) {
|
|
29
|
+
super()
|
|
30
|
+
const emitErrors = options?.emitErrors === undefined ? true : options.emitErrors
|
|
31
|
+
uri = uri ?? options?.uri
|
|
32
|
+
/*if (!uri) {
|
|
33
|
+
throw Error('No URI provided')
|
|
34
|
+
}*/
|
|
35
|
+
this.opts = {
|
|
36
|
+
namespace: 'keyv',
|
|
37
|
+
serialize: JSONB.stringify,
|
|
38
|
+
deserialize: JSONB.parse,
|
|
39
|
+
...(typeof uri === 'string' ? { uri } : uri),
|
|
40
|
+
...options,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!this.opts.store) {
|
|
44
|
+
if (typeof uri !== 'string') {
|
|
45
|
+
this.opts.store = uri as KeyvStore<Value | undefined>
|
|
46
|
+
} /* else {
|
|
47
|
+
const adapterOptions = { ...this.opts }
|
|
48
|
+
this.opts.store = loadStore(adapterOptions)
|
|
49
|
+
}*/
|
|
50
|
+
}
|
|
51
|
+
if (!this.opts.store) {
|
|
52
|
+
throw Error('No store')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (this.opts.compression) {
|
|
56
|
+
const compression = this.opts.compression
|
|
57
|
+
this.opts.serialize = compression.serialize.bind(compression)
|
|
58
|
+
this.opts.deserialize = compression.deserialize.bind(compression)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (typeof this.opts.store.on === 'function' && emitErrors) {
|
|
62
|
+
this.opts.store.on('error', (error) => this.emit('error', error))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.opts.store.namespace = this.opts.namespace || 'keyv'
|
|
66
|
+
this.namespace = this.opts.store.namespace
|
|
67
|
+
|
|
68
|
+
const generateIterator = (iterator: any, keyv: Keyv<any>) =>
|
|
69
|
+
async function* () {
|
|
70
|
+
for await (const [key, raw] of typeof iterator === 'function' ? iterator(keyv.store.namespace) : iterator) {
|
|
71
|
+
const data = await keyv.deserialize(raw)
|
|
72
|
+
if (keyv.store.namespace && !key.includes(keyv.store.namespace)) {
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (data && typeof data.expires === 'number' && Date.now() > data.expires) {
|
|
77
|
+
keyv.delete(key)
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
yield [keyv._getKeyUnprefix(key), data?.value]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Attach iterators
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
if (typeof this.store[Symbol.iterator] === 'function' && this.store instanceof Map) {
|
|
88
|
+
this.iterator = generateIterator(this.store, this)
|
|
89
|
+
} else if (typeof this.store.iterator === 'function' && this.store.opts && this._checkIterableAdapter()) {
|
|
90
|
+
this.iterator = generateIterator(this.store.iterator.bind(this.store), this)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get store(): Required<KeyvStore<Value>> {
|
|
95
|
+
return this.opts.store as Required<KeyvStore<Value>>
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get deserialize() {
|
|
99
|
+
return this.opts.deserialize!
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get serialize() {
|
|
103
|
+
return this.opts.serialize!
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
_checkIterableAdapter() {
|
|
107
|
+
return (
|
|
108
|
+
(this.store.opts.dialect && iterableAdapters.includes(this.store.opts.dialect)) ||
|
|
109
|
+
(this.store.opts.url && iterableAdapters.findIndex((element) => this.store.opts.url.includes(element)) >= 0)
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
_getKeyPrefix(key: string): string {
|
|
114
|
+
return `${this.opts.namespace}:${key}`
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
_getKeyPrefixArray(keys: string[]): string[] {
|
|
118
|
+
return keys.map((key) => this._getKeyPrefix(key))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
_getKeyUnprefix(key: string): string {
|
|
122
|
+
return key.split(':').splice(1).join(':')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async getMany(keys: string[], options?: { raw?: boolean }): Promise<Array<KeyvStoredData<Value>>> {
|
|
126
|
+
const keyPrefixed = this._getKeyPrefixArray(keys)
|
|
127
|
+
let promise: Promise<Array<KeyvStoredData<Value>>>
|
|
128
|
+
if (this.store.getMany !== undefined) {
|
|
129
|
+
promise = this.store.getMany(keyPrefixed, options) as Promise<KeyvStoredData<Value>[]> //.then(value => !!value ? value.values() : undefined)
|
|
130
|
+
// todo: Probably wise to check expired ValueData here, if the getMany does not implement this feature itself!
|
|
131
|
+
} else {
|
|
132
|
+
promise = Promise.all(keyPrefixed.map((k) => this.store.get(k, options) as Promise<KeyvStoredData<Value>>))
|
|
133
|
+
}
|
|
134
|
+
const allValues = Promise.resolve(promise)
|
|
135
|
+
const results: Promise<KeyvStoredData<Value>>[] = []
|
|
136
|
+
|
|
137
|
+
return Promise.resolve(allValues)
|
|
138
|
+
.then((all) => {
|
|
139
|
+
keys.forEach((key, index) => {
|
|
140
|
+
const data = all[index]
|
|
141
|
+
|
|
142
|
+
let result = typeof data === 'string' ? this.deserialize(data) : !!data && this.opts.compression ? this.deserialize(data) : data
|
|
143
|
+
|
|
144
|
+
if (result && typeof result === 'object' && 'expires' in result && typeof result.expires === 'number' && Date.now() > result.expires) {
|
|
145
|
+
this.delete(key)
|
|
146
|
+
result = undefined
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const final = (
|
|
150
|
+
options && options.raw ? result : result && typeof result === 'object' && 'value' in result ? result.value : result
|
|
151
|
+
) as Promise<KeyvStoredData<Value>>
|
|
152
|
+
|
|
153
|
+
results.push(final)
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
.then(() => Promise.all(results))
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async get(
|
|
160
|
+
key: string | string[],
|
|
161
|
+
options?: { raw?: boolean }
|
|
162
|
+
): Promise<Value | string | KeyvDeserializedData<Value> | KeyvStoredData<Value>[] | undefined> {
|
|
163
|
+
const isArray = Array.isArray(key)
|
|
164
|
+
return Promise.resolve()
|
|
165
|
+
.then(() => (isArray ? this.getMany(this._getKeyPrefixArray(key), options) : this.store.get(this._getKeyPrefix(key))))
|
|
166
|
+
.then((data) => (typeof data === 'string' ? this.deserialize(data) : this.opts.compression ? this.deserialize(data) : data))
|
|
167
|
+
.then((data) => {
|
|
168
|
+
if (data === undefined || data === null) {
|
|
169
|
+
return undefined
|
|
170
|
+
}
|
|
171
|
+
const rows = Array.isArray(data) ? data : [data]
|
|
172
|
+
|
|
173
|
+
if (isArray) {
|
|
174
|
+
const result = []
|
|
175
|
+
|
|
176
|
+
for (let row of rows) {
|
|
177
|
+
if (row === undefined || row === null) {
|
|
178
|
+
result.push(undefined)
|
|
179
|
+
continue
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (this.isExpired(row)) {
|
|
183
|
+
this.delete(key).then(() => undefined)
|
|
184
|
+
result.push(undefined)
|
|
185
|
+
} else {
|
|
186
|
+
result.push(options && options.raw ? row : toValue(row))
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return result
|
|
191
|
+
} else if (!Array.isArray(data)) {
|
|
192
|
+
if (this.isExpired(data)) {
|
|
193
|
+
return this.delete(key).then(() => undefined)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return options && options.raw ? data : Array.isArray(data) ? data.map((d) => toValue(d)) : toValue(data)
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private isExpired(data: KeyvDeserializedData<any> | string | Value): boolean {
|
|
202
|
+
return (
|
|
203
|
+
typeof data !== 'string' &&
|
|
204
|
+
data &&
|
|
205
|
+
typeof data === 'object' &&
|
|
206
|
+
'expires' in data &&
|
|
207
|
+
typeof data.expires === 'number' &&
|
|
208
|
+
Date.now() > data.expires
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
set(key: string, value: Value, ttl?: number) {
|
|
213
|
+
const keyPrefixed = this._getKeyPrefix(key)
|
|
214
|
+
if (typeof ttl === 'undefined') {
|
|
215
|
+
ttl = this.opts.ttl
|
|
216
|
+
}
|
|
217
|
+
if (ttl === 0) {
|
|
218
|
+
ttl = undefined
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// @ts-ignore
|
|
222
|
+
return Promise.resolve()
|
|
223
|
+
.then(() => {
|
|
224
|
+
const expires = typeof ttl === 'number' ? Date.now() + ttl : undefined
|
|
225
|
+
if (typeof value === 'symbol') {
|
|
226
|
+
this.emit('error', 'symbol cannot be serialized')
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const input = { value, expires }
|
|
230
|
+
return this.serialize(input)
|
|
231
|
+
})
|
|
232
|
+
.then((value) => this.store.set(keyPrefixed, value as Value, ttl))
|
|
233
|
+
.then(() => true)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
delete(key: string | string[]) {
|
|
237
|
+
if (!Array.isArray(key)) {
|
|
238
|
+
const keyPrefixed = this._getKeyPrefix(key)
|
|
239
|
+
return Promise.resolve().then(() => this.store.delete(keyPrefixed))
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const keyPrefixed = this._getKeyPrefixArray(key)
|
|
243
|
+
if (this.store.deleteMany !== undefined) {
|
|
244
|
+
return Promise.resolve().then(() => this.store.deleteMany!(keyPrefixed))
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const promises = []
|
|
248
|
+
for (const key of keyPrefixed) {
|
|
249
|
+
promises.push(this.store.delete(key))
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return Promise.allSettled(promises).then((values) => values.every((x) => x.valueOf() === true))
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async clear(): Promise<void> {
|
|
256
|
+
return Promise.resolve().then(() => this.store.clear())
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
has(key: string) {
|
|
260
|
+
const keyPrefixed = this._getKeyPrefix(key)
|
|
261
|
+
return Promise.resolve().then(async () => {
|
|
262
|
+
if (typeof this.store.has === 'function') {
|
|
263
|
+
return this.store.has(keyPrefixed)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const value = await this.store.get(keyPrefixed)
|
|
267
|
+
return value !== undefined
|
|
268
|
+
})
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
disconnect() {
|
|
272
|
+
if (typeof this.store.disconnect === 'function') {
|
|
273
|
+
return this.store.disconnect()
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const iterableAdapters = ['sqlite', 'postgres', 'mysql', 'mongo', 'redis', 'tiered']
|
|
279
|
+
|
|
280
|
+
function toValue<Value>(input: KeyvDeserializedData<Value> | string | Value) {
|
|
281
|
+
return input !== null && typeof input === 'object' && 'value' in input ? input.value : input
|
|
282
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { EventEmitter } from 'events'
|
|
2
|
+
import type { Options, Options_ } from './types'
|
|
3
|
+
import { KeyvStore, KeyvStoredData } from '../../keyv/keyv-types'
|
|
4
|
+
import { Keyv } from '../../keyv/keyv'
|
|
5
|
+
import { IKeyValueStoreAdapter } from '../../key-value-types'
|
|
6
|
+
|
|
7
|
+
type KeyvTieredIndex = 'local' | 'remote'
|
|
8
|
+
|
|
9
|
+
export class KeyValueTieredStoreAdapter<Value> extends EventEmitter implements KeyvStore<Value>, IKeyValueStoreAdapter<Value> {
|
|
10
|
+
opts: Options_
|
|
11
|
+
remote: KeyvStore<Value>
|
|
12
|
+
local: KeyvStore<Value>
|
|
13
|
+
iterationLimit?: string | number
|
|
14
|
+
|
|
15
|
+
namespace?: string | undefined
|
|
16
|
+
|
|
17
|
+
constructor({ remote, local, ...options }: Options<Value>) {
|
|
18
|
+
super()
|
|
19
|
+
this.opts = {
|
|
20
|
+
validator: () => true,
|
|
21
|
+
dialect: 'tiered',
|
|
22
|
+
...options,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// todo: since we are instantiating a new Keyv object in case we encounter a map, the key prefix applied twice, given it will be part of a an outer keyv object as well.
|
|
26
|
+
// Probably wise to simply create a Map Store class. As it is an in memory construct, and will work in terms of resolution it does not have highest priority
|
|
27
|
+
this.local = (isMap(local) ? new Keyv<Value>(local as Map<string, Value>) : local) as KeyvStore<Value>
|
|
28
|
+
this.remote = (isMap(remote) ? new Keyv<Value>(remote as Map<string, Value>) : remote) as KeyvStore<Value>
|
|
29
|
+
this.namespace = this.local.namespace
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async get(key: string | string[], options?: { raw?: boolean }): Promise<KeyvStoredData<Value> | Array<KeyvStoredData<Value>>> {
|
|
33
|
+
if (Array.isArray(key)) {
|
|
34
|
+
return await this.getMany(key, options)
|
|
35
|
+
}
|
|
36
|
+
const localResult = (await this.local.get(key, options)) as KeyvStoredData<Value>
|
|
37
|
+
|
|
38
|
+
if (localResult === undefined || !this.opts.validator(localResult, key)) {
|
|
39
|
+
const remoteResult = await this.remote.get(key, options)
|
|
40
|
+
|
|
41
|
+
if (remoteResult !== localResult) {
|
|
42
|
+
const value = (
|
|
43
|
+
!!remoteResult ? (typeof remoteResult === 'object' && 'value' in remoteResult ? remoteResult.value : remoteResult) : undefined
|
|
44
|
+
) as Value
|
|
45
|
+
const ttl =
|
|
46
|
+
!!remoteResult && typeof remoteResult === 'object' && 'expires' in remoteResult && remoteResult.expires
|
|
47
|
+
? remoteResult.expires - Date.now()
|
|
48
|
+
: undefined
|
|
49
|
+
if (!ttl || ttl > 0) {
|
|
50
|
+
await this.local.set(key, value, ttl)
|
|
51
|
+
} else {
|
|
52
|
+
this.local.delete(key)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return remoteResult
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return localResult
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async getMany(keys: string[], options?: { raw?: boolean }): Promise<Array<KeyvStoredData<Value>>> {
|
|
62
|
+
const promises: Array<Promise<KeyvStoredData<Value>>> = []
|
|
63
|
+
for (const key of keys) {
|
|
64
|
+
promises.push(this.get(key, options) as Promise<KeyvStoredData<Value>>)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const values = await Promise.all(promises)
|
|
68
|
+
const data: Array<KeyvStoredData<Value>> | undefined = []
|
|
69
|
+
for (const value of values) {
|
|
70
|
+
data.push(value as KeyvStoredData<Value>)
|
|
71
|
+
}
|
|
72
|
+
return data
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async set(key: string, value: any, ttl?: number) {
|
|
76
|
+
const toSet: KeyvTieredIndex[] = ['local', 'remote']
|
|
77
|
+
return Promise.all(toSet.map(async (store) => this[store].set(key, value, ttl)))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async clear(): Promise<undefined> {
|
|
81
|
+
const toClear: KeyvTieredIndex[] = ['local']
|
|
82
|
+
if (!this.opts.localOnly) {
|
|
83
|
+
toClear.push('remote')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
await Promise.all(toClear.map(async (store) => this[store].clear()))
|
|
87
|
+
|
|
88
|
+
return undefined
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async delete(key: string): Promise<boolean> {
|
|
92
|
+
const toDelete: KeyvTieredIndex[] = ['local']
|
|
93
|
+
if (!this.opts.localOnly) {
|
|
94
|
+
toDelete.push('remote')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const deleted = await Promise.all(toDelete.map(async (store) => this[store].delete(key)))
|
|
98
|
+
|
|
99
|
+
return deleted.every(Boolean)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async deleteMany(keys: string[]): Promise<boolean> {
|
|
103
|
+
const promises = []
|
|
104
|
+
for (const key of keys) {
|
|
105
|
+
promises.push(this.delete(key))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const values = await Promise.all(promises)
|
|
109
|
+
|
|
110
|
+
return values.every(Boolean)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async has(key: string): Promise<boolean> {
|
|
114
|
+
let response
|
|
115
|
+
if (typeof this.local.has === 'function') {
|
|
116
|
+
response = this.local.has(key)
|
|
117
|
+
} else {
|
|
118
|
+
const value = await this.local.get(key)
|
|
119
|
+
response = value !== undefined
|
|
120
|
+
}
|
|
121
|
+
if (!response || !this.opts.validator(response, key)) {
|
|
122
|
+
if (typeof this.remote.has === 'function') {
|
|
123
|
+
response = this.remote.has(key)
|
|
124
|
+
} else {
|
|
125
|
+
const value = await this.remote.get(key)
|
|
126
|
+
response = value !== undefined
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return response
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async *iterator(namespace?: string): AsyncGenerator<any, void, any> {
|
|
133
|
+
const limit = Number.parseInt(this.iterationLimit as string, 10) || 10
|
|
134
|
+
this.remote.opts.iterationLimit = limit
|
|
135
|
+
if (this.remote && typeof this.remote.iterator === 'function') {
|
|
136
|
+
for await (const entries of this.remote.iterator(namespace)) {
|
|
137
|
+
yield entries
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isMap(map: any) {
|
|
144
|
+
if (map instanceof Map) {
|
|
145
|
+
return true
|
|
146
|
+
} else if (
|
|
147
|
+
map &&
|
|
148
|
+
typeof map.clear === 'function' &&
|
|
149
|
+
typeof map.delete === 'function' &&
|
|
150
|
+
typeof map.get === 'function' &&
|
|
151
|
+
typeof map.has === 'function' &&
|
|
152
|
+
typeof map.set === 'function'
|
|
153
|
+
) {
|
|
154
|
+
return true
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return false
|
|
158
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { IKeyValueStoreAdapter } from '../../key-value-types'
|
|
2
|
+
|
|
3
|
+
export type Options<ValueType> = {
|
|
4
|
+
local: IKeyValueStoreAdapter<ValueType> | Map<string, ValueType>
|
|
5
|
+
remote: IKeyValueStoreAdapter<ValueType> | Map<string, ValueType>
|
|
6
|
+
localOnly?: boolean
|
|
7
|
+
iterationLimit?: number | string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
11
|
+
export type Options_ = {
|
|
12
|
+
validator: (value: any, key: string) => boolean
|
|
13
|
+
dialect: string
|
|
14
|
+
iterationLimit?: number | string
|
|
15
|
+
localOnly?: boolean
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @beta This API may change without a BREAKING CHANGE notice.
|
|
6
|
+
*/
|
|
7
|
+
@Entity('keyvaluestore')
|
|
8
|
+
export class KeyValueStoreEntity extends BaseEntity {
|
|
9
|
+
@PrimaryColumn()
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
key: string
|
|
12
|
+
|
|
13
|
+
@Column({
|
|
14
|
+
type: 'text',
|
|
15
|
+
})
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
data: string
|
|
18
|
+
|
|
19
|
+
expires?: number
|
|
20
|
+
}
|