wxt 0.12.5 → 0.13.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.
- package/dist/{chunk-JK5CNMJT.js → chunk-GNIOSZT6.js} +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/storage.cjs +310 -83
- package/dist/storage.d.cts +185 -12
- package/dist/storage.d.ts +185 -12
- package/dist/storage.js +309 -84
- package/dist/testing.js +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -4334,7 +4334,7 @@ function getChunkSortWeight(filename) {
|
|
|
4334
4334
|
var import_picocolors3 = __toESM(require("picocolors"), 1);
|
|
4335
4335
|
|
|
4336
4336
|
// package.json
|
|
4337
|
-
var version = "0.
|
|
4337
|
+
var version = "0.13.0";
|
|
4338
4338
|
|
|
4339
4339
|
// src/core/utils/log/printHeader.ts
|
|
4340
4340
|
var import_consola2 = require("consola");
|
package/dist/index.d.cts
CHANGED
|
@@ -62,6 +62,6 @@ declare function prepare(config: InlineConfig): Promise<void>;
|
|
|
62
62
|
*/
|
|
63
63
|
declare function zip(config?: InlineConfig): Promise<string[]>;
|
|
64
64
|
|
|
65
|
-
var version = "0.
|
|
65
|
+
var version = "0.13.0";
|
|
66
66
|
|
|
67
67
|
export { BuildOutput, ExtensionRunnerConfig, InlineConfig, UserConfig, WxtDevServer, build, clean, createServer, defineConfig, defineRunnerConfig, initialize, prepare, version, zip };
|
package/dist/index.d.ts
CHANGED
|
@@ -62,6 +62,6 @@ declare function prepare(config: InlineConfig): Promise<void>;
|
|
|
62
62
|
*/
|
|
63
63
|
declare function zip(config?: InlineConfig): Promise<string[]>;
|
|
64
64
|
|
|
65
|
-
var version = "0.
|
|
65
|
+
var version = "0.13.0";
|
|
66
66
|
|
|
67
67
|
export { BuildOutput, ExtensionRunnerConfig, InlineConfig, UserConfig, WxtDevServer, build, clean, createServer, defineConfig, defineRunnerConfig, initialize, prepare, version, zip };
|
package/dist/index.js
CHANGED
package/dist/storage.cjs
CHANGED
|
@@ -17,7 +17,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
}
|
|
18
18
|
return to;
|
|
19
19
|
};
|
|
20
|
-
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
21
20
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
21
|
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
22
|
// file that has been converted to a CommonJS file using a Babel-
|
|
@@ -31,111 +30,339 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
30
|
// src/storage.ts
|
|
32
31
|
var storage_exports = {};
|
|
33
32
|
__export(storage_exports, {
|
|
34
|
-
storage: () => storage
|
|
35
|
-
webExtensionDriver: () => webExtensionDriver
|
|
33
|
+
storage: () => storage
|
|
36
34
|
});
|
|
37
35
|
module.exports = __toCommonJS(storage_exports);
|
|
38
|
-
var import_unstorage = require("unstorage");
|
|
39
36
|
|
|
40
37
|
// src/browser.ts
|
|
41
38
|
var import_webextension_polyfill = __toESM(require("webextension-polyfill"), 1);
|
|
42
39
|
var browser = import_webextension_polyfill.default;
|
|
43
40
|
|
|
44
41
|
// src/storage.ts
|
|
45
|
-
|
|
46
|
-
var
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
var import_lite = require("dequal/lite");
|
|
43
|
+
var storage = createStorage();
|
|
44
|
+
function createStorage() {
|
|
45
|
+
const drivers = {
|
|
46
|
+
local: createDriver("local"),
|
|
47
|
+
session: createDriver("session"),
|
|
48
|
+
sync: createDriver("sync"),
|
|
49
|
+
managed: createDriver("managed")
|
|
50
|
+
};
|
|
51
|
+
const getDriver = (area) => {
|
|
52
|
+
const driver = drivers[area];
|
|
53
|
+
if (driver == null) {
|
|
54
|
+
const areaNames = Object.keys(drivers).join(", ");
|
|
55
|
+
throw Error(`Invalid area "${area}". Options: ${areaNames}`);
|
|
56
|
+
}
|
|
57
|
+
return driver;
|
|
58
|
+
};
|
|
59
|
+
const resolveKey = (key) => {
|
|
60
|
+
const [driverArea, driverKey] = key.split(":", 2);
|
|
61
|
+
if (driverKey == null)
|
|
49
62
|
throw Error(
|
|
50
|
-
|
|
63
|
+
`Storage key should be in the form of "area:key", but recieved "${key}"`
|
|
51
64
|
);
|
|
65
|
+
return {
|
|
66
|
+
driverArea,
|
|
67
|
+
driverKey,
|
|
68
|
+
driver: getDriver(driverArea)
|
|
69
|
+
};
|
|
52
70
|
};
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
const getMetaKey = (key) => key + "$";
|
|
72
|
+
const getValueOrDefault = (value, defaultValue) => value ?? defaultValue ?? null;
|
|
73
|
+
const getMetaValue = (properties) => typeof properties === "object" && !Array.isArray(properties) ? properties : {};
|
|
74
|
+
const getItem = async (driver, driverKey, opts) => {
|
|
75
|
+
const res = await driver.getItem(driverKey);
|
|
76
|
+
return getValueOrDefault(res, opts?.defaultValue);
|
|
77
|
+
};
|
|
78
|
+
const getMeta = async (driver, driverKey) => {
|
|
79
|
+
const metaKey = getMetaKey(driverKey);
|
|
80
|
+
const res = await driver.getItem(metaKey);
|
|
81
|
+
return getMetaValue(res);
|
|
82
|
+
};
|
|
83
|
+
const setItem = async (driver, driverKey, value) => {
|
|
84
|
+
await driver.setItem(driverKey, value ?? null);
|
|
85
|
+
};
|
|
86
|
+
const setMeta = async (driver, driverKey, properties) => {
|
|
87
|
+
const metaKey = getMetaKey(driverKey);
|
|
88
|
+
const existingFields = getMetaValue(await driver.getItem(metaKey));
|
|
89
|
+
const newFields = { ...existingFields };
|
|
90
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
91
|
+
if (value == null) {
|
|
92
|
+
delete newFields[key];
|
|
93
|
+
} else {
|
|
94
|
+
newFields[key] = value;
|
|
95
|
+
}
|
|
58
96
|
});
|
|
97
|
+
await driver.setItem(metaKey, newFields);
|
|
59
98
|
};
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
99
|
+
const removeItem = async (driver, driverKey, opts) => {
|
|
100
|
+
await driver.removeItem(driverKey);
|
|
101
|
+
if (opts?.removeMeta) {
|
|
102
|
+
const metaKey = getMetaKey(driverKey);
|
|
103
|
+
await driver.removeItem(metaKey);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const removeMeta = async (driver, driverKey, properties) => {
|
|
107
|
+
const metaKey = getMetaKey(driverKey);
|
|
108
|
+
if (properties == null) {
|
|
109
|
+
await driver.removeItem(metaKey);
|
|
110
|
+
} else {
|
|
111
|
+
const newFields = getMetaValue(await driver.getItem(metaKey));
|
|
112
|
+
[properties].flat().forEach((field) => delete newFields[field]);
|
|
113
|
+
await driver.setItem(metaKey, newFields);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const watch = (driver, driverKey, cb) => {
|
|
117
|
+
return driver.watch(driverKey, cb);
|
|
118
|
+
};
|
|
119
|
+
const storage2 = {
|
|
120
|
+
getItem: async (key, opts) => {
|
|
121
|
+
const { driver, driverKey } = resolveKey(key);
|
|
122
|
+
return await getItem(driver, driverKey, opts);
|
|
123
|
+
},
|
|
124
|
+
getItems: async (keys) => {
|
|
125
|
+
const areaToKeyMap = /* @__PURE__ */ new Map();
|
|
126
|
+
const keyToOptsMap = /* @__PURE__ */ new Map();
|
|
127
|
+
keys.forEach((key) => {
|
|
128
|
+
let keyStr;
|
|
129
|
+
let opts;
|
|
130
|
+
if (typeof key === "string") {
|
|
131
|
+
keyStr = key;
|
|
132
|
+
} else {
|
|
133
|
+
keyStr = key.key;
|
|
134
|
+
opts = key.options;
|
|
135
|
+
}
|
|
136
|
+
const { driverArea, driverKey } = resolveKey(keyStr);
|
|
137
|
+
const keys2 = areaToKeyMap.get(driverArea) ?? [];
|
|
138
|
+
areaToKeyMap.set(driverArea, keys2.concat(driverKey));
|
|
139
|
+
keyToOptsMap.set(keyStr, opts);
|
|
140
|
+
});
|
|
141
|
+
const results = await Promise.all(
|
|
142
|
+
Array.from(areaToKeyMap.entries()).map(async ([driverArea, keys2]) => {
|
|
143
|
+
const driverResults = await drivers[driverArea].getItems(keys2);
|
|
144
|
+
return driverResults.map((driverResult) => {
|
|
145
|
+
const key = `${driverArea}:${driverResult.key}`;
|
|
146
|
+
const value = getValueOrDefault(
|
|
147
|
+
driverResult.value,
|
|
148
|
+
keyToOptsMap.get(key)?.defaultValue
|
|
149
|
+
);
|
|
150
|
+
return { key, value };
|
|
151
|
+
});
|
|
152
|
+
})
|
|
77
153
|
);
|
|
78
|
-
return
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
154
|
+
return results.flat();
|
|
155
|
+
},
|
|
156
|
+
getMeta: async (key) => {
|
|
157
|
+
const { driver, driverKey } = resolveKey(key);
|
|
158
|
+
return await getMeta(driver, driverKey);
|
|
159
|
+
},
|
|
160
|
+
setItem: async (key, value) => {
|
|
161
|
+
const { driver, driverKey } = resolveKey(key);
|
|
162
|
+
await setItem(driver, driverKey, value);
|
|
163
|
+
},
|
|
164
|
+
setItems: async (values) => {
|
|
165
|
+
const areaToKeyValueMap = /* @__PURE__ */ new Map();
|
|
166
|
+
values.forEach(({ key, value }) => {
|
|
167
|
+
const { driverArea, driverKey } = resolveKey(key);
|
|
168
|
+
const values2 = areaToKeyValueMap.get(driverArea) ?? [];
|
|
169
|
+
areaToKeyValueMap.set(
|
|
170
|
+
driverArea,
|
|
171
|
+
values2.concat({ key: driverKey, value })
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
await Promise.all(
|
|
175
|
+
Array.from(areaToKeyValueMap.entries()).map(
|
|
176
|
+
async ([driverArea, values2]) => {
|
|
177
|
+
const driver = getDriver(driverArea);
|
|
178
|
+
await driver.setItems(values2);
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
},
|
|
183
|
+
setMeta: async (key, properties) => {
|
|
184
|
+
const { driver, driverKey } = resolveKey(key);
|
|
185
|
+
await setMeta(driver, driverKey, properties);
|
|
186
|
+
},
|
|
187
|
+
removeItem: async (key, opts) => {
|
|
188
|
+
const { driver, driverKey } = resolveKey(key);
|
|
189
|
+
await removeItem(driver, driverKey, opts);
|
|
190
|
+
},
|
|
191
|
+
removeItems: async (keys) => {
|
|
192
|
+
const areaToKeysMap = /* @__PURE__ */ new Map();
|
|
193
|
+
keys.forEach((key) => {
|
|
194
|
+
let keyStr;
|
|
195
|
+
let opts;
|
|
196
|
+
if (typeof key === "string") {
|
|
197
|
+
keyStr = key;
|
|
198
|
+
} else {
|
|
199
|
+
keyStr = key.key;
|
|
200
|
+
opts = key.options;
|
|
201
|
+
}
|
|
202
|
+
const { driverArea, driverKey } = resolveKey(keyStr);
|
|
203
|
+
const areaKeys = areaToKeysMap.get(driverArea) ?? [];
|
|
204
|
+
areaKeys.push(driverKey);
|
|
205
|
+
if (opts?.removeMeta) {
|
|
206
|
+
areaKeys.push(getMetaKey(driverKey));
|
|
207
|
+
}
|
|
208
|
+
areaToKeysMap.set(driverArea, areaKeys);
|
|
209
|
+
});
|
|
210
|
+
await Promise.all(
|
|
211
|
+
Array.from(areaToKeysMap.entries()).map(async ([driverArea, keys2]) => {
|
|
212
|
+
const driver = getDriver(driverArea);
|
|
213
|
+
await driver.removeItems(keys2);
|
|
214
|
+
})
|
|
215
|
+
);
|
|
216
|
+
},
|
|
217
|
+
removeMeta: async (key, properties) => {
|
|
218
|
+
const { driver, driverKey } = resolveKey(key);
|
|
219
|
+
await removeMeta(driver, driverKey, properties);
|
|
220
|
+
},
|
|
221
|
+
snapshot: async (base, opts) => {
|
|
222
|
+
const driver = getDriver(base);
|
|
223
|
+
const data = await driver.snapshot();
|
|
224
|
+
opts?.excludeKeys?.forEach((key) => {
|
|
225
|
+
delete data[key];
|
|
226
|
+
delete data[getMetaKey(key)];
|
|
227
|
+
});
|
|
228
|
+
return data;
|
|
229
|
+
},
|
|
230
|
+
restoreSnapshot: async (base, data) => {
|
|
231
|
+
const driver = getDriver(base);
|
|
232
|
+
await driver.restoreSnapshot(data);
|
|
233
|
+
},
|
|
234
|
+
watch: (key, cb) => {
|
|
235
|
+
const { driver, driverKey } = resolveKey(key);
|
|
236
|
+
return watch(driver, driverKey, cb);
|
|
237
|
+
},
|
|
238
|
+
unwatch() {
|
|
239
|
+
Object.values(drivers).forEach((driver) => {
|
|
240
|
+
driver.unwatch();
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
defineItem: (key, opts) => {
|
|
244
|
+
const { driver, driverKey } = resolveKey(key);
|
|
245
|
+
const { version: targetVersion = 1, migrations = {} } = opts ?? {};
|
|
246
|
+
if (targetVersion < 1) {
|
|
247
|
+
throw Error(
|
|
248
|
+
"Storage item version cannot be less than 1. Initial versions should be set to 1, not 0."
|
|
114
249
|
);
|
|
115
250
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
251
|
+
const runMigrations = async () => {
|
|
252
|
+
const [value, meta] = await Promise.all([
|
|
253
|
+
// TODO: Optimize with getItems
|
|
254
|
+
getItem(driver, driverKey, void 0),
|
|
255
|
+
getMeta(driver, driverKey)
|
|
256
|
+
]);
|
|
257
|
+
if (value == null)
|
|
258
|
+
return;
|
|
259
|
+
const currentVersion = meta.v ?? 1;
|
|
260
|
+
if (currentVersion > targetVersion) {
|
|
261
|
+
throw Error(
|
|
262
|
+
`[wxt/storage] Migration ignored for "${key}", version downgrade detected (${currentVersion} -> ${targetVersion})`
|
|
121
263
|
);
|
|
122
264
|
}
|
|
265
|
+
const migrationsToRun = Array.from(
|
|
266
|
+
{ length: targetVersion - currentVersion },
|
|
267
|
+
(_, i) => currentVersion + i + 1
|
|
268
|
+
);
|
|
269
|
+
let migratedValue = value;
|
|
270
|
+
for (const migrateToVersion of migrationsToRun) {
|
|
271
|
+
migratedValue = await migrations?.[migrateToVersion]?.(migratedValue) ?? migratedValue;
|
|
272
|
+
}
|
|
273
|
+
await Promise.all([
|
|
274
|
+
// TODO: Optimize with `setItem`
|
|
275
|
+
setItem(driver, driverKey, migratedValue),
|
|
276
|
+
setMeta(driver, driverKey, { v: targetVersion })
|
|
277
|
+
]);
|
|
278
|
+
};
|
|
279
|
+
let _migrationsCompleted = runMigrations();
|
|
280
|
+
return {
|
|
281
|
+
_migrationsCompleted,
|
|
282
|
+
getValue: () => getItem(driver, driverKey, opts),
|
|
283
|
+
getMeta: () => getMeta(driver, driverKey),
|
|
284
|
+
setValue: (value) => setItem(driver, driverKey, value),
|
|
285
|
+
setMeta: (properties) => setMeta(driver, driverKey, properties),
|
|
286
|
+
removeValue: (opts2) => removeItem(driver, driverKey, opts2),
|
|
287
|
+
removeMeta: (properties) => removeMeta(driver, driverKey, properties),
|
|
288
|
+
watch: (cb) => watch(driver, driverKey, cb)
|
|
123
289
|
};
|
|
124
290
|
}
|
|
125
291
|
};
|
|
126
|
-
});
|
|
127
|
-
function createWebExtensionStorage() {
|
|
128
|
-
const storage2 = (0, import_unstorage.createStorage)();
|
|
129
|
-
storage2.mount("local", webExtensionDriver({ storageArea: "local" }));
|
|
130
|
-
storage2.mount("session", webExtensionDriver({ storageArea: "session" }));
|
|
131
|
-
storage2.mount("sync", webExtensionDriver({ storageArea: "sync" }));
|
|
132
|
-
storage2.mount("managed", webExtensionDriver({ storageArea: "managed" }));
|
|
133
292
|
return storage2;
|
|
134
293
|
}
|
|
135
|
-
|
|
294
|
+
function createDriver(storageArea) {
|
|
295
|
+
const getStorageArea = () => {
|
|
296
|
+
if (browser.storage == null)
|
|
297
|
+
throw Error(
|
|
298
|
+
"You must add the 'storage' permission to your manifest to use 'wxt/storage'"
|
|
299
|
+
);
|
|
300
|
+
return browser.storage[storageArea];
|
|
301
|
+
};
|
|
302
|
+
const watchListeners = /* @__PURE__ */ new Set();
|
|
303
|
+
return {
|
|
304
|
+
getItem: async (key) => {
|
|
305
|
+
const res = await getStorageArea().get(key);
|
|
306
|
+
return res[key];
|
|
307
|
+
},
|
|
308
|
+
getItems: async (keys) => {
|
|
309
|
+
const result = await getStorageArea().get(keys);
|
|
310
|
+
return keys.map((key) => ({ key, value: result[key] ?? null }));
|
|
311
|
+
},
|
|
312
|
+
setItem: async (key, value) => {
|
|
313
|
+
if (value == null) {
|
|
314
|
+
await getStorageArea().remove(key);
|
|
315
|
+
} else {
|
|
316
|
+
await getStorageArea().set({ [key]: value });
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
setItems: async (values) => {
|
|
320
|
+
const map = values.reduce(
|
|
321
|
+
(map2, { key, value }) => {
|
|
322
|
+
map2[key] = value;
|
|
323
|
+
return map2;
|
|
324
|
+
},
|
|
325
|
+
{}
|
|
326
|
+
);
|
|
327
|
+
await getStorageArea().set(map);
|
|
328
|
+
},
|
|
329
|
+
removeItem: async (key) => {
|
|
330
|
+
await getStorageArea().remove(key);
|
|
331
|
+
},
|
|
332
|
+
removeItems: async (keys) => {
|
|
333
|
+
await getStorageArea().remove(keys);
|
|
334
|
+
},
|
|
335
|
+
snapshot: async () => {
|
|
336
|
+
return await getStorageArea().get();
|
|
337
|
+
},
|
|
338
|
+
restoreSnapshot: async (data) => {
|
|
339
|
+
await getStorageArea().set(data);
|
|
340
|
+
},
|
|
341
|
+
watch(key, cb) {
|
|
342
|
+
const listener = (changes) => {
|
|
343
|
+
const change = changes[key];
|
|
344
|
+
if (change == null)
|
|
345
|
+
return;
|
|
346
|
+
if ((0, import_lite.dequal)(change.newValue, change.oldValue))
|
|
347
|
+
return;
|
|
348
|
+
cb(change.newValue ?? null, change.oldValue ?? null);
|
|
349
|
+
};
|
|
350
|
+
getStorageArea().onChanged.addListener(listener);
|
|
351
|
+
watchListeners.add(listener);
|
|
352
|
+
return () => {
|
|
353
|
+
getStorageArea().onChanged.removeListener(listener);
|
|
354
|
+
watchListeners.delete(listener);
|
|
355
|
+
};
|
|
356
|
+
},
|
|
357
|
+
unwatch() {
|
|
358
|
+
watchListeners.forEach((listener) => {
|
|
359
|
+
getStorageArea().onChanged.removeListener(listener);
|
|
360
|
+
});
|
|
361
|
+
watchListeners.clear();
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
}
|
|
136
365
|
// Annotate the CommonJS export names for ESM import in node:
|
|
137
366
|
0 && (module.exports = {
|
|
138
|
-
storage
|
|
139
|
-
webExtensionDriver,
|
|
140
|
-
...require("unstorage")
|
|
367
|
+
storage
|
|
141
368
|
});
|
package/dist/storage.d.cts
CHANGED
|
@@ -1,15 +1,188 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
declare const storage: WxtStorage;
|
|
2
|
+
interface WxtStorage {
|
|
3
|
+
/**
|
|
4
|
+
* Get an item from storage, or return `null` if it doesn't exist.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* await storage.getItem<number>("local:installDate");
|
|
8
|
+
*/
|
|
9
|
+
getItem<T>(key: string, opts?: GetItemOptions<T>): Promise<T | null>;
|
|
10
|
+
/**
|
|
11
|
+
* Get multiple items from storage. There is no guarentee of order in the returned array.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* await storage.getItems(["local:installDate", "session:someCounter"]);
|
|
15
|
+
*/
|
|
16
|
+
getItems(keys: Array<string | {
|
|
17
|
+
key: string;
|
|
18
|
+
options?: GetItemOptions<any>;
|
|
19
|
+
}>): Promise<Array<{
|
|
20
|
+
key: string;
|
|
21
|
+
value: any;
|
|
22
|
+
}>>;
|
|
23
|
+
/**
|
|
24
|
+
* Return an object containing metadata about the key. Object is stored at `key + "$"`. If value
|
|
25
|
+
* is not an object, it returns an empty object.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* await storage.getMeta("local:installDate");
|
|
29
|
+
*/
|
|
30
|
+
getMeta<T extends Record<string, unknown>>(key: string): Promise<T>;
|
|
31
|
+
/**
|
|
32
|
+
* Set a value in storage. Setting a value to `null` or `undefined` is equivalent to calling
|
|
33
|
+
* `removeItem`.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* await storage.setItem<number>("local:installDate", Date.now());
|
|
37
|
+
*/
|
|
38
|
+
setItem<T>(key: string, value: T | null): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Set multiple values in storage. If a value is set to `null` or `undefined`, the key is removed.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* await storage.setItem([
|
|
44
|
+
* { key: "local:installDate", value: Date.now() },
|
|
45
|
+
* { key: "session:someCounter, value: 5 },
|
|
46
|
+
* ]);
|
|
47
|
+
*/
|
|
48
|
+
setItems(values: Array<{
|
|
49
|
+
key: string;
|
|
50
|
+
value: any;
|
|
51
|
+
}>): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Sets metadata properties. If some properties are already set, but are not included in the
|
|
54
|
+
* `properties` parameter, they will not be removed.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* await storage.setMeta("local:installDate", { appVersion });
|
|
58
|
+
*/
|
|
59
|
+
setMeta<T extends Record<string, unknown>>(key: string, properties: T | null): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Removes an item from storage.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* await storage.removeItem("local:installDate");
|
|
65
|
+
*/
|
|
66
|
+
removeItem(key: string, opts?: RemoveItemOptions): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Remove a list of keys from storage.
|
|
69
|
+
*/
|
|
70
|
+
removeItems(keys: Array<string | {
|
|
71
|
+
key: string;
|
|
72
|
+
options?: RemoveItemOptions;
|
|
73
|
+
}>): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Remove the entire metadata for a key, or specific properties by name.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* // Remove all metadata properties from the item
|
|
79
|
+
* await storage.removeMeta("local:installDate");
|
|
80
|
+
*
|
|
81
|
+
* // Remove only specific the "v" field
|
|
82
|
+
* await storage.removeMeta("local:installDate", "v")
|
|
83
|
+
*/
|
|
84
|
+
removeMeta(key: string, properties?: string | string[]): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Return all the items in storage.
|
|
87
|
+
*/
|
|
88
|
+
snapshot(base: string, opts?: SnapshotOptions): Promise<Record<string, unknown>>;
|
|
89
|
+
/**
|
|
90
|
+
* Restores the results of `snapshot`. If new properties have been saved since the snapshot, they are
|
|
91
|
+
* not overridden. Only values existing in the snapshot are overritten.
|
|
92
|
+
*/
|
|
93
|
+
restoreSnapshot(base: string, data: any): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Watch for changes to a specific key in storage.
|
|
96
|
+
*/
|
|
97
|
+
watch<T>(key: string, cb: WatchCallback<T>): Unwatch;
|
|
98
|
+
/**
|
|
99
|
+
* Remove all watch listeners.
|
|
100
|
+
*/
|
|
101
|
+
unwatch(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Define a constant with utilities for reading/writing to a single value in storage.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* export const installDate = storage.defineItem<number>("local:installDate");
|
|
107
|
+
*/
|
|
108
|
+
defineItem<TValue, TMetadata extends Record<string, unknown> = {}>(key: string, options?: WxtStorageItemOptions<TValue>): WxtStorageItem<TValue, TMetadata>;
|
|
109
|
+
}
|
|
110
|
+
interface WxtStorageItem<TValue, TMetadata extends Record<string, unknown>> {
|
|
111
|
+
/**
|
|
112
|
+
* Get the latest value from storage.
|
|
113
|
+
*/
|
|
114
|
+
getValue(): Promise<TValue>;
|
|
115
|
+
/**
|
|
116
|
+
* Get metadata.
|
|
117
|
+
*/
|
|
118
|
+
getMeta(): Promise<NullablePartial<TMetadata>>;
|
|
119
|
+
/**
|
|
120
|
+
* Set the value in storage.
|
|
121
|
+
*/
|
|
122
|
+
setValue(value: TValue | null): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Set metadata properties.
|
|
125
|
+
*/
|
|
126
|
+
setMeta(properties: NullablePartial<TMetadata>): Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* Remove the value from storage.
|
|
129
|
+
*/
|
|
130
|
+
removeValue(opts?: RemoveItemOptions): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Remove all metadata or certain properties from metadata.
|
|
133
|
+
*/
|
|
134
|
+
removeMeta(properties?: string[]): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Listen for changes to the value in storage.
|
|
137
|
+
*/
|
|
138
|
+
watch(cb: WatchCallback<TValue>): Unwatch;
|
|
139
|
+
}
|
|
140
|
+
interface GetItemOptions<T> {
|
|
141
|
+
/**
|
|
142
|
+
* Value returned from `getValue` when it would otherwise return null.
|
|
143
|
+
*/
|
|
144
|
+
defaultValue?: T;
|
|
145
|
+
}
|
|
146
|
+
interface RemoveItemOptions {
|
|
147
|
+
/**
|
|
148
|
+
* Optionally remove metadata when deleting a key.
|
|
149
|
+
*
|
|
150
|
+
* @default false
|
|
151
|
+
*/
|
|
152
|
+
removeMeta?: boolean;
|
|
153
|
+
}
|
|
154
|
+
interface SnapshotOptions {
|
|
155
|
+
/**
|
|
156
|
+
* Exclude a list of keys. The storage area prefix should be removed since the snapshot is for a
|
|
157
|
+
* specific storage area already.
|
|
158
|
+
*/
|
|
159
|
+
excludeKeys?: string[];
|
|
160
|
+
}
|
|
161
|
+
interface WxtStorageItemOptions<T> extends GetItemOptions<T> {
|
|
162
|
+
/**
|
|
163
|
+
* Provide a version number for the storage item to enable migrations. When changing the version
|
|
164
|
+
* in the future, migration functions will be ran on application startup.
|
|
165
|
+
*/
|
|
166
|
+
version?: number;
|
|
167
|
+
/**
|
|
168
|
+
* A map of version numbers to the functions used to migrate the data to that version.
|
|
169
|
+
*/
|
|
170
|
+
migrations?: Record<number, (oldValue: any) => any>;
|
|
171
|
+
}
|
|
4
172
|
/**
|
|
5
|
-
*
|
|
173
|
+
* Same as `Partial`, but includes `| null`. It makes all the properties of an object optional and
|
|
174
|
+
* nullable.
|
|
6
175
|
*/
|
|
176
|
+
type NullablePartial<T> = {
|
|
177
|
+
[key in keyof T]+?: T[key] | undefined | null;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Callback called when a value in storage is changed.
|
|
181
|
+
*/
|
|
182
|
+
type WatchCallback<T> = (newValue: T | null, oldValue: T | null) => void;
|
|
183
|
+
/**
|
|
184
|
+
* Call to remove a watch listener
|
|
185
|
+
*/
|
|
186
|
+
type Unwatch = () => void;
|
|
7
187
|
|
|
8
|
-
|
|
9
|
-
storageArea: 'sync' | 'local' | 'managed' | 'session';
|
|
10
|
-
}
|
|
11
|
-
declare const webExtensionDriver: (opts: WebExtensionDriverOptions) => Driver;
|
|
12
|
-
type StorageValue = null | string | number | boolean | object;
|
|
13
|
-
declare const storage: Storage<StorageValue>;
|
|
14
|
-
|
|
15
|
-
export { type StorageValue, type WebExtensionDriverOptions, storage, webExtensionDriver };
|
|
188
|
+
export { type GetItemOptions, type NullablePartial, type RemoveItemOptions, type SnapshotOptions, type Unwatch, type WatchCallback, type WxtStorage, type WxtStorageItem, type WxtStorageItemOptions, storage };
|
package/dist/storage.d.ts
CHANGED
|
@@ -1,15 +1,188 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
declare const storage: WxtStorage;
|
|
2
|
+
interface WxtStorage {
|
|
3
|
+
/**
|
|
4
|
+
* Get an item from storage, or return `null` if it doesn't exist.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* await storage.getItem<number>("local:installDate");
|
|
8
|
+
*/
|
|
9
|
+
getItem<T>(key: string, opts?: GetItemOptions<T>): Promise<T | null>;
|
|
10
|
+
/**
|
|
11
|
+
* Get multiple items from storage. There is no guarentee of order in the returned array.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* await storage.getItems(["local:installDate", "session:someCounter"]);
|
|
15
|
+
*/
|
|
16
|
+
getItems(keys: Array<string | {
|
|
17
|
+
key: string;
|
|
18
|
+
options?: GetItemOptions<any>;
|
|
19
|
+
}>): Promise<Array<{
|
|
20
|
+
key: string;
|
|
21
|
+
value: any;
|
|
22
|
+
}>>;
|
|
23
|
+
/**
|
|
24
|
+
* Return an object containing metadata about the key. Object is stored at `key + "$"`. If value
|
|
25
|
+
* is not an object, it returns an empty object.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* await storage.getMeta("local:installDate");
|
|
29
|
+
*/
|
|
30
|
+
getMeta<T extends Record<string, unknown>>(key: string): Promise<T>;
|
|
31
|
+
/**
|
|
32
|
+
* Set a value in storage. Setting a value to `null` or `undefined` is equivalent to calling
|
|
33
|
+
* `removeItem`.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* await storage.setItem<number>("local:installDate", Date.now());
|
|
37
|
+
*/
|
|
38
|
+
setItem<T>(key: string, value: T | null): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Set multiple values in storage. If a value is set to `null` or `undefined`, the key is removed.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* await storage.setItem([
|
|
44
|
+
* { key: "local:installDate", value: Date.now() },
|
|
45
|
+
* { key: "session:someCounter, value: 5 },
|
|
46
|
+
* ]);
|
|
47
|
+
*/
|
|
48
|
+
setItems(values: Array<{
|
|
49
|
+
key: string;
|
|
50
|
+
value: any;
|
|
51
|
+
}>): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Sets metadata properties. If some properties are already set, but are not included in the
|
|
54
|
+
* `properties` parameter, they will not be removed.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* await storage.setMeta("local:installDate", { appVersion });
|
|
58
|
+
*/
|
|
59
|
+
setMeta<T extends Record<string, unknown>>(key: string, properties: T | null): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Removes an item from storage.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* await storage.removeItem("local:installDate");
|
|
65
|
+
*/
|
|
66
|
+
removeItem(key: string, opts?: RemoveItemOptions): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Remove a list of keys from storage.
|
|
69
|
+
*/
|
|
70
|
+
removeItems(keys: Array<string | {
|
|
71
|
+
key: string;
|
|
72
|
+
options?: RemoveItemOptions;
|
|
73
|
+
}>): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Remove the entire metadata for a key, or specific properties by name.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* // Remove all metadata properties from the item
|
|
79
|
+
* await storage.removeMeta("local:installDate");
|
|
80
|
+
*
|
|
81
|
+
* // Remove only specific the "v" field
|
|
82
|
+
* await storage.removeMeta("local:installDate", "v")
|
|
83
|
+
*/
|
|
84
|
+
removeMeta(key: string, properties?: string | string[]): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Return all the items in storage.
|
|
87
|
+
*/
|
|
88
|
+
snapshot(base: string, opts?: SnapshotOptions): Promise<Record<string, unknown>>;
|
|
89
|
+
/**
|
|
90
|
+
* Restores the results of `snapshot`. If new properties have been saved since the snapshot, they are
|
|
91
|
+
* not overridden. Only values existing in the snapshot are overritten.
|
|
92
|
+
*/
|
|
93
|
+
restoreSnapshot(base: string, data: any): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Watch for changes to a specific key in storage.
|
|
96
|
+
*/
|
|
97
|
+
watch<T>(key: string, cb: WatchCallback<T>): Unwatch;
|
|
98
|
+
/**
|
|
99
|
+
* Remove all watch listeners.
|
|
100
|
+
*/
|
|
101
|
+
unwatch(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Define a constant with utilities for reading/writing to a single value in storage.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* export const installDate = storage.defineItem<number>("local:installDate");
|
|
107
|
+
*/
|
|
108
|
+
defineItem<TValue, TMetadata extends Record<string, unknown> = {}>(key: string, options?: WxtStorageItemOptions<TValue>): WxtStorageItem<TValue, TMetadata>;
|
|
109
|
+
}
|
|
110
|
+
interface WxtStorageItem<TValue, TMetadata extends Record<string, unknown>> {
|
|
111
|
+
/**
|
|
112
|
+
* Get the latest value from storage.
|
|
113
|
+
*/
|
|
114
|
+
getValue(): Promise<TValue>;
|
|
115
|
+
/**
|
|
116
|
+
* Get metadata.
|
|
117
|
+
*/
|
|
118
|
+
getMeta(): Promise<NullablePartial<TMetadata>>;
|
|
119
|
+
/**
|
|
120
|
+
* Set the value in storage.
|
|
121
|
+
*/
|
|
122
|
+
setValue(value: TValue | null): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Set metadata properties.
|
|
125
|
+
*/
|
|
126
|
+
setMeta(properties: NullablePartial<TMetadata>): Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* Remove the value from storage.
|
|
129
|
+
*/
|
|
130
|
+
removeValue(opts?: RemoveItemOptions): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Remove all metadata or certain properties from metadata.
|
|
133
|
+
*/
|
|
134
|
+
removeMeta(properties?: string[]): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Listen for changes to the value in storage.
|
|
137
|
+
*/
|
|
138
|
+
watch(cb: WatchCallback<TValue>): Unwatch;
|
|
139
|
+
}
|
|
140
|
+
interface GetItemOptions<T> {
|
|
141
|
+
/**
|
|
142
|
+
* Value returned from `getValue` when it would otherwise return null.
|
|
143
|
+
*/
|
|
144
|
+
defaultValue?: T;
|
|
145
|
+
}
|
|
146
|
+
interface RemoveItemOptions {
|
|
147
|
+
/**
|
|
148
|
+
* Optionally remove metadata when deleting a key.
|
|
149
|
+
*
|
|
150
|
+
* @default false
|
|
151
|
+
*/
|
|
152
|
+
removeMeta?: boolean;
|
|
153
|
+
}
|
|
154
|
+
interface SnapshotOptions {
|
|
155
|
+
/**
|
|
156
|
+
* Exclude a list of keys. The storage area prefix should be removed since the snapshot is for a
|
|
157
|
+
* specific storage area already.
|
|
158
|
+
*/
|
|
159
|
+
excludeKeys?: string[];
|
|
160
|
+
}
|
|
161
|
+
interface WxtStorageItemOptions<T> extends GetItemOptions<T> {
|
|
162
|
+
/**
|
|
163
|
+
* Provide a version number for the storage item to enable migrations. When changing the version
|
|
164
|
+
* in the future, migration functions will be ran on application startup.
|
|
165
|
+
*/
|
|
166
|
+
version?: number;
|
|
167
|
+
/**
|
|
168
|
+
* A map of version numbers to the functions used to migrate the data to that version.
|
|
169
|
+
*/
|
|
170
|
+
migrations?: Record<number, (oldValue: any) => any>;
|
|
171
|
+
}
|
|
4
172
|
/**
|
|
5
|
-
*
|
|
173
|
+
* Same as `Partial`, but includes `| null`. It makes all the properties of an object optional and
|
|
174
|
+
* nullable.
|
|
6
175
|
*/
|
|
176
|
+
type NullablePartial<T> = {
|
|
177
|
+
[key in keyof T]+?: T[key] | undefined | null;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Callback called when a value in storage is changed.
|
|
181
|
+
*/
|
|
182
|
+
type WatchCallback<T> = (newValue: T | null, oldValue: T | null) => void;
|
|
183
|
+
/**
|
|
184
|
+
* Call to remove a watch listener
|
|
185
|
+
*/
|
|
186
|
+
type Unwatch = () => void;
|
|
7
187
|
|
|
8
|
-
|
|
9
|
-
storageArea: 'sync' | 'local' | 'managed' | 'session';
|
|
10
|
-
}
|
|
11
|
-
declare const webExtensionDriver: (opts: WebExtensionDriverOptions) => Driver;
|
|
12
|
-
type StorageValue = null | string | number | boolean | object;
|
|
13
|
-
declare const storage: Storage<StorageValue>;
|
|
14
|
-
|
|
15
|
-
export { type StorageValue, type WebExtensionDriverOptions, storage, webExtensionDriver };
|
|
188
|
+
export { type GetItemOptions, type NullablePartial, type RemoveItemOptions, type SnapshotOptions, type Unwatch, type WatchCallback, type WxtStorage, type WxtStorageItem, type WxtStorageItemOptions, storage };
|
package/dist/storage.js
CHANGED
|
@@ -1,108 +1,333 @@
|
|
|
1
1
|
import "./chunk-VBXJIVYU.js";
|
|
2
2
|
|
|
3
|
-
// src/storage.ts
|
|
4
|
-
import {
|
|
5
|
-
createStorage,
|
|
6
|
-
defineDriver
|
|
7
|
-
} from "unstorage";
|
|
8
|
-
|
|
9
3
|
// src/browser.ts
|
|
10
4
|
import originalBrowser from "webextension-polyfill";
|
|
11
5
|
var browser = originalBrowser;
|
|
12
6
|
|
|
13
7
|
// src/storage.ts
|
|
14
|
-
|
|
15
|
-
var
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
import { dequal } from "dequal/lite";
|
|
9
|
+
var storage = createStorage();
|
|
10
|
+
function createStorage() {
|
|
11
|
+
const drivers = {
|
|
12
|
+
local: createDriver("local"),
|
|
13
|
+
session: createDriver("session"),
|
|
14
|
+
sync: createDriver("sync"),
|
|
15
|
+
managed: createDriver("managed")
|
|
16
|
+
};
|
|
17
|
+
const getDriver = (area) => {
|
|
18
|
+
const driver = drivers[area];
|
|
19
|
+
if (driver == null) {
|
|
20
|
+
const areaNames = Object.keys(drivers).join(", ");
|
|
21
|
+
throw Error(`Invalid area "${area}". Options: ${areaNames}`);
|
|
22
|
+
}
|
|
23
|
+
return driver;
|
|
24
|
+
};
|
|
25
|
+
const resolveKey = (key) => {
|
|
26
|
+
const [driverArea, driverKey] = key.split(":", 2);
|
|
27
|
+
if (driverKey == null)
|
|
18
28
|
throw Error(
|
|
19
|
-
|
|
29
|
+
`Storage key should be in the form of "area:key", but recieved "${key}"`
|
|
20
30
|
);
|
|
31
|
+
return {
|
|
32
|
+
driverArea,
|
|
33
|
+
driverKey,
|
|
34
|
+
driver: getDriver(driverArea)
|
|
35
|
+
};
|
|
21
36
|
};
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
const getMetaKey = (key) => key + "$";
|
|
38
|
+
const getValueOrDefault = (value, defaultValue) => value ?? defaultValue ?? null;
|
|
39
|
+
const getMetaValue = (properties) => typeof properties === "object" && !Array.isArray(properties) ? properties : {};
|
|
40
|
+
const getItem = async (driver, driverKey, opts) => {
|
|
41
|
+
const res = await driver.getItem(driverKey);
|
|
42
|
+
return getValueOrDefault(res, opts?.defaultValue);
|
|
43
|
+
};
|
|
44
|
+
const getMeta = async (driver, driverKey) => {
|
|
45
|
+
const metaKey = getMetaKey(driverKey);
|
|
46
|
+
const res = await driver.getItem(metaKey);
|
|
47
|
+
return getMetaValue(res);
|
|
48
|
+
};
|
|
49
|
+
const setItem = async (driver, driverKey, value) => {
|
|
50
|
+
await driver.setItem(driverKey, value ?? null);
|
|
51
|
+
};
|
|
52
|
+
const setMeta = async (driver, driverKey, properties) => {
|
|
53
|
+
const metaKey = getMetaKey(driverKey);
|
|
54
|
+
const existingFields = getMetaValue(await driver.getItem(metaKey));
|
|
55
|
+
const newFields = { ...existingFields };
|
|
56
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
57
|
+
if (value == null) {
|
|
58
|
+
delete newFields[key];
|
|
59
|
+
} else {
|
|
60
|
+
newFields[key] = value;
|
|
61
|
+
}
|
|
27
62
|
});
|
|
63
|
+
await driver.setItem(metaKey, newFields);
|
|
28
64
|
};
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
65
|
+
const removeItem = async (driver, driverKey, opts) => {
|
|
66
|
+
await driver.removeItem(driverKey);
|
|
67
|
+
if (opts?.removeMeta) {
|
|
68
|
+
const metaKey = getMetaKey(driverKey);
|
|
69
|
+
await driver.removeItem(metaKey);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const removeMeta = async (driver, driverKey, properties) => {
|
|
73
|
+
const metaKey = getMetaKey(driverKey);
|
|
74
|
+
if (properties == null) {
|
|
75
|
+
await driver.removeItem(metaKey);
|
|
76
|
+
} else {
|
|
77
|
+
const newFields = getMetaValue(await driver.getItem(metaKey));
|
|
78
|
+
[properties].flat().forEach((field) => delete newFields[field]);
|
|
79
|
+
await driver.setItem(metaKey, newFields);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const watch = (driver, driverKey, cb) => {
|
|
83
|
+
return driver.watch(driverKey, cb);
|
|
84
|
+
};
|
|
85
|
+
const storage2 = {
|
|
86
|
+
getItem: async (key, opts) => {
|
|
87
|
+
const { driver, driverKey } = resolveKey(key);
|
|
88
|
+
return await getItem(driver, driverKey, opts);
|
|
89
|
+
},
|
|
90
|
+
getItems: async (keys) => {
|
|
91
|
+
const areaToKeyMap = /* @__PURE__ */ new Map();
|
|
92
|
+
const keyToOptsMap = /* @__PURE__ */ new Map();
|
|
93
|
+
keys.forEach((key) => {
|
|
94
|
+
let keyStr;
|
|
95
|
+
let opts;
|
|
96
|
+
if (typeof key === "string") {
|
|
97
|
+
keyStr = key;
|
|
98
|
+
} else {
|
|
99
|
+
keyStr = key.key;
|
|
100
|
+
opts = key.options;
|
|
101
|
+
}
|
|
102
|
+
const { driverArea, driverKey } = resolveKey(keyStr);
|
|
103
|
+
const keys2 = areaToKeyMap.get(driverArea) ?? [];
|
|
104
|
+
areaToKeyMap.set(driverArea, keys2.concat(driverKey));
|
|
105
|
+
keyToOptsMap.set(keyStr, opts);
|
|
106
|
+
});
|
|
107
|
+
const results = await Promise.all(
|
|
108
|
+
Array.from(areaToKeyMap.entries()).map(async ([driverArea, keys2]) => {
|
|
109
|
+
const driverResults = await drivers[driverArea].getItems(keys2);
|
|
110
|
+
return driverResults.map((driverResult) => {
|
|
111
|
+
const key = `${driverArea}:${driverResult.key}`;
|
|
112
|
+
const value = getValueOrDefault(
|
|
113
|
+
driverResult.value,
|
|
114
|
+
keyToOptsMap.get(key)?.defaultValue
|
|
115
|
+
);
|
|
116
|
+
return { key, value };
|
|
117
|
+
});
|
|
118
|
+
})
|
|
119
|
+
);
|
|
120
|
+
return results.flat();
|
|
121
|
+
},
|
|
122
|
+
getMeta: async (key) => {
|
|
123
|
+
const { driver, driverKey } = resolveKey(key);
|
|
124
|
+
return await getMeta(driver, driverKey);
|
|
125
|
+
},
|
|
126
|
+
setItem: async (key, value) => {
|
|
127
|
+
const { driver, driverKey } = resolveKey(key);
|
|
128
|
+
await setItem(driver, driverKey, value);
|
|
129
|
+
},
|
|
130
|
+
setItems: async (values) => {
|
|
131
|
+
const areaToKeyValueMap = /* @__PURE__ */ new Map();
|
|
132
|
+
values.forEach(({ key, value }) => {
|
|
133
|
+
const { driverArea, driverKey } = resolveKey(key);
|
|
134
|
+
const values2 = areaToKeyValueMap.get(driverArea) ?? [];
|
|
135
|
+
areaToKeyValueMap.set(
|
|
136
|
+
driverArea,
|
|
137
|
+
values2.concat({ key: driverKey, value })
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
await Promise.all(
|
|
141
|
+
Array.from(areaToKeyValueMap.entries()).map(
|
|
142
|
+
async ([driverArea, values2]) => {
|
|
143
|
+
const driver = getDriver(driverArea);
|
|
144
|
+
await driver.setItems(values2);
|
|
145
|
+
}
|
|
146
|
+
)
|
|
46
147
|
);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
},
|
|
52
|
-
async
|
|
53
|
-
|
|
54
|
-
await
|
|
55
|
-
},
|
|
56
|
-
async
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
await
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
148
|
+
},
|
|
149
|
+
setMeta: async (key, properties) => {
|
|
150
|
+
const { driver, driverKey } = resolveKey(key);
|
|
151
|
+
await setMeta(driver, driverKey, properties);
|
|
152
|
+
},
|
|
153
|
+
removeItem: async (key, opts) => {
|
|
154
|
+
const { driver, driverKey } = resolveKey(key);
|
|
155
|
+
await removeItem(driver, driverKey, opts);
|
|
156
|
+
},
|
|
157
|
+
removeItems: async (keys) => {
|
|
158
|
+
const areaToKeysMap = /* @__PURE__ */ new Map();
|
|
159
|
+
keys.forEach((key) => {
|
|
160
|
+
let keyStr;
|
|
161
|
+
let opts;
|
|
162
|
+
if (typeof key === "string") {
|
|
163
|
+
keyStr = key;
|
|
164
|
+
} else {
|
|
165
|
+
keyStr = key.key;
|
|
166
|
+
opts = key.options;
|
|
167
|
+
}
|
|
168
|
+
const { driverArea, driverKey } = resolveKey(keyStr);
|
|
169
|
+
const areaKeys = areaToKeysMap.get(driverArea) ?? [];
|
|
170
|
+
areaKeys.push(driverKey);
|
|
171
|
+
if (opts?.removeMeta) {
|
|
172
|
+
areaKeys.push(getMetaKey(driverKey));
|
|
173
|
+
}
|
|
174
|
+
areaToKeysMap.set(driverArea, areaKeys);
|
|
175
|
+
});
|
|
176
|
+
await Promise.all(
|
|
177
|
+
Array.from(areaToKeysMap.entries()).map(async ([driverArea, keys2]) => {
|
|
178
|
+
const driver = getDriver(driverArea);
|
|
179
|
+
await driver.removeItems(keys2);
|
|
180
|
+
})
|
|
181
|
+
);
|
|
182
|
+
},
|
|
183
|
+
removeMeta: async (key, properties) => {
|
|
184
|
+
const { driver, driverKey } = resolveKey(key);
|
|
185
|
+
await removeMeta(driver, driverKey, properties);
|
|
186
|
+
},
|
|
187
|
+
snapshot: async (base, opts) => {
|
|
188
|
+
const driver = getDriver(base);
|
|
189
|
+
const data = await driver.snapshot();
|
|
190
|
+
opts?.excludeKeys?.forEach((key) => {
|
|
191
|
+
delete data[key];
|
|
192
|
+
delete data[getMetaKey(key)];
|
|
193
|
+
});
|
|
194
|
+
return data;
|
|
195
|
+
},
|
|
196
|
+
restoreSnapshot: async (base, data) => {
|
|
197
|
+
const driver = getDriver(base);
|
|
198
|
+
await driver.restoreSnapshot(data);
|
|
199
|
+
},
|
|
200
|
+
watch: (key, cb) => {
|
|
201
|
+
const { driver, driverKey } = resolveKey(key);
|
|
202
|
+
return watch(driver, driverKey, cb);
|
|
203
|
+
},
|
|
204
|
+
unwatch() {
|
|
205
|
+
Object.values(drivers).forEach((driver) => {
|
|
206
|
+
driver.unwatch();
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
defineItem: (key, opts) => {
|
|
210
|
+
const { driver, driverKey } = resolveKey(key);
|
|
211
|
+
const { version: targetVersion = 1, migrations = {} } = opts ?? {};
|
|
212
|
+
if (targetVersion < 1) {
|
|
213
|
+
throw Error(
|
|
214
|
+
"Storage item version cannot be less than 1. Initial versions should be set to 1, not 0."
|
|
83
215
|
);
|
|
84
216
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
217
|
+
const runMigrations = async () => {
|
|
218
|
+
const [value, meta] = await Promise.all([
|
|
219
|
+
// TODO: Optimize with getItems
|
|
220
|
+
getItem(driver, driverKey, void 0),
|
|
221
|
+
getMeta(driver, driverKey)
|
|
222
|
+
]);
|
|
223
|
+
if (value == null)
|
|
224
|
+
return;
|
|
225
|
+
const currentVersion = meta.v ?? 1;
|
|
226
|
+
if (currentVersion > targetVersion) {
|
|
227
|
+
throw Error(
|
|
228
|
+
`[wxt/storage] Migration ignored for "${key}", version downgrade detected (${currentVersion} -> ${targetVersion})`
|
|
90
229
|
);
|
|
91
230
|
}
|
|
231
|
+
const migrationsToRun = Array.from(
|
|
232
|
+
{ length: targetVersion - currentVersion },
|
|
233
|
+
(_, i) => currentVersion + i + 1
|
|
234
|
+
);
|
|
235
|
+
let migratedValue = value;
|
|
236
|
+
for (const migrateToVersion of migrationsToRun) {
|
|
237
|
+
migratedValue = await migrations?.[migrateToVersion]?.(migratedValue) ?? migratedValue;
|
|
238
|
+
}
|
|
239
|
+
await Promise.all([
|
|
240
|
+
// TODO: Optimize with `setItem`
|
|
241
|
+
setItem(driver, driverKey, migratedValue),
|
|
242
|
+
setMeta(driver, driverKey, { v: targetVersion })
|
|
243
|
+
]);
|
|
244
|
+
};
|
|
245
|
+
let _migrationsCompleted = runMigrations();
|
|
246
|
+
return {
|
|
247
|
+
_migrationsCompleted,
|
|
248
|
+
getValue: () => getItem(driver, driverKey, opts),
|
|
249
|
+
getMeta: () => getMeta(driver, driverKey),
|
|
250
|
+
setValue: (value) => setItem(driver, driverKey, value),
|
|
251
|
+
setMeta: (properties) => setMeta(driver, driverKey, properties),
|
|
252
|
+
removeValue: (opts2) => removeItem(driver, driverKey, opts2),
|
|
253
|
+
removeMeta: (properties) => removeMeta(driver, driverKey, properties),
|
|
254
|
+
watch: (cb) => watch(driver, driverKey, cb)
|
|
92
255
|
};
|
|
93
256
|
}
|
|
94
257
|
};
|
|
95
|
-
});
|
|
96
|
-
function createWebExtensionStorage() {
|
|
97
|
-
const storage2 = createStorage();
|
|
98
|
-
storage2.mount("local", webExtensionDriver({ storageArea: "local" }));
|
|
99
|
-
storage2.mount("session", webExtensionDriver({ storageArea: "session" }));
|
|
100
|
-
storage2.mount("sync", webExtensionDriver({ storageArea: "sync" }));
|
|
101
|
-
storage2.mount("managed", webExtensionDriver({ storageArea: "managed" }));
|
|
102
258
|
return storage2;
|
|
103
259
|
}
|
|
104
|
-
|
|
260
|
+
function createDriver(storageArea) {
|
|
261
|
+
const getStorageArea = () => {
|
|
262
|
+
if (browser.storage == null)
|
|
263
|
+
throw Error(
|
|
264
|
+
"You must add the 'storage' permission to your manifest to use 'wxt/storage'"
|
|
265
|
+
);
|
|
266
|
+
return browser.storage[storageArea];
|
|
267
|
+
};
|
|
268
|
+
const watchListeners = /* @__PURE__ */ new Set();
|
|
269
|
+
return {
|
|
270
|
+
getItem: async (key) => {
|
|
271
|
+
const res = await getStorageArea().get(key);
|
|
272
|
+
return res[key];
|
|
273
|
+
},
|
|
274
|
+
getItems: async (keys) => {
|
|
275
|
+
const result = await getStorageArea().get(keys);
|
|
276
|
+
return keys.map((key) => ({ key, value: result[key] ?? null }));
|
|
277
|
+
},
|
|
278
|
+
setItem: async (key, value) => {
|
|
279
|
+
if (value == null) {
|
|
280
|
+
await getStorageArea().remove(key);
|
|
281
|
+
} else {
|
|
282
|
+
await getStorageArea().set({ [key]: value });
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
setItems: async (values) => {
|
|
286
|
+
const map = values.reduce(
|
|
287
|
+
(map2, { key, value }) => {
|
|
288
|
+
map2[key] = value;
|
|
289
|
+
return map2;
|
|
290
|
+
},
|
|
291
|
+
{}
|
|
292
|
+
);
|
|
293
|
+
await getStorageArea().set(map);
|
|
294
|
+
},
|
|
295
|
+
removeItem: async (key) => {
|
|
296
|
+
await getStorageArea().remove(key);
|
|
297
|
+
},
|
|
298
|
+
removeItems: async (keys) => {
|
|
299
|
+
await getStorageArea().remove(keys);
|
|
300
|
+
},
|
|
301
|
+
snapshot: async () => {
|
|
302
|
+
return await getStorageArea().get();
|
|
303
|
+
},
|
|
304
|
+
restoreSnapshot: async (data) => {
|
|
305
|
+
await getStorageArea().set(data);
|
|
306
|
+
},
|
|
307
|
+
watch(key, cb) {
|
|
308
|
+
const listener = (changes) => {
|
|
309
|
+
const change = changes[key];
|
|
310
|
+
if (change == null)
|
|
311
|
+
return;
|
|
312
|
+
if (dequal(change.newValue, change.oldValue))
|
|
313
|
+
return;
|
|
314
|
+
cb(change.newValue ?? null, change.oldValue ?? null);
|
|
315
|
+
};
|
|
316
|
+
getStorageArea().onChanged.addListener(listener);
|
|
317
|
+
watchListeners.add(listener);
|
|
318
|
+
return () => {
|
|
319
|
+
getStorageArea().onChanged.removeListener(listener);
|
|
320
|
+
watchListeners.delete(listener);
|
|
321
|
+
};
|
|
322
|
+
},
|
|
323
|
+
unwatch() {
|
|
324
|
+
watchListeners.forEach((listener) => {
|
|
325
|
+
getStorageArea().onChanged.removeListener(listener);
|
|
326
|
+
});
|
|
327
|
+
watchListeners.clear();
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
105
331
|
export {
|
|
106
|
-
storage
|
|
107
|
-
webExtensionDriver
|
|
332
|
+
storage
|
|
108
333
|
};
|
package/dist/testing.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wxt",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.13.0",
|
|
5
5
|
"description": "Next gen framework for developing web extensions",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=18",
|
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
"chokidar": "^3.5.3",
|
|
90
90
|
"consola": "^3.2.3",
|
|
91
91
|
"defu": "^6.1.3",
|
|
92
|
+
"dequal": "^2.0.3",
|
|
92
93
|
"esbuild": "^0.19.5",
|
|
93
94
|
"fast-glob": "^3.3.1",
|
|
94
95
|
"filesize": "^10.0.8",
|
|
@@ -107,7 +108,6 @@
|
|
|
107
108
|
"prompts": "^2.4.2",
|
|
108
109
|
"rollup-plugin-visualizer": "^5.9.2",
|
|
109
110
|
"unimport": "^3.4.0",
|
|
110
|
-
"unstorage": "^1.9.0",
|
|
111
111
|
"vite": "^5.0.0",
|
|
112
112
|
"web-ext-run": "^0.1.0",
|
|
113
113
|
"webextension-polyfill": "^0.10.0",
|