@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.
Files changed (70) hide show
  1. package/.yalc/@veramo/kv-store/LICENSE +201 -0
  2. package/.yalc/@veramo/kv-store/README.md +132 -0
  3. package/.yalc/@veramo/kv-store/build/index.d.ts +11 -0
  4. package/.yalc/@veramo/kv-store/build/index.js +45 -0
  5. package/.yalc/@veramo/kv-store/build/key-value-store.d.ts +27 -0
  6. package/.yalc/@veramo/kv-store/build/key-value-store.js +143 -0
  7. package/.yalc/@veramo/kv-store/build/key-value-types.d.ts +51 -0
  8. package/.yalc/@veramo/kv-store/build/key-value-types.js +3 -0
  9. package/.yalc/@veramo/kv-store/build/keyv/keyv-types.d.ts +62 -0
  10. package/.yalc/@veramo/kv-store/build/keyv/keyv-types.js +3 -0
  11. package/.yalc/@veramo/kv-store/build/keyv/keyv.d.ts +53 -0
  12. package/.yalc/@veramo/kv-store/build/keyv/keyv.js +385 -0
  13. package/.yalc/@veramo/kv-store/build/store-adapters/index.d.ts +3 -0
  14. package/.yalc/@veramo/kv-store/build/store-adapters/index.js +30 -0
  15. package/.yalc/@veramo/kv-store/build/store-adapters/tiered/index.d.ts +32 -0
  16. package/.yalc/@veramo/kv-store/build/store-adapters/tiered/index.js +301 -0
  17. package/.yalc/@veramo/kv-store/build/store-adapters/tiered/types.d.ts +14 -0
  18. package/.yalc/@veramo/kv-store/build/store-adapters/tiered/types.js +3 -0
  19. package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/entities/keyValueStoreEntity.d.ts +11 -0
  20. package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/entities/keyValueStoreEntity.js +50 -0
  21. package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/index.d.ts +41 -0
  22. package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/index.js +186 -0
  23. package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/1.createKVDatabase.d.ts +14 -0
  24. package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/1.createKVDatabase.js +90 -0
  25. package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/index.d.ts +10 -0
  26. package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/migrations/index.js +13 -0
  27. package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/types.d.ts +15 -0
  28. package/.yalc/@veramo/kv-store/build/store-adapters/typeorm/types.js +3 -0
  29. package/.yalc/@veramo/kv-store/build/tsdoc-metadata.json +11 -0
  30. package/.yalc/@veramo/kv-store/package.json +39 -0
  31. package/.yalc/@veramo/kv-store/src/__tests__/keyv.test.ts +204 -0
  32. package/.yalc/@veramo/kv-store/src/__tests__/kvstore.test.ts +93 -0
  33. package/.yalc/@veramo/kv-store/src/index.ts +10 -0
  34. package/.yalc/@veramo/kv-store/src/key-value-store.ts +109 -0
  35. package/.yalc/@veramo/kv-store/src/key-value-types.ts +97 -0
  36. package/.yalc/@veramo/kv-store/src/keyv/keyv-types.ts +71 -0
  37. package/.yalc/@veramo/kv-store/src/keyv/keyv.ts +282 -0
  38. package/.yalc/@veramo/kv-store/src/store-adapters/index.ts +2 -0
  39. package/.yalc/@veramo/kv-store/src/store-adapters/tiered/index.ts +158 -0
  40. package/.yalc/@veramo/kv-store/src/store-adapters/tiered/types.ts +16 -0
  41. package/.yalc/@veramo/kv-store/src/store-adapters/typeorm/entities/keyValueStoreEntity.ts +20 -0
  42. package/.yalc/@veramo/kv-store/src/store-adapters/typeorm/index.ts +122 -0
  43. package/.yalc/@veramo/kv-store/src/store-adapters/typeorm/migrations/1.createKVDatabase.ts +47 -0
  44. package/.yalc/@veramo/kv-store/src/store-adapters/typeorm/migrations/index.ts +10 -0
  45. package/.yalc/@veramo/kv-store/src/store-adapters/typeorm/types.ts +18 -0
  46. package/.yalc/@veramo/kv-store/yalc.sig +1 -0
  47. package/LICENSE +201 -0
  48. package/README.md +56 -0
  49. package/dist/agent/PresentationExchange.d.ts +25 -0
  50. package/dist/agent/PresentationExchange.d.ts.map +1 -0
  51. package/dist/agent/PresentationExchange.js +182 -0
  52. package/dist/agent/PresentationExchange.js.map +1 -0
  53. package/dist/functions.d.ts +12 -0
  54. package/dist/functions.d.ts.map +1 -0
  55. package/dist/functions.js +41 -0
  56. package/dist/functions.js.map +1 -0
  57. package/dist/index.d.ts +9 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +27 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/types/IPresentationExchange.d.ts +81 -0
  62. package/dist/types/IPresentationExchange.d.ts.map +1 -0
  63. package/dist/types/IPresentationExchange.js +3 -0
  64. package/dist/types/IPresentationExchange.js.map +1 -0
  65. package/package.json +67 -0
  66. package/plugin.schema.json +329 -0
  67. package/src/agent/PresentationExchange.ts +204 -0
  68. package/src/functions.ts +56 -0
  69. package/src/index.ts +8 -0
  70. 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,2 @@
1
+ export * from './tiered/index'
2
+ export * from './typeorm/index'
@@ -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
+ }