mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-03 17:58:55 +02:00
229 lines
6.2 KiB
JavaScript
229 lines
6.2 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
const gKeyValueService = Cc["@mozilla.org/key-value-service;1"].getService(
|
|
Ci.nsIKeyValueService
|
|
);
|
|
|
|
function promisify(fn, ...args) {
|
|
return new Promise((resolve, reject) => {
|
|
fn({ resolve, reject }, ...args);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* This module wraps the nsIKeyValue* interfaces in a Promise-based API.
|
|
* To use it, import it, then call the KeyValueService.getOrCreate() method
|
|
* with a database's path and (optionally) its name:
|
|
*
|
|
* ```
|
|
* let { keyValueService } =
|
|
* ChromeUtils.importESModule("resource://gre/modules/kvstore.sys.mjs");
|
|
* let database = await KeyValueService.getOrCreate(path, name);
|
|
* ```
|
|
*
|
|
* See the documentation in nsIKeyValue.idl for more information about the API
|
|
* for key/value storage.
|
|
*/
|
|
|
|
export class KeyValueService {
|
|
static RecoveryStrategy = {
|
|
ERROR: gKeyValueService.ERROR,
|
|
DISCARD: gKeyValueService.DISCARD,
|
|
RENAME: gKeyValueService.RENAME,
|
|
};
|
|
|
|
static async getOrCreate(dir, name) {
|
|
return new KeyValueDatabase(
|
|
await promisify(gKeyValueService.getOrCreate, dir, name)
|
|
);
|
|
}
|
|
|
|
static async getOrCreateWithOptions(
|
|
dir,
|
|
name,
|
|
{ strategy = gKeyValueService.RENAME } = {}
|
|
) {
|
|
return new KeyValueDatabase(
|
|
await promisify(
|
|
gKeyValueService.getOrCreateWithOptions,
|
|
dir,
|
|
name,
|
|
strategy
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A class that wraps an nsIKeyValueDatabase in a Promise-based API.
|
|
*
|
|
* This class isn't exported, so you can't instantiate it directly, but you
|
|
* can retrieve an instance of this class via KeyValueService.getOrCreate():
|
|
*
|
|
* ```
|
|
* const database = await KeyValueService.getOrCreate(path, name);
|
|
* ```
|
|
*
|
|
* You can then call its put(), get(), has(), and delete() methods to access
|
|
* and manipulate key/value pairs:
|
|
*
|
|
* ```
|
|
* await database.put("foo", 1);
|
|
* await database.get("foo") === 1; // true
|
|
* await database.has("foo"); // true
|
|
* await database.delete("foo");
|
|
* await database.has("foo"); // false
|
|
* ```
|
|
*
|
|
* You can also call writeMany() to put/delete multiple key/value pairs:
|
|
*
|
|
* ```
|
|
* await database.writeMany({
|
|
* key1: "value1",
|
|
* key2: "value2",
|
|
* key3: "value3",
|
|
* key4: null, // delete
|
|
* });
|
|
* ```
|
|
*
|
|
* And you can call its enumerate() method to retrieve a KeyValueEnumerator,
|
|
* which is described below.
|
|
*/
|
|
class KeyValueDatabase {
|
|
constructor(database) {
|
|
this.database = database;
|
|
}
|
|
|
|
put(key, value) {
|
|
return promisify(this.database.put, key, value);
|
|
}
|
|
|
|
/**
|
|
* Writes multiple key/value pairs to the database.
|
|
*
|
|
* Note:
|
|
* * Each write could be either put or delete.
|
|
* * Given multiple values with the same key, only the last value will be stored.
|
|
* * If the same key gets put and deleted for multiple times, the final state
|
|
* of that key is subject to the ordering of the put(s) and delete(s).
|
|
*
|
|
* @param pairs Pairs could be any of following types:
|
|
* * An Object, all its properties and the corresponding values will
|
|
* be used as key value pairs. A property with null or undefined indicating
|
|
* a deletion.
|
|
* * An Array or an iterable whose elements are key-value pairs. such as
|
|
* [["key1", "value1"], ["key2", "value2"]]. Use a pair with value null
|
|
* to delete a key-value pair, e.g. ["delete-key", null].
|
|
* * A Map. A key with null or undefined value indicating a deletion.
|
|
* @return A promise that is fulfilled when all the key/value pairs are written
|
|
* to the database.
|
|
*/
|
|
writeMany(pairs) {
|
|
if (!pairs) {
|
|
throw new Error("writeMany(): unexpected argument.");
|
|
}
|
|
|
|
let entries;
|
|
|
|
if (
|
|
pairs instanceof Map ||
|
|
pairs instanceof Array ||
|
|
typeof pairs[Symbol.iterator] === "function"
|
|
) {
|
|
try {
|
|
// Let Map constructor validate the argument. Note that Map remembers
|
|
// the original insertion order of the keys, which satisfies the ordering
|
|
// premise of this function.
|
|
const map = pairs instanceof Map ? pairs : new Map(pairs);
|
|
entries = Array.from(map, ([key, value]) => ({ key, value }));
|
|
} catch (error) {
|
|
throw new Error("writeMany(): unexpected argument.");
|
|
}
|
|
} else if (typeof pairs === "object") {
|
|
entries = Array.from(Object.entries(pairs), ([key, value]) => ({
|
|
key,
|
|
value,
|
|
}));
|
|
} else {
|
|
throw new Error("writeMany(): unexpected argument.");
|
|
}
|
|
|
|
if (entries.length) {
|
|
return promisify(this.database.writeMany, entries);
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
has(key) {
|
|
return promisify(this.database.has, key);
|
|
}
|
|
|
|
get(key, defaultValue) {
|
|
return promisify(this.database.get, key, defaultValue);
|
|
}
|
|
|
|
delete(key) {
|
|
return promisify(this.database.delete, key);
|
|
}
|
|
|
|
clear() {
|
|
return promisify(this.database.clear);
|
|
}
|
|
|
|
async enumerate(from_key, to_key) {
|
|
return new KeyValueEnumerator(
|
|
await promisify(this.database.enumerate, from_key, to_key)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A class that wraps an nsIKeyValueEnumerator in a Promise-based API.
|
|
*
|
|
* This class isn't exported, so you can't instantiate it directly, but you
|
|
* can retrieve an instance of this class by calling enumerate() on an instance
|
|
* of KeyValueDatabase:
|
|
*
|
|
* ```
|
|
* const database = await KeyValueService.getOrCreate(path, name);
|
|
* const enumerator = await database.enumerate();
|
|
* ```
|
|
*
|
|
* And then iterate pairs via its hasMoreElements() and getNext() methods:
|
|
*
|
|
* ```
|
|
* while (enumerator.hasMoreElements()) {
|
|
* const { key, value } = enumerator.getNext();
|
|
* …
|
|
* }
|
|
* ```
|
|
*
|
|
* Or with a `for...of` statement:
|
|
*
|
|
* ```
|
|
* for (const { key, value } of enumerator) {
|
|
* …
|
|
* }
|
|
* ```
|
|
*/
|
|
class KeyValueEnumerator {
|
|
constructor(enumerator) {
|
|
this.enumerator = enumerator;
|
|
}
|
|
|
|
hasMoreElements() {
|
|
return this.enumerator.hasMoreElements();
|
|
}
|
|
|
|
getNext() {
|
|
return this.enumerator.getNext();
|
|
}
|
|
|
|
*[Symbol.iterator]() {
|
|
while (this.enumerator.hasMoreElements()) {
|
|
yield this.enumerator.getNext();
|
|
}
|
|
}
|
|
}
|