1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 const gKeyValueService = Cc["@mozilla.org/key-value-service;1"].getService(
9 function promisify(fn, ...args) {
10 return new Promise((resolve, reject) => {
11 fn({ resolve, reject }, ...args);
16 * This module wraps the nsIKeyValue* interfaces in a Promise-based API.
17 * To use it, import it, then call the KeyValueService.getOrCreate() method
18 * with a database's path and (optionally) its name:
21 * let { keyValueService } =
22 * ChromeUtils.importESModule("resource://gre/modules/kvstore.sys.mjs");
23 * let database = await KeyValueService.getOrCreate(path, name);
26 * See the documentation in nsIKeyValue.idl for more information about the API
27 * for key/value storage.
30 export class KeyValueService {
31 static RecoveryStrategy = {
32 ERROR: gKeyValueService.ERROR,
33 DISCARD: gKeyValueService.DISCARD,
34 RENAME: gKeyValueService.RENAME,
37 static async getOrCreate(dir, name) {
38 return new KeyValueDatabase(
39 await promisify(gKeyValueService.getOrCreate, dir, name)
43 static async getOrCreateWithOptions(
46 { strategy = gKeyValueService.RENAME } = {}
48 return new KeyValueDatabase(
50 gKeyValueService.getOrCreateWithOptions,
60 * A class that wraps an nsIKeyValueDatabase in a Promise-based API.
62 * This class isn't exported, so you can't instantiate it directly, but you
63 * can retrieve an instance of this class via KeyValueService.getOrCreate():
66 * const database = await KeyValueService.getOrCreate(path, name);
69 * You can then call its put(), get(), has(), and delete() methods to access
70 * and manipulate key/value pairs:
73 * await database.put("foo", 1);
74 * await database.get("foo") === 1; // true
75 * await database.has("foo"); // true
76 * await database.delete("foo");
77 * await database.has("foo"); // false
80 * You can also call writeMany() to put/delete multiple key/value pairs:
83 * await database.writeMany({
87 * key4: null, // delete
91 * And you can call its enumerate() method to retrieve a KeyValueEnumerator,
92 * which is described below.
94 class KeyValueDatabase {
95 constructor(database) {
96 this.database = database;
100 return promisify(this.database.put, key, value);
104 * Writes multiple key/value pairs to the database.
107 * * Each write could be either put or delete.
108 * * Given multiple values with the same key, only the last value will be stored.
109 * * If the same key gets put and deleted for multiple times, the final state
110 * of that key is subject to the ordering of the put(s) and delete(s).
112 * @param pairs Pairs could be any of following types:
113 * * An Object, all its properties and the corresponding values will
114 * be used as key value pairs. A property with null or undefined indicating
116 * * An Array or an iterable whose elements are key-value pairs. such as
117 * [["key1", "value1"], ["key2", "value2"]]. Use a pair with value null
118 * to delete a key-value pair, e.g. ["delete-key", null].
119 * * A Map. A key with null or undefined value indicating a deletion.
120 * @return A promise that is fulfilled when all the key/value pairs are written
125 throw new Error("writeMany(): unexpected argument.");
131 pairs instanceof Map ||
132 pairs instanceof Array ||
133 typeof pairs[Symbol.iterator] === "function"
136 // Let Map constructor validate the argument. Note that Map remembers
137 // the original insertion order of the keys, which satisfies the ordering
138 // premise of this function.
139 const map = pairs instanceof Map ? pairs : new Map(pairs);
140 entries = Array.from(map, ([key, value]) => ({ key, value }));
142 throw new Error("writeMany(): unexpected argument.");
144 } else if (typeof pairs === "object") {
145 entries = Array.from(Object.entries(pairs), ([key, value]) => ({
150 throw new Error("writeMany(): unexpected argument.");
153 if (entries.length) {
154 return promisify(this.database.writeMany, entries);
156 return Promise.resolve();
160 return promisify(this.database.has, key);
163 get(key, defaultValue) {
164 return promisify(this.database.get, key, defaultValue);
168 return promisify(this.database.delete, key);
172 return promisify(this.database.clear);
175 async enumerate(from_key, to_key) {
176 return new KeyValueEnumerator(
177 await promisify(this.database.enumerate, from_key, to_key)
183 * A class that wraps an nsIKeyValueEnumerator in a Promise-based API.
185 * This class isn't exported, so you can't instantiate it directly, but you
186 * can retrieve an instance of this class by calling enumerate() on an instance
187 * of KeyValueDatabase:
190 * const database = await KeyValueService.getOrCreate(path, name);
191 * const enumerator = await database.enumerate();
194 * And then iterate pairs via its hasMoreElements() and getNext() methods:
197 * while (enumerator.hasMoreElements()) {
198 * const { key, value } = enumerator.getNext();
203 * Or with a `for...of` statement:
206 * for (const { key, value } of enumerator) {
211 class KeyValueEnumerator {
212 constructor(enumerator) {
213 this.enumerator = enumerator;
217 return this.enumerator.hasMoreElements();
221 return this.enumerator.getNext();
224 *[Symbol.iterator]() {
225 while (this.enumerator.hasMoreElements()) {
226 yield this.enumerator.getNext();