/
KVStore.ts
353 lines (314 loc) · 16.8 KB
/
KVStore.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
import {BaseKVStore, PatchUpdater, Value} from './BaseKVStore';
import {NumberSet} from './NumberSet';
import {StringSet} from './StringSet';
/**
* A value type that is safe to write to the key-value storage
*/
export declare type KVValue = Value | NumberSet | StringSet;
/**
* An object with some restrictions. An interface that follows the KVHash interface
* can be safely written to the key-value storage.
*/
export interface KVHash {
[field: string]: KVValue | KVValue[] | undefined;
}
/**
* A hash of fields to values of a specific type. Used in the key-value store's "multi" accessors.
*/
export interface MultiValue<T> {
[field: string]: T;
}
/**
* Used when patching data.
* The previous value is provided as the first parameter. The current TTL is provided in the second parameter.
* The return value will be used to update the record. Update the options object directly to make a change to the TTL.
* WARNING: A patch updater may be called multiple times until the update is successful.
*/
type KVPatchUpdaterWithOptions<T extends KVHash> = (previous: T, options: KVRowOptions) => T;
export type KVPatchUpdater<T extends KVHash> = PatchUpdater<T> | KVPatchUpdaterWithOptions<T>;
/**
* Options for writing a row to key-value storage.
*/
export interface KVRowOptions {
/**
* The number of seconds until the row should expire.
* Set to undefined for no TTL.
*/
ttl?: number;
}
export interface KVStore extends BaseKVStore<KVHash, KVHash> {
// ===================================================================================================================
// Basic Operations
// ===================================================================================================================
/**
* Retrieve an object from the store given a key.
* @async
* @param key of the stored object
* @param fields to retrieve from the stored object, or undefined to retrieve the full object
* @returns hash of the complete object or only the specified fields, if supplied.
* An empty object is returned if the object, or all specified fields, does not exist.
*/
get<T extends KVHash>(key: string, fields?: string[]): Promise<T>;
/**
* Write an object to the store at a given key. Overwrites the entire object.
* @async
* @param key of the stored object
* @param value complete hash to write
* @param options optionally set a TTL for this row
* @returns the previous value found at the key if successful. Otherwise throws an error.
*/
put<T extends KVHash>(key: string, value: T, options?: KVRowOptions): Promise<T>;
/**
* Write a set of fields to an object in the store at a given key. Does not overwrite the entire object.
* @async
* @param key of the stored object
* @param value hash of fields and values to update the object with. Leaves all other fields untouched.
* @returns the complete object from before the update
* An empty object is returned if the object previously did not exist.
*/
patch<T extends KVHash>(key: string, value: T): Promise<T>;
/**
* Update a stored object using a callback to make changes.
* @async
* @param key of the stored object
* @param updater function to manipulate the existing object (may be called multiple times to ensure an atomic change)
* @returns the complete object from before the update
* An empty object is returned if the object previously did not exist.
*/
// eslint-disable-next-line @typescript-eslint/unified-signatures
patch<T extends KVHash>(key: string, updater: KVPatchUpdater<T>): Promise<T>;
/**
* Delete an object or a single field from the store at a given key.
* If fields is undefined, the entire object will be deleted.
* @async
* @param key of the stored object
* @param fields to delete or undefined to delete all fields
* @returns the deleted value if successful, or an empty object if it did not exist.
*/
delete<T extends KVHash>(key: string, fields?: string[]): Promise<T>;
/**
* Check if an object exists at a given key.
* @async
* @param key of the stored object
* @returns true if the object exists
*/
exists(key: string): Promise<boolean>;
// ===================================================================================================================
// Numeric Operations
// ===================================================================================================================
/**
* Atomically increment the value of a numeric field. If the object or field did not previously exist, the resulting
* field will be set to the given amount. If the field does already exist but is not a number, this will result in
* an error.
* @async
* @param key of the stored object
* @param field to increment
* @param amount by which to increment (can be negative, defaults to 1)
* @returns the value of the field after incrementing
*/
increment(key: string, field: string, amount?: number): Promise<number>;
/**
* Atomically increment the values of multiple numeric fields. If the object or fields did not previously exist, the
* resulting fields will be set to their respective given amount. If any of the fields does already exist but is not a
* number, this will result in an error.
* @async
* @param key of the stored object
* @param fieldAmounts hash of fields to amounts by which to increment (can be negative)
* @returns hash of fields to values after incrementing
*/
incrementMulti(key: string, fieldAmounts: MultiValue<number>): Promise<MultiValue<number>>;
// ===================================================================================================================
// List Operations
// ===================================================================================================================
/**
* Atomically retrieve and remove the *first* element from a list. If the object or field does not exist or is empty,
* the result will be `undefined`. Calling this method on a non-existent object will not cause it to be created. If
* the field exists but is not a list, this will result in an error.
* @async
* @param key of the stored object
* @param field that holds the list
* @returns the first element of the list, if available, otherwise `undefined`
*/
shift<T extends Value>(key: string, field: string): Promise<T | undefined>;
/**
* Atomically retrieve and remove *up to* the given number of elements from the *front* of the given lists. If the
* object or fields do not exist or are empty, the result of each missing field will be an empty array. Calling this
* method on a non-existent object will not cause it to be created. If any field exists but is not a list, this will
* result in an error.
* @async
* @param key of the stored object
* @param fieldCounts hash of fields to number of elements to retrieve and remove
* @returns hash of fields to array of list elements
*/
shiftMulti<T extends Value>(key: string, fieldCounts: MultiValue<number>): Promise<MultiValue<T[]>>;
/**
* Atomically insert an element at the *front* of a list. If the object or field does not exist, it will be created
* with a list consisting of the given element. If the field exists but is not a list, this will result in an error.
* @async
* @param key of the stored object
* @param field that holds the list
* @param value to insert
*/
unshift<T extends Value>(key: string, field: string, value: T): Promise<void>;
/**
* Atomically insert the given arrays of elements at the *front* of the given lists. If the object or fields do not
* exist, they will be created from the given arrays. If any field exists but is not a list, this will result in an
* error.
* @async
* @param key of the stored object
* @param fieldValues hash of fields to array of values to insert
*/
unshiftMulti<T extends Value>(key: string, fieldValues: MultiValue<T[]>): Promise<void>;
/**
* Retrieve (without removing) the first element from a list. If the object or field does not exist, is empty, or is
* not a list, the result will be `undefined`.
* @async
* @param key of the stored object
* @param field that holds the list
* @returns the first element of the list, if available, otherwise `undefined`
*/
peek<T extends Value>(key: string, field: string): Promise<T | undefined>;
/**
* Retrieve (without removing) *up to* the given number of elements from the *front* of the given lists. If the object
* or fields do not exist, are empty, or are not lists, the result of each missing field will be an empty array.
* @async
* @param key of the stored object
* @param fieldCounts hash of fields to number of elements to retrieve
* @returns hash of fields to array of list elements
*/
peekMulti<T extends Value>(key: string, fieldCounts: MultiValue<number>): Promise<MultiValue<T[]>>;
/**
* Atomically append an element to the *end* of a list. If the object or field does not exist, it will be created
* with a list consisting of the given element. If the field exists but is not a list, this will result in an error.
* @async
* @param key of the stored object
* @param field that holds the list
* @param value to append
*/
append<T extends Value>(key: string, field: string, value: T): Promise<void>;
/**
* Atomically append the given arrays of elements to the *end* of the given lists. If the object or fields do not
* exist, they will be created from the given arrays. If any field exists but is not a list, this will result in an
* error.
* @async
* @param key of the stored object
* @param fieldValues hash of fields to array of values to append
*/
appendMulti<T extends Value>(key: string, fieldValues: MultiValue<T[]>): Promise<void>;
// ===================================================================================================================
// NumberSet Operations
// ===================================================================================================================
/**
* Atomically adds a value to a NumberSet. If the object or field does not exist, it will be created with a NumberSet
* containing the given element. If the field exists but is not a NumberSet, this will result in an error.
* @param key of the stored object
* @param field that holds the NumberSet
* @param value to add
* @returns `true` if the value was newly added, `false` if it was already a member of the set
*/
addNumber(key: string, field: string, value: number): Promise<boolean>;
/**
* Atomically adds the given arrays of values to the given NumberSet fields. If the object or fields do not exist,
* they will be created from the given arrays. If any field exists but is not a NumberSet, this will result in an
* error.
* @param key of the stored object
* @param fieldValues hash of fields to array of values to add
* @returns hash of fields to NumberSet containing only the values that were newly added (any values that already
* existed in the target NumberSet will not exist in the return value)
*/
addNumberMulti(key: string, fieldValues: MultiValue<number[]>): Promise<MultiValue<NumberSet>>;
/**
* Atomically removes a value from a NumberSet. Calling this method on a non-existent object will not cause it to
* be created. If the field exists but is not a NumberSet, this will result in an error.
* @param key of the stored object
* @param field that holds the NumberSet
* @param value to remove
* @returns `true` if the value was removed, `false` if it was not a member of the set
*/
removeNumber(key: string, field: string, value: number): Promise<boolean>;
/**
* Atomically removes the given arrays of values from the given NumberSet fields. Calling this method on a
* non-existent object will not cause it to be created. If any field exists but is not a NumberSet, this will result
* in an error.
* @param key of the stored object
* @param fieldValues hash of fields to array of values to remove
* @returns hash of fields to NumberSet containing only the values that were removed (any values that did not exist in
* the target NumberSet will not exist in the return value)
*/
removeNumberMulti(key: string, fieldValues: MultiValue<number[]>): Promise<MultiValue<NumberSet>>;
/**
* Checks if a value exists in a NumberSet. Calling this method on a non-existent object will not cause it to be
* created. If the field exists but is not a NumberSet, this will result in an error.
* @param key of the stored object
* @param field that holds the NumberSet
* @param value to check
* @returns `true` if the value is a member of the set, `false` if it is not
*/
hasNumber(key: string, field: string, value: number): Promise<boolean>;
/**
* Checks if the given arrays of values exist in the given NumberSet fields. Calling this method on a non-existent
* object will not cause it to be created. If any field exists but is not a NumberSet, this will result in an error.
* @param key of the stored object
* @param fieldValues hash of fields to array of values to check
* @returns hash of fields to NumberSet containing only the values that exist in the target NumberSet
*/
hasNumberMulti(key: string, fieldValues: MultiValue<number[]>): Promise<MultiValue<NumberSet>>;
// ===================================================================================================================
// StringSet Operations
// ===================================================================================================================
/**
* Atomically adds a value to a StringSet. If the object or field does not exist, it will be created with a StringSet
* containing the given element. If the field exists but is not a StringSet, this will result in an error.
* @param key of the stored object
* @param field that holds the StringSet
* @param value to add
* @returns `true` if the value was newly added, `false` if it was already a member of the set
*/
addString(key: string, field: string, value: string): Promise<boolean>;
/**
* Atomically adds the given arrays of values to the given StringSet fields. If the object or fields do not exist,
* they will be created from the given arrays. If any field exists but is not a StringSet, this will result in an
* error.
* @param key of the stored object
* @param fieldValues hash of fields to array of values to add
* @returns hash of fields to StringSet containing only the values that were newly added (any values that already
* existed in the target StringSet will not exist in the return value)
*/
addStringMulti(key: string, fieldValues: MultiValue<string[]>): Promise<MultiValue<StringSet>>;
/**
* Atomically removes a value from a StringSet. Calling this method on a non-existent object will not cause it to
* be created. If the field exists but is not a StringSet, this will result in an error.
* @param key of the stored object
* @param field that holds the StringSet
* @param value to remove
* @returns `true` if the value was removed, `false` if it was not a member of the set
*/
removeString(key: string, field: string, value: string): Promise<boolean>;
/**
* Atomically removes the given arrays of values from the given StringSet fields. Calling this method on a
* non-existent object will not cause it to be created. If any field exists but is not a StringSet, this will result
* in an error.
* @param key of the stored object
* @param fieldValues hash of fields to array of values to remove
* @returns hash of fields to StringSet containing only the values that were removed (any values that did not exist in
* the target StringSet will not exist in the return value)
*/
removeStringMulti(key: string, fieldValues: MultiValue<string[]>): Promise<MultiValue<StringSet>>;
/**
* Checks if a value exists in a StringSet. Calling this method on a non-existent object will not cause it to be
* created. If the field exists but is not a StringSet, this will result in an error.
* @param key of the stored object
* @param field that holds the StringSet
* @param value to check
* @returns `true` if the value is a member of the set, `false` if it is not
*/
hasString(key: string, field: string, value: string): Promise<boolean>;
/**
* Checks if the given arrays of values exist in the given StringSet fields. Calling this method on a non-existent
* object will not cause it to be created. If any field exists but is not a StringSet, this will result in an error.
* @param key of the stored object
* @param fieldValues hash of fields to array of values to check
* @returns hash of fields to StringSet containing only the values that exist in the target StringSet
*/
hasStringMulti(key: string, fieldValues: MultiValue<string[]>): Promise<MultiValue<StringSet>>;
}