wxt 0.19.13 → 0.19.14

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.
@@ -23,4 +23,4 @@ export interface WxtI18n {
23
23
  export declare const browser: AugmentedBrowser;
24
24
  /** @ignore */
25
25
  export type { ActivityLog, Alarms, Bookmarks, Action, BrowserAction, BrowserSettings, BrowsingData, CaptivePortal, Clipboard, Commands, ContentScripts, ContextualIdentities, Cookies, DeclarativeNetRequest, Devtools, Dns, Downloads, Events, Experiments, Extension, ExtensionTypes, Find, GeckoProfiler, History, I18n, Identity, Idle, Management, Manifest, // TODO: Export custom manifest types that are valid for both Chrome and Firefox.
26
- ContextMenus, Menus, NetworkStatus, NormandyAddonStudy, Notifications, Omnibox, PageAction, Permissions, Pkcs11, Privacy, Proxy, Runtime, Scripting, Search, Sessions, SidebarAction, Storage, Tabs, Theme, TopSites, Types, Urlbar, UserScripts, WebNavigation, WebRequest, Windows, } from 'webextension-polyfill';
26
+ ContextMenus, Menus, NetworkStatus, NormandyAddonStudy, Notifications, Omnibox, PageAction, Permissions, Pkcs11, Privacy, Proxy, Runtime, Scripting, Search, Sessions, SidebarAction, Storage, Tabs, Theme, TopSites, Types, UserScripts, WebNavigation, WebRequest, Windows, } from 'webextension-polyfill';
@@ -18,6 +18,6 @@ export declare function createIframeUi<TMounted>(ctx: ContentScriptContext, opti
18
18
  *
19
19
  * > This function is async because it has to load the CSS via a network call.
20
20
  *
21
- * @see https://wxt.dev/guide/essentials/content-scripts.html#shadowroot
21
+ * @see https://wxt.dev/guide/essentials/content-scripts.html#shadow-root
22
22
  */
23
23
  export declare function createShadowRootUi<TMounted>(ctx: ContentScriptContext, options: ShadowRootContentScriptUiOptions<TMounted>): Promise<ShadowRootContentScriptUi<TMounted>>;
@@ -23,6 +23,9 @@ export async function resolveConfig(inlineConfig, command) {
23
23
  esmResolve: true
24
24
  }
25
25
  });
26
+ if (inlineConfig.configFile && metadata.layers?.length === 0) {
27
+ throw Error(`Config file "${inlineConfig.configFile}" not found`);
28
+ }
26
29
  userConfig = loadedConfig ?? {};
27
30
  userConfigMetadata = metadata;
28
31
  }
@@ -34,7 +34,10 @@ export function createWebExtRunner() {
34
34
  wxtUserConfig?.chromiumPref,
35
35
  DEFAULT_CHROMIUM_PREFS
36
36
  ),
37
- args: wxtUserConfig?.chromiumArgs
37
+ args: [
38
+ "--unsafely-disable-devtools-self-xss-warnings",
39
+ ...wxtUserConfig?.chromiumArgs ?? []
40
+ ]
38
41
  }
39
42
  };
