@lossless.org/client 1.10.0 → 1.11.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.
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@lossless.org/client',
|
|
6
|
-
version: '1.
|
|
6
|
+
version: '1.11.0',
|
|
7
7
|
description: 'One typed client for NoSQLDB, MongoDB, SQLDB, MariaDB, ClickHouse and S3 object storage.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxzQkFBc0I7SUFDNUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLDBGQUEwRjtDQUN4RyxDQUFBIn0=
|
|
@@ -23,32 +23,61 @@ export declare class EasyStore<T> {
|
|
|
23
23
|
private easyStorePromise?;
|
|
24
24
|
private getEasyStore;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
26
|
+
* Runs store operations one at a time, in the order they were called. Every
|
|
27
|
+
* read and write of this instance goes through here, so a write builds its
|
|
28
|
+
* next state from the state every earlier write left, and a read never sees
|
|
29
|
+
* the state of a write whose save has not resolved yet. A rejected operation
|
|
30
|
+
* does not stop the ones queued behind it.
|
|
27
31
|
*/
|
|
28
|
-
|
|
32
|
+
private operationQueue;
|
|
33
|
+
private runExclusive;
|
|
29
34
|
/**
|
|
30
|
-
*
|
|
35
|
+
* Persists `nextDataArg` as the store's data. The document's `save()` writes
|
|
36
|
+
* the instance's own fields, so the next state is installed for the save and
|
|
37
|
+
* the previous one is put back when the save rejects: a failed write leaves
|
|
38
|
+
* the cache as it was. Callers hold the operation queue.
|
|
39
|
+
*/
|
|
40
|
+
private commit;
|
|
41
|
+
/**
|
|
42
|
+
* reads all keyValue pairs at once and returns them as a copy in the
|
|
43
|
+
* persisted form: mutating the result does not change the store, and later
|
|
44
|
+
* writes do not change the result.
|
|
45
|
+
*/
|
|
46
|
+
readAll(): Promise<Partial<T>>;
|
|
47
|
+
/**
|
|
48
|
+
* reads one key and returns a copy of its value in the persisted form, or
|
|
49
|
+
* `undefined` when the key is absent
|
|
31
50
|
*/
|
|
32
51
|
readKey<TKey extends keyof T>(keyArg: TKey): Promise<Partial<T>[TKey]>;
|
|
33
52
|
/**
|
|
34
|
-
* writes a specific key to the keyValueStore
|
|
53
|
+
* writes a specific key to the keyValueStore. The store keeps a copy, so
|
|
54
|
+
* mutating `valueArg` afterwards does not change the store. When the save
|
|
55
|
+
* rejects, the store keeps its previous state.
|
|
35
56
|
*/
|
|
36
57
|
writeKey<TKey extends keyof T>(keyArg: TKey, valueArg: T[TKey]): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* deletes a key. When the save rejects, the store keeps its previous state.
|
|
60
|
+
*/
|
|
37
61
|
deleteKey(keyArg: keyof T): Promise<void>;
|
|
38
62
|
/**
|
|
39
63
|
* merges all keyValue pairs from the object argument into the store.
|
|
40
64
|
* Existing keys that are not present in `keyValueObject` are preserved.
|
|
41
65
|
* To overwrite the entire store and drop missing keys, use `replace()`.
|
|
66
|
+
* The store keeps a copy, so mutating `keyValueObject` afterwards does not
|
|
67
|
+
* change the store. When the save rejects, the store keeps its previous state.
|
|
42
68
|
*/
|
|
43
69
|
writeAll(keyValueObject: Partial<T>): Promise<void>;
|
|
44
70
|
/**
|
|
45
71
|
* atomically replaces the entire store with the given object.
|
|
46
72
|
* Unlike `writeAll` (which merges), `replace` clears any keys not
|
|
47
73
|
* present in `keyValueObject`. Useful when you need to drop a key.
|
|
74
|
+
* The store keeps a copy, so mutating `keyValueObject` afterwards does not
|
|
75
|
+
* change the store. When the save rejects, the store keeps its previous state.
|
|
48
76
|
*/
|
|
49
77
|
replace(keyValueObject: Partial<T>): Promise<void>;
|
|
50
78
|
/**
|
|
51
|
-
* wipes a key value store from disk
|
|
79
|
+
* wipes a key value store from disk. When the save rejects, the store keeps
|
|
80
|
+
* its previous state.
|
|
52
81
|
*/
|
|
53
82
|
wipe(): Promise<void>;
|
|
54
83
|
cleanUpEphemeral(): Promise<void>;
|
|
@@ -91,6 +91,21 @@ const getEasyStoreClassForDb = (smartdataDbArg) => {
|
|
|
91
91
|
easyStoreClassByDb.set(smartdataDbArg, SmartdataEasyStore);
|
|
92
92
|
return SmartdataEasyStore;
|
|
93
93
|
};
|
|
94
|
+
/**
|
|
95
|
+
* Copies a store value into the form the store persists: undefined object
|
|
96
|
+
* properties are dropped, an undefined array element becomes null and every
|
|
97
|
+
* value takes its BSON form, exactly as a save and a later read from the
|
|
98
|
+
* database do. The copy shares no object with the argument, so neither side can
|
|
99
|
+
* change the other afterwards. A value BSON cannot encode (a cycle, for
|
|
100
|
+
* example) throws here, before the store's cache is touched.
|
|
101
|
+
*/
|
|
102
|
+
const copyInPersistedForm = (valueArg) => {
|
|
103
|
+
if (valueArg === undefined) {
|
|
104
|
+
return valueArg;
|
|
105
|
+
}
|
|
106
|
+
const document = plugins.mongodb.BSON.deserialize(plugins.mongodb.BSON.serialize({ value: valueArg }, { ignoreUndefined: true }));
|
|
107
|
+
return document.value;
|
|
108
|
+
};
|
|
94
109
|
/**
|
|
95
110
|
* EasyStore allows the storage of easy objects. It also allows easy sharing of the object between different instances
|
|
96
111
|
*/
|
|
@@ -148,59 +163,118 @@ export class EasyStore {
|
|
|
148
163
|
return deferred.promise;
|
|
149
164
|
}
|
|
150
165
|
/**
|
|
151
|
-
*
|
|
166
|
+
* Runs store operations one at a time, in the order they were called. Every
|
|
167
|
+
* read and write of this instance goes through here, so a write builds its
|
|
168
|
+
* next state from the state every earlier write left, and a read never sees
|
|
169
|
+
* the state of a write whose save has not resolved yet. A rejected operation
|
|
170
|
+
* does not stop the ones queued behind it.
|
|
171
|
+
*/
|
|
172
|
+
operationQueue = Promise.resolve();
|
|
173
|
+
runExclusive(operationArg) {
|
|
174
|
+
const result = this.operationQueue.then(operationArg);
|
|
175
|
+
this.operationQueue = result.then(() => undefined, () => undefined);
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Persists `nextDataArg` as the store's data. The document's `save()` writes
|
|
180
|
+
* the instance's own fields, so the next state is installed for the save and
|
|
181
|
+
* the previous one is put back when the save rejects: a failed write leaves
|
|
182
|
+
* the cache as it was. Callers hold the operation queue.
|
|
183
|
+
*/
|
|
184
|
+
async commit(easyStoreArg, nextDataArg) {
|
|
185
|
+
const previousData = easyStoreArg.data;
|
|
186
|
+
easyStoreArg.data = nextDataArg;
|
|
187
|
+
try {
|
|
188
|
+
await easyStoreArg.save();
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
easyStoreArg.data = previousData;
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* reads all keyValue pairs at once and returns them as a copy in the
|
|
197
|
+
* persisted form: mutating the result does not change the store, and later
|
|
198
|
+
* writes do not change the result.
|
|
152
199
|
*/
|
|
153
200
|
async readAll() {
|
|
154
|
-
|
|
155
|
-
|
|
201
|
+
return this.runExclusive(async () => {
|
|
202
|
+
const easyStore = await this.getEasyStore();
|
|
203
|
+
return copyInPersistedForm(easyStore.data);
|
|
204
|
+
});
|
|
156
205
|
}
|
|
157
206
|
/**
|
|
158
|
-
* reads a
|
|
207
|
+
* reads one key and returns a copy of its value in the persisted form, or
|
|
208
|
+
* `undefined` when the key is absent
|
|
159
209
|
*/
|
|
160
210
|
async readKey(keyArg) {
|
|
161
|
-
|
|
162
|
-
|
|
211
|
+
return this.runExclusive(async () => {
|
|
212
|
+
const easyStore = await this.getEasyStore();
|
|
213
|
+
return copyInPersistedForm(easyStore.data[keyArg]);
|
|
214
|
+
});
|
|
163
215
|
}
|
|
164
216
|
/**
|
|
165
|
-
* writes a specific key to the keyValueStore
|
|
217
|
+
* writes a specific key to the keyValueStore. The store keeps a copy, so
|
|
218
|
+
* mutating `valueArg` afterwards does not change the store. When the save
|
|
219
|
+
* rejects, the store keeps its previous state.
|
|
166
220
|
*/
|
|
167
221
|
async writeKey(keyArg, valueArg) {
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
222
|
+
const value = copyInPersistedForm(valueArg);
|
|
223
|
+
await this.runExclusive(async () => {
|
|
224
|
+
const easyStore = await this.getEasyStore();
|
|
225
|
+
const nextData = { ...easyStore.data };
|
|
226
|
+
nextData[keyArg] = value;
|
|
227
|
+
await this.commit(easyStore, nextData);
|
|
228
|
+
});
|
|
171
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* deletes a key. When the save rejects, the store keeps its previous state.
|
|
232
|
+
*/
|
|
172
233
|
async deleteKey(keyArg) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
234
|
+
await this.runExclusive(async () => {
|
|
235
|
+
const easyStore = await this.getEasyStore();
|
|
236
|
+
const nextData = { ...easyStore.data };
|
|
237
|
+
delete nextData[keyArg];
|
|
238
|
+
await this.commit(easyStore, nextData);
|
|
239
|
+
});
|
|
176
240
|
}
|
|
177
241
|
/**
|
|
178
242
|
* merges all keyValue pairs from the object argument into the store.
|
|
179
243
|
* Existing keys that are not present in `keyValueObject` are preserved.
|
|
180
244
|
* To overwrite the entire store and drop missing keys, use `replace()`.
|
|
245
|
+
* The store keeps a copy, so mutating `keyValueObject` afterwards does not
|
|
246
|
+
* change the store. When the save rejects, the store keeps its previous state.
|
|
181
247
|
*/
|
|
182
248
|
async writeAll(keyValueObject) {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
249
|
+
const values = copyInPersistedForm(keyValueObject);
|
|
250
|
+
await this.runExclusive(async () => {
|
|
251
|
+
const easyStore = await this.getEasyStore();
|
|
252
|
+
await this.commit(easyStore, { ...easyStore.data, ...values });
|
|
253
|
+
});
|
|
186
254
|
}
|
|
187
255
|
/**
|
|
188
256
|
* atomically replaces the entire store with the given object.
|
|
189
257
|
* Unlike `writeAll` (which merges), `replace` clears any keys not
|
|
190
258
|
* present in `keyValueObject`. Useful when you need to drop a key.
|
|
259
|
+
* The store keeps a copy, so mutating `keyValueObject` afterwards does not
|
|
260
|
+
* change the store. When the save rejects, the store keeps its previous state.
|
|
191
261
|
*/
|
|
192
262
|
async replace(keyValueObject) {
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
263
|
+
const values = copyInPersistedForm(keyValueObject);
|
|
264
|
+
await this.runExclusive(async () => {
|
|
265
|
+
const easyStore = await this.getEasyStore();
|
|
266
|
+
await this.commit(easyStore, values);
|
|
267
|
+
});
|
|
196
268
|
}
|
|
197
269
|
/**
|
|
198
|
-
* wipes a key value store from disk
|
|
270
|
+
* wipes a key value store from disk. When the save rejects, the store keeps
|
|
271
|
+
* its previous state.
|
|
199
272
|
*/
|
|
200
273
|
async wipe() {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
274
|
+
await this.runExclusive(async () => {
|
|
275
|
+
const easyStore = await this.getEasyStore();
|
|
276
|
+
await this.commit(easyStore, {});
|
|
277
|
+
});
|
|
204
278
|
}
|
|
205
279
|
async cleanUpEphemeral() {
|
|
206
280
|
// Clean up ephemeral data periodically while connected
|
|
@@ -211,4 +285,4 @@ export class EasyStore {
|
|
|
211
285
|
}
|
|
212
286
|
}
|
|
213
287
|
}
|
|
214
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
288
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5lYXN5c3RvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9ub3NxbGRiL2NsYXNzZXMuZWFzeXN0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDckQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxjQUFjLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRTdELE1BQU0sa0JBQWtCLEdBQUcsSUFBSSxPQUFPLEVBQW9CLENBQUM7QUFFM0QsTUFBTSxzQkFBc0IsR0FBRyxDQUFJLGNBQTJCLEVBQUUsRUFBRTtJQUNoRSxNQUFNLGFBQWEsR0FBRyxrQkFBa0IsQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLENBQUM7SUFDN0QsSUFBSSxhQUFhLEVBQUUsQ0FBQztRQUNsQixPQUFPLGFBQWEsQ0FBQztJQUN2QixDQUFDO1FBRUssa0JBQWtCO2dDQUR2QixVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsY0FBYyxDQUFDOzs7OzBCQUNBLGNBQWM7Ozs7Ozs7Ozs7Ozs7c0NBQXRCLFNBQVEsV0FHaEM7Ozs7c0NBQ0UsR0FBRyxFQUFFO3lDQUdMLElBQUksRUFBRTt3Q0FNTixJQUFJLEVBQUU7b0NBR04sSUFBSSxFQUFFO2dCQVhQLG9LQUFPLE1BQU0sNkJBQU4sTUFBTSx1RkFBVTtnQkFHdkIsNktBQU8sU0FBUyw2QkFBVCxTQUFTLDZGQUdkO2dCQUdGLDBLQUFBLFFBQVEsNkJBQVIsUUFBUSwyRkFBVTtnQkFHbEIsOEpBQU8sSUFBSSw2QkFBSixJQUFJLG1GQUFjO2dCQWpCM0IsNktBa0JDOzs7Z0JBbEJLLHVEQUFrQjs7WUFLZixNQUFN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
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -102,6 +102,7 @@ Family imports preserve the established SmartData and SmartBucket names and cons
|
|
|
102
102
|
| Migration-owned indexes | `toleratedIndexNames` on the model — named undeclared indexes are observed, never created, dropped or verified, and no longer make the topology divergent |
|
|
103
103
|
| Partial index filters in inspection | `inspectNamespace()`, `inspectCollectionTopology()` and `getIndexInfo()` report a partial index's `partialFilterExpression` exactly as the backend lists it, as a frozen snapshot present only on a partial index |
|
|
104
104
|
| Key-value store readiness | `EasyStore.ensureInitialized()` — installs the store's declared index at startup, outside any transaction window, so a first `readKey()`/`writeKey()` needs no index DDL |
|
|
105
|
+
| Key-value store copies | `EasyStore` `readKey()`/`readAll()` return copies and `writeKey()`/`writeAll()`/`replace()` store copies, in the persisted BSON form, so a read result is a stable snapshot and a caller-side mutation never reaches the store |
|
|
105
106
|
| Client deadlines | `timeoutMS` (1..120000) on every ordinary model read and write, instance `save()`/`saveIf()`/`delete()` included, on every exact call, and `session.withTransaction(callback, { timeoutMS })` for a whole transaction, retries and commit included; expiry rejects with `SmartdataPersistenceError` code `timeout`, and an exact write interrupted in flight with `ambiguous_write` |
|
|
106
107
|
| Cancellation | `signal` on every exact call, `atomicDeleteMany()` and `session.withTransaction(callback, { signal })`, which interrupts the session's operation in flight, rolls the attempt back and never commits after an abort |
|
|
107
108
|
| Disposable test databases on a unix socket | `SmartdataTestDatabase.create({ connection: { socketPath } })` from `@lossless.org/client/testsupport` reaches an engine that listens only on a unix-domain socket, fault helpers included; the loopback `host`/`port` form is unchanged |
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -34,6 +34,24 @@ const getEasyStoreClassForDb = <T>(smartdataDbArg: SmartdataDb) => {
|
|
|
34
34
|
return SmartdataEasyStore;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Copies a store value into the form the store persists: undefined object
|
|
39
|
+
* properties are dropped, an undefined array element becomes null and every
|
|
40
|
+
* value takes its BSON form, exactly as a save and a later read from the
|
|
41
|
+
* database do. The copy shares no object with the argument, so neither side can
|
|
42
|
+
* change the other afterwards. A value BSON cannot encode (a cycle, for
|
|
43
|
+
* example) throws here, before the store's cache is touched.
|
|
44
|
+
*/
|
|
45
|
+
const copyInPersistedForm = <TValue>(valueArg: TValue): TValue => {
|
|
46
|
+
if (valueArg === undefined) {
|
|
47
|
+
return valueArg;
|
|
48
|
+
}
|
|
49
|
+
const document = plugins.mongodb.BSON.deserialize(
|
|
50
|
+
plugins.mongodb.BSON.serialize({ value: valueArg }, { ignoreUndefined: true }),
|
|
51
|
+
);
|
|
52
|
+
return document.value as TValue;
|
|
53
|
+
};
|
|
54
|
+
|
|
37
55
|
/**
|
|
38
56
|
* EasyStore allows the storage of easy objects. It also allows easy sharing of the object between different instances
|
|
39
57
|
*/
|
|
@@ -98,65 +116,131 @@ export class EasyStore<T> {
|
|
|
98
116
|
}
|
|
99
117
|
|
|
100
118
|
/**
|
|
101
|
-
*
|
|
119
|
+
* Runs store operations one at a time, in the order they were called. Every
|
|
120
|
+
* read and write of this instance goes through here, so a write builds its
|
|
121
|
+
* next state from the state every earlier write left, and a read never sees
|
|
122
|
+
* the state of a write whose save has not resolved yet. A rejected operation
|
|
123
|
+
* does not stop the ones queued behind it.
|
|
124
|
+
*/
|
|
125
|
+
private operationQueue: Promise<void> = Promise.resolve();
|
|
126
|
+
private runExclusive<TResult>(operationArg: () => Promise<TResult>): Promise<TResult> {
|
|
127
|
+
const result = this.operationQueue.then(operationArg);
|
|
128
|
+
this.operationQueue = result.then(
|
|
129
|
+
() => undefined,
|
|
130
|
+
() => undefined,
|
|
131
|
+
);
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Persists `nextDataArg` as the store's data. The document's `save()` writes
|
|
137
|
+
* the instance's own fields, so the next state is installed for the save and
|
|
138
|
+
* the previous one is put back when the save rejects: a failed write leaves
|
|
139
|
+
* the cache as it was. Callers hold the operation queue.
|
|
102
140
|
*/
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
141
|
+
private async commit(
|
|
142
|
+
easyStoreArg: InstanceType<typeof this.easyStoreClass>,
|
|
143
|
+
nextDataArg: Partial<T>,
|
|
144
|
+
): Promise<void> {
|
|
145
|
+
const previousData = easyStoreArg.data;
|
|
146
|
+
easyStoreArg.data = nextDataArg;
|
|
147
|
+
try {
|
|
148
|
+
await easyStoreArg.save();
|
|
149
|
+
} catch (error) {
|
|
150
|
+
easyStoreArg.data = previousData;
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
106
153
|
}
|
|
107
154
|
|
|
108
155
|
/**
|
|
109
|
-
* reads a
|
|
156
|
+
* reads all keyValue pairs at once and returns them as a copy in the
|
|
157
|
+
* persisted form: mutating the result does not change the store, and later
|
|
158
|
+
* writes do not change the result.
|
|
159
|
+
*/
|
|
160
|
+
public async readAll(): Promise<Partial<T>> {
|
|
161
|
+
return this.runExclusive(async () => {
|
|
162
|
+
const easyStore = await this.getEasyStore();
|
|
163
|
+
return copyInPersistedForm(easyStore.data);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* reads one key and returns a copy of its value in the persisted form, or
|
|
169
|
+
* `undefined` when the key is absent
|
|
110
170
|
*/
|
|
111
171
|
public async readKey<TKey extends keyof T>(keyArg: TKey): Promise<Partial<T>[TKey]> {
|
|
112
|
-
|
|
113
|
-
|
|
172
|
+
return this.runExclusive(async () => {
|
|
173
|
+
const easyStore = await this.getEasyStore();
|
|
174
|
+
return copyInPersistedForm(easyStore.data[keyArg]);
|
|
175
|
+
});
|
|
114
176
|
}
|
|
115
177
|
|
|
116
178
|
/**
|
|
117
|
-
* writes a specific key to the keyValueStore
|
|
179
|
+
* writes a specific key to the keyValueStore. The store keeps a copy, so
|
|
180
|
+
* mutating `valueArg` afterwards does not change the store. When the save
|
|
181
|
+
* rejects, the store keeps its previous state.
|
|
118
182
|
*/
|
|
119
183
|
public async writeKey<TKey extends keyof T>(keyArg: TKey, valueArg: T[TKey]) {
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
184
|
+
const value = copyInPersistedForm(valueArg);
|
|
185
|
+
await this.runExclusive(async () => {
|
|
186
|
+
const easyStore = await this.getEasyStore();
|
|
187
|
+
const nextData: Partial<T> = { ...easyStore.data };
|
|
188
|
+
nextData[keyArg] = value;
|
|
189
|
+
await this.commit(easyStore, nextData);
|
|
190
|
+
});
|
|
123
191
|
}
|
|
124
192
|
|
|
193
|
+
/**
|
|
194
|
+
* deletes a key. When the save rejects, the store keeps its previous state.
|
|
195
|
+
*/
|
|
125
196
|
public async deleteKey(keyArg: keyof T) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
197
|
+
await this.runExclusive(async () => {
|
|
198
|
+
const easyStore = await this.getEasyStore();
|
|
199
|
+
const nextData: Partial<T> = { ...easyStore.data };
|
|
200
|
+
delete nextData[keyArg];
|
|
201
|
+
await this.commit(easyStore, nextData);
|
|
202
|
+
});
|
|
129
203
|
}
|
|
130
204
|
|
|
131
205
|
/**
|
|
132
206
|
* merges all keyValue pairs from the object argument into the store.
|
|
133
207
|
* Existing keys that are not present in `keyValueObject` are preserved.
|
|
134
208
|
* To overwrite the entire store and drop missing keys, use `replace()`.
|
|
209
|
+
* The store keeps a copy, so mutating `keyValueObject` afterwards does not
|
|
210
|
+
* change the store. When the save rejects, the store keeps its previous state.
|
|
135
211
|
*/
|
|
136
212
|
public async writeAll(keyValueObject: Partial<T>) {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
213
|
+
const values = copyInPersistedForm(keyValueObject);
|
|
214
|
+
await this.runExclusive(async () => {
|
|
215
|
+
const easyStore = await this.getEasyStore();
|
|
216
|
+
await this.commit(easyStore, { ...easyStore.data, ...values });
|
|
217
|
+
});
|
|
140
218
|
}
|
|
141
219
|
|
|
142
220
|
/**
|
|
143
221
|
* atomically replaces the entire store with the given object.
|
|
144
222
|
* Unlike `writeAll` (which merges), `replace` clears any keys not
|
|
145
223
|
* present in `keyValueObject`. Useful when you need to drop a key.
|
|
224
|
+
* The store keeps a copy, so mutating `keyValueObject` afterwards does not
|
|
225
|
+
* change the store. When the save rejects, the store keeps its previous state.
|
|
146
226
|
*/
|
|
147
227
|
public async replace(keyValueObject: Partial<T>) {
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
228
|
+
const values = copyInPersistedForm(keyValueObject);
|
|
229
|
+
await this.runExclusive(async () => {
|
|
230
|
+
const easyStore = await this.getEasyStore();
|
|
231
|
+
await this.commit(easyStore, values);
|
|
232
|
+
});
|
|
151
233
|
}
|
|
152
234
|
|
|
153
235
|
/**
|
|
154
|
-
* wipes a key value store from disk
|
|
236
|
+
* wipes a key value store from disk. When the save rejects, the store keeps
|
|
237
|
+
* its previous state.
|
|
155
238
|
*/
|
|
156
239
|
public async wipe() {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
240
|
+
await this.runExclusive(async () => {
|
|
241
|
+
const easyStore = await this.getEasyStore();
|
|
242
|
+
await this.commit(easyStore, {});
|
|
243
|
+
});
|
|
160
244
|
}
|
|
161
245
|
|
|
162
246
|
public async cleanUpEphemeral() {
|