40
43
  const finalConfig = {
@@ -176,7 +176,7 @@ async function getPopupEntrypoint(info) {
176
176
  const options = await getHtmlEntrypointOptions(
177
177
  info,
178
178
  {
179
- browserStyle: "browse_style",
179
+ browserStyle: "browser_style",
180
180
  exclude: "exclude",
181
181
  include: "include",
182
182
  defaultIcon: "default_icon",
@@ -204,7 +204,7 @@ async function getOptionsEntrypoint(info) {
204
204
  const options = await getHtmlEntrypointOptions(
205
205
  info,
206
206
  {
207
- browserStyle: "browse_style",
207
+ browserStyle: "browser_style",
208
208
  chromeStyle: "chrome_style",
209
209
  exclude: "exclude",
210
210
  include: "include",
@@ -15,7 +15,6 @@ export function hashContentScriptOptions(options) {
15
15
  match_about_blank: false,
16
16
  run_at: "document_idle",
17
17
  all_frames: false,
18
- // @ts-expect-error - not in type
19
18
  match_origin_as_fallback: false,
20
19
  world: "ISOLATED",
21
20
  ...simplifiedOptions
@@ -38,7 +37,6 @@ export function mapWxtOptionsToContentScript(options, js, css) {
38
37
  run_at: options.runAt,
39
38
  css,
40
39
  js,
41
- // @ts-expect-error: untyped chrome options
42
40
  match_origin_as_fallback: options.matchOriginAsFallback,
43
41
  world: options.world
44
42
  };
@@ -51,7 +49,6 @@ export function mapWxtOptionsToRegisteredContentScript(options, js, css) {
51
49
  runAt: options.runAt,
52
50
  js,
53
51
  css,
54
- // @ts-expect-error: Chrome accepts this, not typed in webextension-polyfill (https://developer.chrome.com/docs/extensions/reference/scripting/#type-RegisteredContentScript)
55
52
  world: options.world
56
53
  };
57
54
  }
package/dist/storage.d.ts CHANGED
@@ -17,7 +17,7 @@ export interface WxtStorage {
17
17
  * @example
18
18
  * await storage.getItems(["local:installDate", "session:someCounter"]);
19
19
  */
20
- getItems(keys: Array<StorageItemKey | {
20
+ getItems(keys: Array<StorageItemKey | WxtStorageItem<any, any> | {
21
21
  key: StorageItemKey;
22
22
  options?: GetItemOptions<any>;
23
23
  }>): Promise<Array<{
@@ -32,6 +32,16 @@ export interface WxtStorage {
32
32
  * await storage.getMeta("local:installDate");
33
33
  */
34
34
  getMeta<T extends Record<string, unknown>>(key: StorageItemKey): Promise<T>;
35
+ /**
36
+ * Get the metadata of multiple storage items.
37
+ *
38
+ * @param items List of keys or items to get the metadata of.
39
+ * @returns An array containing storage keys and their metadata.
40
+ */
41
+ getMetas(keys: Array<StorageItemKey | WxtStorageItem<any, any>>): Promise<Array<{
42
+ key: StorageItemKey;
43
+ meta: any;
44
+ }>>;
35
45
  /**
36
46
  * Set a value in storage. Setting a value to `null` or `undefined` is equivalent to calling
37
47
  * `removeItem`.
@@ -52,6 +62,9 @@ export interface WxtStorage {
52
62
  setItems(values: Array<{
53
63
  key: StorageItemKey;
54
64
  value: any;
65
+ } | {
66
+ item: WxtStorageItem<any, any>;
67
+ value: any;
55
68
  }>): Promise<void>;
56
69
  /**
57
70
  * Sets metadata properties. If some properties are already set, but are not included in the
@@ -61,6 +74,18 @@ export interface WxtStorage {
61
74
  * await storage.setMeta("local:installDate", { appVersion });
62
75
  */
63
76
  setMeta<T extends Record<string, unknown>>(key: StorageItemKey, properties: T | null): Promise<void>;
77
+ /**
78
+ * Set the metadata of multiple storage items.
79
+ *
80
+ * @param items List of storage keys or items and metadata to set for each.
81
+ */
82
+ setMetas(metas: Array<{
83
+ key: StorageItemKey;
84
+ meta: Record<string, any>;
85
+ } | {
86
+ item: WxtStorageItem<any, any>;
87
+ meta: Record<string, any>;
88
+ }>): Promise<void>;
64
89
  /**
65
90
  * Removes an item from storage.
66
91
  *
@@ -71,9 +96,12 @@ export interface WxtStorage {
71
96
  /**
72
97
  * Remove a list of keys from storage.
73
98
  */
74
- removeItems(keys: Array<StorageItemKey | {
99
+ removeItems(keys: Array<StorageItemKey | WxtStorageItem<any, any> | {
75
100
  key: StorageItemKey;
76
101
  options?: RemoveItemOptions;
102
+ } | {
103
+ item: WxtStorageItem<any, any>;
104
+ options?: RemoveItemOptions;
77
105
  }>): Promise<void>;
78
106
  /**
79
107
  * Remove the entire metadata for a key, or specific properties by name.
@@ -113,7 +141,11 @@ export interface WxtStorage {
113
141
  }
114
142
  export interface WxtStorageItem<TValue, TMetadata extends Record<string, unknown>> {
115
143
  /**
116
- * @deprecated Renamed to `fallback`, use it instead.
144
+ * The storage key passed when creating the storage item.
145
+ */
146
+ key: StorageItemKey;
147
+ /**
148
+ * @deprecated Renamed to fallback, use it instead.
117
149
  */
118
150
  defaultValue: TValue;
119
151
  /**
@@ -224,3 +256,8 @@ export type WatchCallback<T> = (newValue: T, oldValue: T) => void;
224
256
  * Call to remove a watch listener
225
257
  */
226
258
  export type Unwatch = () => void;
259
+ export declare class MigrationError extends Error {
260
+ key: string;
261
+ version: number;
262
+ constructor(key: string, version: number, options?: ErrorOptions);
263
+ }
package/dist/storage.mjs CHANGED
@@ -34,6 +34,14 @@ function createStorage() {
34
34
  };
35
35
  };
36
36
  const getMetaKey = (key) => key + "$";
37
+ const mergeMeta = (oldMeta, newMeta) => {
38
+ const newFields = { ...oldMeta };
39
+ Object.entries(newMeta).forEach(([key, value]) => {
40
+ if (value == null) delete newFields[key];
41
+ else newFields[key] = value;
42
+ });
43
+ return newFields;
44
+ };
37
45
  const getValueOrFallback = (value, fallback) => value ?? fallback ?? null;
38
46
  const getMetaValue = (properties) => typeof properties === "object" && !Array.isArray(properties) ? properties : {};
39
47
  const getItem = async (driver, driverKey, opts) => {
@@ -51,15 +59,7 @@ function createStorage() {
51
59
  const setMeta = async (driver, driverKey, properties) => {
52
60
  const metaKey = getMetaKey(driverKey);
53
61
  const existingFields = getMetaValue(await driver.getItem(metaKey));
54
- const newFields = { ...existingFields };
55
- Object.entries(properties).forEach(([key, value]) => {
56
- if (value == null) {
57
- delete newFields[key];
58
- } else {
59
- newFields[key] = value;
60
- }
61
- });
62
- await driver.setItem(metaKey, newFields);
62
+ await driver.setItem(metaKey, mergeMeta(existingFields, properties));
63
63
  };
64
64
  const removeItem = async (driver, driverKey, opts) => {
65
65
  await driver.removeItem(driverKey);
@@ -89,92 +89,171 @@ function createStorage() {
89
89
  getItems: async (keys) => {
90
90
  const areaToKeyMap = /* @__PURE__ */ new Map();
91
91
  const keyToOptsMap = /* @__PURE__ */ new Map();
92
+ const orderedKeys = [];
92
93
  keys.forEach((key) => {
93
94
  let keyStr;
94
95
  let opts;
95
96
  if (typeof key === "string") {
96
97
  keyStr = key;
98
+ } else if ("getValue" in key) {
99
+ keyStr = key.key;
100
+ opts = { fallback: key.fallback };
97
101
  } else {
98
102
  keyStr = key.key;
99
103
  opts = key.options;
100
104
  }
105
+ orderedKeys.push(keyStr);
101
106
  const { driverArea, driverKey } = resolveKey(keyStr);
102
- const keys2 = areaToKeyMap.get(driverArea) ?? [];
103
- areaToKeyMap.set(driverArea, keys2.concat(driverKey));
107
+ const areaKeys = areaToKeyMap.get(driverArea) ?? [];
108
+ areaToKeyMap.set(driverArea, areaKeys.concat(driverKey));
104
109
  keyToOptsMap.set(keyStr, opts);
105
110
  });
106
- const results = await Promise.all(
111
+ const resultsMap = /* @__PURE__ */ new Map();
112
+ await Promise.all(
107
113
  Array.from(areaToKeyMap.entries()).map(async ([driverArea, keys2]) => {
108
114
  const driverResults = await drivers[driverArea].getItems(keys2);
109
- return driverResults.map((driverResult) => {
115
+ driverResults.forEach((driverResult) => {
110
116
  const key = `${driverArea}:${driverResult.key}`;
111
117
  const opts = keyToOptsMap.get(key);
112
118
  const value = getValueOrFallback(
113
119
  driverResult.value,
114
120
  opts?.fallback ?? opts?.defaultValue
115
121
  );
116
- return { key, value };
122
+ resultsMap.set(key, value);
117
123
  });
118
124
  })
119
125
  );
120
- return results.flat();
126
+ return orderedKeys.map((key) => ({
127
+ key,
128
+ value: resultsMap.get(key)
129
+ }));
121
130
  },
122
131
  getMeta: async (key) => {
123
132
  const { driver, driverKey } = resolveKey(key);
124
133
  return await getMeta(driver, driverKey);
125
134
  },
135
+ getMetas: async (args) => {
136
+ const keys = args.map((arg) => {
137
+ const key = typeof arg === "string" ? arg : arg.key;
138
+ const { driverArea, driverKey } = resolveKey(key);
139
+ return {
140
+ key,
141
+ driverArea,
142
+ driverKey,
143
+ driverMetaKey: getMetaKey(driverKey)
144
+ };
145
+ });
146
+ const areaToDriverMetaKeysMap = keys.reduce((map, key) => {
147
+ map[key.driverArea] ??= [];
148
+ map[key.driverArea].push(key);
149
+ return map;
150
+ }, {});
151
+ const resultsMap = {};
152
+ await Promise.all(
153
+ Object.entries(areaToDriverMetaKeysMap).map(async ([area, keys2]) => {
154
+ const areaRes = await browser.storage[area].get(
155
+ keys2.map((key) => key.driverMetaKey)
156
+ );
157
+ keys2.forEach((key) => {
158
+ resultsMap[key.key] = areaRes[key.driverMetaKey] ?? {};
159
+ });
160
+ })
161
+ );
162
+ return keys.map((key) => ({
163
+ key: key.key,
164
+ meta: resultsMap[key.key]
165
+ }));
166
+ },
126
167
  setItem: async (key, value) => {
127
168
  const { driver, driverKey } = resolveKey(key);
128
169
  await setItem(driver, driverKey, value);
129
170
  },
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 })
171
+ setItems: async (items) => {
172
+ const areaToKeyValueMap = {};
173
+ items.forEach((item) => {
174
+ const { driverArea, driverKey } = resolveKey(
175
+ "key" in item ? item.key : item.item.key
138
176
  );
177
+ areaToKeyValueMap[driverArea] ??= [];
178
+ areaToKeyValueMap[driverArea].push({
179
+ key: driverKey,
180
+ value: item.value
181
+ });
139
182
  });
140
183
  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
- )
184
+ Object.entries(areaToKeyValueMap).map(async ([driverArea, values]) => {
185
+ const driver = getDriver(driverArea);
186
+ await driver.setItems(values);
187
+ })
147
188
  );
148
189
  },
149
190
  setMeta: async (key, properties) => {
150
191
  const { driver, driverKey } = resolveKey(key);
151
192
  await setMeta(driver, driverKey, properties);
152
193
  },
194
+ setMetas: async (items) => {
195
+ const areaToMetaUpdatesMap = {};
196
+ items.forEach((item) => {
197
+ const { driverArea, driverKey } = resolveKey(
198
+ "key" in item ? item.key : item.item.key
199
+ );
200
+ areaToMetaUpdatesMap[driverArea] ??= [];
201
+ areaToMetaUpdatesMap[driverArea].push({
202
+ key: driverKey,
203
+ properties: item.meta
204
+ });
205
+ });
206
+ await Promise.all(
207
+ Object.entries(areaToMetaUpdatesMap).map(
208
+ async ([storageArea, updates]) => {
209
+ const driver = getDriver(storageArea);
210
+ const metaKeys = updates.map(({ key }) => getMetaKey(key));
211
+ console.log(storageArea, metaKeys);
212
+ const existingMetas = await driver.getItems(metaKeys);
213
+ const existingMetaMap = Object.fromEntries(
214
+ existingMetas.map(({ key, value }) => [key, getMetaValue(value)])
215
+ );
216
+ const metaUpdates = updates.map(({ key, properties }) => {
217
+ const metaKey = getMetaKey(key);
218
+ return {
219
+ key: metaKey,
220
+ value: mergeMeta(existingMetaMap[metaKey] ?? {}, properties)
221
+ };
222
+ });
223
+ await driver.setItems(metaUpdates);
224
+ }
225
+ )
226
+ );
227
+ },
153
228
  removeItem: async (key, opts) => {
154
229
  const { driver, driverKey } = resolveKey(key);
155
230
  await removeItem(driver, driverKey, opts);
156
231
  },
157
232
  removeItems: async (keys) => {
158
- const areaToKeysMap = /* @__PURE__ */ new Map();
233
+ const areaToKeysMap = {};
159
234
  keys.forEach((key) => {
160
235
  let keyStr;
161
236
  let opts;
162
237
  if (typeof key === "string") {
163
238
  keyStr = key;
239
+ } else if ("getValue" in key) {
240
+ keyStr = key.key;
241
+ } else if ("item" in key) {
242
+ keyStr = key.item.key;
243
+ opts = key.options;
164
244
  } else {
165
245
  keyStr = key.key;
166
246
  opts = key.options;
167
247
  }
168
248
  const { driverArea, driverKey } = resolveKey(keyStr);
169
- const areaKeys = areaToKeysMap.get(driverArea) ?? [];
170
- areaKeys.push(driverKey);
249
+ areaToKeysMap[driverArea] ??= [];
250
+ areaToKeysMap[driverArea].push(driverKey);
171
251
  if (opts?.removeMeta) {
172
- areaKeys.push(getMetaKey(driverKey));
252
+ areaToKeysMap[driverArea].push(getMetaKey(driverKey));
173
253
  }
174
- areaToKeysMap.set(driverArea, areaKeys);
175
254
  });
176
255
  await Promise.all(
177
- Array.from(areaToKeysMap.entries()).map(async ([driverArea, keys2]) => {
256
+ Object.entries(areaToKeysMap).map(async ([driverArea, keys2]) => {
178
257
  const driver = getDriver(driverArea);
179
258
  await driver.removeItems(keys2);
180
259
  })
@@ -236,7 +315,13 @@ function createStorage() {
236
315
  );
237
316
  let migratedValue = value;
238
317
  for (const migrateToVersion of migrationsToRun) {
239
- migratedValue = await migrations?.[migrateToVersion]?.(migratedValue) ?? migratedValue;
318
+ try {
319
+ migratedValue = await migrations?.[migrateToVersion]?.(migratedValue) ?? migratedValue;
320
+ } catch (err) {
321
+ throw Error(`v${migrateToVersion} migration failed for "${key}"`, {
322
+ cause: err
323
+ });
324
+ }
240
325
  }
241
326
  await driver.setItems([
242
327
  { key: driverKey, value: migratedValue },
@@ -261,6 +346,7 @@ function createStorage() {
261
346
  });
262
347
  migrationsDone.then(getOrInitValue);
263
348
  return {
349
+ key,
264
350
  get defaultValue() {
265
351
  return getFallback();
266
352
  },
@@ -388,3 +474,10 @@ function createDriver(storageArea) {
388
474
  }
389
475
  };
390
476
  }
477
+ export class MigrationError extends Error {
478
+ constructor(key, version, options) {
479
+ super(`v${version} migration failed for "${key}"`, options);
480
+ this.key = key;
481
+ this.version = version;
482
+ }
483
+ }
package/dist/version.mjs CHANGED
@@ -1 +1 @@
1
- export const version = "0.19.13";
1
+ export const version = "0.19.14";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wxt",
3
3
  "type": "module",
4
- "version": "0.19.13",
4
+ "version": "0.19.14",
5
5
  "description": "Next gen framework for developing web extensions",
6
6
  "repository": {
7
7
  "type": "git",
@@ -71,8 +71,8 @@
71
71
  },
72
72
  "dependencies": {
73
73
  "@aklinker1/rollup-plugin-visualizer": "5.12.0",
74
- "@types/chrome": "^0.0.269",
75
- "@types/webextension-polyfill": "^0.10.7",
74
+ "@types/chrome": "^0.0.280",
75
+ "@types/webextension-polyfill": "^0.12.1",
76
76
  "@webext-core/fake-browser": "^1.3.1",
77
77
  "@webext-core/isolated-element": "^1.1.2",
78
78
  "@webext-core/match-patterns": "^1.0.3",
@@ -80,7 +80,7 @@
80
80
  "c12": "^1.11.2",
81
81
  "cac": "^6.7.14",
82
82
  "chokidar": "^3.6.0",
83
- "ci-info": "^4.0.0",
83
+ "ci-info": "^4.1.0",
84
84
  "consola": "^3.2.3",
85
85
  "defu": "^6.1.4",
86
86
  "dequal": "^2.0.3",
@@ -99,40 +99,40 @@
99
99
  "linkedom": "^0.18.5",
100
100
  "magicast": "^0.3.5",
101
101
  "minimatch": "^10.0.1",
102
- "nano-spawn": "^0.1.0",
102
+ "nano-spawn": "^0.2.0",
103
103
  "normalize-path": "^3.0.0",
104
104
  "nypm": "^0.3.12",
105
105
  "ohash": "^1.1.4",
106
106
  "open": "^10.1.0",
107
- "ora": "^8.1.0",
107
+ "ora": "^8.1.1",
108
108
  "perfect-debounce": "^1.0.0",
109
- "picocolors": "^1.1.0",
109
+ "picocolors": "^1.1.1",
110
110
  "prompts": "^2.4.2",
111
111
  "publish-browser-extension": "^2.2.2",
112
112
  "scule": "^1.3.0",
113
113
  "unimport": "^3.13.1",
114
- "vite": "^5.4.8",
115
- "vite-node": "^2.1.2",
114
+ "vite": "^5.4.11",
115
+ "vite-node": "^2.1.4",
116
116
  "web-ext-run": "^0.2.1",
117
117
  "webextension-polyfill": "^0.12.0"
118
118
  },
119
119
  "devDependencies": {
120
120
  "@aklinker1/check": "^1.4.5",
121
- "@faker-js/faker": "^8.4.1",
121
+ "@faker-js/faker": "^9.2.0",
122
122
  "@types/fs-extra": "^11.0.4",
123
123
  "@types/lodash.merge": "^4.6.9",
124
- "@types/node": "^20.16.10",
124
+ "@types/node": "^20.17.6",
125
125
  "@types/normalize-path": "^3.0.2",
126
126
  "@types/prompts": "^2.4.9",
127
127
  "extract-zip": "^2.0.1",
128
- "happy-dom": "^14.12.3",
128
+ "happy-dom": "^15.11.4",
129
129
  "lodash.merge": "^4.6.2",
130
- "oxlint": "^0.9.9",
131
- "publint": "^0.2.11",
130
+ "oxlint": "^0.11.1",
131
+ "publint": "^0.2.12",
132
132
  "tsx": "4.15.7",
133
- "typescript": "^5.6.2",
133
+ "typescript": "^5.6.3",
134
134
  "unbuild": "^2.0.0",
135
- "vitest": "^2.1.2",
135
+ "vitest": "^2.1.4",
136
136
  "vitest-plugin-random-seed": "^1.1.0"
137
137
  },
138
138
  "peerDependenciesMeta": {