From cd7285c3ef7350a9944d39399580fb82b9376464 Mon Sep 17 00:00:00 2001 From: imnaK <43909328+imnaK@users.noreply.github.com> Date: Wed, 23 Oct 2024 05:58:22 +0200 Subject: [PATCH 001/130] docs: fix wrong anchor to "Environment Variables" (#1107) --- docs/guide/essentials/project-structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/essentials/project-structure.md b/docs/guide/essentials/project-structure.md index d8cb3959..4654e834 100644 --- a/docs/guide/essentials/project-structure.md +++ b/docs/guide/essentials/project-structure.md @@ -35,7 +35,7 @@ Here's a brief summary of each of these files and directories: - `hooks/`: Auto-imported by default, contains hooks for React and Solid - `public/`: Contains any files you want to copy into the output folder as-is, without being processed by WXT - `utils/`: Auto-imported by default, contains generic utilities used throughout your project -- `.env`: Contains [Environment Variables](/guide/essentials/config/runtime#environment-variables) +- `.env`: Contains [Environment Variables](/guide/essentials/config/environment-variables) - `.env.publish`: Contains Environment Variables for [publishing](/guide/essentials/publishing) - `app.config.ts`: Contains [Runtime Config](/guide/essentials/config/runtime) - `package.json`: The standard file used by your package manager From ab83031462a50ea80237adcaf478b9a0ffc9939c Mon Sep 17 00:00:00 2001 From: Bread Grocery Date: Wed, 23 Oct 2024 21:39:30 +0800 Subject: [PATCH 002/130] feat: Add support for configuring the `locales` directory (#1109) --- packages/i18n/README.md | 15 +++++++++++++++ packages/i18n/src/module.ts | 34 +++++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index d1fc05dc..68ab3ab5 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -98,6 +98,21 @@ And you're done! Using WXT, you get type-safety out of the box. i18n.t('helloWorld'); // "Hello world!"; ``` +## Configuration + +The module can be configured via the `i18n` config: + +```ts +export default defineConfig({ + modules: ['@wxt-dev/i18n'], + i18n: { + // ... + }, +}); +``` + +Options have JSDocs available in your editor, or you can read them in the source code: [`I18nOptions`](https://github.com/wxt-dev/wxt/blob/main/packages/i18n/src/module.ts). + ## Messages File Format > [!DANGER] diff --git a/packages/i18n/src/module.ts b/packages/i18n/src/module.ts index 2699f503..d097e553 100644 --- a/packages/i18n/src/module.ts +++ b/packages/i18n/src/module.ts @@ -23,11 +23,11 @@ import { watch } from 'chokidar'; import { GeneratedPublicFile, WxtDirFileEntry } from 'wxt'; import { writeFile } from 'node:fs/promises'; -export default defineWxtModule({ +export default defineWxtModule({ name: '@wxt-dev/i18n', + configKey: 'i18n', imports: [{ from: '#i18n', name: 'i18n' }], - - setup(wxt) { + setup(wxt, options) { if (wxt.config.manifest.default_locale == null) { wxt.logger.warn( `\`[i18n]\` manifest.default_locale not set, \`@wxt-dev/i18n\` disabled.`, @@ -38,9 +38,12 @@ export default defineWxtModule({ '`[i18n]` Default locale: ' + wxt.config.manifest.default_locale, ); + const { localesDir = resolve(wxt.config.srcDir, 'locales') } = + options ?? {}; + const getLocalizationFiles = async () => { - const files = await glob('locales/*', { - cwd: wxt.config.srcDir, + const files = await glob('*.{json,json5,yml,yaml,toml}', { + cwd: localesDir, absolute: true, }); return files.map((file) => ({ @@ -71,7 +74,7 @@ export default defineWxtModule({ )!; if (defaultLocaleFile == null) { throw Error( - `\`[i18n]\` Required localization file does not exist: \`/locales/${wxt.config.manifest.default_locale}.{json|json5|yml|yaml|toml}\``, + `\`[i18n]\` Required localization file does not exist: \`/${wxt.config.manifest.default_locale}.{json|json5|yml|yaml|toml}\``, ); } @@ -154,9 +157,26 @@ export { type GeneratedI18nStructure } if (wxt.config.command === 'serve') { wxt.hooks.hookOnce('build:done', () => { - const watcher = watch(resolve(wxt.config.srcDir, 'locales')); + const watcher = watch(localesDir); watcher.on('change', updateLocalizations); }); } }, }); + +/** + * Options for the i18n module + */ +export interface I18nOptions { + /** + * Directory containing files that define the translations. + * @default "${config.srcDir}/locales" + */ + localesDir?: string; +} + +declare module 'wxt' { + export interface InlineConfig { + i18n?: I18nOptions; + } +} From c30adb409cbd040d1dab7516b07601f2d08dc5b2 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 23 Oct 2024 13:44:57 +0000 Subject: [PATCH 003/130] chore(release): @wxt-dev/i18n v0.2.2 --- packages/i18n/CHANGELOG.md | 12 ++++++++++++ packages/i18n/package.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/i18n/CHANGELOG.md b/packages/i18n/CHANGELOG.md index ec1a39df..859bb487 100644 --- a/packages/i18n/CHANGELOG.md +++ b/packages/i18n/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## v0.2.2 + +[compare changes](https://github.com/wxt-dev/wxt/compare/i18n-v0.2.1...i18n-v0.2.2) + +### 🚀 Enhancements + +- Add support for configuring the `locales` directory ([#1109](https://github.com/wxt-dev/wxt/pull/1109)) + +### ❤️ Contributors + +- Bread Grocery + ## v0.2.1 [compare changes](https://github.com/wxt-dev/wxt/compare/i18n-v0.2.0...i18n-v0.2.1) diff --git a/packages/i18n/package.json b/packages/i18n/package.json index d079c9e1..66a2f189 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,7 +1,7 @@ { "name": "@wxt-dev/i18n", "description": "Type-safe wrapper around browser.i18n.getMessage with additional features", - "version": "0.2.1", + "version": "0.2.2", "type": "module", "repository": { "type": "git", From bb5ea34396f9396bec79bc3da9d4655c8f5f7853 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 23 Oct 2024 08:47:16 -0500 Subject: [PATCH 004/130] docs: Cleanup typos and broken links --- packages/i18n/README.md | 6 +++--- packages/wxt/src/client/content-scripts/ui/index.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index 68ab3ab5..aeb5487f 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -240,11 +240,11 @@ A key is treated as "verbose" when it is: { "appName": { "message": "GitHub - Better Line Counts", - "description": "The app's name, should not be translated", + "description": "The app's name, should not be translated" }, "ok": "OK", "deleteConfirmation": { - "title": "Delete XYZ?" + "title": "Delete XYZ?", "message": "You cannot undo this action once taken." } } @@ -304,7 +304,7 @@ const messages = { // ... }; -// Generate JSON files for the browser +// Generate JSON files for the extension await generateChromeMessagesFile('dist/_locales/en/messages.json', messages.en); await generateChromeMessagesFile('dist/_locales/de/messages.json', messages.de); // ... diff --git a/packages/wxt/src/client/content-scripts/ui/index.ts b/packages/wxt/src/client/content-scripts/ui/index.ts index 6c7a614a..ee1097b6 100644 --- a/packages/wxt/src/client/content-scripts/ui/index.ts +++ b/packages/wxt/src/client/content-scripts/ui/index.ts @@ -96,7 +96,7 @@ export function createIframeUi( * * > This function is async because it has to load the CSS via a network call. * - * @see https://wxt.dev/guide/essentials/content-scripts.html#shadowroot + * @see https://wxt.dev/guide/essentials/content-scripts.html#shadow-root */ export async function createShadowRootUi( ctx: ContentScriptContext, From 4304f71df283c846fdb1425a06e438e80d23f58a Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 23 Oct 2024 09:01:53 -0500 Subject: [PATCH 005/130] docs: Add section about env vars in the manifest --- .../config/environment-variables.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/guide/essentials/config/environment-variables.md b/docs/guide/essentials/config/environment-variables.md index 8e299665..e287b34a 100644 --- a/docs/guide/essentials/config/environment-variables.md +++ b/docs/guide/essentials/config/environment-variables.md @@ -56,3 +56,26 @@ Vite provides two other environment variables, but they aren't useful in WXT pro - `import.meta.env.BASE_URL`: Use `browser.runtime.getURL` instead. - `import.meta.env.SSR`: Always `false`. ::: + +## Manifest + +To use environment variables in the manifest, you need to use the function syntax: + +```ts +export default defineConfig({ + extensionApi: 'chrome', + modules: ['@wxt-dev/module-vue'], + manifest: { // [!code --] + oauth2: { // [!code --] + client_id: import.meta.env.WXT_APP_CLIENT_ID // [!code --] + } // [!code --] + } // [!code --] + manifest: () => ({ // [!code ++] + oauth2: { // [!code ++] + client_id: import.meta.env.WXT_APP_CLIENT_ID // [!code ++] + } // [!code ++] + }), // [!code ++] +}); +``` + +WXT can't load your `.env` files until after the config file has been loaded. So by using the function syntax for `manifest`, it defers creating the object until after the `.env` files are loaded into the process. From 0e4d3ad8ab5140ebbd27ee0a0aff8b643910fc0b Mon Sep 17 00:00:00 2001 From: Berkin Anik Date: Mon, 28 Oct 2024 14:30:23 +0100 Subject: [PATCH 006/130] docs: fix typo in introduction page (#1117) --- docs/guide/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/introduction.md b/docs/guide/introduction.md index c373e7c5..f5a1c179 100644 --- a/docs/guide/introduction.md +++ b/docs/guide/introduction.md @@ -5,7 +5,7 @@ WXT is a modern, open-source framework for building web extensions. Inspired by - Provide an awesome [DX](https://about.gitlab.com/topics/devops/what-is-developer-experience/) - Provide first-class support for all major browsers -Check out the [comparison](/guide/resources/compare) to see how WXT compares to other tools for building web extnesions. +Check out the [comparison](/guide/resources/compare) to see how WXT compares to other tools for building web extensions. ## Prerequisites From c17ce34a27e57b0d05c9701085d88a6e367ff7a3 Mon Sep 17 00:00:00 2001 From: Bread Grocery Date: Mon, 28 Oct 2024 21:43:21 +0800 Subject: [PATCH 007/130] fix: Prevent app crashes from parse errors due to incomplete file saves (#1114) --- packages/i18n/src/module.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/i18n/src/module.ts b/packages/i18n/src/module.ts index d097e553..fdf9f29b 100644 --- a/packages/i18n/src/module.ts +++ b/packages/i18n/src/module.ts @@ -158,7 +158,9 @@ export { type GeneratedI18nStructure } if (wxt.config.command === 'serve') { wxt.hooks.hookOnce('build:done', () => { const watcher = watch(localesDir); - watcher.on('change', updateLocalizations); + watcher.on('change', (path) => { + updateLocalizations(path).catch(wxt.logger.error); + }); }); } }, From ea570c12b71c32dc998eead1551cbc00361406ae Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 28 Oct 2024 13:48:20 +0000 Subject: [PATCH 008/130] chore(release): @wxt-dev/i18n v0.2.3 --- packages/i18n/CHANGELOG.md | 17 +++++++++++++++++ packages/i18n/package.json | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/i18n/CHANGELOG.md b/packages/i18n/CHANGELOG.md index 859bb487..4a60b50f 100644 --- a/packages/i18n/CHANGELOG.md +++ b/packages/i18n/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## v0.2.3 + +[compare changes](https://github.com/wxt-dev/wxt/compare/i18n-v0.2.2...i18n-v0.2.3) + +### 🩹 Fixes + +- Prevent app crashes from parse errors due to incomplete file saves ([#1114](https://github.com/wxt-dev/wxt/pull/1114)) + +### 📖 Documentation + +- Cleanup typos and broken links ([bb5ea34](https://github.com/wxt-dev/wxt/commit/bb5ea34)) + +### ❤️ Contributors + +- Bread Grocery +- Aaron ([@aklinker1](http://github.com/aklinker1)) + ## v0.2.2 [compare changes](https://github.com/wxt-dev/wxt/compare/i18n-v0.2.1...i18n-v0.2.2) diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 66a2f189..533b3cf7 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,7 +1,7 @@ { "name": "@wxt-dev/i18n", "description": "Type-safe wrapper around browser.i18n.getMessage with additional features", - "version": "0.2.2", + "version": "0.2.3", "type": "module", "repository": { "type": "git", From 4603ebb5113f795c0af625be8ec927aa15b91eb8 Mon Sep 17 00:00:00 2001 From: Ayub Kokabi Date: Mon, 28 Oct 2024 23:03:10 +0330 Subject: [PATCH 009/130] docs: Add missing closing bracket (#1112) Co-authored-by: Aaron --- docs/guide/essentials/config/browser-startup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/essentials/config/browser-startup.md b/docs/guide/essentials/config/browser-startup.md index 676d5e22..e5daa60c 100644 --- a/docs/guide/essentials/config/browser-startup.md +++ b/docs/guide/essentials/config/browser-startup.md @@ -62,7 +62,7 @@ import { resolve } from 'node:path'; export default defineRunnerConfig({ // On Windows, the path must be absolute - chromiumArgs: [`--user-data-dir="${resolve(".wxt/chrome-data")}"` + chromiumArgs: [`--user-data-dir="${resolve('.wxt/chrome-data')}"`], }); ``` From e7c66304d391541911dc375d785042dd8c7229be Mon Sep 17 00:00:00 2001 From: 1natsu <1natsu172@users.noreply.github.com> Date: Tue, 29 Oct 2024 04:33:21 +0900 Subject: [PATCH 010/130] chore: add "@types/chrome" to templates (#1119) --- templates/react/package.json | 1 + templates/solid/package.json | 1 + templates/svelte/package.json | 1 + templates/vanilla/package.json | 1 + templates/vue/package.json | 1 + 5 files changed, 5 insertions(+) diff --git a/templates/react/package.json b/templates/react/package.json index 1b702c13..5943cd45 100644 --- a/templates/react/package.json +++ b/templates/react/package.json @@ -19,6 +19,7 @@ "react-dom": "^18.3.1" }, "devDependencies": { + "@types/chrome": "^0.0.269", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", "@wxt-dev/module-react": "^1.1.1", diff --git a/templates/solid/package.json b/templates/solid/package.json index 9115717c..7d0044d6 100644 --- a/templates/solid/package.json +++ b/templates/solid/package.json @@ -18,6 +18,7 @@ "solid-js": "^1.9.1" }, "devDependencies": { + "@types/chrome": "^0.0.269", "@wxt-dev/module-solid": "^1.1.2", "typescript": "^5.6.2", "wxt": "^0.19.11" diff --git a/templates/svelte/package.json b/templates/svelte/package.json index bdad117b..537940ed 100644 --- a/templates/svelte/package.json +++ b/templates/svelte/package.json @@ -15,6 +15,7 @@ "postinstall": "wxt prepare" }, "devDependencies": { + "@types/chrome": "^0.0.269", "@tsconfig/svelte": "^5.0.4", "@wxt-dev/module-svelte": "^1.0.1", "svelte": "^4.2.19", diff --git a/templates/vanilla/package.json b/templates/vanilla/package.json index a5bc9d58..a8e373e8 100644 --- a/templates/vanilla/package.json +++ b/templates/vanilla/package.json @@ -15,6 +15,7 @@ "postinstall": "wxt prepare" }, "devDependencies": { + "@types/chrome": "^0.0.269", "typescript": "^5.6.2", "wxt": "^0.19.11" } diff --git a/templates/vue/package.json b/templates/vue/package.json index eff1642f..236095ca 100644 --- a/templates/vue/package.json +++ b/templates/vue/package.json @@ -18,6 +18,7 @@ "vue": "^3.5.11" }, "devDependencies": { + "@types/chrome": "^0.0.269", "@wxt-dev/module-vue": "^1.0.1", "typescript": "^5.6.2", "vue-tsc": "^2.1.6", From 0318a340466dd17f178c1461732b34568f91f687 Mon Sep 17 00:00:00 2001 From: Bread Grocery Date: Tue, 29 Oct 2024 21:05:13 +0800 Subject: [PATCH 011/130] docs: Fix typo in `popup` and `options` EntrypointOptions (#1121) --- packages/wxt/src/core/utils/building/find-entrypoints.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/wxt/src/core/utils/building/find-entrypoints.ts b/packages/wxt/src/core/utils/building/find-entrypoints.ts index 2dca6563..72ac7436 100644 --- a/packages/wxt/src/core/utils/building/find-entrypoints.ts +++ b/packages/wxt/src/core/utils/building/find-entrypoints.ts @@ -228,7 +228,7 @@ async function getPopupEntrypoint( const options = await getHtmlEntrypointOptions( info, { - browserStyle: 'browse_style', + browserStyle: 'browser_style', exclude: 'exclude', include: 'include', defaultIcon: 'default_icon', @@ -262,7 +262,7 @@ async function getOptionsEntrypoint( const options = await getHtmlEntrypointOptions( info, { - browserStyle: 'browse_style', + browserStyle: 'browser_style', chromeStyle: 'chrome_style', exclude: 'exclude', include: 'include', From 5d991eda31cb27807d8ac0f079f2cfa03abf4d1e Mon Sep 17 00:00:00 2001 From: Florian Metz Date: Thu, 31 Oct 2024 05:36:24 +0100 Subject: [PATCH 012/130] feat(storage): Support storage items in batch functions (#990) Co-authored-by: Aaron --- docs/storage.md | 39 +- .../__snapshots__/auto-imports.test.ts.snap | 4 + packages/wxt/e2e/tests/auto-imports.test.ts | 1 + packages/wxt/src/__tests__/storage.test.ts | 536 ++++++++++++++++-- packages/wxt/src/storage.ts | 240 ++++++-- 5 files changed, 729 insertions(+), 91 deletions(-) diff --git a/docs/storage.md b/docs/storage.md index 6fb876d3..cb215b1c 100644 --- a/docs/storage.md +++ b/docs/storage.md @@ -55,7 +55,7 @@ For a full list of methods available, see the [API reference](/api/reference/wxt ## Watchers -To listen for storage changes, use the `storage.watch` function. It lets you setup a listener for a single key: +To listen for storage changes, use the `storage.watch` function. It lets you set up a listener for a single key: ```ts const unwatch = storage.watch('local:counter', (newCount, oldCount) => { @@ -100,7 +100,7 @@ You can remove all metadata associated with a key, or just specific properties: // Remove all properties await storage.removeMeta('local:preference'); -// Remove one property +// Remove only the "lastModified" property await storage.removeMeta('local:preference', 'lastModified'); // Remove multiple properties @@ -225,13 +225,13 @@ export const ignoredWebsites = storage.defineItem( // [!code Internally, this uses a metadata property called `v` to track the value's current version. ::: -In this case, we thought that the ignored website list might change in the future, and were able to setup a versioned storage item from the start. +In this case, we thought that the ignored website list might change in the future, and were able to set up a versioned storage item from the start. -Realistically, you won't know a item needs versioned until you need to change it's schema. Thankfully, it's simple to add versioning to an unversioned storage item. +Realistically, you won't know an item needs versioning until you need to change its schema. Thankfully, it's simple to add versioning to an unversioned storage item. When a previous version isn't found, WXT assumes the version was `1`. That means you just need to set `version: 2` and add a migration for `2`, and it will just work! -Lets look at the same ignored websites example from before, but start with an unversioned item this time: +Let's look at the same ignored websites example from before, but start with an unversioned item this time: :::code-group @@ -275,13 +275,13 @@ export const ignoredWebsites = storage.defineItem( // [!code ### Running Migrations -As soon as `storage.defineItem` is called, WXT checks if migrations need to be ran, and if so, runs them. Calls to get or update the storage item's value or metadata (`getValue`, `setValue`, `removeValue`, `getMeta`, etc) will automatically wait for the migration process to finish before actually reading or writing values. +As soon as `storage.defineItem` is called, WXT checks if migrations need to be run, and if so, runs them. Calls to get or update the storage item's value or metadata (`getValue`, `setValue`, `removeValue`, `getMeta`, etc.) will automatically wait for the migration process to finish before actually reading or writing values. ### Default Values With `storage.defineItem`, there are multiple ways of defining default values: -1. `fallback` - Return this value from `getValue` instead of `null` if the value is missing. +1. **`fallback`** - Return this value from `getValue` instead of `null` if the value is missing. This option is great for providing default values for settings: @@ -294,7 +294,7 @@ With `storage.defineItem`, there are multiple ways of defining default values: }); ``` -2. `init` - Initialize and save a value in storage if it is not already saved. +2. **`init`** - Initialize and save a value in storage if it is not already saved. This is great for values that need to be initialized or set once: @@ -308,3 +308,26 @@ With `storage.defineItem`, there are multiple ways of defining default values: ``` The value is initialized in storage immediately. + +## Bulk Operations + +When getting or setting multiple values in storage, you can perform bulk operations to improve performance by reducing the number of individual storage calls. The `storage` API provides several methods for performing bulk operations: + +- **`getItems`** - Get multiple values at once. +- **`getMetas`** - Get metadata for multiple items at once. +- **`setItems`** - Set multiple values at once. +- **`setMetas`** - Set metadata for multiple items at once. +- **`removeItems`** - Remove multiple values (and optionally metadata) at once. + +All these APIs support both string keys and defined storage items: + +```ts +const userId = storage.defineItem('local:userId'); + +await storage.setItems([ + { key: 'local:installDate', value: Date.now() }, + { item: userId, value: generateUserId() }, +]); +``` + +Refer to the [API Reference](/api/reference/wxt/storage/interfaces/WxtStorage) for types and examples of how to use all the bulk APIs. diff --git a/packages/wxt/e2e/tests/__snapshots__/auto-imports.test.ts.snap b/packages/wxt/e2e/tests/__snapshots__/auto-imports.test.ts.snap index 10e9209b..0000ea9c 100644 --- a/packages/wxt/e2e/tests/__snapshots__/auto-imports.test.ts.snap +++ b/packages/wxt/e2e/tests/__snapshots__/auto-imports.test.ts.snap @@ -8,6 +8,7 @@ exports[`Auto Imports > eslintrc > "enabled: 8" should output a JSON config file "ContentScriptContext": true, "InvalidMatchPattern": true, "MatchPattern": true, + "MigrationError": true, "browser": true, "createIframeUi": true, "createIntegratedUi": true, @@ -34,6 +35,7 @@ const globals = { "ContentScriptContext": true, "InvalidMatchPattern": true, "MatchPattern": true, + "MigrationError": true, "browser": true, "createIframeUi": true, "createIntegratedUi": true, @@ -68,6 +70,7 @@ exports[`Auto Imports > eslintrc > "enabled: true" should output a JSON config f "ContentScriptContext": true, "InvalidMatchPattern": true, "MatchPattern": true, + "MigrationError": true, "browser": true, "createIframeUi": true, "createIntegratedUi": true, @@ -95,6 +98,7 @@ exports[`Auto Imports > eslintrc > should allow customizing the output 1`] = ` "ContentScriptContext": "readonly", "InvalidMatchPattern": "readonly", "MatchPattern": "readonly", + "MigrationError": "readonly", "browser": "readonly", "createIframeUi": "readonly", "createIntegratedUi": "readonly", diff --git a/packages/wxt/e2e/tests/auto-imports.test.ts b/packages/wxt/e2e/tests/auto-imports.test.ts index 167b33a4..b9cd24bb 100644 --- a/packages/wxt/e2e/tests/auto-imports.test.ts +++ b/packages/wxt/e2e/tests/auto-imports.test.ts @@ -20,6 +20,7 @@ describe('Auto Imports', () => { const ContentScriptContext: typeof import('wxt/client')['ContentScriptContext'] const InvalidMatchPattern: typeof import('wxt/sandbox')['InvalidMatchPattern'] const MatchPattern: typeof import('wxt/sandbox')['MatchPattern'] + const MigrationError: typeof import('wxt/storage')['MigrationError'] const browser: typeof import('wxt/browser')['browser'] const createIframeUi: typeof import('wxt/client')['createIframeUi'] const createIntegratedUi: typeof import('wxt/client')['createIntegratedUi'] diff --git a/packages/wxt/src/__tests__/storage.test.ts b/packages/wxt/src/__tests__/storage.test.ts index d5fae852..b0fb0ed3 100644 --- a/packages/wxt/src/__tests__/storage.test.ts +++ b/packages/wxt/src/__tests__/storage.test.ts @@ -1,7 +1,7 @@ import { fakeBrowser } from '@webext-core/fake-browser'; import { describe, it, expect, beforeEach, vi, expectTypeOf } from 'vitest'; import { browser } from 'wxt/browser'; -import { WxtStorageItem, storage } from '../storage'; +import { MigrationError, WxtStorageItem, storage } from '../storage'; /** * This works because fakeBrowser is synchronous, and is will finish any number of chained @@ -38,7 +38,7 @@ describe('Storage Utils', () => { expect(actual).toBe(expected); }); - it('should return the value if multiple : are use in the key', async () => { + it('should return the value if multiple : are used in the key', async () => { const expected = 'value'; await fakeBrowser.storage[storageArea].set({ 'some:key': expected }); @@ -64,27 +64,97 @@ describe('Storage Utils', () => { }); describe('getItems', () => { - it('should return an array of values', async () => { - const expected = [ - { key: `${storageArea}:count`, value: 234 }, - { key: `${storageArea}:installDate`, value: null }, - { key: `${storageArea}:otherValue`, value: 345 }, - ] as const; - const params = [ - expected[0].key, - expected[1].key, - { - key: expected[2].key, - options: { defaultValue: expected[2].value }, - }, - ]; + it('should get values from multiple storage keys', async () => { + const item1 = { + key: `${storageArea}:one`, + expectedValue: 1, + } as const; + const item2 = { + key: `${storageArea}:two`, + expectedValue: null, + } as const; + await fakeBrowser.storage[storageArea].set({ - count: expected[0].value, + one: item1.expectedValue, }); - const actual = await storage.getItems(params); + const actual = await storage.getItems([item1.key, item2.key]); - expect(actual).toEqual(expected); + expect(actual).toEqual([ + { key: item1.key, value: item1.expectedValue }, + { key: item2.key, value: item2.expectedValue }, + ]); + }); + + it('should get values from multiple storage items', async () => { + const item1 = storage.defineItem(`${storageArea}:one`); + const expectedValue1 = 1; + const item2 = storage.defineItem(`${storageArea}:two`); + const expectedValue2 = null; + + await fakeBrowser.storage[storageArea].set({ + one: expectedValue1, + }); + + const actual = await storage.getItems([item1, item2]); + + expect(actual).toEqual([ + { key: item1.key, value: expectedValue1 }, + { key: item2.key, value: expectedValue2 }, + ]); + }); + + it('should get values for a combination of different input types', async () => { + const key1 = `${storageArea}:one` as const; + const expectedValue1 = 1; + const item2 = storage.defineItem(`${storageArea}:two`); + const expectedValue2 = 2; + + await fakeBrowser.storage[storageArea].set({ + one: expectedValue1, + two: expectedValue2, + }); + + const actual = await storage.getItems([key1, item2]); + + expect(actual).toEqual([ + { key: key1, value: expectedValue1 }, + { key: item2.key, value: expectedValue2 }, + ]); + }); + + it('should return fallback values for keys when provided', async () => { + const key1 = `${storageArea}:one` as const; + const expectedValue1 = null; + const key2 = `${storageArea}:two` as const; + const fallback2 = 2; + const expectedValue2 = fallback2; + + const actual = await storage.getItems([ + key1, + { key: key2, options: { fallback: fallback2 } }, + ]); + + expect(actual).toEqual([ + { key: key1, value: expectedValue1 }, + { key: key2, value: expectedValue2 }, + ]); + }); + + it('should return fallback values for items when provided', async () => { + const item1 = storage.defineItem(`${storageArea}:one`); + const expectedValue1 = null; + const item2 = storage.defineItem(`${storageArea}:two`, { + fallback: 2, + }); + const expectedValue2 = item2.fallback; + + const actual = await storage.getItems([item1, item2]); + + expect(actual).toEqual([ + { key: item1.key, value: expectedValue1 }, + { key: item2.key, value: expectedValue2 }, + ]); }); }); @@ -180,6 +250,74 @@ describe('Storage Utils', () => { ); }); + describe('setMetas', () => { + it('should set key metadata correctly', async () => { + const key1 = `${storageArea}:one` as const; + const initialMeta1 = {}; + const setMeta1 = { v: 1 }; + const expectedMeta1 = setMeta1; + + const key2 = `${storageArea}:two` as const; + const initialMeta2 = { v: 1 }; + const setMeta2 = { v: 2 }; + const expectedMeta2 = setMeta2; + + const key3 = `${storageArea}:three` as const; + const initialMeta3 = { v: 1 }; + const setMeta3 = { d: Date.now() }; + const expectedMeta3 = { ...initialMeta3, ...setMeta3 }; + + await fakeBrowser.storage[storageArea].set({ + one$: initialMeta1, + two$: initialMeta2, + three$: initialMeta3, + }); + + await storage.setMetas([ + { key: key1, meta: setMeta1 }, + { key: key2, meta: setMeta2 }, + { key: key3, meta: setMeta3 }, + ]); + + expect(await storage.getMeta(key1)).toEqual(expectedMeta1); + expect(await storage.getMeta(key2)).toEqual(expectedMeta2); + expect(await storage.getMeta(key3)).toEqual(expectedMeta3); + }); + + it('should set item metadata correctly', async () => { + const item1 = storage.defineItem(`${storageArea}:one`); + const initialMeta1 = {}; + const setMeta1 = { v: 1 }; + const expectedMeta1 = setMeta1; + + const item2 = storage.defineItem(`${storageArea}:two`); + const initialMeta2 = { v: 1 }; + const setMeta2 = { v: 2 }; + const expectedMeta2 = setMeta2; + + const item3 = storage.defineItem(`${storageArea}:three`); + const initialMeta3 = { v: 1 }; + const setMeta3 = { d: Date.now() }; + const expectedMeta3 = { ...initialMeta3, ...setMeta3 }; + + await fakeBrowser.storage[storageArea].set({ + one$: initialMeta1, + two$: initialMeta2, + three$: initialMeta3, + }); + + await storage.setMetas([ + { item: item1, meta: setMeta1 }, + { item: item2, meta: setMeta2 }, + { item: item3, meta: setMeta3 }, + ]); + + expect(await item1.getMeta()).toEqual(expectedMeta1); + expect(await item2.getMeta()).toEqual(expectedMeta2); + expect(await item3.getMeta()).toEqual(expectedMeta3); + }); + }); + describe('removeItem', () => { it('should remove the key from storage', async () => { await fakeBrowser.storage[storageArea].set({ count: 456 }); @@ -219,29 +357,136 @@ describe('Storage Utils', () => { }); describe('removeItems', () => { - it('should remove multiple items', async () => { + it('should remove multiple keys', async () => { const key1 = `${storageArea}:one` as const; const key2 = `${storageArea}:two` as const; - const key3 = `${storageArea}:three` as const; await fakeBrowser.storage[storageArea].set({ - ['one']: '1', - ['two']: null, - ['two$']: { v: 1 }, - ['three']: '1', - ['three$']: { v: 1 }, + one: 1, + two: 2, + }); + + await storage.removeItems([key1, key2]); + + expect(await storage.getItem(key1)).toBeNull(); + expect(await storage.getItem(key2)).toBeNull(); + }); + + it('should remove multiple keys and metadata when requested', async () => { + const key1 = `${storageArea}:one` as const; + const key2 = `${storageArea}:two` as const; + await fakeBrowser.storage[storageArea].set({ + one: 1, + one$: { v: 1 }, + two: 2, + two$: { v: 1 }, }); await storage.removeItems([ key1, - key2, - { key: key3, options: { removeMeta: true } }, + { key: key2, options: { removeMeta: true } }, ]); expect(await storage.getItem(key1)).toBeNull(); + expect(await storage.getMeta(key1)).toEqual({ v: 1 }); expect(await storage.getItem(key2)).toBeNull(); - expect(await storage.getMeta(key2)).toEqual({ v: 1 }); - expect(await storage.getItem(key3)).toBeNull(); - expect(await storage.getMeta(key3)).toEqual({}); + expect(await storage.getMeta(key2)).toEqual({}); + }); + + it('should remove multiple items', async () => { + const item1 = storage.defineItem(`${storageArea}:one`); + const item2 = storage.defineItem(`${storageArea}:two`); + await fakeBrowser.storage[storageArea].set({ + one: 1, + two: 2, + }); + + await storage.removeItems([item1, item2]); + + expect(await item1.getValue()).toBeNull(); + expect(await item2.getValue()).toBeNull(); + }); + + it('should remove multiple items and metadata when requested', async () => { + const item1 = storage.defineItem(`${storageArea}:one`); + const item2 = storage.defineItem(`${storageArea}:two`); + await fakeBrowser.storage[storageArea].set({ + one: 1, + one$: { v: 1 }, + two: 2, + two$: { v: 1 }, + }); + + await storage.removeItems([ + item1, + { item: item2, options: { removeMeta: true } }, + ]); + + expect(await item1.getValue()).toBeNull(); + expect(await item1.getMeta()).toEqual({ v: 1 }); + expect(await item2.getValue()).toBeNull(); + expect(await item2.getMeta()).toEqual({}); + }); + + it('should remove multiple items', async () => { + const item1 = storage.defineItem(`${storageArea}:one`); + const item2 = storage.defineItem(`${storageArea}:two`); + + await fakeBrowser.storage.local.set({ + one: 1, + two: 2, + }); + + await storage.removeItems([item1, item2]); + + expect(await item1.getValue()).toBeNull(); + expect(await item2.getValue()).toBeNull(); + }); + + it('should remove items using { item: WxtStorageItem, options?: RemoveItemOptions } objects', async () => { + const item1 = storage.defineItem('local:item1'); + const item2 = storage.defineItem('session:item2'); + + await item1.setValue('value1'); + await item1.setMeta({ v: 1 }); + await item2.setValue('value2'); + await item2.setMeta({ v: 1 }); + + await storage.removeItems([ + { item: item1, options: { removeMeta: true } }, + { item: item2, options: { removeMeta: false } }, + ]); + + expect(await item1.getValue()).toBeNull(); + expect(await item1.getMeta()).toEqual({}); + expect(await item2.getValue()).toBeNull(); + expect(await item2.getMeta()).toEqual({ v: 1 }); + }); + + it('should handle a mix of different input types', async () => { + const item1 = storage.defineItem('local:item1'); + const item2 = storage.defineItem('session:item2'); + const item3 = storage.defineItem('local:item3'); + + await item1.setValue('value1'); + await item2.setValue('value2'); + await item3.setValue('value3'); + + await storage.removeItems([ + 'local:item1', + item2, + { key: 'local:item3', options: { removeMeta: true } }, + ]); + + expect(await storage.getItem('local:item1')).toBeNull(); + expect(await item2.getValue()).toBeNull(); + expect(await item3.getValue()).toBeNull(); + expect(await item3.getMeta()).toEqual({}); + }); + + it('should not throw an error when removing non-existent items', async () => { + const item = storage.defineItem('local:non_existent'); + + await expect(storage.removeItems([item])).resolves.not.toThrow(); }); }); @@ -400,6 +645,22 @@ describe('Storage Utils', () => { }, ); + describe('Invalid storage areas', () => { + it('should not accept keys without a valid storage area prefix', async () => { + // @ts-expect-error + await storage.getItem('test').catch(() => {}); + // @ts-expect-error + await storage.getItem('loca:test').catch(() => {}); + }); + + it('should throw an error when using an invalid storage area', async () => { + // @ts-expect-error: Test passes if there is a type error here + await expect(storage.getItem('invalidArea:key')).rejects.toThrow( + 'Invalid area', + ); + }); + }); + describe('defineItem', () => { describe('versioning', () => { it('should migrate values to the latest when a version upgrade is detected', async () => { @@ -553,6 +814,28 @@ describe('Storage Utils', () => { 'Version downgrade detected (v2 -> v1) for "local:count"', ); }); + + it('should throw an error when defining an item with an invalid version', () => { + expect(() => storage.defineItem('local:key', { version: 0 })).toThrow( + 'Storage item version cannot be less than 1', + ); + }); + + it('should handle errors in migration functions', async () => { + const cause = Error('Some error'); + const expectedError = new MigrationError('local:key', 2, { cause }); + const item = storage.defineItem('local:key', { + version: 3, + migrations: { + 2: () => { + throw cause; + }, + }, + }); + await fakeBrowser.storage.local.set({ key: 1, key$: { v: 1 } }); + + await expect(item.migrate()).rejects.toThrow(expectedError); + }); }); describe('getValue', () => { @@ -872,6 +1155,24 @@ describe('Storage Utils', () => { await expect(storage.getItem('local:test')).resolves.toBe(expected); }); + + it("should re-initialize a value on the next call to getValue if it's been removed", async () => { + const init = vi.fn().mockImplementation(Math.random); + const item = storage.defineItem('local:key', { init }); + await waitForInit(); + + await item.removeValue(); + // Make sure it's actually blank before running the test + expect(await browser.storage.local.get()).toEqual({}); + init.mockClear(); + + const [value1, value2] = await Promise.all([ + item.getValue(), + item.getValue(), + ]); + expect(init).toBeCalledTimes(1); + expect(value1).toBe(value2); + }); }); describe('types', () => { @@ -896,12 +1197,173 @@ describe('Storage Utils', () => { }); }); - describe('types', () => { - it('should not accept keys without a valid storage area prefix', async () => { - // @ts-expect-error: Test passes if there is a type error here - await storage.getItem('test').catch(() => {}); - // @ts-expect-error - await storage.getItem('loca:test').catch(() => {}); + describe('Multiple Storage Areas', () => { + describe('getItems', () => { + it('should get the values of multiple storage items efficiently', async () => { + const item1 = storage.defineItem('local:item1'); + const item2 = storage.defineItem('session:item2'); + const item3 = storage.defineItem('local:item3'); + + await item1.setValue(42); + await item2.setValue('hello'); + await item3.setValue(false); + + const localGetSpy = vi.spyOn(fakeBrowser.storage.local, 'get'); + const sessionGetSpy = vi.spyOn(fakeBrowser.storage.session, 'get'); + + const values = await storage.getItems([item1, item2, item3]); + + expect(values).toEqual([ + { + key: 'local:item1', + value: 42, + }, + { + key: 'session:item2', + value: 'hello', + }, + { + key: 'local:item3', + value: false, + }, + ]); + + expect(localGetSpy).toBeCalledTimes(1); + expect(localGetSpy).toBeCalledWith(['item1', 'item3']); + expect(sessionGetSpy).toBeCalledTimes(1); + expect(sessionGetSpy).toBeCalledWith(['item2']); + }); + + it('should return values in the same order as input', async () => { + const item1 = storage.defineItem('local:item1'); + const item2 = storage.defineItem('session:item2'); + const item3 = storage.defineItem('local:item3'); + + expect(await storage.getItems([item1, item2, item3])).toEqual([ + { key: item1.key, value: null }, + { key: item2.key, value: null }, + { key: item3.key, value: null }, + ]); + expect(await storage.getItems([item3, item2, item1])).toEqual([ + { key: item3.key, value: null }, + { key: item2.key, value: null }, + { key: item1.key, value: null }, + ]); + }); + }); + + describe('getMetas', () => { + it('should get the metadata of multiple storage items efficiently', async () => { + const item1 = storage.defineItem('local:item1'); + const item2 = storage.defineItem( + 'session:item2', + ); + const item3 = storage.defineItem( + 'local:item3', + ); + + await item1.setMeta({ v: 1 }); + await item2.setMeta({ date: 1234567890 }); + // item3 has no meta + + const localGetSpy = vi.spyOn(fakeBrowser.storage.local, 'get'); + const sessionGetSpy = vi.spyOn(fakeBrowser.storage.session, 'get'); + + const metas = await storage.getMetas([item1, item2, item3]); + + expect(metas).toEqual([ + { key: item1.key, meta: { v: 1 } }, + { key: item2.key, meta: { date: 1234567890 } }, + { key: item3.key, meta: {} }, + ]); + + expect(localGetSpy).toBeCalledTimes(1); + expect(localGetSpy).toBeCalledWith(['item1$', 'item3$']); + expect(sessionGetSpy).toBeCalledTimes(1); + expect(sessionGetSpy).toBeCalledWith(['item2$']); + }); + + it('should return the metadata in the same order as input', async () => { + const item1 = storage.defineItem('local:item1'); + const item2 = storage.defineItem( + 'session:item2', + ); + const item3 = storage.defineItem( + 'local:item3', + ); + + expect(await storage.getMetas([item1, item2, item3])).toEqual([ + { key: item1.key, meta: {} }, + { key: item2.key, meta: {} }, + { key: item3.key, meta: {} }, + ]); + expect(await storage.getMetas([item3, item2, item1])).toEqual([ + { key: item3.key, meta: {} }, + { key: item2.key, meta: {} }, + { key: item1.key, meta: {} }, + ]); + }); + }); + + describe('setItems', () => { + it('should set the values of multiple storage items efficiently', async () => { + const item1 = storage.defineItem('local:item1'); + const value1 = 100; + const item2 = storage.defineItem('session:item2'); + const value2 = 'test'; + const item3 = storage.defineItem('local:item3'); + const value3 = true; + + const localSetSpy = vi.spyOn(fakeBrowser.storage.local, 'set'); + const sessionSetSpy = vi.spyOn(fakeBrowser.storage.session, 'set'); + + await storage.setItems([ + { item: item1, value: value1 }, + { item: item2, value: value2 }, + { item: item3, value: value3 }, + ]); + + expect(localSetSpy).toBeCalledTimes(1); + expect(localSetSpy).toBeCalledWith({ item1: value1, item3: value3 }); + expect(sessionSetSpy).toBeCalledTimes(1); + expect(sessionSetSpy).toBeCalledWith({ item2: value2 }); + }); + }); + + describe('setMetas', () => { + it('should set metadata efficiently', async () => { + const item1 = storage.defineItem('local:one'); + const item2 = storage.defineItem('session:two'); + const item3 = storage.defineItem('local:three'); + await waitForInit(); + + const localGetSpy = vi.spyOn(fakeBrowser.storage.local, 'get'); + const sessionGetSpy = vi.spyOn(fakeBrowser.storage.session, 'get'); + const localSetSpy = vi.spyOn(fakeBrowser.storage.local, 'set'); + const sessionSetSpy = vi.spyOn(fakeBrowser.storage.session, 'set'); + + await storage.setMetas([ + { item: item1, meta: { v: 1 } }, + { item: item2, meta: { v: 2 } }, + { item: item3, meta: { v: 3 } }, + ]); + + console.log(localGetSpy.mock.calls); + expect(localGetSpy).toBeCalledTimes(1); + expect(localGetSpy).toBeCalledWith(['one$', 'three$']); + expect(sessionGetSpy).toBeCalledTimes(1); + expect(sessionGetSpy).toBeCalledWith(['two$']); + + expect(localSetSpy).toBeCalledTimes(1); + expect(localSetSpy).toBeCalledWith({ + one$: { v: 1 }, + three$: { v: 3 }, + }); + expect(sessionSetSpy).toBeCalledTimes(1); + expect(sessionSetSpy).toBeCalledWith({ + two$: { v: 2 }, + }); + }); }); it('should return a nullable type when getItem is called without a fallback', async () => { diff --git a/packages/wxt/src/storage.ts b/packages/wxt/src/storage.ts index 55795d4a..7491d84c 100644 --- a/packages/wxt/src/storage.ts +++ b/packages/wxt/src/storage.ts @@ -44,6 +44,14 @@ function createStorage(): WxtStorage { }; }; const getMetaKey = (key: string) => key + '$'; + const mergeMeta = (oldMeta: any, newMeta: any): any => { + const newFields = { ...oldMeta }; + Object.entries(newMeta).forEach(([key, value]) => { + if (value == null) delete newFields[key]; + else newFields[key] = value; + }); + return newFields; + }; const getValueOrFallback = (value: any, fallback: any) => value ?? fallback ?? null; const getMetaValue = (properties: any) => @@ -78,15 +86,7 @@ function createStorage(): WxtStorage { ) => { const metaKey = getMetaKey(driverKey); const existingFields = getMetaValue(await driver.getItem(metaKey)); - const newFields = { ...existingFields }; - Object.entries(properties).forEach(([key, value]) => { - if (value == null) { - delete newFields[key]; - } else { - newFields[key] = value; - } - }); - await driver.setItem(metaKey, newFields); + await driver.setItem(metaKey, mergeMeta(existingFields, properties)); }; const removeItem = async ( driver: WxtStorageDriver, @@ -129,98 +129,195 @@ function createStorage(): WxtStorage { getItems: async (keys) => { const areaToKeyMap = new Map(); const keyToOptsMap = new Map | undefined>(); + const orderedKeys: StorageItemKey[] = []; + keys.forEach((key) => { let keyStr: StorageItemKey; let opts: GetItemOptions | undefined; if (typeof key === 'string') { + // key: string keyStr = key; + } else if ('getValue' in key) { + // key: WxtStorageItem + keyStr = key.key; + opts = { fallback: key.fallback }; } else { + // key: { key, options } keyStr = key.key; opts = key.options; } + orderedKeys.push(keyStr); const { driverArea, driverKey } = resolveKey(keyStr); - const keys = areaToKeyMap.get(driverArea) ?? []; - areaToKeyMap.set(driverArea, keys.concat(driverKey)); + const areaKeys = areaToKeyMap.get(driverArea) ?? []; + areaToKeyMap.set(driverArea, areaKeys.concat(driverKey)); keyToOptsMap.set(keyStr, opts); }); - const results = await Promise.all( + const resultsMap = new Map(); + await Promise.all( Array.from(areaToKeyMap.entries()).map(async ([driverArea, keys]) => { const driverResults = await drivers[driverArea].getItems(keys); - return driverResults.map((driverResult) => { + driverResults.forEach((driverResult) => { const key = `${driverArea}:${driverResult.key}` as StorageItemKey; const opts = keyToOptsMap.get(key); const value = getValueOrFallback( driverResult.value, opts?.fallback ?? opts?.defaultValue, ); - return { key, value }; + resultsMap.set(key, value); }); }), ); - return results.flat(); + + return orderedKeys.map((key) => ({ + key, + value: resultsMap.get(key), + })); }, getMeta: async (key) => { const { driver, driverKey } = resolveKey(key); return await getMeta(driver, driverKey); }, + getMetas: async (args) => { + const keys = args.map((arg) => { + const key = typeof arg === 'string' ? arg : arg.key; + const { driverArea, driverKey } = resolveKey(key); + return { + key, + driverArea, + driverKey, + driverMetaKey: getMetaKey(driverKey), + }; + }); + const areaToDriverMetaKeysMap = keys.reduce< + Partial> + >((map, key) => { + map[key.driverArea] ??= []; + map[key.driverArea]!.push(key); + return map; + }, {}); + + const resultsMap: Record = {}; + await Promise.all( + Object.entries(areaToDriverMetaKeysMap).map(async ([area, keys]) => { + const areaRes = await browser.storage[area as StorageArea].get( + keys.map((key) => key.driverMetaKey), + ); + keys.forEach((key) => { + resultsMap[key.key] = areaRes[key.driverMetaKey] ?? {}; + }); + }), + ); + + return keys.map((key) => ({ + key: key.key, + meta: resultsMap[key.key], + })); + }, setItem: async (key, value) => { const { driver, driverKey } = resolveKey(key); await setItem(driver, driverKey, value); }, - setItems: async (values) => { - const areaToKeyValueMap = new Map< - StorageArea, - Array<{ key: string; value: any }> - >(); - values.forEach(({ key, value }) => { - const { driverArea, driverKey } = resolveKey(key); - const values = areaToKeyValueMap.get(driverArea) ?? []; - areaToKeyValueMap.set( - driverArea, - values.concat({ key: driverKey, value }), + setItems: async (items) => { + const areaToKeyValueMap: Partial< + Record> + > = {}; + items.forEach((item) => { + const { driverArea, driverKey } = resolveKey( + 'key' in item ? item.key : item.item.key, ); + areaToKeyValueMap[driverArea] ??= []; + areaToKeyValueMap[driverArea].push({ + key: driverKey, + value: item.value, + }); }); await Promise.all( - Array.from(areaToKeyValueMap.entries()).map( - async ([driverArea, values]) => { - const driver = getDriver(driverArea); - await driver.setItems(values); - }, - ), + Object.entries(areaToKeyValueMap).map(async ([driverArea, values]) => { + const driver = getDriver(driverArea as StorageArea); + await driver.setItems(values); + }), ); }, setMeta: async (key, properties) => { const { driver, driverKey } = resolveKey(key); await setMeta(driver, driverKey, properties); }, + setMetas: async (items) => { + const areaToMetaUpdatesMap: Partial< + Record + > = {}; + items.forEach((item) => { + const { driverArea, driverKey } = resolveKey( + 'key' in item ? item.key : item.item.key, + ); + areaToMetaUpdatesMap[driverArea] ??= []; + areaToMetaUpdatesMap[driverArea].push({ + key: driverKey, + properties: item.meta, + }); + }); + + await Promise.all( + Object.entries(areaToMetaUpdatesMap).map( + async ([storageArea, updates]) => { + const driver = getDriver(storageArea as StorageArea); + const metaKeys = updates.map(({ key }) => getMetaKey(key)); + console.log(storageArea, metaKeys); + const existingMetas = await driver.getItems(metaKeys); + const existingMetaMap = Object.fromEntries( + existingMetas.map(({ key, value }) => [key, getMetaValue(value)]), + ); + + const metaUpdates = updates.map(({ key, properties }) => { + const metaKey = getMetaKey(key); + return { + key: metaKey, + value: mergeMeta(existingMetaMap[metaKey] ?? {}, properties), + }; + }); + + await driver.setItems(metaUpdates); + }, + ), + ); + }, removeItem: async (key, opts) => { const { driver, driverKey } = resolveKey(key); await removeItem(driver, driverKey, opts); }, removeItems: async (keys) => { - const areaToKeysMap = new Map(); + const areaToKeysMap: Partial> = {}; + keys.forEach((key) => { let keyStr: StorageItemKey; let opts: RemoveItemOptions | undefined; if (typeof key === 'string') { + // key: string keyStr = key; + } else if ('getValue' in key) { + // key: WxtStorageItem + keyStr = key.key; + } else if ('item' in key) { + // key: { item, options } + keyStr = key.item.key; + opts = key.options; } else { + // key: { key, options } keyStr = key.key; opts = key.options; } const { driverArea, driverKey } = resolveKey(keyStr); - const areaKeys = areaToKeysMap.get(driverArea) ?? []; - areaKeys.push(driverKey); + areaToKeysMap[driverArea] ??= []; + areaToKeysMap[driverArea].push(driverKey); if (opts?.removeMeta) { - areaKeys.push(getMetaKey(driverKey)); + areaToKeysMap[driverArea].push(getMetaKey(driverKey)); } - areaToKeysMap.set(driverArea, areaKeys); }); await Promise.all( - Array.from(areaToKeysMap.entries()).map(async ([driverArea, keys]) => { - const driver = getDriver(driverArea); + Object.entries(areaToKeysMap).map(async ([driverArea, keys]) => { + const driver = getDriver(driverArea as StorageArea); await driver.removeItems(keys); }), ); @@ -284,9 +381,15 @@ function createStorage(): WxtStorage { ); let migratedValue = value; for (const migrateToVersion of migrationsToRun) { - migratedValue = - (await migrations?.[migrateToVersion]?.(migratedValue)) ?? - migratedValue; + try { + migratedValue = + (await migrations?.[migrateToVersion]?.(migratedValue)) ?? + migratedValue; + } catch (err) { + throw Error(`v${migrateToVersion} migration failed for "${key}"`, { + cause: err, + }); + } } await driver.setItems([ { key: driverKey, value: migratedValue }, @@ -323,6 +426,7 @@ function createStorage(): WxtStorage { migrationsDone.then(getOrInitValue); return { + key, get defaultValue() { return getFallback(); }, @@ -480,7 +584,9 @@ export interface WxtStorage { */ getItems( keys: Array< - StorageItemKey | { key: StorageItemKey; options?: GetItemOptions } + | StorageItemKey + | WxtStorageItem + | { key: StorageItemKey; options?: GetItemOptions } >, ): Promise>; /** @@ -491,6 +597,15 @@ export interface WxtStorage { * await storage.getMeta("local:installDate"); */ getMeta>(key: StorageItemKey): Promise; + /** + * Get the metadata of multiple storage items. + * + * @param items List of keys or items to get the metadata of. + * @returns An array containing storage keys and their metadata. + */ + getMetas( + keys: Array>, + ): Promise>; /** * Set a value in storage. Setting a value to `null` or `undefined` is equivalent to calling * `removeItem`. @@ -508,7 +623,12 @@ export interface WxtStorage { * { key: "session:someCounter, value: 5 }, * ]); */ - setItems(values: Array<{ key: StorageItemKey; value: any }>): Promise; + setItems( + values: Array< + | { key: StorageItemKey; value: any } + | { item: WxtStorageItem; value: any } + >, + ): Promise; /** * Sets metadata properties. If some properties are already set, but are not included in the * `properties` parameter, they will not be removed. @@ -520,6 +640,17 @@ export interface WxtStorage { key: StorageItemKey, properties: T | null, ): Promise; + /** + * Set the metadata of multiple storage items. + * + * @param items List of storage keys or items and metadata to set for each. + */ + setMetas( + metas: Array< + | { key: StorageItemKey; meta: Record } + | { item: WxtStorageItem; meta: Record } + >, + ): Promise; /** * Removes an item from storage. * @@ -532,7 +663,10 @@ export interface WxtStorage { */ removeItems( keys: Array< - StorageItemKey | { key: StorageItemKey; options?: RemoveItemOptions } + | StorageItemKey + | WxtStorageItem + | { key: StorageItemKey; options?: RemoveItemOptions } + | { item: WxtStorageItem; options?: RemoveItemOptions } >, ): Promise; /** @@ -602,7 +736,11 @@ export interface WxtStorageItem< TMetadata extends Record, > { /** - * @deprecated Renamed to `fallback`, use it instead. + * The storage key passed when creating the storage item. + */ + key: StorageItemKey; + /** + * @deprecated Renamed to fallback, use it instead. */ defaultValue: TValue; /** @@ -719,3 +857,13 @@ export type WatchCallback = (newValue: T, oldValue: T) => void; * Call to remove a watch listener */ export type Unwatch = () => void; + +export class MigrationError extends Error { + constructor( + public key: string, + public version: number, + options?: ErrorOptions, + ) { + super(`v${version} migration failed for "${key}"`, options); + } +} From f5aec7ec9bd85805d8e6c372136f2f485d1a5ba9 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 11 Nov 2024 15:29:36 -0600 Subject: [PATCH 013/130] chore: Create FUNDING.yml --- .github/FUNDING.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..b3fbe6f6 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,15 @@ +# These are supported funding model platforms + +github: [aklinker1] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +polar: # Replace with a single Polar username +buy_me_a_coffee: # Replace with a single Buy Me a Coffee username +thanks_dev: # Replace with a single thanks.dev username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From 9b780f2d0012cd17dc116d2cb3391c96c29b8649 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 11 Nov 2024 16:36:04 -0600 Subject: [PATCH 014/130] docs: Add FAQ about component library styles in content scripts --- docs/guide/resources/faq.md | 51 ++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/docs/guide/resources/faq.md b/docs/guide/resources/faq.md index 275bfb3b..593a2508 100644 --- a/docs/guide/resources/faq.md +++ b/docs/guide/resources/faq.md @@ -8,9 +8,7 @@ Commonly asked questions about how to use WXT or why it behaves the way it does. [[toc]] ---- - -### Why are content scripts not showing up in the manifest? +## Why aren't content scripts added to the manifest? During development, WXT registers content scripts dynamically so they can be reloaded individually when a file is saved without reloading your entire extension. @@ -19,3 +17,50 @@ To list the content scripts registered during development, open the service work ```js await chrome.scripting.getRegisteredContentScripts(); ``` + +## My component library doesn't work in content scripts! + +Component libraries place their CSS in the document's `` by default. When using `createShadowRoot`, your UI is isolated from the document's styles because it's inside a ShadowRoot. + +To fix this, you need to tell your component library to insert it's CSS inside the shadow root. Here's the docs for a couple of popular libraries: + +- React + - Ant Design: [`StyleProvider`](https://ant.design/docs/react/compatible-style#shadow-dom-usage) + - Mantine: [`MantineProvider#getRootElement` and `MantineProvider#cssVariablesSelector`](https://mantine.dev/theming/mantine-provider/) + +> If your library isn't listed above, try searching it's docs/issues for "shadow root", "shadow dom", or "css container". + +`createShadowRoot` provides it's own `` element inside the shadow root, so that were you should tell the library to add the CSS. Here's an example with Ant Design: + +```tsx +import { StyleProvider } from '@ant-design/cssinjs'; // [!code ++] +import ReactDOM from 'react-dom/client'; +import App from './App.tsx'; + +const ui = await createShadowRootUi(ctx, { + name: 'example-ui', + position: 'inline', + anchor: 'body', + onMount: (container) => { // [!code --] + onMount: (container, shadow) => { // [!code ++] + const cssContainer = shadow.querySelector("head")!; // [!code ++] + const root = ReactDOM.createRoot(container); + root.render( + // [!code ++] + + // [!code ++] + ); + return root; + }, + onRemove: (root) => { + root?.unmount(); + }, +}); +``` + +Note that this doesn't effect all component libraries, just ones that inject CSS themselves rather than having you import their CSS. This approach is more prevailent in the React community, but not limited to it. That's why only React libraries are listed above. Vuetify, for example, works just fine because you import its CSS - WXT picks up on this and the CSS is added inside the shadow root automatically: + +```ts +import 'vuetify/styles'; // <-- This line imports the CSS, just like importing a .css file +import { createVuetify } from 'vuetify'; +``` From 51c7ee5fa82caead63f3c6ff2d7ab069abfef7d6 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 11 Nov 2024 17:19:20 -0600 Subject: [PATCH 015/130] chore: Update FUNDING.yml --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index b3fbe6f6..e4f65931 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: [aklinker1] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +github: [wxt-dev] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username From 4fac3645429a1095ad7070901adc184a21a780b7 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 13 Nov 2024 07:48:57 -0600 Subject: [PATCH 016/130] feat!: Svelte 5 support (#1104) --- packages/module-svelte/package.json | 5 +- pnpm-lock.yaml | 163 +++++++++++++++------------- 2 files changed, 92 insertions(+), 76 deletions(-) diff --git a/packages/module-svelte/package.json b/packages/module-svelte/package.json index 61a6d1d6..9281bd7d 100644 --- a/packages/module-svelte/package.json +++ b/packages/module-svelte/package.json @@ -42,10 +42,11 @@ "check": "pnpm build && check" }, "peerDependencies": { - "wxt": ">=0.18.6" + "wxt": ">=0.18.6", + "svelte": ">=5" }, "dependencies": { - "@sveltejs/vite-plugin-svelte": "^3.1.2" + "@sveltejs/vite-plugin-svelte": "^4.0.0" }, "devDependencies": { "@aklinker1/check": "^1.4.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6686a44..94ccaf1c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -216,8 +216,11 @@ importers: packages/module-svelte: dependencies: '@sveltejs/vite-plugin-svelte': - specifier: ^3.1.2 - version: 3.1.2(svelte@4.2.18)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + specifier: ^4.0.0 + version: 4.0.0(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + svelte: + specifier: '>=5' + version: 5.1.6 devDependencies: '@aklinker1/check': specifier: ^1.4.5 @@ -1672,19 +1675,19 @@ packages: resolution: {integrity: sha512-axlrvsHlHlFmKKMEg4VyvMzFr93JWJj4eIfXY1STVuO2fsImCa7ncaiG5gC8HKOX590AW5RtRsC41/B+OfrSqw==} engines: {node: '>=14.16'} - '@sveltejs/vite-plugin-svelte-inspector@2.1.0': - resolution: {integrity: sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==} - engines: {node: ^18.0.0 || >=20} + '@sveltejs/vite-plugin-svelte-inspector@3.0.1': + resolution: {integrity: sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} peerDependencies: - '@sveltejs/vite-plugin-svelte': ^3.0.0 - svelte: ^4.0.0 || ^5.0.0-next.0 + '@sveltejs/vite-plugin-svelte': ^4.0.0-next.0||^4.0.0 + svelte: ^5.0.0-next.96 || ^5.0.0 vite: ^5.0.0 - '@sveltejs/vite-plugin-svelte@3.1.2': - resolution: {integrity: sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==} - engines: {node: ^18.0.0 || >=20} + '@sveltejs/vite-plugin-svelte@4.0.0': + resolution: {integrity: sha512-kpVJwF+gNiMEsoHaw+FJL76IYiwBikkxYU83+BpqQLdVMff19KeRKLd2wisS8niNBMJ2omv5gG+iGDDwd8jzag==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} peerDependencies: - svelte: ^4.0.0 || ^5.0.0-next.0 + svelte: ^5.0.0-next.96 || ^5.0.0 vite: ^5.0.0 '@szmarczak/http-timer@5.0.1': @@ -2029,6 +2032,11 @@ packages: '@webext-core/match-patterns@1.0.3': resolution: {integrity: sha512-NY39ACqCxdKBmHgw361M9pfJma8e4AZo20w9AY+5ZjIj1W2dvXC8J31G5fjfOGbulW9w4WKpT8fPooi0mLkn9A==} + acorn-typescript@1.4.13: + resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} + peerDependencies: + acorn: '>=8.9.0' + acorn@8.12.1: resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} engines: {node: '>=0.4.0'} @@ -2082,8 +2090,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} array-differ@4.0.0: resolution: {integrity: sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw==} @@ -2114,8 +2123,9 @@ packages: peerDependencies: postcss: ^8.1.0 - axobject-query@4.0.0: - resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} babel-plugin-jsx-dom-expressions@0.37.21: resolution: {integrity: sha512-WbQo1NQ241oki8bYasVzkMXOTSIri5GO/K47rYJb2ZBh8GaPUEWiWbMV3KwXz+96eU2i54N6ThzjQG/f5n8Azw==} @@ -2343,9 +2353,6 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} - code-red@1.0.4: - resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} - color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -2710,6 +2717,12 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} + esm-env@1.1.4: + resolution: {integrity: sha512-oO82nKPHKkzIj/hbtuDYy/JHqBHFlMIW36SDiPCVsj87ntDLcWN+sJ1erdVryd4NxODacFTsdrIE3b7IamqbOg==} + + esrap@1.2.2: + resolution: {integrity: sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==} + estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -3348,6 +3361,9 @@ packages: magic-string@0.30.11: resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + magic-string@0.30.12: + resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} + magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} @@ -3741,9 +3757,6 @@ packages: perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - periscopic@3.1.0: - resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} - picocolors@1.1.0: resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} @@ -4375,15 +4388,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-hmr@0.16.0: - resolution: {integrity: sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==} - engines: {node: ^12.20 || ^14.13.1 || >= 16} - peerDependencies: - svelte: ^3.19.0 || ^4.0.0 - - svelte@4.2.18: - resolution: {integrity: sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==} - engines: {node: '>=16'} + svelte@5.1.6: + resolution: {integrity: sha512-bYS/DpkqXk0j5UZgiNXrEjZYPRZ4Ncd87w4KUSbcZGyojA0+i/Ls9OGUjETHmdLe8RcQ0G8SX/T0PypPpAA/ew==} + engines: {node: '>=18'} svgo@3.3.2: resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} @@ -4689,6 +4696,14 @@ packages: vite: optional: true + vitefu@1.0.3: + resolution: {integrity: sha512-iKKfOMBHob2WxEJbqbJjHAkmYgvFDPhuqrO82om83S8RLk+17FtyMBfcyeH8GqD0ihShtkMW/zzJgiA51hCNCQ==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + vitepress@1.3.4: resolution: {integrity: sha512-I1/F6OW1xl3kW4PaIMC6snxjWgf3qfziq2aqsDoFc/Gt41WbcRv++z8zjw8qGRIJ+I4bUW7ZcKFDHHN/jkH9DQ==} hasBin: true @@ -4891,6 +4906,9 @@ packages: yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + zip-dir@2.0.0: resolution: {integrity: sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg==} @@ -5895,26 +5913,25 @@ snapshots: '@sindresorhus/is@5.4.1': {} - '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.18)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)))(svelte@4.2.18)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.0(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)))(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))': dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.18)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) - debug: 4.3.5 - svelte: 4.2.18 + '@sveltejs/vite-plugin-svelte': 4.0.0(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + debug: 4.3.7 + svelte: 5.1.6 vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.18)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))': + '@sveltejs/vite-plugin-svelte@4.0.0(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.18)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)))(svelte@4.2.18)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) - debug: 4.3.5 + '@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.0(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)))(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + debug: 4.3.7 deepmerge: 4.3.1 kleur: 4.1.5 - magic-string: 0.30.11 - svelte: 4.2.18 - svelte-hmr: 0.16.0(svelte@4.2.18) + magic-string: 0.30.12 + svelte: 5.1.6 vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) - vitefu: 0.2.5(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + vitefu: 1.0.3(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) transitivePeerDependencies: - supports-color @@ -6376,6 +6393,10 @@ snapshots: '@webext-core/match-patterns@1.0.3': {} + acorn-typescript@1.4.13(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + acorn@8.12.1: {} adm-zip@0.5.10: {} @@ -6432,9 +6453,7 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 - aria-query@5.3.0: - dependencies: - dequal: 2.0.3 + aria-query@5.3.2: {} array-differ@4.0.0: {} @@ -6460,9 +6479,7 @@ snapshots: postcss: 8.4.39 postcss-value-parser: 4.2.0 - axobject-query@4.0.0: - dependencies: - dequal: 2.0.3 + axobject-query@4.1.0: {} babel-plugin-jsx-dom-expressions@0.37.21(@babel/core@7.24.7): dependencies: @@ -6738,14 +6755,6 @@ snapshots: clone@1.0.4: {} - code-red@1.0.4: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - '@types/estree': 1.0.6 - acorn: 8.12.1 - estree-walker: 3.0.3 - periscopic: 3.1.0 - color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -7140,6 +7149,13 @@ snapshots: escape-string-regexp@5.0.0: {} + esm-env@1.1.4: {} + + esrap@1.2.2: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + '@types/estree': 1.0.6 + estree-walker@2.0.2: {} estree-walker@3.0.3: @@ -7800,6 +7816,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + magic-string@0.30.12: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + magicast@0.3.5: dependencies: '@babel/parser': 7.25.6 @@ -8199,12 +8219,6 @@ snapshots: perfect-debounce@1.0.0: {} - periscopic@3.1.0: - dependencies: - '@types/estree': 1.0.6 - estree-walker: 3.0.3 - is-reference: 3.0.2 - picocolors@1.1.0: {} picomatch@2.3.1: {} @@ -8857,26 +8871,21 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-hmr@0.16.0(svelte@4.2.18): - dependencies: - svelte: 4.2.18 - - svelte@4.2.18: + svelte@5.1.6: dependencies: '@ampproject/remapping': 2.3.0 '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 '@types/estree': 1.0.6 acorn: 8.12.1 - aria-query: 5.3.0 - axobject-query: 4.0.0 - code-red: 1.0.4 - css-tree: 2.3.1 - estree-walker: 3.0.3 + acorn-typescript: 1.4.13(acorn@8.12.1) + aria-query: 5.3.2 + axobject-query: 4.1.0 + esm-env: 1.1.4 + esrap: 1.2.2 is-reference: 3.0.2 locate-character: 3.0.0 - magic-string: 0.30.11 - periscopic: 3.1.0 + magic-string: 0.30.12 + zimmerframe: 1.1.2 svgo@3.3.2: dependencies: @@ -9231,6 +9240,10 @@ snapshots: optionalDependencies: vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + vitefu@1.0.3(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)): + optionalDependencies: + vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + vitepress@1.3.4(@algolia/client-search@4.20.0)(@types/node@20.16.10)(@types/react@18.3.11)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.4)(search-insights@2.15.0)(typescript@5.6.2): dependencies: '@docsearch/css': 3.6.2 @@ -9490,6 +9503,8 @@ snapshots: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 + zimmerframe@1.1.2: {} + zip-dir@2.0.0: dependencies: async: 3.2.4 From b4d569a915d5c9f65426eb979c803c85b13411bc Mon Sep 17 00:00:00 2001 From: dsrrlldr Date: Wed, 13 Nov 2024 14:52:30 +0100 Subject: [PATCH 017/130] docs: Add "Keyword Rank Checker" to homepage (#1141) --- docs/.vitepress/components/UsingWxtSection.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/.vitepress/components/UsingWxtSection.vue b/docs/.vitepress/components/UsingWxtSection.vue index 610f4e02..2feb626e 100644 --- a/docs/.vitepress/components/UsingWxtSection.vue +++ b/docs/.vitepress/components/UsingWxtSection.vue @@ -50,6 +50,7 @@ const chromeExtensionIds = [ 'fcphghnknhkimeagdglkljinmpbagone', // YouTube Auto HD + FPS 'lpomjgbicdemjkgmbnkjncgdebogkhlb', // MultiViewer Companion 'ggiafipgeeaaahnjamgpjcgkdpanhddg', // Sync Watch - Watch videos together on any site + 'nmldnjcblcihmegipecakhmnieiofmgl', // Keyword Rank Checker ]; const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds); From 607e1162e5d3c70a03189f63b16507961bb10c6a Mon Sep 17 00:00:00 2001 From: Johnson Towoju Date: Wed, 13 Nov 2024 14:55:41 +0100 Subject: [PATCH 018/130] docs: Add "YouTube Simple View" to homepage (#1142) Co-authored-by: Aaron --- docs/.vitepress/components/UsingWxtSection.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/.vitepress/components/UsingWxtSection.vue b/docs/.vitepress/components/UsingWxtSection.vue index 2feb626e..e1709d96 100644 --- a/docs/.vitepress/components/UsingWxtSection.vue +++ b/docs/.vitepress/components/UsingWxtSection.vue @@ -51,6 +51,7 @@ const chromeExtensionIds = [ 'lpomjgbicdemjkgmbnkjncgdebogkhlb', // MultiViewer Companion 'ggiafipgeeaaahnjamgpjcgkdpanhddg', // Sync Watch - Watch videos together on any site 'nmldnjcblcihmegipecakhmnieiofmgl', // Keyword Rank Checker + 'gppllamhaciichleihemgilcpledblpn', // YouTube Simple View - Hide distractions & more ]; const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds); From b121ed2fe218b753cc04ea71510a606116fa2e63 Mon Sep 17 00:00:00 2001 From: nirtamir2 Date: Wed, 13 Nov 2024 15:56:34 +0200 Subject: [PATCH 019/130] docs: Fix solid-js example (#1145) --- docs/guide/essentials/content-scripts.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/guide/essentials/content-scripts.md b/docs/guide/essentials/content-scripts.md index caf74255..727ed096 100644 --- a/docs/guide/essentials/content-scripts.md +++ b/docs/guide/essentials/content-scripts.md @@ -239,6 +239,7 @@ export default defineContentScript({ onMount: (container) => { // Render your app to the UI container const unmount = render(() =>
...
, container); + return unmount; }, onRemove: (unmount) => { // Unmount the app when the UI is removed From 63dee97ac35790ad490c1b22dc4cd5258c753adf Mon Sep 17 00:00:00 2001 From: jasonbla Date: Wed, 13 Nov 2024 22:56:58 +0900 Subject: [PATCH 020/130] docs: fix incorrect syntax for MatchPattern (#1148) --- docs/guide/essentials/content-scripts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/essentials/content-scripts.md b/docs/guide/essentials/content-scripts.md index 727ed096..e8b350d9 100644 --- a/docs/guide/essentials/content-scripts.md +++ b/docs/guide/essentials/content-scripts.md @@ -603,7 +603,7 @@ export default defineContentScript({ matches: ['*://*.youtube.com/*'], main(ctx) { ctx.addEventListener(window, 'wxt:locationchange', ({ newUrl }) => { - if (watchPattern.matches(newUrl)) mainWatch(ctx); + if (watchPattern.includes(newUrl)) mainWatch(ctx); }); }, }); From 199f330324725e6610d0986d64b3d6b63e6cb49a Mon Sep 17 00:00:00 2001 From: Goran Nikic Date: Wed, 13 Nov 2024 13:59:14 +0000 Subject: [PATCH 021/130] docs: Add "propbar.co.uk" to homepage (#1149) Co-authored-by: Aaron --- docs/.vitepress/components/UsingWxtSection.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/.vitepress/components/UsingWxtSection.vue b/docs/.vitepress/components/UsingWxtSection.vue index e1709d96..06549ebd 100644 --- a/docs/.vitepress/components/UsingWxtSection.vue +++ b/docs/.vitepress/components/UsingWxtSection.vue @@ -52,6 +52,7 @@ const chromeExtensionIds = [ 'ggiafipgeeaaahnjamgpjcgkdpanhddg', // Sync Watch - Watch videos together on any site 'nmldnjcblcihmegipecakhmnieiofmgl', // Keyword Rank Checker 'gppllamhaciichleihemgilcpledblpn', // YouTube Simple View - Hide distractions & more + 'pccbghdfdnnkkbcdcibchpbffdgednkf', // Propbar - Property Data Enhancer ]; const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds); From aa14676f917ea3ecb4640f4428e0053287d50348 Mon Sep 17 00:00:00 2001 From: kongmoumou <35442047+kongmoumou@users.noreply.github.com> Date: Wed, 13 Nov 2024 21:59:49 +0800 Subject: [PATCH 022/130] fix: Throw when config file does not exist (#1156) Co-authored-by: kongmoumou --- docs/assets/init-demo.gif | Bin 140670 -> 167668 bytes packages/wxt/e2e/tests/user-config.test.ts | 14 ++++++++++++++ packages/wxt/src/core/resolve-config.ts | 3 +++ 3 files changed, 17 insertions(+) diff --git a/docs/assets/init-demo.gif b/docs/assets/init-demo.gif index 353c8b73aaa92ed97061b187bfbc35bca429b93f..03dc2acf425c8f9c4a0421e2f9d74a88ab0a4e86 100644 GIT binary patch literal 167668 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 yF<>ESR65Xs{!pRz7#)f)-vo z36~a$pcfNcg*bQIk4liWxW=Va?{s2XXSnE;M{U|tWska5lvHK2bWE2T+vU&UiTo0?aLds>VCg)Dc_xpLs}!uD?gho>~!nbfbC ztLCEhGQ8KQnV6J&O1OLeG>ye8q)K;PVKv>lDk$#%S)(VbR`Z-G zS|BbmB}#xb7Bu;@pN;#?hC}~eooxI6!b*Vqzrlxw%@^#nbGKYDN{;w00kbaW2dg1(gd9!D~dbeZmM!$dSr2oAAu;=J=x%b``mT#Vv&kb(b z7rrat)ui<1Z`sk+Zx6MFD`|?!{xh(9_h*gto&_AjYF_UjAD*v&fA1phTaln*a{vE- z4F8_rXVw9CcH|hD(5H3|@`gaNiE|Y4?D;3BLzC1bH_WlTta@gKlKZ|rKMfwa2nb2o zFdTgJz;7P6v(%k}B_hVL(JB(Iw~8Mz`@DFv^KvLNi{qq_znZEUj}95sz4&67z3JiW zz*WiYvle{lklA0c+Gj?AwAsotGi92N1YUX0(K;=E;=CoCUm9EZb#lLnzAk3v=BnqH z$hh~X;n1ZcHdEsS7JFV@d3kxrRIkS~-={1Uvf26AefE62+8<%IGk)#b#Nv8sN9^sg z+&R08=aub#w_1cn#JX|OWzRPLoke{M^84;8EUXUj{k7)v|FnP8RoU5A9m=k1s4z;M zrF>y^`uZTFN}(U0pQjgO?W+@#Oko#x`TtV=e!b=Ezv2t33k6FOiXOLKec-?!r!Ejt z5SkXyDwwuHu>PUrjyWw-Vn-f!$oa7>s?g9{rpRq{MB^~?>K5@X7NboEnA-HXL=FkA zR&eGqJp3cc*XFs$qDH zGma_uTkWf_Ihl|O$|iG4u0^Kv+O#cO7+3S{-AG(ap$9wjCQ?4%U^PQo47->Mm*wS-0bOMDNOvJ{_mt1E8UR5Q++%w`88K6SmF7W1sKXFRE#tkxT2TYLZ028T7P zbxs`C_WAIkruVjiQ`#kmNo9WbjkQIV-K+n5D^hl6ijhjdf&&g`wpM&)3iPm!S$R!x zlkMuwizcx$$L=*f`JcmaT1CX0%~vav7hY;%@wxxy;G>BP_muLfd`kbXqrAVKv#Vq= zzpmqz>$9@GQy$!}yVmnuo|A#g!<(ON*R~M0jHTBTVwqhn5(E@-z6BQYiak+mR19Mm z&EYv!e_)}5dXq+yk4~0Gb0=fnj76;~_dEi66y-X?d*|i;Nbb`(mmxIq&)ywO6TFgG zoC-GH>{#4lv?@ZBLx|(bqR9bSGexJzeG2fmxxVAaW99oFioK`VG<{Iww*7FR*|F;5 z1!vxl&Xp25akCz%L^TyjJoRtzQjM9@b?a!x;#xbdm-P$zRkcKy@)}6Zd`JF zvbE%-H5pEan%PBkPVs1MQ9mkR%3pmVn-gEp$Q$+KsJtrO8BwSl|1T;iW%RF0e5gt^bkZ72^ zi(6i}K85iSWAph1n%YNeraf*`oe(xRYVWQoi+DMtx!tr6_L|r2`1$g3yZ)?}8%wqZ zF&tiVdu#6XJv*(BpV3OVX}z_LL#RDqfkUOq)#P_`5+1zB2ygj6!6A-`S>x9Z=HoU$ z)L8Xi&h*@I=APQQ?;oG-CAPA4|UrmTGqb)S%cc%rO zXyS1=#lR$C6(_KSM`sSlqZU8Mr5;8{Tr7KxKCL)>)*#SLvWw@>-WMVqj!%O6tx~3C zrp>%~t)z5cHg!sq9dT(ewmlBG9ODGF4C%x_?m%HZ|7mNA{PLv7WIMac^# zgnH~Mmp@-@=Jsph!fAcW5|>F>2 zYH)3T*7bVbu3KIltn-Ub9SY>BNXyxHLN0p^%YPPF(wQt7EK_ffQM)*Dr9lc2XJ635 z0T0I>$asYXbiCru3&AJu(1io3o|7ddx6HBpta_%M$8FM~|Y15@*1o(&$Cr~7a`I?S)JL4YS%xkGPO$kb0JXJ+ro z-flBjYAu)W4DKnuSxX#F%I?4Z>(0vV<^12W%V#L5$9S7OYIV4@Eq3>_w{xtE?yTe7 ze@s_Ok&~nH`?^~y{tF!HE4>O=i}z0Y`nvIr$$!R)QcF(o3SBa0xNLq?knQE;=7_+5 zPnxF*{rI&sJKQRIulCdH*W>?sP8SD_S2WhutG-C6m5_a*z{$<$alM7ZNg_CaC#W$j z{9j<`!#epc(WD6X(uhZa<*Kbo?pn)AmDAbh@6Ji4hjYz8aAELd!4iSj1k)8d@xcG1Z&ElgruUwa%#{4if$XUZ5gf| z%Jo63Voq*K^_0ne+)+)?5;23)th-&;Qo`28EZw(OYP#mtt);#9T5pRzxwE`- z<17Q|ZSRk7YRMIQd#aha^Y{d1?|C^tl==lG8`o#A>oGhMwXOT9)l=zNFYhmDEPS{0 z^v#Ev3s)xYyC-@3ZN{~wz31d2rJrOSSUbBe?&9yq`Bx4~zIzt&=Sl8^o%M3d@7{g=;E2g-Ww-N_7&os|wB#{YJlLxSU1PhyPV)aBV;OEC zgAWaj942xr5}G(?eeic+KoscdOqffb#9gc+soZqr;9ZkMn&4jJ^3z z*DD;^e`bMmewoZ6J;x(vzBA-Hs**2XT`MakmvM-tTVU1Jd9Jf>FX~ZE$U3p(#*BBb zn-wZrCWZIUp3A+fTzZ;7`yA`icS~$L>dLC*YF}MB+PPb8$(QSQeX6CWOn7#?wEXH!6#8DW0Jcpa(-Q<jdx!LXtB0ObXBrql1EzJp)u3JvZkE z7&F#yWcY6gS(f(y-^74p8x|gJ=bM>XHQ~biqumn5XRX6+)7uX#5$e&T%njx1#f+VvF&&zY7yNt#qVo##~RR88L#0V@8> zeByNqxMaPythl(?W3pE4sVPrdZSLOLSnM7;-$=`Rb{>!7*9!?GdJc^D?tos`@SC}4T;*E_zJY%P|d)bms0Y#rTtvZzt z9t%uUOwL;|Susqs{^~-{-E)0+ZwuJ*qb+da!De^fR-rpn9=q+a*q*WJ?5A&c5Bu93 zOLg2N(B$Sbp;2+$49DV@)+f9HmlFQ3O#HwhWfHMS`;HQu?}P`{%VQFIdzD>*Lj*>4&+Uo!f{ggL2dS1z!KB{Upt6?NKBupqf!&B1WKL|TI4 zVds8Ni3P0^SsAyxbhiBv%-bj9$jHelsM09==gQ(_+fM;k&o=wYiZF4P*j$hB&|!J# zCgsk<(j<|0I=Y?bJEJa5v> z0+v}Z999AnrQaMT@Py?&d?Oao!MIE!!e)1?=##JN&cX>+yHe(+$vn(*dpa|*ex-DR z#P0C%7+uM1bx`}zkS-NHLHwaukk}W0j@qJ|M-KkqP~bB6rKZZ^(+0)T>>@f>7&h~4 z&SGg2c)RF&&h|_etsvg8Kb%|kze>_!7Ol_l<7O3!keJ&hd8#eM`HFYl>-=;7b2hl_ zR*bsndii^w=ZBja^HVoCt2I0@;}GHa_+bajMusL9euD!CnClAv_sZU@wlBEMTIFr9 z?#P8e9bGYr+2LCxp0qAVm8pt4X1pszC{0f)&ADR2^-PbQjIIh_rPxoK?U!lUQhHD8 z?uN+v=b7odPPQtrOvzioc=W7e!AD&&#`VVz3Hi4F{CbV!`h)7ZAxrd{1=4g*%5r9g z=l>1Vme_NMi$7QXkRX5ihQCga|8H*MI)WN_*x>9G95%=4qq! zj}Afh!xm0D=gxN3Gi%0km|b8`t8kY6alqZ?m|}16hX$sCKkfpP5Av42=#ahrf?0Ln zLH_s;2@O9dIj&i7*rk6119!(Kxyum}?SE_Xtt&O>b2fcYH<+;8$1z$-`epc`3v+cQ z$VMJK_wNat_JR!xItJlt+LeMVeD8T5PGUTn_I!c-st@u~XJS{ZtEjh=On4aW_+Y8) zoEt3`9gZA_o-m3&)HtnrVH4+em6;ch6dVkl^l{FMEgY=pW*k#aQB}GxeA=u@U4T>I zk<`}<$1Rt9{LhpBWTVVZfoD!B3wX*;Fw0-}m};}buqXS)V=>bi^64oK?3DpUDtEjN zcQ`$;UUc-KujG$e5>*8YJPSFw>J0+wdO<6T!qzs8_t~guW(9yM?f}rX2mhnV+-s1 zqg<{i#;knd>RQBNw&bC?;(`S{lWr|hXj{qV^t4gp#)tJ@54DwaZ4*6{yu4?%DKM|N z>bbZe>s^6Y@S(X0p4_s?h$st@iwc7lVcDPw`N+;RiQa!CfZ>?6$b z0=BQ5gt9L(cd2-KKXN>EPPuuiX(Oxn1%+uUOE+D1JZY=gBY?jtH$m!_FA$Vx6#F6WFq;CJ% znYHCa2|s&%Q_72GnU)F$3C#^H8CMod$4pq^kmbNvHf1sABj*Rqc?bL2xdb+ftj!k< zj%Zfpd?;^v;PU4S8?*%?I3rls92B3`#+qrA^8cWEwt?=-54I+;lUd_8Ty8aFa=*`2 z(xT9iP_(?{R{fU;Gs|y%a{uMy`e0qe4$abqJSjfO($Tk8*Jn>Y(I>yDQQ5#*{#pTZ z++3BGms``E1@Swkba>En7A^UAu6(MJ-@qYuO>DyB3okncG)!8Zpc? zQqO(QqoJ@y9_Z;E0)++(}Qlm-|j6#Z>J2%%Af11NRi^%43r{R^({jXem_lJegg|IMtN# zm{Gt_&vkRxONnTCWh7Z2Kj!<+?eU*G;y=y{-D|nCK=|*+tH!%ZCax=4_S*S=@PBrW z1^;^8Cv1Hc?!M#DZXG z)ms?u}O5phClg=JAMS4exKEL+w9S?9d{?3w>zlPA8~5)kxw(~ zfBoNY{r_i^e2qe+{NJg&-+lPq=FXE^emMV(OuL-J1>fpNGb=vrTcXfYeN**e^+nT~ z(k3CPuzE8av6_T!toxlSTH8%7rH6hht?gT0D|SNj&T&bP^!y)Xm44UrAFG$LFtV-@ z<9)6!!2f~c-hon%G!$^4OFT>_s@goB<$i`CM4sbn@jAtwGMxy;K1+zYI>xk#KYX1VfN zz}Z4*TW}Qn|3$*PTG#|OI@?~b6uQ_VAn1JYu+5oc0#h;BNN=NX1U5e_1OdGA+LZf{nTO?H|1(Dl*7hE??u6Fd%fO)6q}e1Pxq z1!n0FW_iU5JR7)PE#TjBfJx+m*`XKg&jt7|EznbY!1??E-yH`_M+Fvl2cA0)e5Dc9 zry_)QZPRxN5pYak3)`Uj|9@$V4g>4*WMSq)&oEZOyAndn4jb7p7;n06)!`ZvXf&BO z(0EU>dQ!m@qXgra%gv?rldKOvZQ0kXX8hPZ;HQ!4g8J`C#w&xx1UK1aHs~5K+g@t! zV3VAl**KwEkmbw=ne_^ZT$@?C#buX#;`vd)x4M9(rIMw#!T)gpSDJ-@qXO?61^!nH zc(y;_zH7jD@DPr-=&q|HC<-ftCuM+}ReBxPE(8RNVNa&1k6SjAjq?GK-Jg7Lm}dk+&hlxhS|$fyHZ@W6UNN z)?K#0gS@-i=C95aiaDrz|B`;N@vLU2MEO&5cFuIpDbmOkVqeCoerS^E!=MGisk8G0 zI;stA(kCq{JEV5BN%qVk{gh&ThRbtZc8T2g3`;%AwCe+}?8|vpNumW7Mv4ra*B$uk zUn%hai{!iKz+-etK>Psnj0HT`1b9y*ut#ohV(CilN{#1dkH`orRx~jCdt6@qIU8@0 z!foNSjHIN#XF=+QD!vICWlic;Nq&w;%6tp#c5+(3`+sT0bkz`Bh7!|Pd6EvSIpr)} zUJ2e47WbW6Jl9yWsY-o@(qh-BRnClB;v4$wRW>o!Da=3ik@s+dBiBa;Zvn0|1-#b< zcy~=;;}Nh?`ygJc$fz)(>F$L|5}zepF4etrm(O@mrjWpzmcaIKhooSTN#&vH00t$$ z&f>@|*?Y>9Oc@kJFR?}>D9rrf63@W)f=xy1v*Pzc`4@%#lM>}F-eOgs(Rn*a(Y>IC zX`xX@{eyMyPpwg!)Nx!(G1_3Arc%kWT^ke|r4MhgUf*ggwvn-(aZRlwBfk)nc)-TG zk8AlqGR$y@*zh6!^rtB6DeR}Bq-H5{`HmxItWQx5%rf-0$ia@J~qSw(4X%vSG{5%@X<< zTjG9iO?WMmt|FSy$RFOg#OolFxiYi(2aY=*=2dQ(p}bi*UQq1ShM9@8w^!}nE_6}o z&I7(z0z2B4@YvlHk$%A0VaQXK$ouR7-`4_e+kkAVg&aQ=b~I-5#h%_iYxmCT(@a^v zcUo-jadu=ppTIY5bv<8Sv~b4l^$ULQTy%Qp2I<|Kx`jmqc;5-|?|9Ak_yAAK1!fk@ z-3O%i9I_U%z9PL>aO2_Gdrs}%Ajb%_g;zKd#!r!joEu|?cRIm_TGEH z_dfV9z3-9rz9-T9o>lLAF?-*u-TU6$-uLeJz7NtpRmS^U7jMW|9QS3lIln_a<9g}+ z|5opmY+zkIfqQ$vfyqBP1h@pX9T+Q_J(v%RPI$o0R>Qq(16SIF%8%2`i@XhLHkwQH z2=Sa@kpHvac_O1!&i*V%#>NAaejVV~eZzk~fmJ!iJYIlnp#Zza8ZpTO%oiW<>%KW; zUlyJr%oNhVxoQHt%I^cF0_>|BIHNZltXJE}Ixm3BV$NZeJA(I}IhQ+dc)n3e6y?94 zzX$dmbe`KgMK5o25P^Fmry1}t)A9z1q;O$~Ky0OH( zc#nCD&GE2oA(06@ZU3+ErWwSo_F@!CIO-I6qV*30mo{V42Ht<2Jl{KoVhX1hDHewN zh}4Tb;C>bmUba9fZ6f0YpH+*C+`ca0Y5brbe(B^KM%F(E_}(<|S6tX%CUc<0fpf+J z&Lc8h2?56?3{}E5sgylPuH!i&Bbd?kfb&Sq*=uV!yB?etQ)CWV$oEIU;o#+k2@R~j z4Wpa_xUT!0nXzGRvlCrCi7oADG%$zu@fRGlC)p!l7*3HV)@K6j@>q zu*Rzio?pssbHKb#QP8`9xAF;Z&t#$J&KzeP_>!WfT|vnSY+D|1Pq}bZ zY8H=vF8hf+f?2M7#{xZG8SuIB3Q7vFbQ$n$5n#VyBUCnldly4}Pn7Rft7EJi9&pcV z=JeiY0;{oqwEW5V{a`=8Bt`BIB6@d>sV^7QVa}8fMCOQsltX`b(vZX7eT%EZW$>$TWjt$AkmVxEm)J zy0II6e--n{T)Kg^%)wIpfYZXo)XmI*0WsMOCGR^#q!?^;0k1VEv&04Ex-~e zTrBfhZC||>bAsKRJ07y@?!Arvuy*3rxBv3kLmEO8CcNLE*UV@5dh0ts+Y1`ox4-WA z%HLR}_Wx35W&*p?r`^088KWnB5INu!xajqopR3>Xv85-m?LO@Kq?D!XfXWO(#X^I3 zX@{8$|0l4UUKbQOs;azLp`Xb>@0NPm137DERrRH(d&h_ZrsSd&}i$s_v_d7 zN&4$M^|P`0#_(TEk`q$ROt_$E(0K03mofvMhSl6G|36JomgeA;ySCw?&e=-_z8km> zGMs$C|Fz-3mJ7V$zI=PK-{w4i{jq>ILg5W-Aah9r&)o_B7vgv_7`}F`cw;?J=p5hM zxn~)!J>Y+5V7%`G?=g>hzEcy}SKWPmX2T`zzoG#La@1>iwbt@au-s~Y=g_YSJQELz zCVb$!*1)H;mhZX&cjKdtU*6q&ti)Jnz?XCX+bMzT2NHSL>Hl(A!~f5LTi#Y6Zu7TA z=Pym+RpD{qDe>oYWZ*9R|1tT@0*;#bX=Mv|9zVEse*(An0*)sS_)mQBP&io6aLVC( zO99Jvhwyv;95x4LG3oG%M)ET__T5~p_TT4^^A86>`{FK@KeroNBX}j7t>+X65E4y45@K!L4UU~lR z%`MVaO%{uKvQAdLx{|pj>#CIKZaMuY58KXPpLi(NTqn@&-RT~FxJAqYCT!HbXR0CeKPo>%_ewUu|HltUC;RBd?X8%Vm9y{k z<7xZy>iH(xx7Qu)`jE?SZd?)e{&w`+JI8ijcIOM`Sg4l8v)bq9Y^(D7vrOK3EbQJ? z^y+Nvv^8}_dSU^-0-xOED?e$w%hhf>*}P}nA|>~^g-uR=O%_Wgt}!aQVaYy|-E6Ug z8Rf}Cah4z90(X;ZVm zd~9VGSkT9;A-mxJFkh$j^{qwz_)P4iLKXeao=qg(|2;Z$RZrR zWMcZ9L<7y6QPy_14)o-|QhY8MA=lV4En~tYHl9-FqzebdWSbvN%!*c9Hjl@=r04&H zS>DT1X5>FAbQfMF_^a#IB=5+=m;Lo|f3D3+I%c|RPO+$E)DqnjC&OliH`t}k$!OD= zbh+IzC`dlJQPp0IU6-k!G%BOx%Cl^HpBCVjI1gOj+`4F_|1GM z=492}UsUk;zaamN2h1%Kcq%tMmQ+l3l-jiEP)@yTW^3d_ca42v+Syly zoI51@DS)kBv`T%GV-N$&&7_O`7v1{u92RwoR5c;;81L{y3yFl>+!&p--5~bjv)VzpX&mCd9=M~dB9yP z;w*dEViMOP!p1OtDw_gCXzr0(aYA4|d$I|LQ7Lsj--)|3lB|D@kD- z6Ax$}3+!Li7{J7{p-JSbV|R4Zfu5QzZPs2}Jt~epl)8GjNm^flQ9$64-SvQb_8Jd3 zAI2Q!6-jZBSiGc1uHY04_f!QAgNeExCPhtmvZgUFY@L&n_1bFD!FIi@3lf|;jU@Q8vW%-K_-Sz&pulT&q82E0T(h#8Wfv4okVP3(%9fz83YzVb;x4N?P>CGq0 z`F&3u$mcoXY~lXXE~+-UTf6s)kj-Wn|4UrL+fN)7lsV4eG=X`hPQ(KDH!HSeH@PsC zT{wP4POOQ0%^{gDF3hsdO3j-*jl_Q(IR4)t?Uo>C#6y|SpBB11HJqxbpRv$Ok>Ma~ zTcBoWSZMQ!6(YJR53D#l9A!W4Xk+?%OCfvBHD!K|BW7O~b}PtuCT@PfZk$rksA|(3 z{;s1f)<=Q2yy8&K`fN>Wo7aMD7Y?mkxT5j?sg->31`7<-JQBAp`gpG1!=dhD2QzER zCkE>^Es}0ceJtxf+BQuK7k+rqrM~lB5OWfPIr5OHW}}`SRLYD##P zLT|BzN1J9;tm_n0bGaOKwIg!ryS0xT+ZBTtc&{CBH`($3gyQB!Z!G&GSIge&S~a15 z)`A19(=JSyqR6tTD`$O0&5d|wX%8#c4^wVAs+TOfbc6e_hTPBI5aGP5ym~qxIPO0@ zCV1~VgHKQL_7872PWqt8A0frYJ!KbLhxH*QgALEcR`1;;&cD}sU4*3YpANU&6&qBw zw>(#R+~F=S5O#H1o$?|P1sC(O-ww%AtEBrS7S&g7dfFTK_^H&FBTbL9j`sM>OgzOf z@4ds~DCSt1t`ig48hI29605r!~F)g|bxGiuLXq7oMo*vz=m}WHdJ=pjqyV0*Cx`N4d5mJO$T3_A%V=nZfG4 zoV#IR+w%o2O$r*!9*bmhpKVB4^5_Mx@g9Dzl4>`)4cAX(KRz>EhObJUV?#keT1>v0cf=)$`nQ6dEFm{03Nab-O=Xu%u;s9| zIJD{XL(Z8k4>;~DP*1e`bdj^{roydr*B0-&dw|n=&7mi@v(rx$=Cv$|mi(Hr{iVB6 z{)f{4tSq(#t)6lZJ;mxRuE=h8_^Dp*6WX-+)p7Ri3lFKDE7%;(xRzt)kI4CJ!g}=Y zDcd$2F@6!oUpeK!-&cj7UX$6<+E>mwbAi!OV1^)n<)7PM9wzIgqG%I3z_JgJsr zXiu5Im}=2(_oLm!puH$#uH0$0k_pzGo0}DP?mqTouKmL1eXASOH?)0p+OB7@!X;|S zag8?nlg*9EdlFvTmRK`gNoucHu%G9OL+On@=YHE%PGHm6v5(c!Hm}(rBysPgVBlm39=v9q$Kw4SZE6k9 zf0B*GAKL#7dN=%3IYn!p$p!0!Kp-PM9UbAy_A&)i! z_L^R?HE&s~x<$zHOIz(>x%RI1C=a{;Hct#~OYG{Kofo}`-jTjypY9nub?3hB>?3Cx z^vh=P`W7&ocr=?ZF#DXCI*U&-PqQx z<^QQi?C2S%lWP{|2HTz}sTVVBQ8=~7;B?RC+o#WbC_H?#;KYhYjWyjKkE$nE{MdeJ z&Z^5XORs!6+{JmMpXa3W6yNT}XA4+d<1fg|7#}d_IQP)Ra-Y#T!{0|d7BXc&ka9h+ z`dH1m&5eRZ23{RA&K*B!Y`a_6>w|G@#i`I+=Vs10_3n>fOG2Z?f?eBKr2k8B*X!v> zv}eE2Rg{<@kg(1{<8Wx#ZtJDiccs|=r-d)!iQ-t@c^n<-?_Yf{&Nq#OH*7>Iw5ZJ>uPl7Zw=$z7UovzG!ph zL_^KKzBLzLGM{u3@pQRk=f)GV-^j6+qorPJp^mkNmD1ZOk8<`u_6gEYZoACEDtF+@ z%+;2Q-q^oPaSh+YeeMy*#03qBFF09Syx7jT8ctPu-?PiDm_4;ZPhf@7vCqtP6>Lrm z)Hx@JHO(xckP>HtVSk!BM8tS9UYB?pNMnnt8&s^DtA~WH!SM-an&4gcZ%& z7B2o@&r!69wTtWKkG2-pMLQ#QE!Yy!FlC2dZJ(&YP0f5b8`6Jtn+V+GF*qBM8##IE#mTIk(^Jn*h-Cb^BUEd} z&Hjj+6KndXrbhOkXi$#O-go(yXu_W41#$|uk-`%gKGi$gpL?k!`D1tSg`N#}Z!Iy6 z^yj&~B$Yw_X!~wv#+`4wHlN+^5f%BW%Wv-PYpedYi!v;=tJIIb#jxv9o5n}htrz?X zr=E&+xg%N-ov@{2YADCIlGb}!k(`Fz(H-q63&Q0p*oqIZlo+sQ+`Vp{$Zm2Wifsq$ zk^()y$VO?F3(s`w*Z)tKP&3)UH#t@R`y<99f1^8eMg5~1*%jm~Kd^Gm5I*{pF=>Z{ zu|V{xB=s7F7SSI!cZo8yFb8m+h_ju<=(~f*r{ng)rJ|GW_VCP5P@lvw%UHg?NJ)Jc z$Mmlkrp=9fa#gwQNTj2ciS(zIl35FaA1utxIQZT6R>W5&^{NK71@+Q-1&e=gjgrbe zyF%4v)ze$QYn63>wJGmd(ybfca-u<@q4mRz){Du9 zLu+bpPq&@u*J2GhI+%oCaxr`*s`)=XSKfzfG)aDCc` zMD<$?K1+A==pGd}W{wDGO}1lo4r})VeY|nRKtE#U{H@UFpzZ`@5uaB+n3N0>@?Q$Z1iXZN~ za}-YCY7f~;9n^wSUN?S? zb)0Emc7rV`L-I~w`>V*Npg$?9DRII#7<@jYrCwn1h+r)g)9`r_y&{6~K`wu)MeBtP zZACwla%SJ2ApL^$9ckzk0{|gn`b=KU>Y`a;! z?^*x1uGzd#Cj`V?>YP-3@a;o|RZ=@rCpe^+Uh`O~%YH{qjQwP;d`~3Lw!ln{fS6;< zMK9j&=U#P3josnos){)iD-7E2{bTd_z%jFw`NHJ(UkM@)O}HK&dAKZf-ly*Ocg)yR z8<W-Hi#8!NMm+NHLv`SXi|Kt$Z@3=K*xR6SNYvoF@Q!cyuC&RY+9&*Z-lu4f%)GUF}()GV1HC zXw5bdIAhVO+|iu-p*`b5fNfOM>Ti=pAF*dM%;)A<=JNilXjfaeL8HQnIR)3*>nqxh zeUtZR_+Idwz39c>a~&l<4kuJMa2DMVaZYbkKG3B1fJvL9i8;iyfHipU3U-tFDShcX zI1L-b#2T9Z2XwIQefJLJs=;p#Ejw9lq+LJE?Ir;PGZ&m+VFSWPMpV>s<_eS;K8`+!1ZZc?PoLuwWNGE~W zV8^iu2_`c_g4chr(Gg%a7GTzypj~Xn!By3e-V$p2hf9y+_a^mfgANUW8%$;f%r`GB zmb$@Y{-9}TBBP>3%@OtACIZYStNj+`8UV@741P%-rPKev?7FfZ6zgQF?dXQ~vqf4;Zx=m`x0t^%|PqJQDk0#boBu ztS8VZBJ^9~!eN`QHhLGTbr?Dn)SUikXIyaM)yk_sz@nJ(LGehlNS4XVq+>i18m1aE zI$nI7&nb5IQSIhU47Lw#wm%0k@-?TWjEuDue>yY;^*jn`QwW=FxJV+A|U9ku$&O;);LN{q zc1rY~!aLh?f1Bl;WWB{{q0qRe@xWn&`)`h)t*HGQs?Z|nWue$A8dZ@PE#k4^VH=Ob zM<1JaHlG;URA))d;Jn~D{Yt0ctOJSd5gx_Ts-`EdC$);OB=On*`tW(N{rLz-#y(dr z4k!MLKRze-|Czyjvf0hcvR-v+Xb`89OwiM(Cyk%JILssV-zCa2tx?m>{BCbblL~WO zkS&w9$(sktydw8kzKr4UW?95BzsBxSTWEP-WVlTKmy_y>A#>C<7WZ03sj{BlK$i?BDonLGoGMjFyDwn;&*r z>+O6R#p$FlW5sGtwf-lCN7&BI%1()WqxDF8gZqZnda7MJHcnjqnajrDAgj2JHZ#A< zABOde(l!e@xh6~Ka0u!9H16Tg-eY)Nw!Zq0;R)Gjo1#OilXcQgXs+I4bo#&UZkZXo z3{Ts9J~Qh^ulE?Aw`G3Kf5DO4_REFq1`{@(_tf5Na@m*J&E!ge{b7b3e$ln2 z*COj>Os_lo$8Nor*llZeb8gw)uh&yo?=`!f8(e92J#)9M`Q6gnvJ!U!PuH5?ul>EZ z>|T&@{egq`TcztPE;kC>nm+8X{%7$-QQOYs@xDq9^a&*NY2h z*IB(%@8kP&Z{_ZPR&OfS->Z7F^|svaSB^oo*6;T&o>%>RAGdwYlLP#IHlI$4=h<93 zW&OYA#<6Bz+pkx@>+Jn{GkgAzH@oxy+y2m(zF+a<>FoC(>wi37y}$0@na%(0{ya*O zxBvUq-fI8fwx6%#Rsa3|{lC8R7yAK5p$!g9Itom}FN7bbIyCatEO3|LILPVrp+RKN zga0}m2YGMl*UJ1^@Xv(nTIiQzabspQ{gvM2FK6pm^JZR`qbQ4;e7uWHX=ocyna zCpjE)hh+VK9<7)iN{WZu1(dz!cx+sBw42F2Dh^y7R9g5crIFge!fF9m#o*6Bj>uzCfDhl+OqQU;(*0obG@c29BN>g z?C5VZxxAH+EBj-;9Cj4{qZTGgoG~uO#Z>hx!8zjAA-ZSSFvoWl{Ec(b`*!E+}$`zkBp>@zLbvZ?+1Foc&)E%FV;N zSuH~2k@)((zrUSyn7^dqr07pI>3z3NHmdl|Qd5XXZ%7AhWRB>fH+ErB-itIG@veZhE9i>-h>{ex19m z9%9UAt{m>O>$ho`SM|f^C+b~e+pfkJyCqHrD2o0FO00JkH{PYl z#{aS{#fd*5Mr-Aa*=47gx2$MYXj#Fie00_74P7pW3MKdh8W?s>3v6IfWq5s&dDaBs zRGa%Mle>)`yS(f(Z;3f@VOnh6PtCpFzR|g}Ui@utGdiIV>|=Cs!{xPeR4!`j?X;L* z@@{_Arq_9w*BrIhzbd3Vi%lY8ZJ6_(`j8l2p{1-Ry;5Fg+zu+o6OXU$p{!;yXGYdX8s95?sQb%iw<+LoijBjk z3d6JZUo2lO&d#fHKHMIyE5)y|f6r@yum7yymQ;t=|JgEq=El|Rk;2`6M}!<24JI_s z_ddxiFER1_%mXFDe`NkkG3|O`Y0xa)!tuiB#lDw<^~;)xz2IX?ReM=rjjanYM)`TvWgu*vP2pr|}2!9_%ZDN*zcmk~pwpIAndfZzFM zKFNoF5^gv)st7iDUwJ5gbdz)6kqw}eBp;bdNUVuSRpb?x`6Z`{7* z-Gye}Ky+s>&^E{d*>LxhO+oRs>u5wuF&W|?pbq9HTuhb{Z;+yEeYp{?f$IDr= z>%wWz7T3<~s$}VlA6#Yr6&~sHaFD!z;<@=5#s8`=UKr1R_JNgI?ocDQfRg0aEo{a% z8(Wlv4)B~YU_UeMKu@lQGA}Or9$3T(zR9C^eiIfh6Jv1m6q@+90iBy_|~h>xX_D^`FxYOTRmolOax znF7s?SqWiVf)=tB=6Kw?w1G8TE|I&4_qCYhp+-3e#dS#!m_$-0Ft!KOXJrRW?UA_9 z&~~mymp9_C2RHxMLoXc}c{3ZFen%9r=$sJciMZkGvZf)*{lCx_w)h*fwB>`CcjR5; z*jdt(AO_bDCI=-*M=9N!h)8JN>8Z?AaiC(h$!Aj^L#fld3yJB}oe=3c2y zirPyQ^|b{KiJe<@?OUP4t^^O!`r?CDaT5c&xUa2US9(#QP1Pl(AjBd&MSXX~mI)ji z<9gpjE!JW-UXaMGqtFx0yYyCDfkS`Dg9fFYTx?n&9?XdN;m=;`EuE1pbDhKGxr>sRQ%q*;{Z3BlBYA{VCf zD}tupS@PU(Gqe0ji4`6H1leuGcb&O^prxMYhFhbF2ebTR2REaxr+w*94vM=4AGfHP zxSV&&8QC8nTx8}=OgAciEcoPevg!49leT~boVj}JbAHzF`s|p(`mpHuy#A*zjaNEd zVd8A$Vfy&OaQaN?t{=soEeb3=JD+AUMo(WLw@AtPVuq73Q**B3B)2P%tk;`mJ#aO9 zn$L8$o`YALFQRJJ12z*GM-Eo|9Rhh<7U~{p~aHJ?d9E^`uww-eH2)jaue&+GMai! z&aK$<;+<;LYp4HbR!c8oKX~uQvx+mWB`nMAyPSCKo9fH{Hn%R6fA~~cJ^$9D3#^JJ ziO(}$G#x)&J6);m$NdO~2oEO#7Q;Oc@B5frh@5I?ogh7Dw}h5~yMqAB+~5qiBnzh_ zDGrN&M1^ro6S8uaQPY~-KIi$r(i3bu1lYB69^T`7zw4m2pM+Qf;~NvF!*>p!Wxm8L z_l9MY({Hn+W=WCy1;=OiG)#E=l3n?Fvt-1H%NXPf;v9tc4 z^5eyF9~Y$Gci@mxVy^ul6B@+NCDHJFIZr@Brj#P%#R+wc7OF3&%htbJz`syUN#_CE zV+YlL;ryQp>YhK~toqKsR*m=T2fqVhI*%22pM0oyN@e5O$T;JJ%vT0(VU4C+X|=Zn zIP_GNPOGUNy^!dJcPy9LpEWGM*d?jUR2Guf##Y#cUCqk8#KJ#ZB?o4uE^{y94*zr+6 z_lIfE6=sPg9<@qke1&BumsmqSv~QVOy(L1+AtZ)>Bjb8;_M2A)6aq9C?NBcJ!M4ja zF13~Q#SPZ&iHa|7^o2F_@E&B|5m+{@w5)JK+4dD~&s5vfwpQ^AGI23QXI@|pVCd+v zXp?-{7I2|2xWr={m=`2M+5sQ+f6?`G?;0w zzfh@jr)eke&cgGm0*kM=Z`{hlWt6bCRXXhdhKw~gq68$Xx9m{#Jusnsqp3h=-=?dA zY8RM1Ga3047`PXBS_q^xI@cU)uet2vVRcX^avSg00^Y+7Jof{1-URS(DX`>GWK1xq z=i73O?~DV_nS#0iC%$`E1$Y>^0~YaV&)_+pkW%%G?@WP|5d-H=H@>9_?A(hut_7(6 zXs|e@!YC)v@O%MJivW99fLg#K-sL|8GdA$iyU_6x8#I`BML&Q}=VT(4=-bWMOGBS7uV2i^?~tg9V#pKaiB zOl;17HuKW~-V+WyH`}J4*pUCkw51@ag=Yb0K$5E8SB?$;3wWF|VEt3CPL zg!u!$%{nE(+g0GjjlBbzb5!6^Tp2P<=xU01S1 z*oX3h#j5o@-_52Zik3apPup3ZU#{6_DCrbnz&Bm5-(o_<2g&(Q*(RHciC$Na-4Hgp zu#8uorH^BA^AAby0_ol_O3F#Bg&SBIi^88@Rf?Ec(lS-cs9YnNkttw;wCb#$#wIqs zTMH^oCzuN{F$XEP1Qs!7E-_wQ9GE3)^MO@JiN*iAP*i<$FRx-=!418f$p&c+is6Y8 z!e0Hx#*&tg$}7K1PI)NssUj||y=?hJgTm`9^BD^bXDxMkQczeG&i7xlQt{$~@Qci1 z%kw8#N$&PoEN>)f&NZoPx}d^_m6gjax{Q;A9xdiGoD@@BslKwF|8RVfxnzxN%yPlW z%_{^dS{E@n8dTI*C{MkWz#HB>Rcdwqt<^EBG~^4`@HenHOb@GZPn#;{ zm|9`UeNL6dH%!Ol8f&DI&gu(l^EBg}Hn8M9;0+SuUteG#&A_^-W%}U;);HT&7BVy~ zouaDj#50$Z^W5<+oe6B$7S!DlV4tL{v$=u)gb`2QLW_Wex`3eiAjwQ?odot@4)xa) zSZ7R>-1Q;t>ja*F1fQ-;dF~C0eZji-Og3pqPvTQy?o!}S`Nm`OiQnh~^VS0!5C4C# zV*g71oeGQ(7xI5h;EhmV4Ed1u%z@`@WwTo||GopPXT({yB=Egc;J=>0Ub<~l|A8E# zkG*FO@c(+ix2i!xOMJtH#`+wd#!c=Am~SiSHC|v&tKN36(QH=3hGz?S90iuUJMhFR z@h4nbDK$}U=U4gN>Y{8?rW~(qVih#C<3dAsX*h617Wkhom4=!Bb6> zmheIcR*54_!cxh?Pl_fyDxB`2(EQzDLHFcHDg9(*gvU0B zDt@TatXlF(NJJ}WvgqVxw@c%=7Vat+k7By0l9phw#;fe}=k=9W75GJ%+z;$H>Ma}? z?r}yq>B9flHnHE$OjMZLwX1ViIOIoEtzPC*W7W^sw6|f_vdqur_m=Jw&SLHGQfRSq z3~*2g5!mRx#36gT*}K`xJ~laQm@QrJt8hSIBO|j?T&K2JO)G1_2Z0q)OJ`i)`T0=g zze~)*R}zdjMH_flO%$@KRga5RW45U}^yI69!UBa&;@ZV3%xjB^O`ZtK8!)g0Fi1~k z;Jd((;=mxXfKgGAu`*!l$#TK7k7mlBW|UE2>^tUFsbKjnQtjP@tl7**|1)sDU{JkO zf1&rkfZQE{t(P3q19TX@9eBRE^I4x3Fj~O#R+DY^sUZY+z0Ov^lB3 ztHp)2|1;0gS;-*|oD=>taONwximNc@PuTWRf!FJsU;xk2BhI?N3T(s+PR8&A**fT) zc*OohSxj1ic~T9p_ngh^4>0ggV7HS#CQ`t1xBda&^8z04vgYTRXU|E{XBz}N-bAs9v2ky#8CwUU=e>DWo`*Tv`221N>zPk*rcS$9M>QnB<88sIJH%TDfy{jaA+tyOUCnRh%2&?WVbhoh@bX5HRwqULD1?>e@WugrLA1d8Y-FCp|pBd{+La!bJ5*2l22~UzaRk zb&?WysFHQ}4_A-N6ueUWH(oGT(DAR9>PJQv2e!}vRz3k4ZU+YDfb)`#N4$6%1spAp zB)EL#WIZo%rlml)UPyuMW*P5P9Rc49{*M(Vs(hCDVZh@wK{eBn^H+k&sSklh20U(u zLFE2OjAU3^u1XEBybE_UZxe)fIxgQb#Y<-g6SryDol0 z#ZgT!TUY2Hqx%P*TV{N=7w+1eW|*0Fx}>LRof`iy2JUrl*f+}Dsdo=JwtdcNU19#f zYnCz%^EgiPEp<3!lzqlHb*E9asEEMb=emsj4I7>q@Cd9hxjLyzO(S?%qAi za91J3-QbFfVRETB%YtGdBLNov<9Rmc@(Or`zSk^Rz?xCMokjb?$$LD4mXFRIxcp?M zGV?Sh*K%Ep8IPZcY}mK!xZ~=2?xieO&*dFBFU-sK;fBDsNBYZxnYRZn(l<$-apD$} zAj@%$Je%W8v%gAAOM1L~)nnUxk5zo)!WIjQ2<*;^lM0k&+5h;7^#2X9c^6o81f0B1 z9r)w?IA^+IiJfxalap?YE_a@W#+6F{Grauj(jl9I7ry5zmi>sA`}>&x;&O&tZQtrU z`d6$lb1ZO~a=rat@J+F@(y~~geBWFD8=mDcxZX3q*!uK9!?qP`GFBD)cVBYnJ#V>%xl7T5T>ei*I z8t)jv3+CMO4^>3i@U1KS<-Q7J>QjJ#1UUDyV;5ax}PsoA&-iP;HA3pYf_&DK1<0}Wg$8#rmK4o9$z_WVitTPAZU2kVw z@&BygyS>ID_x5-v@CG{ZO<2I8`2S;zKz*|2=YP7q40;cQ93D)rVcFdC;AFuTnb+JY zrv&%|xNi&9^RHvbbIN$=>LDnuuw}{u?vocjJzbLfRN;|;$bWya zD&70TJH={gy%?dI7rE_m#+Wv^4jZWqa)H66*bm%l!`PC)Umnc!ij|F8AM z_OPVy&WcqNarnPQq42}sCXet}+cw$!nlLdxf9A|(rMYIp2Vd1!^*2TxNZz+;!uI`} zXDoePe?noRLAJ9X!$s!$UTJNkf3h|ii`b@H#8k96NCZAQ(y7?e(b=5FJyp%U=}N-J zgDkF+nU@+2B`zwtX&Jf6u_&teNogsl>3KeW#=+epEm5>GQOQNXAhWN*K(X0N#=uci zjz{qruhepXVXaFJwqan8x* z7SU{tS6^0_{L<`Dy?$q9@blZ|_2xyBU%UQySL`);xFbw5kBr$8Bz5J&w{&Z)bbrLSe#Vq}iyGK{mi%m!t(kFo`Mi^-B;Bk!RX)74GPjx2 z!DRR@__)lyBd?Fie7IAXV=4rya_|Neesmb7YM9%W zoL3y{gdQn&D9HVBU#R=&#i8|pYG(KG``j;4Q&O1-N8IGK1? zy^}fBBY(SunM+wvON-a=00VQc$)g)D&#j)tQNP*OaODF|Pn|R?Cl$#xC$uj6D{oN=lXF(hf1* zmUE7&E} ztFt}l(B}G1FRM=fRQ;~Ss_S%Stz0&{=+?^R^P8krtynxMYSqdj!9(T2>m&LO_pD`b z~*`|oyuOnPls7?>p?DSosCD=BntgEI;pXl$N21rg-0JX zKTtlqg=M2e+nlGbR=Urt5L^9R+DT|D@Akbv7Vgfgn{l|keR_Rr3L|&stBKt*nTtA~ zNoJpjx)hX&TR=gRh??V)E%;xM~GMJObX)}lWiG`>VCg%cj|DN z&ATwDWZ@2Zu^f@*))zT>PpQ84PXUw&QTt&-WZ8!KE9bl4P@u6QWqU*m~ z=7vM;I~AJiV^ynfJ1_j!641PSikDCRD!Df)7g~A}9@d%XI0@XjI=R%bk+t6U@CmUs zizcY%D2D30I8R^0ZoA$_n>RKffy2STcJ7-GT6>(DMGGqIs{>o&AOD)SbHNlnm9?v5 z8N0*zrNoX_d%DKY;kv%Vj$%|V!$%#Dd zO4Mj}YWN?_Za42B>&qj#S#<$Nltl#36?PSyF&H!Q*DO>lGf?JV&crOvR49LJ0ee+_ z?{2@cHA*}0OlTH(#*$&7SUBa&n&r0dKRVYu3!eY`NxJ>tz~l8HMR+9^dOb;&R=-3x*^HA}K%lATdb0$Z>SzVt66GfyrDj3+zUAjBF zCMj{WScM4aOz87+I{cZ>X`e=5ii**R%im@x)E}6slgL~=DSmg#fu^-(Q&(T`ey+eD z!My&TGsoXs^Er7kmOl*H(CP)0_Z@`Q%f)4#3~y?<1|;0ptb`wUOvbzU-JR~p!FM>C0R z+n{Aq@ojm%u@~2r0}C|dcwBhmg0HNbp`?1|TiE7+&c}+oi}ajVopwtPnR3AD_J;hO zh4BaHDo0HHeXT>{OfutR<>K|zr>ro##+Al#GedCF3D=@Gy8rMsd2W3D3)7mL~a;9=!3oOQ{8k41XMQ|7FKmiM;~3V)B} zRotS?XjO9fz?lRF<_yaerRg7h3^m(}kNt0CXT9gM|&@Cyc=QEXcd2Ptk^v#oL`+?(GaqTLSDIv0ao4A;>1o_KOD2wY< z9GLu{vD0F4i4woUypIYjZTDG&PbA1~nmALUiS1e^>tdM?fk&^aXwNuO;}@zBv||^G zbZ%n%$7Z1=J{z*awnt^CH5_uN+2Igo&byYioLqT6Ao16)=O?}5>s87 zl<0Zk1HaA-m0b+ZZ}=x(%Cyw;UOQQoX>Ih`EVu~#UZOK*wd zzI|~koDW=;`WmuM;6`y%sqIq~PrAUarE}m&#+8XYoO@bW9cN4vF@NyHf&a10n+xn}8U{j~OBD4!RkUTt zdewWgg+#ik9bmFMP~5vy`3Y0Z2S3}t`xa|!KU&1`Q^#1srH|L`2@}7-CSC4ZJI||{ zl<=tDbv`Z3pCTObC%$5U_3m zSB=%B=&Q>W6HKB)7{rlwcc921JyPxPMYvq;h}Yu(BS%ULs> zBo}V~|JJ!fw1AOe0t3T}o%IooqCYgb7Br;`^b~z);pN!E9?@8TFS$2+hj2_)tBhp_ z&jy1Pp8Q$?Y}XaoqZTx%X(4gDA+wO*&H%nvG$p%#qwu3*|JZ`uaZ)u9JKhok5aKtmZ z(aTao%wYcu;aNNk!WNv3i7{G+2AsW)^ZO=fS4OWhU(;w_vB)~IQN^Z7^#P;UgOkRN z_F)?jTkko{YtaIeCKHE)(^+PS zS~yhOFnthWmOIg4)Fh;BFiG@5{eIO5rU@OV)<2%At0>0N(;+3`qqxI)>J3fHsxIpk zA1#ZfTY|HM=5%Z?Y2NE3r0UtcYU3$U0j7gIU57%Zolu#6QRj??38!(xspBEjUUoK@ z`K*f;^j?2Jy!Pi*$s;~VDu@1GRN=hcb+$R8k@vxjrXODGJLDHGG1gcixu%=L*r2{s zO5hyNhxw{6d}kIg>qVG8X7SMyVk$q`FgZ}+=cxvb9Le6qvokDxAKJ{HcAM+ko33>{ zOd1VLvRxgIWf&Noo1``{+-#9!Rcgy#)1LjIrKYnv_(R(r1NQhCZZ0*RZZTHwJx*Q; z8}l!)mW?T@IcmwSlc>hd|T;CbVVpLf(~ zopik3c=d7D7L8I116Ky-#6Y&s8`#We9P4<%WMRZsfv01Euf~P& z6EZ4m9l}{xG0y8^mJ_(palmu31c&GyY3VauwY_0$Sl0iamY`QzEUXdXCt0{iZvw|0 znFaN&HX>7|H`e<)eJE|6flJfZu6^~S>(iDcpF?JTxuasId5ibMlC_}rUW_lgtPp43`Z8n9be?6Y}m(<(H#O>1twg>2fXFvIMvA1Um2iJ@K_Z{td8VT72N4{hG@?h(K zwYl)bj`_dVG#WU^weDNe;KwJ*B=?|EZin~e9U`4K8lD|)l3dp)&&MP;Psnqnu$;kK zg$3J|-MFE+s?kwsf4_mQ;sYkb4Evo;`wMuzqmNF`ifmF?kf^e4;vJu^?KR;ZmK|$! zy8a6W#jLh)Sa)G%dg|)$*-hpzH0AAd>$QwbUshbJkCO%(Y_CQ?+?rlFOcIES)%$p*iMT zO7-0ab;C{ziT%wAjT{b4>;(;M84OAi4PrYiOSl+45*}r1?i>2 zJj-@-SqI$PI+6YVfk>VUrSWV^h6fXPx6TYM^I+4ckC1s0%d>NbfK;gTwN{opQk}%GMAOL>uunkyQ=kH>g-8< zZQ&N0T=$$#p04j*WR~USATVoN+qAq_)7P=*Rj}Mlp5}A1ORp$#_MN?MWs{wbcI2&{ zTVfPpa$}P24JPS_0ST^?>R*^>GdAlgXzrKwe{r+n*d`&v8%$qka%m-W{El7~)1ACc z;QHKBr%UT*>b+S8Li>i{NLS`oqZ}p5r#nS) z(G>HAYdpF-x>nCj{`lsZp71Nbv*wN|)v43^dSzs5o;})jOFAXboRLd?&5jyJ(U6@N z%a%@>^FmYkQF82F@ldsJ1rv)cuhP>PmCOZKet*VvUoY>L8MEGwT!l`t_;rrnZs|G; za;6*&l2F-tDeS{DK9TUww0fP6>0kCm$%;AuG}<}Mbaz2zeu(8|k@s)xybhR4e)yht zpflsSbK7$so*2a+jKM`rnjH=AYjpX3u-gZ;y%v2{v!^Ykqgi1pN5qVb+7+!*0-x$1 zG$>TGJ#}C|-p#?6&}iXtuSg=JXa`$rM>AK4%73mEj9wLO`2ttkrnN<9G}SkXG(}Bl zOP`Z*z>+<`qisr@3||DJxkubt1{uTfHXDZ5#+)st^DozIX^YHg%8ua6m)IA!sVVV6 zTZhx^efwC`A2DcuV9l0j|JU1I{D3X3q9whE+q|KqZU?I(H%tA2_H2QsUq9GtCa|X( z*!WZ&TXU}|W=F}^pKNIz%o1C>xa%8QGH!hJIo@9Jp(!t_DQX8>@r3s5FKnqCEcOrB zZ!7O!K7ql|gDw9>eCqo4k9R-S3oxhbU~}weFS@}NytF~H-jwfC!FijUxTlOdA5Lmp za@sEH;1*!gS#VC=h*^h&sp7uBR^)sqqh{R&EMKmf4sC@HmZp{DD&n&d+2Sa)yv$p5&?aeO3|7)nP8G{h+ab zK1aqSuMf*UsY+BDo~-V((Ckc! z7I_YH&0p;Af6UG27Jk4Wy^yPO#i!2uObhnchktO^S>!LqyXdiG> zY>{}=>hwlA1x+WWNoo^pyCqWAHk5PkU|<$#xG~)_?39TlH%pcmTf7D9f9nsDXO&t~ z0@zY5>X$d)_z|4Ima5Ta5)mRifk9wLt&kjB_=o0}EnIvF4ayBI(H?DguCo{x7Ycj><3Z0)^Eaa&8DP=|4Eo#LmvQ(TPLk zzjoJb7Q;gEmY#ZL-!%yMDN`M^Y;H zRH-f5uit*G+&H0Lo}oD6(J^0PfreOl<+Ums_OU&&}n^(?qn&v?wKI6%SVj!;Cw z#Cwj~5@JVEy5BCF@1(Xyz@=qCKq+ijHbuj=2gmGNm!GPl;TZHcOT*6t(?xIk8hsAYG=&eT~BWUiZd=+kLANmq|KRT`Z63aI%;oz;08} zcvR3^>bXqX|F**GN5nLR>O+`CBUluJQlc5$Z*$pXSg6m9cyi3Sch1!hl5@kfj?5J3 zGK{#uA@5P}f>o+#!jc3Z>}{;5A!o%E6_R z{J11oS0=3zn!?rjYt^zIxz<&ym`l4>uG}W}L}S^(RZ|667q6P)=pJ0*~@eC_pypk`RMpxy7-MzLcQAjJsS=P&-N)gt;t@q{?vZ1HJ=X|3EwF` zV){77N-=A!X+Dzu+0Z8h+nxn?!u$)_5ZBiObE`~dAs0vo%Oq&um4qW*pvts z@msvNv-x;by58o~N#SjcYEO<7?|gg8+uru;(dG>CN&A2Qf4<&dZ~uROe!&VBy$gH)32ta$`*U$WTg3rZ zqYaIGH3}@s8V5OpHZ+OtQD8G(ageuYL$ll;1rBG8LxPhww5ZuAas{tAB)Vxst6q&F zPqM~g$x9pB%=Req6|Xof`)NbF-5*7PW{o3?LK{0|V-9k;9bgn$;Lzz|q9n3dz1i^AfD+Nl;7cmAYJ;ES1B)i>OCL4LQc~oVHBWj56&X}9Hn1`*@bsDV zz+EBb5ns=P4y%@h%on{EvnM=g4BaHerWC-z>9N7>e^~vcOU&9YQdEmqo{oJMs4wWi z#K<Mg=1U9Nm1pYvm;|jI4myUt?C?9GB)aKErk0gp8<))&1=Sk|_$q!~p3i0~YI-0e zAz|00{-CX^!eSK|COlwZH(9`FZg7BA>A|k^Hx1{pNFG!QOt@BWQvd%WOHWLQ(}h__ zx11|nTkzyLr_F{IV}nFaxrCSrA)Or_7K=9YF=S}H`p_WZuvH;59+qGD>ZCLx7;eA!p zo~{MVvI2}e0xPG7NFC$|ws=?1r!_l*&EVP!rHaPF&r#$_v}hLm!^Jd5s)g0!$4AL)C!VMqH*(f_ZkSa6CPhbl=27;8O6*~g?S3lX z7}Wz5qAVN2gx)9|xh7kfd(o?r??=TM9|NX(?yx(N62iMKw4IB7%rv2uZH75F%Z>)t zsOUz`KU;)8q$)Yiib$U?^^kSOKX>*5gI24ML(9*uX;HrQ;l7%XO0VZsSJg5BHg^V7 z(LRpD+%5$kfBOzF`z+Y1<2I42p-)L@p57|QgzASOTMnMB3`<(*pp*1wk^;*G*9_y$ zb6?4>so%ufZ1>+}$6?)fO-na(a2w=Gaw#-=l=scIUQnt!<3gkV;s?y0zOJeT4>ITU zHJtZax<+}^-A>~(((BsSeAdtsZ1XEQBsfL3Ol8xa*MeIPT+d#Xvy!o^L%gQp)Jeb9 zvu?()vnl*OQ|P9B=)KQdAsyireEOvUf{o4W_0@OG_FLAUV_n=RrE*}8i2(z{kzL7W z4KH#eN|>z^Ul|{|ZK2Sn6^C6fzG@O`X`DILfhB(CgOnNm4==O-Pgk|N$foi8#zW5I z2lxEn7$>5G*~-gQ%imi*aO|K0RJ z{{8zeo$h?@%D4Hpng5++zygL)gIwk!hK7w+LuPK&tY_!+xR?H+*T|6h{o*#qnq@{3ES{#fYnER#>f&+)*~+3YI9YU`w?z6|I5a6nRl>F`M>vwv4^ z>MFi{c$-si(R#)Z2?<8_BT35^Ixj*xd=aG5O#4= z`5`DMx`S5^K;(XtIs zOP2g+Vtx9DSNxq1u8j0ImCj9D%CuL>_o2gk zQMKqq2IEJw_ar*BhBlbqU7*M2r>~?|;-=24%f~Rm(a`Jwzs;@WPZ39NCImc`5;mEv zvW$VNBuLovp~jYjd=B*w^Pb+%cs5&gTAF9u4bL!luR~9~s_tcHO}CnsgYXRTs+Ho^ zwomq29@_nIcKt=^_+zq_S{J3RrmDs%%?@SC>Qb(Al4O5ysejqjde+;TLMz3kRwgNC zcB;BGMNMa)&@fr(2&YYh{Y@$13Wj=dvy0p<&lAi7rxj@~FMGvW>YG_Ansv3|!a_cc zj|r*=5~Z&k2sn4$+bT9`?Y&ngrBdh0@;uO})k^Gakg5}F5{zM3{B730ldt$#?rSVq zx#piZ--(;+4l%Im#2k9-uxVW!`(A( z?%Z7yy&grC2F$Tja#Cc`QDV8X@tWh2Hb$i-2lteA#H|TD<;tWaA#&`~)a{BbUS&)t zlHQzPO8?#3=x*htXd-DgEB4phw3+L)&pp%mKf!^aUf^I@p{V4NaE=&-ly6Z&0XZA@ zzWBQI;v_96W5uw5yHU~yr1++!W-=*6ElT-#aEZ>3SFBSU_MW`8LGbp5Ufm?o+f~!< zxI1ZWY8&H9&MRFnbsmbXW8^&`Ga)4W zZ6aGo@T)t^c-iW2*B{Do+Hm02(|ZwNP7#;V1$6!=dVad%`A%zoXjp&KTh2WUN;bM1 zId8uzdd9t_;Zfimt=_2DXZ-Lj@9{+}6*O4!P)`5TF@F}F zdGLgk z&#{4lCuix`W9$E%TCX)%A^#u8o2Z+A!q&QddZsk@0Q&*`zo!nYJ3EylB%ghQ7JH3@ z|D6|S)%0213qGz0yTBUDk+b|u+R~nL8t*x8HJ-YYlcZ&D>dbx5S$;yx zcWzy&4mKuCS##}}QLf%dBYi^k!W;EdjWp+P(p+w&wSJS73uBbKOYycX;#3M2+`G`rV({9mJGaHXHC88;Wl>ls7g~-)y9BY;3;S*nYF& zV+M`|3HsriP4xx1LJnNf6E!d2Y+i3{(Z1QDzTen#`ew`d##Ysv{WBK}?zn2Y-`Ga_ z|AgJG2Me!mw!Lp`_k6S6dt>|Wo9+J_JFstYcwYSI=w?Uxl7Dj=(!&^>&9^w)o4B}d zaq%~C4d3D#Z{n7|#Z5TW#L&c4*~Fv$%NG|T&*@t{=bLyf-{Q62#Cv;*a;J&U@hv{* zO?Zx3{;`5L}{N`!L=XT{gpC4rH}rqSC=@2xR)>ew1S`D^%W z(k=M6Izc-Cv%**1yP{gI>n9;!Y$CN$efc=B(90~Od3CiCb1-`{|mn9uOkodne z#aJog`_{Dl|J(LtG;n=r;AuFUeA3WUsp0;rTcSVeRZlPqIUKOq`a|_d zlj4Dcq6ZGjdmK{la1s&uZ`LMnnZBEWN8^A%%poNSCguqTu1saO^`U?H(u-OdUJd7o)5616b&vf9R>Y2)p`mgb;4=$>I{Htg3 zP7RM=kuwjloTwDpVYy4YK}MxX(T8Eh{r{Gmk5>mD|2|dTscOGIQ^YTo6%N}rSFA1H zJ<0mm;?)jI=I^{^`&(V-kWvKGVe>tz9!^Rdc5?}A+Y??Bl5Np${Zmwdfn$fm&czmq z>{8Q;k39Vm%z8Xn2M&#&Yts&}{MacY)0pi2Z@s%CZ%f>=eh1c?@60|{&;S4PKfYbH z=8&R{dFAKom(B;6GaQTD53+5j3}Sy8 z6*nBrWVZkH-sG4x(?@>?C7&k649mj#p{zWOOHVgGtT$&_Vf`qZ(ZQqOW4{6?$3h|1 zON$sBo!Nx1#0asnJ8?9L8>U^^F)`VF`o5`*npR%IY&F0oycUR)O7kn-Z$W0Az% zWZ$VLd#UNk|5F0a-J7%&6sJD8=-O?!a_6JRTr#GUT~0bOF&>#b@2FTkUrU3_q!le2 zMRq7uUv%v}+7{J0tK@}1OmzDmhYK&74o!`i@8W5t!oF^Wubo?rgU#%++}qm=pI_TM zyFCB?{>Ir;cpO`$qNnRgIti4PaPkPKWhClqt-Qv0-h5NQ1Lv~~y=R)ce{6JUZhP?j z;_~qQ^J=TVzkhiAJ#Q1Mkj3|j3H1qvlF3im|LRDOfIO;n2XyqENxm^w(mNPqTn@)O0eAfJ7&E*zL-;73<$!a~p=WTz!xqRN9kvHUmBd6|_ z3(kVRAs1aGbFW->) zuZAb~hF*)zTzmCebm7_1>#>z@uU?OD{qYVG)Tp(--!J-TEh1OW^^aA;RT{njD{a3$fk$uqV#zdyh6d)mAB_!?IT?!n4Tk3;9?NpFt$aLDNbbg? ziMB}+3@Snk>J_^?b1fqFv4FL;H?N$5*@6Eq0~6DRoB6^F919xG^B2rzC||DjqNI#{ z$KKM5%p48R?(1qObh8xsA7Eq``=4O<{be!3gDN5%+u^Dx&VEnYmQlg5zq7K^+aQ2A`}I1s2vk zUwxnd#02IOit8B~7RdjV`ml#BW<#C%gr=_@+iV5N$NJ3}+s^PP<^uppnfl)kooiw_)5%BbIeHnD(_ zRic46?SLNBpZDyXKMsD*o6w-Oqo5~tLmcPD8E&TY1lXAtG&1jrU|{Mw&%k$KA!FJE zb>W(XZr+Fn#wQl;%qj~QvkeX~=QK=Ym~gO>!lTt!s(}aYHEHO^3lUB0)Kf0kx%vh4~jmAFSoP_B@ z0!~bM3yx$+tz><&rICeW!ltQji`b+SZm>C7c-OxBoVq_-`4`ItJ=GfrSk)97{)!pY zGcfQ7Gd$Ss$jIj9ZzpuHKV*XJS*8iiA2S(PZn8KCCPlE+%}ij^JMn^TvjM|e!z&D5 ztsPAK%XcL%>Y2n6kj5=hwyWmT6vXG;2vT#0K7c%JBcx0|qILk{MmahZxJ$ z8OwKOmrA~Q<{IU|xY>8=YSs}C*X+Og_uO84R!af#Bmi?0eLIZth1eO2P5`DPoN zpn>G6Jb`*=KECVC3Mw9N*;Zb8cxVG-!rwPMC$hq}oMTW{Td?qGN84qFBgO5)i`V5G zl)jfecLQ7A8Yjj@GjEISxfJ{6&`W_}vArw?`TN)>%wy15>EWay+V>?jj&Jts|2OL! zwk_qh(9RSVxxDZ1M?rLVfJGtVm$MF?K)BEV2cnQ{U7JT?jN5a1_t&u zjSM;*N!)EKA2ZY^oOP{G78Hvzoa;1+Vd-wU=JXABwQq}b8-D$~I&tr+C_Br&dyR`2 zaw6?r{%bQXU!HtWh@+u}L*%9WglSuZwQ64`3MC%5-6qR(QsC8$Z3~*}vwt|Qjhvkx z7Juv7#&6%YTYudx$MWmY>boc2cbz=RtTE*xgF!=&_=RShNCy@H;bI2A?kJ`|3T+8b z3!5+cG_!~>+_6_Ct#RZ!{Gv#XrIA6z)q+u}JyiOc)kDp+ z`wUeI%*F9@UiyY#;jC={Ns{~h~j<^R9^=vphsm;^>n2?hbfDer4o1NQPyIr*~He9t^Bn=9+?#wyo~ z6^9qS{o(e)v2@e_11lbxc-X&?T+qjuXYDAlEXy<9pt-<=;r%XNo33L9ZCnABXWKmb+x2_{+lPi*`b??<-iwZ@l&p&Rvf}ot z)#X>RJv!drIkl7}Vu8mJwm(8G2M_Tp|MzHEthRwQ_JjKueigj}=Gr!9&u7Xp%eLfg zU@M-WQ1#4^wb6jXd`aZ0rz#y2I2<2wOcmg?PvZ1U;_MFKSX#jK^f5=^Av0ZK-l&GbIgHrUXKL*8-#o}3I-k!GBgx+PhyE;V4v}T=UfA)&4DDJCJ}!_appn~ zs{_n&3z+LZirao%)=?Z6^eB%;O0?&6^M z*ic13PtM&zzWa^xC&7A`Z-UBR3M_J)gc5|*f*mDW6%^lR`F;AxvS}mVs|Ld1&W;v zlq@{J9M&LjdWhMTfz2ghj*EiG#Ud@eOXoUtmCZkC)E8)e{d7BMB6IzJI|XK=1ZJHC zW{U*9pBuHjgJdifm|Y$)86{{hXJWT1G@3Y3=dz04ZX*^w2Lp2l7Mo(`MMB2SpOh>a zSQw0%og7%o6of1`>3lBIPn>+7=c91>0gh!4%nm28FMA;F{(-%I19O3c`EmvG@&~MO z3G%KRnd}}2X+L0%_^e>7z*7IHNVwUUC$HF|bTX%vf{@`MP45S+Jr3r@mpS4ROrwKY z;*8C%1|K>yQC4}O@PlA6D@CESjmEW?&$%f`3o_dM{LItyfaOh~h^L_7+{rq^MON+y zm}fn(eQK=maI%mUgNw-_&C~-N-zM81KEQE^K`_eLZ1% zp|FpMXwM~!;EU|;3z%mpNJg5tMqbf4xmm^hk@ft|I^Nk%mIs*AMC^Kl#HJVu#U(HY zG$?P{DCE}Y`uMWoD+NjGqL&U|ygqxmG#?POI;ioQ!Thy@n&3m$I1`P}!h%^-)Nd>J zXe){;B`Qrf(d8}?={umNZsJ&&z;&xW!M8F@GeX6l5O-VRA4?J z=q)>SKF>zxo(CS~3z&iyFliiMp48xde+$#PPr|nkxM(+u9Y4ujkl_0IfR4G7sbz^s zOt6V}f}h!eoQ98{$3r}K8++I;;M;XY@YE(<+XEavQUxdR&q8D#w zsoKbwwn@mwfn~zixUZg(^SAJpJrK4pWYKS6GE;~vVzcnwBD_q2HOPVa$VMieDax52 z-6MS?XWeNM>$aV6=#(Y(Z#tkfQ!J=1C68lS5+vD~j@(iOMJ@ zii@!>_@r03ATYcvd*kx-tOdNgzIY06<>R?(VH>O($RMHZDCQLA{(B=!&x3S#N9)U< zqpPnl*Eb2N1bt-vKh0G1p9r%7Q&xOJZgCj1fkL{QNcPrg*^=QoMaw)UT-3;(V*he; zT=6rd{A<4D2Xeg?nBy3HpPM;doaD}HlHYBZ+xsA9_SMW_LH4)u&nY0KJ8Cf>$w;+T+NkT8j?PSnJ}BX{Vq#p zp3ZEQzjakG^7j4HmHsC(%a!H56;XaOP0jUz zR&+&1k(yv*U@7BOfx=JB4aK>Y63jI_SPm(4RQGp0t%&C(0TBhhTtY+->Kg25Bs-% zvyT4C!*kO(e&!_OP4z5h2ej2i8}uJA{r_SS=IAh?daF)lWQ1=dOF-n*oj;?^1nVq| z{J)lX^g`LYCDgwXQ-u~iwax%-KEeYvgJeO{(_s*VTGr!DT zb+TJ;kn6+C;fV`W;uUy)2)nj!UlO=Q_?U^Pq>#ImugK3W-l+K7?BwKStxx0+e74HIY%w!o!}DG1Y@?$VEmHAcz@)Qv zi~bCy&zVloQquLm1_^&uu{|WJ`GduvVVkHK%cmnkqT9CkJ0!|K2-5!%GZdrZ2;p|pf7&J_<&|72kLq?>W$&YANz zXHV|A_>lRW@`7xoxu+-PU!43t^T@jI3bB`Vw_VIkn?6P1it}9lA2FOh3a7h$nHOcU z^)*NxVmza$FzZRo*~a>`OX_nk%GzGPd^qiy%axxFoOQNWat~zN#a?08O8wryzUMA$ z3{U!hw*%K3ZLe6~`Q$Qzb&c!|@4M&qo6@G#UTM60wYibMuJ%sr-OF?D-foWN%zJjS zbno5l=Jcop0f&PUD;lM$7K%45Y&7$h7+^*vbH!#?EAx!R=?n;`vc}( zF%oZLCD;@>v`*iN&pAF_;r7|y+mH8(zuJ4RdC$Fp&+qJQD)V{XZ3reRwzn1$*#+A<8)Z~%(0PQ?*P-BIV@8cxV+}xY;WV2 z`S)z0oy-bG5w@W$7$ecN@cnn06EkC8F1vH};GXO`HY^n~CszLZ?Dd+N zC+^j6JBfeyBpe#opQ%0Gzu?NwxYIvZoo-Hi9vl0reBOI^d9EwAMPmOxmfw5dIOkJg z^}E-0_1~Bmr|q+q?rG$2I>46IaGL$$v;6x^R<+Z=?U8bL$SAYtb1&nG)&p#I1|JVA z^8avPOF6(+(ZD~+UZ&51Q(}JBrhiwbK49K-=T`ZA_IEY<%1`=)Pe7hGXugyb2BK zNS={#kma!Z z1hzs0C;xYvjd2zWG(PsTp1T=#F{NH-ONfxu!49=EQ(uNAI89d24U=e*`EW$k-|1t7 z2wO^;sHnVcROeCyy{AXmr^nsvNS{~r?qvG>x__J*3!1)7y%b}}Ox8c>6TgviYCmhn8-9Khr;*h!${{NDY zam$29_6oaPcr~8QyM26T+=mUTC;EL#yv>oF6?!?~>XObi{wfnLG;xPatIj*n+`z=f zZ!FPWaEMF)Ou=D3d!E82{A)#??2Kwl?h)$t+*bcS@Su&f53A6Jhav92o1Qo=KjC?L zQvln?MyC`z%aqBvtBX?K3avlqec9D4K(gobB{@YAezuzm&PTlT&z4;Fv*#_nl5MJT zhn-)hHKqRQhd^iU-|t>Cu}=3ra-3&@!gq&##=YJPX1#M=pvK)LIX!f*kmiva`)vMi zen0a->Wc}k_3G_U^i5g95*pj2HW)~78*Ki5Emi2-zdMYa_y;5|PM2dA2>mDwe$1Klrki)B>$#m68 z1qW9S)c}P;%UK7HuVUnS%Ck-_te|tJ&k7cS7wZ*fRCXR?b7oX!X$t#s@RZN5ZwkpD z9x{r*NaR0wRX97c!%y@>{lYp2g9Deh2-yF+=)hC`agAe3e^bbsMWX9Fj_Mp+n!H=s zcfvjio;fc*WSc+n<#(98BJY3hvPQ)~F+Ywkt^qB9y=oZ`_N5(Q&S8An9p7{2q*WSo zVn;{6V-wFWCj(~f2S>U^xsy#-6tYM&1a?nZvqV+D)AQ9=UXEfNPp2m;6YD>1Rqxo- zd17Ic+paYgIeuM}E-Ovna!g^8gX(4{5!0SR`QF5($@i+07v*X8uv)RG$Z;Qc6FYiJ z^{hkv%$#M$^EuCEh;UGhDB@&8JD{k(t_pm8Xfl650vrE@Gt6lQvUAV5Ff06M;GOr2 zvHs)+FJ|2Z2Lji5HS)GF==4fDvhMp8$|<9xeLv+O*STL;g>O7y4*D7tp8g<|x%KOc znzyUt|8qs?OR}%k{4M11swzTQrd1(6@(h;<_mrhGjJ3Nn8J9OqnzA(KC{w>{w%0z- z$aBY@uhug-n8MtB>&mLKts!@O?{z#E-K*O+a~G$T48u){+t=@H(`MMBn^Sx}`qrkh z9Y=K66{LY~&EuiaD`i;bFw_+7iZ2CK&;+|WU0CkF$YZir?#YcWyOstl_L}-)%gZaP zBi4t#ow0TG^@Y*gb+X>y(r(l@r(Rwq`qSjit?dQI^J=BGue;k;{QlM5+2!l+@2LIH zCiOk`Yo4Q+QpAA+iWd&s->VOc z)H=fG?8Yh1;Vtoh&)NyuPFw!3@m|um_i^xB?!uOWET*~|K|PLJ{x24`evsOydu2|s zOyz=k9L&5&E*AZtGC_co$x=hWok{Eo6E~CM38q*Bg)5EhvKk_f>}5ARV9fLVs`9AP zSJIq`=UVR#Mjq9U?T0vI3|bPn+5RarRh_BdnI7J~W2MER7_DpS7mD7#+&-y^?c=gX z71|qBV!RIhEmocKMbxsXKx2-`v?=lHTAB)6z8w)_*ISn&H$8u!>yyd0ikbp_wjb4E zCYwL2c+wPC)R{a(U@nXL0)M5J$1}b@jR=`l_bYPA>?GM~x0cRGTlI3;oU&aj6X&&Q zy;`wk+OB%h70Z^bdbMiJv6JDeHyrz~rM;}{Y}V_wo2=SiuiG>2)UEXg*tFklI3kw) zX5$I9)jB?ETQ1~mKJ8XLck>mm-D_CIGA68Ly%1Hc!)@9k&B)(bWv%1LETVW|7n6X% zfx2g#+yWRPFFau3HJM<@Q23FJyFrA{;(`OSpGbpl%U>pLhk6!no;wGm*jy4A4{>h3 z#>C2W=R(5)P96bA?yaGFq)xD@7!(``@^ElEp)~v28-`RC8v{Y+1n!j6vrVNL1dYrC z*cinl3XIRqj%+x>#3Y>4z`|hRy(L2>Lcx*A(^}DiMX=| znreK;;Q+rhe~ioJwEFqBk++yP%-yn2`iSoLzB z;;+47?6_Ruz|doEC&^%`VEaLet$6kJr)`V@F6Zq<6d1SvY08VZU$$Ge;Z^bZgah~a z`u|$KsgL}?aFy9xj)lSaPT%f0u@4I>nN3{Y{QS_x8gS%eF+(on=Wf<|19o1%bVb{* zXT2R=1YHCgS+*@+c`e}8$soQ1zZfDel>GF6d2GVZQ_FG>|25lUzT6w}%Y0|{bn))Rw zvYjWKV;Gt4e^ho;_`twcA;dH<RLS*j7*c`9_?q^TQto~p|;===J) zFbkYeWieQAI_}q}=?8e08MNQ@R=xR2Nv%hLDRqHQ8qGUBF%wmCSz)jodTqd#6&DwKOxB7$wPoey<%*TJ?s$f3U0EHmIqUAJt*fuE zPdMBq`+94Z+P{xA@~byHqY8J9>Kii`Z$Qnd>Bcb`(6^*CDJO zS99?LS6iE;ai31*;>V}e4E3+wxo+rlPQbQ#56`YmFE7`xINBxWVOe_j+B(%qYQCFF zo?opg+`NT1vi$hXJ;~hiem0Npb*lMfT;bjQy?ai&7|5<;T?3%0>o^@qWO!3|o zi$aVJNk}+bw@N&Ym%OVnq2FRws!TuI(ugHdc26Z#CV3m(ToUJ}wNkm#>zak~lt4#K z6_>Cy&LzF+zLL-SvuZ0526KV_o6|!>w?X66kp0w=c z(wSMGmWa<_UHHH1$#iogO;vY?T{E>NZa$QiwR+XL673Bki*BW@t5>|OHv4o_NzR7N zPPYR$`}eJUGwW*BPVLp_y`lpX>v_v0jw-h6IB_sZ@0jp>*PEoLOss)Em%K!z94;(y z(DN{8)L68Fk&(^dKtlu9bf2USCPs6Mmjcsbgj{(gHYf7^g*fUM*{-~ zZ>9K3XQghHO>JxD_Ix^`HQ8ov$&{mK=DBXT`K>7b#nU&jzV{8c7oXDVKl7>DK)#-> zZ zMZe$kwQ}X_WN*Lwfl~sBd^g4MxiYN-JT~JnPxf3 zJ+o@xy|l94FPkT4!KVq!9k{BVlV{|f!~zs=*wv=(l@&K)c=3IR^P#z)7T(5fuSHk=f?eaTMfKL_>#D)8~Bqpc4(AvI0&p@sQ+HGqlM+thfL$ek}m>g z91@f2uP9&gxVmJ!we+u?ewCgFY{lZor#uPhR|rW|Xf^i`T{K}r=aUmM`R&J@X1%MJ z!t+q3a{BJ}jt|k=83LbWT3Af~7>GG1FtUq;9tqnuNp*_V5s}aZ3)nOrxF_yrZhK^I z@Le{i=8xnZwS63#T#oe$T-zob5l~@ho~ZbMuQP(>M_KCPoXpqC=TC{PSus;9PbfP& z)Ju5V8p$VwCYNzMf8BJbW zBqEXjP&i_Cu_LFCz$xAp2LvYnP?Qk}VC0?iVXDO07w!fD%$6!jVsR$1{L~Qkul#r!nFFkL`tg@S(720i4I{U+%EOWuA3!CJ& zgv(!KU>EwbKG{#HYGZ&iua<#b@2~o=>t!m#MeG#26df2?RU>YzMlEFH`7m{QV2%6( z5jT5%slFQ171GDA9JqOI)xpKxFII9hW^VQ0^r7NHByWZhhqZkY+k|b-Pu_Q_-}d;w z&ur_w*(cR}S9{#Nv2E+d)Lf;{%%?2weOPoLI#G@FrH61|S>w{)wWoD71$nP9@U<;y zsppb#Xm`D1D%du|!Sy%SjZd?}m9nG{DgOFuCVoPJYwZRXkrFBU#;yLF8V|>(c6!>$ zJtz{K{$P3otIkn-g##_!Yu1)b{(V5YNT5F=dM&fX4v`j&t5m zl+0an?>xSLL*W3k>4Nj@LJ}T3COo*?tm?DhS>jo0EJNd&ZAn5|4b3x-%cL1rFSMVq z#-Kw|X7}}#lK&5LOfzU#c6~TCv*Iwf-IL--o0I1+l%3uCc5z9owCByEGk4Bj{Bc3H z&Eq@!cI;F=Qe5AptnZsKgX*WY+BJ> z`@+t>{}a>2_-UmBul>9Rrn0lyEE)+3Uq5MPf0>acw!Pwz#1g$iPTwt3c4`N=Of7j| zA)TA4d|maVS>fEvi<5Hq-@J60S8uU%v3y$m?6dl3lo$Qau$FAE|9z+983)V12ZuA< z{JWO(Ij(R&`~H)b2vc~&9r-d_mhUe=#r>c0No84wK_vgVO{Nzdnk~A#y?q>53^E#p znc16LCrR(x^R`d6bbF3aHpk8L|JlB8T)6r{|8nVr`t4^tJ$OEQ9{+u}ov)-Tlh4$( zd-krQtUI^29?M(w+VXjC*v@)ahP?MLR1LYd=p5Yl+xKfv(zlw+kK;TA8wzw*F(l7^ zuxpKczsI;)$hFC%@M{%Gq6ZaQWSB9iRDr z2p&?uV_Lp*BXfRwvH$NocQkihoO<^2gp%Fwy{13jZfKu$&c5!G;`RlP9?mV{?X6$H zcCU2Tmk{bhS%y&6duuQcb#Q zhVh-|FFNfc4hb~v5fF;_|Lf|0yKk(EYG0@C|GoaY$N%sD8S6gqFZm^R`~N;2um3AK>HnYl_}^-b4fTJv*ZzNA`;JI} z@@X_EE^OeAXkhDTkS(m|iD+cEs1wv^knm`fkf>MkXpsKVAhsi4*@1D{lB)0q2AKrc zhvL;H9kyA34AJMhiyg2Pr*U#cRc?W^Iq2Szf$Yy<~}Tp2hNF-SDc`^xO&VF})Mh)zXVM zh353SSI#pR(lpPkZOePm5p`^Zqq}7+tvFK-c6lZ?S{-SY# zVMWU8j%3ENdX6>)?;sU+tp*>qx20FX=h=BHD0? zjunhIrcPOOp`L4g0%z|7W>o>EVs(Ml zQy4uyuuuEIxz=FXv<;l~24>eLhdxyaO==B|KC0vVGuUCPYQR^ue_Q<58>ys|@JxCllRUNxC^Nbs|KSYBXKmE}6z@81PJGX7A6CZ{iAdkyGYL z%4#PtiC$n1JP;XWU{>FLUAoBBz%ukwfMWw+Ot&A6UGEEa^suwK?=J~+T zXvB0~jLF%J(cD#?;S{6rp*H1?_H2vBXX5j#D+K#0Iy!GOPP7mzJJG-wwQyF&!txz; zLR<@tEf&gJE#jHg!GCI@nBqbWE}>~Z7Al+)Y+cbN&efpJ)x?s~K4HcpxnK2sie4Xf zF8mz17v5xV!?!2 z1%<|f2~x}AxJ1v)SeCO(Hs;o{f?r}EgO`aIF6T&JRwlLF>*Bw6pW7g_jT@t3d zR`2_@`oOA{d!^PKiCS}vOW1hAnp3x!bQc6}clDUMZP{_FwO3ZHI<;%f*;#?7I(jR@ z*53QIwoa5u;lSjQi86u(>z-#ZzM8d0H-V{2qv-0hM2F`yzD}9>lrv<8@T4_WlRm#% zddW&sbEi#@m;B`VUowBWgq#{!BNSL1!0nnc=@RbfG4ryf!2EFMh=j8+raaFzQ#X(q>?izL0e( zk;#UeX^-ZLg)>_fCT!jqxFvGO7TeR4c}j)U92PjwmS|So>aki%v3^3U|8F6q2DZEj zY)J~t%Y?V`lx}rMWRgi>VBfHfNr8bafPtBT;ad4dc}K>i1>C15Y_FQlbJT%*!UYzd z3DX)3)ubI*w=dv16Tltjz<66S^m^oa9qE}4tV}iyoM$F%Khwa;vw=zZ0Hf^$p6b`^ zItE+U?qc%Yz#107yeE^%sGxpVfWo%+EGE+g)(8hy=P(7u3(PSKHtgwU4P#(+S75gD z2Avr07{JVzEpK#z*(ree&^2*|4J^?OthNQabvH8E2(V{v*lX$_Xmo(t>Hh&{Q!1|_ppGm+mZU+X& z2^+-~8OsWIKQ7>VC&2$xoB!hjzOM;fIThY5O?CqXaV+w1-t4u zMl;88vsLrRnXK8Hc7b)v9o7Vf!+8m8?g5MB3RyBAuz7ztSu4oon7~$;z-n~hblyhB zFo#1`Jio{KX@$f4Buk~ z-t!;MT?^p*!EpS|0`B#DRI4@4k=+YU`Z-Cb8?P^->M@A6_CP@W#h6WbS z1^d_-7zFDNtoeNV*hdBaOAZ_n3~bxiaGaTN^4$mC427d=2@5Q~&KI%X(ieE`Tjw?D z0?B%r6U{LvS_3z!Yj1Wqd@Y@KiueK<((8tS_ zc<1q|sRE2!Y*))%^1ZwG>}F@Nt>^#iZgKp3_d@Kc>$U=F30tJ@ZkA6xsrXjFW;bKp zo4fa8&%Wmse6)+vO;^C-cm2J~Z!hXOUJ58+WU+c+l)$<_hgC%P{@=d>k=a|^A8d8M z@W7k<-V@tE)iK&Au;n|j>Kxee^z4I2bMGrE-aq~J z(fBi-F)k=K;(+4=hw=7y8HOq-+Nkh=dbN$6c1ot-ot8Cz*z5- z{m?B@?*D-`!rBbn4_Md>)`grEe5G^zOad#v10$OjV@3k+y90br3b+~f2n6h7ZE4_K z_<(bY07t-rCqd57A{;oTJm8$iz*$znA|$}nCV2gi&-Ld2g^W%gIMx+#EqCBJ=qn-4 zz!WR+yx5Pk?gC4|zw49on36tl%v-?O;=o$Zzky}dgBM)~j-@_Z94)APQz<=oii}VAwUmtj99N?YZdsN(kG2j9F6alV<4V+Q2*UoG2uDtMK zVFRm30n0Q#&b)@_?|d28@o~<(@TRMQ%~T+jUy;#$0{e^yoHGR2HSe*n-@rACfh{-h z9vj#5*Y$mzT?uTv-wKF4U|Y3-Yl8qgj{upH24p7wvvN7?_E zB;%N+^jU;1Gn>5s_%ZjS(}GX#2bf*_HaiM@UZ}R)_X3N1{RRQk4IhnT1r^@1t-Ev6 z{Q^sX!aiO>ri*tzJ1Kk)KF#EPpM~cfvo`~))p>!Ffy|EUKg$09YV_~E`+~gz3WqoC z-kYItM&SUHt`(U z3VoK~+sxkUzcM#IcG|$GZ~w{m!EIi}Z@veZjV65TI{#fJ{?F>&e>(2}TpasT+MhM; z0b7y-tJHt{Z{7!(H@p`Xzs+Rq@XsrOndJksNdW`1!PQTz1>@uR&p5niW~^s2YT*35 zhVOjBJEp*!3=2DFnwS50wej_`o*;fsoqvC$1uwdEC^PNdxH|m(ovleN5iTs1j6GW` zKCW3fQNfT!vHaJUoy{(cNk@1CoNRxGZJ1}3cS`Q+0tO)!R=F9Armj}ck1?`N__1?J z*x@72vy9&S*wt3@d|OIa)rlI5;1>}S>Jty=aTuLRF!K1dgLP6NSG>8_vnwkvKQYi@ zRh%%TFu9$7{_gxUHoBP-Vl5L={9g+)Irk{om0q)9&xxT*mB0> z!k+-9S&4V<$nFta#c5Rk>;H|ws)Vv6;l{@XyOh88h|Lg~dDdvg(@XMe>ep=(?K!?S zLh4HW%C+(SXPex4SKioL&n_Rg=h@1bM>7tt*?nx9MZ&Jv?-E^mW?Ou{yWn7vOMaHH z(X;}`!*+lACN#EuJo1^};nyP%XCAGJAL&|~og&Wj88*FW>^6Dfy|`O7<$-^P$%_q( z+Ekx)Kep3p>9}AeHthxz@2_{C1za{~bv-_GMq|-pTgm!mNe?;LzFP;_N*-iz+$`Xd zvVr|yRt8g#$s1#3dodo9ZdT)0%?73d3p$$lG@@!wv?y$xuyLQr(G5x*oZT&#tp!?q zn7dShjgy_FOCK|GX}(Pk<`BF0=6sjjRiEVl&Ze&yJM+l(bZ_9azLW9joKfTfrK1Wb zxS4pZHmrEaVUSjTr2nY7@|uP21`!QRuA=6)rsp`OM@(^a6|~nCxsYYr?{t^${!bIklGg9OjDO zUc%g?JmraFkFz+d^0ju2SwX($d9hb|RhOxnC4BVBSlBAb6XsOk$yB#Mkx_*~!yG~`Z&aXw;NrNqV9m7=5PId4*koV&2F zk3`mzNq%0Lteh(n*yPKuII^{@bjVVg+2dDdC0BdmW6S?`-WH~XY(7_39+xfGIre;( z`<ofJzq6dy_z18V?_lJ5ZZ%&*Wa;8J4vAiOg_~DzDA;eg(D8q?SG8dC zfdkA6jtgVIX-(qHDp-=vR{7ot;Q7C?(Wi4E=QBfv z_L>!vVk%tRNe2$Ckl|p{JF=p4LXe`EPUbO}QpQ&9ko!`?Q`-{PZRB>Ja8GTi!g z1QxOX6B-zW9<(s?C@={sUpt~zwy{Ukjcv+;9;rmES)QSheeHwl9FPlXTYW=fej48?1r3T2a>jn{&=KtptH^9d9yMDbj8a&m82b ztvD=oJtNNcu4DV$R#&FN9d5@fimqln*;0IQMPr)7gq|(e9!saI@;DYO-yO=vD7me$ z-F!+QPwIkD*&iKl|Al9T+LV>NTk_80rNxtnhNUXXDV;o*rpSoapUs^1&FaqQc~yt5 zA1gdr7-{=}k-v$dm0{t-z+Ie)QlB=@m6KV>{X2?@>+z9QBDLNejvR;9FWcfcf6=O` z(FNi~{SB?WOBL6b<~$H%YQ3ZSC$o7umyq;3=2m4EM~=W*LL%Rscm8~{Du9%%_xR-G$ncP=vBk#TfUjy}@;m zD>!Ou=5gVpR*XJur&WUL&or}X<*a7gy!Uad&e0|*j)O;?F1+uGd-F)HWd~>elBF^) zEj+DvIqqhB=O{4kLaS!(L-)yR8Wn#3cW+hruweT=+f4`j-aJw;adeB(c;C|;P`^M& z_8{-$vqqB3udvAaoo~vkP|M-0InMv8ZOTilRISb=AV!nBOO_Z4jhtY$B z+_M9iCFf}K9elgKGXBj|KG|TlF0oHFi&m#z%Ziw#_QPSBssJP7od!0h4h9B3HN&pR z9?6J`18jN@j9di|C8sI3>zkCZ^jtYCv!R|NS@^|)o}vxT@+UKzPG8v8Xj9QE`|Lxf zEYm`+s#VHzmtHt>@Vzo_UDm>sx1-IbL70n;#Z<}HLDJIip`NGhVZC1p#{}1HYg)5& zQF6;kSNTUDI_}+Fy7WC~BhMnotBDPcZ0}dIS{fwr{@2JkVCAqkI*R8Ik70$A{Ph4< zyFCx<+4OS0txTPC(2R%4vi{=)iM{LEO;oDZpSt^0=1hZ&%%2I|_iet3IqwnYC^^V~ z+;%a)?}uECqDOqiPZ)y~H#nVUb?>yeaZprq?Q`>@@MZNE6zAR!XsK(Oz@B->Res6} z#w8ljH&3-3;w!n(Vq!KsLddRA%O)p$sqWry^)uABdeq11R*3ertr1se@mhRHRn_y; zn^O!dW#(b?E)=*%er4y$d~)!9_h!aVwL96Db3NO)EufiOgq`K*?dvl(bUTRUBz$ya zaPIqO;VCZlw<9s!p?a=1f^(9S*Sj)^N)*PSVq2QppbVEj^YaR#p z1ol1e|E4k>y*<~)-0krGqCM+nr>{I-n$-Bn{6h1E?uQ3by^Ks2>F;*#)~ed`=HWhp z8~d#{&Htv&cOrX_?PYs5FOjX?!om|4m~@!!{%vj4rGK`Aro zU+0_?nMJ$*?{%KKSee*HXAIotXd>)+4SF<+u6G58J8Lxr`g#D zjL#p7*q+|yX~byY$^27WSnk2Wi8UR(37&fy7FkG47I?vE_@GJk#1h^Y{QO)@noCZs z+|qa3c~aa&p;$%XBO9AOdmQ9EIsbFS-VFj1Gdhg~Pfv87GeJQ^bwQ87&0m|9j_~mX zi@&RX&?mP-%w*Mmj)!uFD;VP#=YQL9px&bCsrKgYhd2JaWau}Of7j`~^*c72tPoMT zJ^i5dRPF`qECN(nwk);?5Z6@UpYPMGd&2GL4ui8M){=tF8oQ@!?q27~bptIS*VquU4!%-&fioOaB z_VZoM@iucfK2Du7g^|6sK`Maz&;i~_+}foE?2+8v+y%@BZ?)+%bpI1*nzE*mcLGD$ zWCr1YmS%(YB#-0K0z0a9ux|as&dI=>DzoPQf}E!G4^sIPWJFx+1KDo0o%_kAEpc+~ zYW7r%rkp(u$^mSrCE0xnPP_bK|GRL?;Un@+kNG4sLYy9NQg>RL5v+S~%5FAJ@t;}R z@0x8M?GiZ8Y$GhtRD07$gQ-z5}{S|Yc=qW@FsyB5G`dk!^3tYHPq?x&p>1TgVh3qSJO(k=>Pn(fUDPbJv8>$BaKYqOCom%OyhYR=SmHu={*i`CniG({+zE zdi5Ey?o%2y0ynsHGDqCdDfVFV*vSyjVCh@IAN_W&>ECwl4NP0SbaYO{oISd;w}@Rw zOc zLwWS8Z#0+)>v!9;CN1#k+QhqK-g7?H?VH(|NUcHX5hjL zkDLDIb|&25s%HvUUE3h4`re?isWr02^rY1;W#KYekz>Is!YX@4x@~Cik8V8JU|{?s=I<-xQo;oMR|IL)ms_>EveLDYJV8?q804s^cfmb6~;O zzJ;B4m_{f#ugjK#%5u$N#m~M)Hag{&g_b|lz{a%6?a+A znw#uktx|fvTw?eCJFb`N4;_@b6T|fIEt}qqIToKgc8uz= zf}=}M$4TeTD?0IfrW||!#P(yn+@E=Q;x+oJUo!gNV9TDsR%5hQ>coM)HH)%VikccS z3tp4Ea>zsU(uwA`B0-GIIt!XUDed{!kYpJ5N?&>NM_RPSz_0<2ONy%C6AQE5PHboYEfW1U(Q8lM6N}F2td{e2 z7c^Nnhp~3~de8IqxhCdNHGfNu@a&_Jfnt91jxK)cCc5Ble5hVjSl?u^8x2e!Oc*&B zxPLH6E6h+|ny#3t57e&0QPcLNYxt7tV zCoJI5oN<9YSDBsJzCc|6rJ8MKk4NU?8_QENGVA}}y?AdWZ_hE#-q{D)FP(WAY!M$n zqoaANfmTD4pWfPhxjPs6BLCx`TQVRy=6V2-uqQHKhuAn#lN*|Zg6cjt5v97@+q-rB9bRxR|_)z7Zfp>S^6!J zF=WyDz*+O_9mPv@6u$lriu>&`<>Itge#c9E-ZLp~5ErOW6k61w%bEReLSvX`oZ&io z`{~|%%}v6-25DA4*UFY8w=b#Z<-Ylk?^d6^#jaer)5*^3KJEW~)8xlnPR@5+dP3h$ z<-U24^EvZmSo=eLKfn7~Uw8!{>p5fvu1z_s;CSmy%8a}3xa(iO`|tlR?*Y&Id8X%r z?eagWMSZ=uq)C%OeL*8LhpF^}_hK8~2koAr_TeLY!cX@-d?(hfDGX=}J;367pd~co zFl+v^@Zt{{Y@MAVT-kQ*`5)TyHyq>Poy8K{cu~{6HKZ+QMWs(cYo5jhvyk4Srkpl+ zFC=M8i>-ft?jc*Y!0(Oq^1ripuwJZ{ytK7%;jNDgepE_+XjZ)6VDf`4BAG3s<5y5e zz@4KkcXfAmyUpqOHmjGd`{$M0t4^`bKF;DlLsGRi&cOZUD_bVxIgvYl4WVAAnWW9@F%3#fnTd0y~8 zQ{^@Hqu-ckdYU9{O5b`zChKaTfBqYxYx>Kw!|v}1|G&zic_Bx!$O)g2SDRjQ1lC@S zPdI&VvBP->nHr&Q1{LnP*FLXr%;%4M(JXIn5d1G-p>ji0%qazT_SC-n$(p-_>(1_Z z$If9;xZ%r$z{PEjE$pcqemwv1@UU`b{T-c5j*pM*g}omwh-7-C#I2ooiDhSoQpXI7 zu#^c0UR-kNHP75J!=RGIcTSgH`KOi&qo=2)S^I9d(ebR~Lc99K9g&fn)k8WIQ#dk{ zTDv6-GcFxrysX^fuUL2~g0Xtz$s@{QvUvv%1)gwUYP9BvpzxEI_F|bQ%uEU|`|t{@ zRn(VsI=iyP{hw@jxx{miG0Vfo#>GMFa()(ZFr_>b;7s~f6vA1y?&#{{J>0CPbe60P z7U!SL$tj{?BXDR<+@8ET6^kVePq$g;m1(Teh`Y1EvHhri!$vXvduIN5B8HQX>%|#y zsdZVmRIW9(_7G3GY^U_ra=V34*q_N0+DdG)6ZGmu%oUvj_gp)ZWiw?$w~Y0#8wVMf z`E5S*H*$Jy5OXn?@}e@&Yi^`(rW2Wpu+d;W_@6!!Mt7MP(hi>(=rZrKOCl%Vsrg zVDS@u$T~SX?(E6So@pknGqSvQT%4Hfz4zCY+K3B{$w8k^h73TBb>7IbOLIt#{jfdThnZqt|=tS=k~J z+hwEI`}Df9Zb^CAwx=XSkL5!NgG$1lEg4H@UUSU~JjozuY2&l=pwRgflW*to6|VHL zeOKY^(RS?OM-G)KA3mHfI(t;P-!#eQfP=!b9g}A(NtRuAZohx##62$QdI!a`H(sv& z&d-|QqTGM0kz0(Z``wG<^PPou81-5I3fSbt!FMhrtVP^$hgGylR0eMwU*3&}?TV9L zs!8nGsLEofcSu9I_wbYU#eFuvI8v_hUs%*{^={4~hIOVkpHimG&t1UP@JVCy5w@nz zXLGFMPp$Y-`KBa!jen=rH;38z$5z&xToym+aMUA!N7nw||BfQV)eHC&<0K^bSVe_% z9&n@?@T@UXV%E6g%JG4FX6*q3ZAPmUcZ+rHR_JAP`Y9e@3a?5y(Ail3 zK~?QV;u(!;`{!=S)>%-+!M(vi$X4@NoXD1*{Uw)#dwI^J{-1H$V^OQY0v#PK-r1gO zRn(3xjnvVwO>E?t9-ysgz{v`^pR)1YX)oB3jyzS$`@*wW}|* z-rCl5b8G#wvi~a%n$@`Q8E2>~i`-x;s0%+RbyvgvpYgr^CX;ny8=YmBRx|`S2((0< zVD>usgNNgnLEp6_&Vn`$%LOJ(od2OwbIz#+cg!C(tbBK30sD_9jLkQmt67}voT8_B z%436p*J8c3y7>jo%u6pdPbqoWU+JiRfbFqs%tnU;&rr% zC-47s%ZMxB->6>tYjm3?%(J_&Ef+Mp;(}T83#|>yqGhGYn0q?Dy`n0`-JoK3Ip~12C1Asqry`*M>X4rdb=;5 zdvetTjxw)KTmN~g(CH69Shjs1` zo5eOQFtO1xN}1n@VXC63Mw`{T$Gw}*dazA7;Qo1!VEc(r3}P|G33uzK-8>`ep}_Z{ zW}=qd^odeC7BBw)?u0Y%p;Jt98p;Ph7RXdTVd+#?y&=6b!qw9H!KMEI3r|``6JPz^ zTaJnE1(Y0bcuVbDvBTe7QF-^Y{|VdQu`?AIKi#|S-ihU5TU(BLeN4-nktq1s{g!sp zNAAj5QXE?(j5`({;oX00xpUqlHoxuD`wL$%u~r%g8Bdwe%iu0jAX|L0^XPe&;Hf^s ziAI8kGTi=o7G}#IT@_v3wtzkE56`n0Rkn|I^)77Dp3R}RKfT*=ZUy_PUysje-8A<3 zmoxe0-L2~VVaA|8rwg2?q7dKDkMUHw`9#_@h*iH z^>vPY46i>aPJA8M#97;&n6k5S--Wcc`G?PZZCJpzx}l)v1+Q@Sn#(e~epG2}GwhSy zA7Oj!{Y%8y~XtlLb3{zQ`|hb82lfabe3a{C{Z!gGkR2{z#XI#70S%Q&DfDqJPd>ec_J8 z!a%756Q-N8oM8z_YG@Fc;TSf9G3LoBAD;(5{$5M`*&^z5L~KHgfJcD4$1TaqQ(`KU z{x3Kk_e9G7f``nDfP@eA@e!KWj(>F7?Q_H>z;VUN*}E^yUJ@uJG2{O%g|l93pR%rc z5ihvQh5t>f#0qa#uj$LzxWvu5xW|iK5`tRovrrsm#x5#tHgau#dR_+z}FDoK;f<>uQs`71&nqob#x@Q!t zT*yTRrN4?@JUfmCwm66hP3L@YGH%B~@rCo9BMu1NX%N1{!0U04VatKK(%|~UyRttW zE!PA$?ew%UV4iv8aFecNpny~7%|!-z&iq$g?i@Ydww8f)Mq}}YW=o#tcWWAW7#8cU zinGyh?iZ2EmzgJ-aL;P0v(b^pXDgiR`wQLUT%FtREP7Phth9#xdFj08qU3inMt{gzcK;-HXDqiBw6aF7ghS!>$Pi|ZW|SoX=;ncF#;f?s^_ouYbi?TJoA`h}N>QnT%o;%i@CqHYqF(D@vVyF_8c5 z(G9IRT&a&sQ zvBn{;9S1F@CAZ`>h#EBaha}s?I4f|7xE)zscqZB5^woAc5jc2Srr!qCfy!ZF0&D$h@c|*hT z2buFGc&$@?)?un}IHZ2*rH?F^IJ2Y-CRM3E;${?->vi3@;kSiyk}L;JkZ>J z$F(|hdgw2g#a6F=22MQS>k|L)d{Uc0J`)dj#x0K%E2RY{M&F#XVBaabkAZ7XWisq- zkyxcBvr#QsG%T-CYh98H|BkK&V%J<76-^^r-p6Rrv= z2Q9DUWvjZ%5@;}CG1n`8S?_fzua8Y#=^5}pds|}GzX=RloClap4lsGdZi#DSd2-O~ zO0%5}^R);z?j4RsCCt`6&8BM(30`3^e&KA}!`y7^QYh1?X5(zW;;?0ovk}W7@xTU& z7-!oDhfS9>TB}{oe{s0j;INq-d%dj6VRMr;rY%hZ9~w*^INR1R%dT0|yx_7^%T?)~ zjD$VP+$|0=3WrUvJTf<6R`EDhCeiMFAmb5-qu~qYQ@aknIO(7e;bBw3WR%*%+0dw; z(`+^2u<0Eq)u&8HCpD{Gh(aE-I=mo@dGOVlHr8w#C`E*!Lf(s){H7EisK zn9~UcjxP+;+s-Sy$cwQ$YPdL`UA0y}Maa$OfYym-n-~8N8#qYS`YxKiER#RtV8Eh- zVkda~9|$rX(~P;6dD_%bJcm`P#8H?>?eY}{5eKf!Ea8PlVyiAVCiFfCVz`|u!YY12 zPvXwI0E=r571AkB65?*X<~_!}UFFi2qE?^!SuFf-^n#Y@y$hQ7cOiesi336^>jf4u zipyjlo8ToSb4=zEXXedmYh$0?QSjmqXbdY+7n>8DdgVZv-;w2sGDl1qC30AEoU)F~ z2<$uZzT>Gv_0Rqz4PyU#mh$rzM6`APT%@01_+Esw@g>)WpMi{TZk_yj>D@0c!Lt`` zom#M7azg!wjVf;x&Yjp5sV;GW@x+pBzB(!X?*{zaUwhBcU-D%G`=Nfe(*Mg}_33f; zFmSju2rr*e7dprB0|UPT%Q7KX-he_;=0LIFkLP9G#lsDqLf&=-ZWOilSe)^3p@h5C z?2WtDI7l2m^(yAHNfw{A#^H<0{M<~uB$SUhSV~Slm=RY}|54JuV`s%i&z4Z$F9*cT zU1Zc=w`2F3Qy{boUQrJs&*T$mMP+&^5MlYqmq>p&JnYw z8?xxcCtcgPN$2@_?RHVIl{eC^eyV?2H1|%CcKY4ygE#jptx3tfW+*SJ>%Mwc(t@lD zMF#f8KmKK!+`eVde5Bq;e*MB&W3%H%g?EiI4s0?~->jQoY!bo4aa@ z4VeEI`|r7?-7RH#Skmh0X1nK~YZ#5gzJ9jsU&pz{fhEF$g~5T_rI5AUXzm;Da)U1c z7F*;#PhRH0ans4pjyKOZu&Bj^bpwNOiHP+TCht9qPsDWB+lQ{bYPTi9 zL~{QOmTfUx5-ymy9scsyVvApJpocyevp|#i30VuvKwtL~ztx*yNBx)1j|h;tqxaPb zanygv_J;85>fz09_O7j0Ul}SS=Xigf?9uD0q19wmFgIJ~vJ1P>>klo@_rCCM-|C%n zV9i@r**#9v>R&n=U6>pvzAaJSEJ@ugwc)@?2k%gAvlRDjDgI`u-#f#rOH&UgN8SEk z8uxj!SGC#Iq&H;(O_mkSnbn8Q7dX4eCiB*q#YUT?_kYbcpP6*{h;hx~%z9=^jvMA% zlGFa16|iqFxU6_d@lG^X>kB4lBPn{7846c70(_@#g0VGv1W^=e6rO ztbFD~Y5Vqye)H-!hp4S;mCLtRtv9dUZeH8Ss`{hZ?u_}-zGfp0rh_}oH64$ce_*aP zK5u%ZDe~lkI`JKitIfG(CP>eD-e|s~+1{c>d%Me(0N&1nVt3w&xLGXc;ciV1^1jE~ zn7yN;e@9Ywq2XkUuI2SRy4LU5bGEHJ_yB7OfA{s1-MfFVzPZx#d`F-0*Buj{^!>M( zz`k<=zvV>n|2rqjTTW8nIZ5AgviZ)*_LfuJcTVxQoEpA!YP{vN^qtf4EvJ|7oL+A^ zqkZR$e#@EDcg~z|IcxdOS?eulZ{In4zvZ0cJLjCYoO^xe-20aEp6{IZ-g5r;`knLt zTP|SVwSeDhq4=(a@>Yw~cP-MlT5P^+vAxw2_gzc;t(J!GS{iS)EPdCqe5>W>{>N@*P7*4 z>%Uj6*>AP>bk+LnyVj}i-lT86Y_|jVjRxKw2kN;hw(@Em+`QiEkqG;i^9OjgSZ#5x z=3H}Ny)(;};OZ^A8TRG<=9$vKY`lB-{{Pl{KJVl{!63lW$Wvju_j`j7!_^hlUmX5A z^D{K^^f*K?Htt(~V~4jCzlJ04mWG4357>L0wP#@Dzrn!0rFu{Lo@4nnv$pRN;&C!- zX*SScln-F8H*IM?(cC2DvU`>DZi5tO(-k%vb80LCoDDu4JmtNIX`zkAl$x`vZR9za zPu<_cyTk0byzM3RztgL2 zSm|7CkkWiI*jfKet$s!At?do08{D~1Fr4~qE6>ApgZuws1&4zt!9TbGP5_ z<@CA<#9DZJXlVeTI^Unv5R`2GOsk2~V-eTOseS<-MfvstS zo&U!@LVuh&dG>$lcRwk3b{~ zmR&%k#_z!8K8)b~aHeTNb)Rm}b@d zTNo?Y;&&=ZV^Yb0s#cXGp zgm0|b)M}jLxUf}H?}pTrl^5DtrnAYvxO#wnikn8gmj@4@Wl(1;w?=5(gWKFn{0}C1 zyHB^9TUGk{`o{F@`{q`czqirs6TRu)*?G-4?FgG{aC7U<()Y9G?|dXLZ(sj7D#Ng* z^if=m&WVQpV@;KBb~6k51RP|Q%(-xoU9o4wt`7mhYb~}!dGt$d&ni%~Xp39KHs#JU z;SJjhrfK-?s6W&&;i1LRDbfOJGga1HJce`+!16=N+H;(yNHDIwer5AP=kM1}JzLkJ z60OLrYP2i6?LnACpNdeN$+HbY22CA`Vj90Lo-w}Y6AZovz<05R&shVFSnu^~zs4+B zyD+C9_{DtDhz<~zyjSgFgR*6i8a zFBy@XyIt$!tdp$|JEk-?xkPdqODLo?@m+@B18}Hi%8CMk2Yp+Dk6izA3D{Et|8da9mMfltxRT+o?Mvf-W8DEp_Om=kaOb|O4S^s+N&Ud$7uWwW;n{dTrlLNDV;$!0we(|gmdI2$6 zI;L4oH>dl|+N7$N6|gGMvw7Miv1Nh$2leiJI4tPDC&oWpB1J$eM`%#(g{|vn)}~P@9unm@AvzI{A*3Ql{~K2 zTsy`7;7|2jmR81V3L6-jKArcUTK8Gmg8T1hdEWc~|1(NBFe;_i2r@WSpIgrO%ax(= zM6plt)6G8lZU{D#e!%W1QST&puP@?^@k7p;;abfG3(7T09&-N_ zHRkV|&}O>jA+M|(6EBNnitUqT{>C59ifw&*HJ7G~wpkoe&@=3G;F-I)K*CkiZ(`Ss zMI0KNCc5h8P3(?UIjJC`*ds5sxI5M4vE=3m4(XZ&^`Rw?r5{STOG~!%9}{3ZS*6$$ z7vS7qQGZ2Q{__m?iCZV|w>(%Z|NqAEepRFX?mPb%D|xE*INo@`lso0Is^LseU%z&i zRf`*V{S*YHJBp_8OzhP3^=#yidBF76vXMo?=~U3N1!l|swCnnMdUblJunO(Cq}C|u zlgKyOXr9wk!-<_|#GYt|zVLizym023Neg;rT;1}_bmPkUDIYhgDT=O1aGG&!OJ3FF zIZsXIt8Sj@SGq2E?roFwzd}YkPxgME`}N6vo|`lMYu`)tj=AxAcXxY?oh$-;_n7N9Vfe&I;_!GfDrnM9tk%Drn-m(~Ff&UwQ_1Y4)Eo zUaDLA();|DOZ|tYEHka&s_EC6^Ue*^}b_ePi*lLcj z2vsmJ%Y9%F(3!?MJ@o+Rjwz96f)X~DPkqCD!He;{T?147CQfdRhMW8$4>rj+zDbR& zj@YerZDrrrxA|WqnFNCsf5Z-^}0pr<%4GQlBdQ0{w z%RV~5roZL`gP4rjTEhYZvv~)&a{_+IsvT?LGg}bM@8Q7z{(zg>hL4Y8UmTEpzrxV? zSU^29leoo3&azLF4K?qu-&uI%O>MjJ2Aw`Ge`f)Sghq~-5B(wr3F7Z(uo)~<ZrdW_eV|FC=b`ND8En%(TC+J?SjnEN?`Y(3akwf{A|Z3R!nkg}0b9@x zDVaG52j(~mFc|(aYdXujrj`BANoHwJC8$hl@; zw@AiZk+OR=tE}~}UC5vRJK^+~?cRI3xj42LUAb1l>eQDXDx^S_a^<1K?k`1PkYB1@ZjSNerc(7tZW+?cD`fS z@5iWlma(*e{buUH6>rXJFR&-SSAYsdqZmecRDd<1L^zWzA(e%V2;pamOh&fiQr z@NLCvmag?zN)CKXP+(ruKmS#i1AijU^SLfyxz+o3#)fZm4E{0bvvL%EC^>tXum6Bw;i(?} zpHtqm&aKa9Nt+-P?7;uVhrh>tDu;D(*3er*%k+8E%(QA4lg-2a7!D$VhCV3?elBPI=1!aB}@Y)-+q)UPdTXM z|LVbOW;H+dj9eZahBG&M&rF-}ZAKc)ybte>B`_x$3camgbG?LtIcNcsmI7*o-$G_+Y3SD&(|;=Z@W5u6Yg|eG2=%awJO}c1$w(_9jPE zc#`yTMy{X(8#vydJK!iVL4l*ifuo`E{58iL92b?37TmvA_^WB7itq;&-W=7fMcVtB z&$#IQS=PIEmf}U7Gxe|i3J>0StB~E#BC_fIHAj|YL7CF^XU_4Q<>Heua%4+#xF8cK z@L%VE_}_Jsatxot_<1+-Yj2*UEm~~2{PCLuyb@)7hc4yGCKy~Qm~`02=at?9{(SxK z^VV){@VowQ&HF7mMsHW|cS|Um_fGuMLJeaF=6MOXmhdxs6g~5MYasZ!UP7|iyi(Xi zOoyrbA!Aj7c#4i>S>tyH2CE`2R*44jMU2wx8kn@)*IPW0uV9p!!^mZKV8elnT*(T; z6$d4!EtL3jfG@XyMa3JbX^Q+>4a^k{;`0*4H4<3-6pkEV0$P-0ELPd$p;Ol=stYS z7^LviZ-Jv`(W?J13VHt)yGBmYjxzeArNH8HfW>Kn+e$9=CT8zHT&YnF8?Je2xPBHW zp2E_Twd}^G4Ru;;7qzS_>0TN0da=bi*QF(1E0qMjbKd@AG!ML@=Eb!!fNjIRz!&um z{HzO}ui9p^W`T)U=_2ooQ@rke@tw_d)l2_v86#7`+Ep!G%iFfpFUw+S%Ddjc&)i|M zyzPtHGA#>^P|Jf{?%zcgKll=;_*LC+Ba@roTR8^xm05w=Oe>^99kUPl|6D5>q7W<` zx`?kd;Acp1?xH|h)1cU|q2{4yq}E7Z%VPVXAfMYs}59v3I}5J~WMcS{nCqYuwwfaUV_N*K4wW-5US*s`t$=@vLGIw)`4g zW*SUo38M97s{Gf~rLL{IQj(||Ci(qx;_NdCuM3k*%Y?PBE!PuE`dOOnDz++WK}xj4 zMxSlbnTJv$w|Uxft@RR1^KD2iJ(Qxp+&$9lc2Uw}w{7Y3wbF&PQ;VBYTg4Jy>Zhfb zrTK14^Zu4LbzA09Jy+vv=_|J_-O=f+>g?XHu4toV`&BK) zf@>@bRCLo8Y!^{Vi*``FqUD_SO^r*`D#FzL|LSe|YripRC@??MbbqeN9I?`8k62DR z1B+S1RE-Brb_;SiKjt(ivsf?`I3}doA1H{_%(=c{tK9 z7lT8dXcI%%1NLhPymJzGS2b{OEMPNvP|>W+wB$iuXF}y^h5GvI57@gB7*3V2t|)J~ z-oV4OfP?QxUA+SrTSN84>(!B_HMTnzU1(-lwSZA<0mFYK1?OicnK%w`W+X5&Dbzh~ zVqsdqAk@H?@qn#BqCN8fW0|@0rm(i93mByiFt8lpoWsyAexQxtp?V>^ZCXV;>j8$I zAMAMt7^loAzsT%s{jIrv=Z%FY?utpm zT~!kmI#)L^L>%s9W|(+=0Ygp#Tbdb@$^iz3gPbf4>?#kK-87lHZZPuIJ20u0t1%sz zAbo(7r(w!TUWTp%j8Y1d&38_aZeUyGz;L3MNkf65gJFX4&K}Dh!T&WaXYqGcDL-K7 zxXEy0S}o@Twu}et91ON;(@eP)!Erlct_t@_C}X92?u zwKqay3oEPUtEDfjU(m{~z$B5xu!>;<`+_C+uPr*+zO3>4O74cm;tCvgR!f?9FpAx% zPw70)#?ruM7g;;!#st|04km|XC*2uxZZQZrEic`+{BG2I1GPobGuDK*FP!^;;Z%CT z|7PVX-UsYr2bSgEXj@&uSjE6_!%Sc8#&p@8x$&JGs}lH%5|*C+)|uI`##wT0hSb`8 zD<-S;3%I|ob(}rdK&|}cj`Gw69r*_sPAp$vWKz$<*1*$a(OxVuK{jEPeUwq%^=aZ3 z%ta+^0S)Dg!{^`J&QW$_lj}}aBkQ$}2bjLx+U)#$=XKAV<^!`GDcDhnDQ! z8~n@GufH|1@wr6wt{st4MIWV?>n2#0S{9?WGuK- z{$Mu4sRhTC6E=U_HGN|h!?h^k_{WD|TTk&8+s~_Dd2`p=`FqZ-KCw8fhN17ryuz72 zA8Obb5@u!|VBlB~tj58>ldz##p)NCN??x+zwwSXwr|&y1VSBIUh`F`-%roaK|DJn# zvUB}y&XyaTCO4TP60EXq*niaTVc-jAVo+e`bGRbxz+t!RqM^-M@q+n>Z|u;I-TZit z^UYnCTWz~ixT2Rtbev9LkT|qh?=SoRISdm-CD#fcn6-S?#nUkx6=iSC&N>^rYt!4^ z*K=>RtY3Sweea#j6*rPr3su4NSyJu5D97OpppIVY^Wro8^| z?TONtc^h({+hm>n`{2L&soaFt`~=1=*NgvV9aMJU;Caxnd`3NI0^f(}?0;IOEmf%R zRA4FzStIwO?&JX`rw1wF4K*DP>bM=)w#-cztb1nf@lf&?8y~~z=~lPz#k96guP8Pz zJH1?ORe3qHf>FRW)`sbo)!|Q1vX`Bl{lGh`zB_Z@tHZ}jr*2=JU$tM^EY;6y%0=Zj zhwrhO9Z=IbR5jyIlK-_!p>;*Syx#ow>K4BI>ZM%GhB}7-1s0Rt<<@zL&D6O&Gf(M^ zljWI!ORpuFvhLnvIkIo<%6;!-=T~>_VmM^MEjQCm?*AuOttp%e+#VL}H)KCM$}cfm z!tic8*OiF+RA>9I@>gS?y&E*|Gzid|7flM z(YgOe@BbeY?SD?K|2cF2&-a1Rrd+?~${*nNW?OTAzjznxns_#+gkP1x>&~(L-oEIA zvkaP0c;%ketKA&K+A1CBNIfA`MUKDa-0Z_^*$ZR_i+7`Pb@`N|kNI{mTk zFj#n~l}FmDhQn~tkxstI6FW{cEjrdKV_o&<#D$g~wZc0KA}0whIW<)$dRNVVPUB@~ zW}0N*+S6&g{M=ld>R*32r!3?aH%QnLA-LsX$5Nl!dVkH69t8=zU)bR}xs98j&vSRt zlMBpeqz!EM%$mAGY3;48g7uL)j+2k|&ekNI1(3D6kO+yxVNN}S#@dJd~xsDZd`?}ol?FJmN6~GTX^J5sFo4Ww{tt6u9DU;GOE9wCb+~?Sk5SN z&%|!I+N4f3pAQ1XQ@g*v{_^_v{(k%Y|Nd5+7BCdK+1(N`XDW&_Y)>fWS-CZ;>~`m| z|MR^SIT+id6Fwx&%~jjL(CX=)@xI3~=1wP-HQWDU@>9 z@VM}}W`zLf@x%p;E%pEZY%y&>z%?I6^?|aZIDQtTXwHAeO}eE3}u0yC(QkQHckak3VAp^QzmjL z{=7YD#ZQjL3ARGFUQCJKv{j+mC_&I&%Do|Asnhg#p10d%7kx~u)LB#CHB%z@($5JV zF)^hsGnd@!%3inY-6?JVS}yi}bqAi^?n0g)7@G^F6t;#0I;ebn!^5`BG)Jg9-~rQ& z{~^2oA7|5x$bP#)Z_9@(_S3VnUlu8{FuKV^7j!1;_e?BihU*>ITu_U{QtnDS#j99GUc#y=(BO`G`M_@NM-sZ_;tibQ(-|MaTWLovQCJwh=wu~97Gv4hAYfx;Hh}O9{H;QBJYa@yJ z`C1~zQW*^k1vp}sD7qy%C0sNUcMlO?Es)*K$R<{Bc-Jh^$cgGl#B%oME;-=av9SDk z#*9D#XT=W*i2}AIivQb0JFhzIxI3d$@32tx7Tz{d#|<@x&o}%nW_g$=f3RsS(`kju z=PFhAc8cEBWpUySC~&kEo9^qg@pRq}&xt%m^+D^4zu&9pulq6U>q!sm4-SVpY;GM$ z-uLS$V_-s~!vluihMt-Qjx(L#GIB3)Kaen?@NHu4ha1ip&Rn;PdBeySPu^Y%wPu za9=!duvN1@#dOXlpJ^r?ZF&jsP=#Ki8*8$oRq5kkEGNIHDG+%-{6jP}m1^^BD)Zd@JU~1hlRc*sw0x_eR5kh=$hH5eGdK zR!orC(BNzmd7d|S&!a;TA6j=T$>ZE+GIQdE8>}an=H==?IkdkofmNqNnER5aQpdAn z&3>hfTn~EsAKpt6dXrnPU=<#jF1$ve&F|vTnF(7iNbF=_)hIdGdgo-1$25a;Hxri@ zJ{D2;`c$fR>dHAT)1H2wIWs2+9C%)J=l67;O_STCSc?8HQPExL=|4;0C#y&8td?tn z-x|0pR_JpYH*~SJh1z@0YtZ&6JSZ2#xI*QRvZ{i|A$4v>T^W_c%!XSj_1rG4X8LPW znddk)3q3Yf-F~E%OKEEdPm99b19!FK6LjY~J}412Oj$Q$lTBx43&VU9orBZGm2Zab zP-b3#DVV2v&keoi8!g*cd2r}_dgc1>;7S#}Kt*4j$E#8v7@0ho>YOU<@`XURBZYfSYB^QXlVGabwn;^LyUri z!NgVurtTQct6KuDcB&sVP6-GfAJCFxCt zQm2ycXn0qi3OUDL@NbHz!WstFfLV>)Nw@giuGBAJJw7wuPco2^M<wy&whZuQ2xT`3LC`~+Cc(6w5ut>W>>fSJ3$6&EdBA1pja=qemvfKGtqNN}q*l$hf z|8^rDl@)=4t(*@T`1c5~>m{sb4?noE$VY(1_=w_$pTDxTg@c$&`$`>U#hP+N4mC#1 zKVs$f#Hrq^rQm+ZIw2lMhN;W^O0BP~P-|9dePDCi?2Hfp)UD;s9hn{c8;R*(!PYurGOPc^sod(a02u2Q@ubPaF51Q0o z&OC1t%CX?|(Fu;>p`E>sCxV(Bv;}e!9trPfNO``|1p`y(_CJd^)mCYlYE4f&eCFgMyX`m6_kG)Pe#w~w zmjWl67))8p%Jty5XIQ<8WZ-+{$Vv5%TNy(<0+_50FOE|Y5Gr7EED+InY1o>-=dt0e z2}Y?4j9c|;oVnzeUr$Knp8fuh*qTdcj5zzB{brqW@?7Txo)sJVidP@Z-F@I!^9i5N z^TeM!++`@?!YKAYF{$3N!-bK4%Qs{Qw6{r8oARZ*e!7>H)mx|4`V$3@PGY}F#GGNGRh82|I~sN5|~=m?UN={bc?I>Kd<0d ztejGAG_71irGO>pgJfYslX^$fd!c%!MbiV16npnP6Ihilw9(yVojCi3>57KMq8cZf z)o!>MmN%PJ2-pg+e7Y`h=y{?KOVaW0{3qRA91G$#7kcwuYSz#Q-_u^(b5t+rc&+5q zS|2fE(W}ix6YF>x%icV%6Ll>6e!MOBMccD!_7+CvtU@-fg2Bb^Y38Q+lcq5J+{{$3 z7Z97z#_#zcJ@r!BYi3@H2TXn9GM(S~6*}T(I%T|V)q5#qtg?gk(ZbHzC;Vb>G?}Fd z1vrHB7#5jWv^vgc-aNf?_KD8vj4dgRRRYZa1-G)*_-@H|e%j(9*2?SV%|sJ>bYM_|{U^k$0=0jmn3{;d?Qv$uE7PIR(6EXZ}LNao!(yQ7Ru?!vL>Ss7V{>X=o6 zEfSa|H!>+KsGoC-p&+wxTD6-D>-A|XSVAK{NqRqUjQtcRus!39Ad`@Lu@8&;9u}Xu zn^>m&OcoWY7yDV27c|}Cm`h~2Uq*@I#FqlfD_f40&s1O8t$wV#f9C)Bk?M^`lCvaD zXPH>qT$$E(N0Qz9fQ#vc$yYS2)(Hh&pP_HWAfCZ6W2fdKWv}{0Ukp7iY+zRsD5#n- zwMRgDKO?hH1NU18nZrTl1_wC4aqt=lWPa5M{;`9pwSC&36%{vMPJ4DJO>LRY4YqmD zf|$b@*4`%E0(YH8?Sh(PyI0jsSa&Dn`)&Mj;1#Bi8@C zJP%}(C7HS3NvI_IFm0tu=6n&uvk(_Pbqye}}D9$%_{YIy?Me zi%6!=W|jXeUZH<>(vD>n2)-*gV*l7tOp`DLm%&w$t^> z>lv$-uMlg^Kcaa?g(cN`>E@Zyf4x@3L>N5MWUK$ZE%mjmlHu$K0Txq*o$0&Hd4B8L zr&x4HZ)?*su=%hsY5B&5+PfBa3kNE;Ul8zgob175#NpXs&u+-1d!eJ##pBzJSd&zR z{|`KkSefgVsVEpMsV-m)xVqV8wpgkbTYf@4+i|Ted@H4NRRj_aus%<3x>&U|VUdLU zf~9Y_7e+L)-8dYUkPtsrv90^r0$(xlO{RP2e@t38BT!-HI-$gc2@d*&-Di)1ePm3Oo=$KZAtba^`1lg7uC}*@GW5pzO{f?SI4APfl+TmQ|cxWy$OtQ zl81HQ*tI4w`?ESWiXZNbVCpSb{xyTY|G}Y;$J%EGBrn2yJ66QDFOc zI>jcSNkc>2<^`Lt!sf-!?4sGSX4?{6LQ<-_{91yW)D-vK)Y4M>Ao+*O@wF0L$%kgX zhgD$*o39l5xhu5IWjZ)Rls)ahUiXOqmWz6tSSHR_YD{dLd}w>m>76__{T3nZVh zVmjz(v_LAdM<96)W9}Taxi|EWWt{4Bo}9T+^U)FJ>=?y0JWMtlSRS&lIv!wpC(b0f z;QVVIn*-$m3|CLB3}k+CJ%Cr&FU+B4*AZ5MxsrP~uW&iS9JYaVPW>_gNe1=rDXbw1 zJq(Xk)dc*O>^W)k!KQ%u!sF`~80G}-+HyW3f%VXgHNG2IHDsG#-MJ7j;R5?XCff}g zPOmuantNqO4x`Y5D|#Ckg&0_P6c`N*_MAAm+RTVaeI6k%YN zeo(Q|n%Ut4L)@{(yD~>7eotOk|6MY5OQiOWv)q$Qh1)kw5NE2e&7PLTByzk&RPuu4 z21X8P!Ka>uZF?2e6bd=J#OixXCi)giZo2N-E>+E|#bjJ~fBJ@swQHwlXjZ&k-4lC5 z(D9Z>`MmElZ>XX%uI$pG{R$$G+h$;iq$xFLGCf9G8 z7NoF$rp|+>Ozw}bavF-b8a7I2Fu53tWS_kjwrE4a!MKvYXHyT!Ot`+pgKOUTomD>1 zC-6UvJ5qgbW#FEhn)>hOYQEIfpDJ-LOzHj}sR~xV}+v;{EvTOwGw`#?yGcmUzl(gJeeTiS9U^U$9vts&n5dRv7o=dvw%cx#jQ%|P3Cr(&_1*K;XsL$yqa(M3 zL}#9UP-r1|`;qUvC;sn*B_@k_%sU;#_cXlkX=I$d!iUPBkVjE`&l2^XCI1u3WL%%p z_bhYWv+Q?*Tn^`+`svpWGV(D-Pk(xlQM-PfLGiX`B;wmaUKPE!p``cF$zJhllw; zE5)q|FibucBJXW?qx@6y@oD_Y_2+CXjGiBzXP@n+RmJIN5}$LT;GnaDh^mfb43A!f-30hO>DR~lq1+Ul=t zUjP5e>P?%!Jr>JJGO3fXxSsI;P-9g?+>W+-og>LxCe%IPdiZK>N^sSqi*6I6LMGNH zH1e7mxbw=ZE!ob*Euxh1FqqA%WMP1jVa0_Su^f#gH_0rEq(YS)28ZqU@)XH%{+zob zu~Y4oMliSD6SWB~M&~>phuCHBVIpY6R(^IFLtLCxn8%mM)i=CSfH9GDwB zlYv1pJLKUJsUV&WN(;gn16r7QU7}nW#W{|z2w@c1;IM>uO`^k6)flO%yv8yP&P(`Y z1Q;1~H)yU3(yjTQa9|m)#Rdj3hBFUdt#&=?;yV2vLKhdC>g~>uNhlwSE;n2Dl&yFN8 zKD_p-L1gEp==Z*BwWM>HU3eb6-*3PgbMyeKe9s1N;}ccPoB5t7Fmm$Q*90A5x^v(q zBeTneRmUtC4H~vitXh?OOl3Mtg9x92^~!AuJ1%VUX8d1&Acr}Ur@(QA`Qty1jNHbv zof#a}eHs}=)Eiv(dv3py#bn~()y%N^m6WL$F{L|@gA zxSt712Nq1=klUe9f9Uywb~!8VMa}P;o~w3D*u|mEP+61edtPt<<6eB6N@u;}LH2F+?E>$w>a$9$ zh-VV5aA2{T(8wP1faz%a1SubbCW$i-nB-3!WSRGX90f;#7YxVklOFIC zBsj|$)GV|}@o<(nXV7MG<{|e%#g1kjWwzZ1*eq`4c^r`v3vBmbIWBBrK2tD_Nyek& zpkv8{p7K4)vJX6tTOTPrs#CVHzB`I%vG~Rnu6lWbGF3O`@%@{~6jrdYC!t4K=A_1P z-8e(p7ZS~^HU=|KEnL86IKy$KodJ{Niv?^}4;+=mZ#KIc6;15mIixIl@`S{i1*{4T z2j%%!HoIzlnmmt3Rm1(}Nta~`Ej^lz+|QodHuXHH=IvRklkRz%ZC(NkyJ3QExyerN zkFD?JcUSwYDA_I~zUDyLCGWJCTH&?{>-=V(Tr>BbUGmPmw!*V6q&#!fYWL0Y`z+7M zxH<6AW=3%v1~tY^mTL?QpvxXP|7>5h;9wK8u-0_JFh;$4&d(1VXIT`#x^r^#a+J%? z^!7*=w_IKxu(&V4YwN13t0NqpVl_-xhtG`Ne`tzVnbyg*N#cnU9&as{)Bc;bD1?W3O$e4Ev_=*iQM?~^o*Ok*3S89^!%K# zrhTnWy=2LgBlEmJKm8-QDC5eqP2efPrR^Ll|!Ev15bs}67Qo6CCm)gAAg|Kf)G6iZ*Pd~&Dwr|FA?xaYCc>*Xqyu6(~+s=DEUV~vuE z_`{Zu8=uc}_;A!cwoSV1`+`8$bu(g`{&$Kj&S5XR_Q=b}F+{oPwCQsZHmM)a9!+o& zbCu=Nuvw78rM_f=3Jd>|2F7OQnIHVQcwBxuPUdl#>BOaIAksXEKcS&%GQYzC2QK*q z4zcxD5+zkazb?nmtndEESsl#q`_0oW6SK5vW`mxns?8v5On6azUkMhp7nlN z2J5)Gy;iJbx30-rv@xvYb&t^h$dA1U@~tnB&f+p{j7*gdH$%T3nV zDr-6KYOT#@6|+wjo&I(6iVO>fk3oHlm~#cABWH-*>MxgcLZ834exLBUMecIPXFsb) zCdwUrq8Fl_CN(+m_VDdaVD__m>BD?RR8z&}F7vq<-4hb$5+B3b6U$!eOP0tZ}ao42Ky|d^V-cdhHIzouh4ojaru(Z z^=IZy;ZD8ciM0B(;c7g?S3`q{hHq{r*=9ta^=eib?a{1 z!|2H2X{`JAi<0HsCW}Wt&RiA`e1gB;eQ-yRxvY9ofa8tGVxg_yA1!#xBc!qCJO7=` zVj*+0%QxooS?oShz*K#$(&^!>jXh~0Z)Lv~yBQ1BZ|Bioa=^*rx5q1ucI#_@`6_mA zk~~rIbXS0@L9)o12P~;YyQcLTlq>7ZeB<_PxA(`6RNXDH=e2%V3eLOGw0gAyS80af zvmFeK0v(4`{_9Qb@Xv5${?sM+b=nD+9t&Nz8=vPHe<^6SesNIpvd3)+jR%d-8*&?N zoH!zStf18=VR3!!0SBSZXUDs@JoN8r+QDq_X4!W34GUPLnPR@ZThMGcr%-n0g@eKs z8~aLLD6(EVxR5)(!b$8{!Ne||2_M?7J9lk8F;U@*ESq-QV%{X4$y>N3S)FG%`(&w1 zv97tvBmL6TyG~%LjmSMWt|S}LKM#dnd|Jdr7#cM-H+CN2S?2txp2f9r&0kSN1rRXeS?945GeS-fOLo6wgB>fUar=4hIf>oI3~c-D7a-}CNj ziq@PfeJWl~v8@4<7d@7FFV-E=R8g`)dP_#i`&Sp6c3o;&%d*Dtxs_py(KUy^H6CRX zo;3+LJm7bZS*fUYfWiFamSr1Kl?-pU^UD6u=~%92(Wo$OB42{SL*}Qm)AHpVn7BF> z*ji$a%kHxZbz1hAkEh^uP;fy@+8XA1>Esg(5x0Nx6)+rTN;N-dMMc51kN=DPJGDQut0>j!64xD$RE z6&1HX&4>^D5g@loPl>0tgUy1$L?w%X$=TwFmun%1ia`R)`rfr&%f*+!?o5{4*6Qf! zRv*AEmKREUE`+@0ir#64hdcfUevhnv_nHgteCY$i@_`fNm?e2E48!eu5 z=1z)s<;sV$Up9-a*^sU&{oLB$g;~epkmx!CCJu#%^#Y3`R;s97@D^KAadd{=wVb&* z(fns-Ff|pl1zwI){M>qWx9v2o32#OHK2B_4sjhO6zJ73<@YDrN9t>_*jukWtxh!$B$fWYYZyEc(BRBU& zu-`5`$mQd}DE)ZH6&GC@1GxvuEB`68?KW7pIx+TI{Qo9z zL;Kymb%{Usyu8c6*Htw>Z6htwd8W<+f$9Ep10!tG|ODp@cWn9mKC3Q zjHleHy>??kzgiExhcJaG-tXUZA zBV3fk4*tFy|A=2ppz*+MzZ&1^54>+2KK7#~rfKKN53~7 zyOeZNJh}WyO<{9#UTT)m^epCyOp?oM`!9#Q+`uOliGL zdm|b(B|V)>xt`%f@t@Mf@9LI++pSzq_}7#*)H80244BcfHK>8nqakWVOH6uw+qEhV z<$!qjSrY*7;LArHC1#-v$6A8H87Q%)HWrUA1GiE zec&q8V0Yj^6VpVdy6}`M{|_>po0=k-z~tA=a`p*NvI3LhgrY|WI1LS$(oExCxBI_R ztz7gyp`OR1Exw~=<#&%jiS)RLwl2}eO-+{3?n0|(RMm%8{S(RS4XygMv?};UYm7$E z-WgqC6|DzvM7(qB-s;}svyf3#fqmza>VgFfk`J6Kcl7cqHH9g2m>Mu$y{Iy$!0bQ+ zqx=GO>uDWw2g)Uu7xw+|U9^pF_W~wehfbvez1IqS=js%Pz8v*_;XQnnJ+qc}3pwTmEH^56s>NH; z@}aa%Yi7^VMQtG&6OS~@s~ND&zK}ove*%YX1EWcL&z&EWr$6ldY0P%=iO%;e_G-@= z54LuQ94`-LYra!I-RzMA*SSYJ$rpHT9OmX(p#ID}^`(1UG;5`kM25cPjIydu1NR>(B2ozu#~^5#u7V$W>gtC&%j+0wUl zQf5bhK*t2JZO(>E^ScW;+YJ~UlBPNAoH;GA;OqsS2M4%!Kj1uFP;ZtLDf_gzLp!6E z`D34pTbQZ?lYm0j>#rtf=ZL@~P%(()lRc$NtZvUS-N1(#so?A7u6O-}*raMQN zI=;2ek(@73R1;uQSEmy8P^F&l!3Vq7CLvV|ibUMQm=0z=`cU3~!|n0{_Gaa<8;?`U z9T+vf@n2)Ku=z1{aYr0;@*KzKbKf7Gu*rOq;Em$-D@yiPq#dY8e{UX`>@l%^X6f(e zUBVrSF*_E8JYVGJ(aqQz?{vL~Q`uX2t0DU%k&NR^$`2NPs$7z(IK^rK^UQ{+^~aa6 z`wKAV6)DBHWF1N`lxR15^jNlf64PcgHq8S6%M;8G7_bQxFg2SmynB?>_(0K(2NjMx z{0&>W*F0I66ESzgj2W>OlQ(6woc`G(s5w*S*94d6a}2D4R%~C+Zn;SPzf@!+S3Sdu z`o+f=&n%y(SLLahBMb2EnqfvSfvx7$oWvj=mN8r0`u#ct8^AH z8!0f?aa3%nWEX8trCaY^N{xrFNQCd z6j}do7SMTZY96gv-m+=Nrj>TmTOt!TIYn==Of2}jB4l~}FN?z^O3xN9G_r2anJgz+ zv^nqb7OU50X4zZrxNV8~y){94n~vN3EveCm+=Z%{I*l{fywtXZ6t2&c-d<$A{j%fy zfWz&bE!#c|o1g#6pjOacvRk-7dPht2j<(g38U?Ij39P?Lgmx~?`0!~>yS7N2_0DP4 zJ7-jjyjbp2m%LS}v3_H`5%ZBrJ8d7XT~fVk#q3>Hf*Un9D%33BwLyA!&OwGP(Yv=* z@7@u;VN><4HPu3UckezRy~k@I&ynao$Ex=n`^_`UVe_Hed(Qpdb78f_KZCtjqW50Q z-h1r;L!!{$JGb}V`@PrYf8pLo+WXEg*!!S*-;3G%Ud#Gll?KvoO=U_$0!E37z%Gn%Ji8*Ay?%sG+w|Mc-G)}D5+PS~wHG0Enn{mB!K zj3*beoSd@fWc{3)B{4pS9UmTVn{%wm=2Usisbw|C*WW(5=FX|3-%r-|oLa1NVw2B_ z(wNiB-kjRC=X6f`N!y;2Q@PHFo;-ad=FFidr#*DelxUtgcIV7F9orLcgctFgSXpzb zsOR*~KWA?3IqM>H*4FUswVD$*atgP{oLzqA?29?)Es3-;O<^jt1*{JkJBSA^@WA`)D;{&N@G4%hoZFotdF)-(3@_WOUT3d3 z*UmU|bGfzoj*z=o*>kVDIBG9@eKjTa8YknmxIZh>_Fl`Wy=EnIG-BhCT-)oZz1Mns zE|$!_9+G=KbMN`8zt>G-k6gTPv|MphL+uR}U%sxrH+t^g=#k|=ykTmi;<}F5o4UQb zXY|(J6k4!*+TEM;{@z?5dux&HttGLymet-`G56Lg+pD>IZms)!OY7pT4Ys$p#7+v2 zxxHiV?U=c@_sqRjargEC-rE*ZcaG@Z$&$TRFa>EstNAC_y6$j{&x2qvuoPVp8K|U@3YO@b!Fz6O;b*8S$vZH--G3|9|Y}rz{BUv z$M;YoZ>Lb*eM8#^vVHd?uw7w;d|DK+Tnd`gnS^B?cvn}tG|9zAjH!*0Q z_qHhd?X7)pSNFWV7q?|s+!fKkZ=bAt;wJY_{%yq5`hD*le^0vE`|jnrcc1K@eu#VV zx$gasdGCMid;jO&`+xu5Gsu5nvj4ym|ADRk1IPRiT>C%p-2cG$|ARpMCPU68bC@RI zko_oOukvW^#LGJ;KKHvRDgSA=;ql@>$DYKT+G2BV>mGr;oYQLhw=egcR{C%MnQze> z-Tbrq|BvhFzj>^4&ZPeT=go#EEbGrY#ecH(KjE(b#bN)KxeLFv&H3`2=j$q;H$L%S zXD+;4a_<3a-q)D<;t}tkiO5}wwf`1(@a=Y=<7w|dr|kchI{%y5`)?lSzZJ<}kBa}k z&F*`J|99rp@3Qm0*TsuIi#f5<=8LZXkC6N?_49u?3I1qX|5@w)kE82;Oxj=n*M9z=&GSFaw*QrtH;+Yj#Vo&HtNdRWHorLh_SI_n-{EIxh(4XO zLI3xT`<`pxzuLC{>(2kbFaD}pe*bq8|DR+1bGR#B9NB;W`29Z`I~RZ4^Jfv?qjT|p z?N=?n@2Sx{>*998un^Mot zbDbUbHfme$-F>an;qPL0mA<`qbawdrxP7&M|FOzMd`LLd$}8_SC*ot$v0hpGHkru! zPiH4j{u#DyPUPpbb8|)4{ZPNM`ObpHzVqGYRwV~qh;iI}FZz;U_L)`xbMGI^mHu|^ z`tFa@(tLK8<~=&v>n%4i#xnfr*~!mK?U?evJmXpEZ9gyeciF*<+vASg-Q6Ag;i2%f z=kwzJ{@Q*hJ6QDI^sVMW0dpY#IJFKtB{5N@nBl~ox z4f`6!czm}#)aofvVUnMdak$5*=7ck!yUNBRGn7Pn6}UKEK05Vkm~2#(uxBxF5llW} z=q{_96}Ux;HEH8<=6stBH-+Gkz->)aRE#`1yVvxbkYBil-&1RCjL|7|b&;ThYCDe< zEz~Vn+3aKdx&C#a_g@1SqqB;~_gp$Gx;E#d@%Kk>a(x6gmt0n0d7QHOtjW}pB43lk zBEtVK8jGiz9CMU!y%Hez@JsP|W!0KZr@h{nZ1gvHf9J|&@8vRI&j*?Eid=E#EPZj> zExvW?evkCIUtPV_eL~OOeYR$EK(PH^(HoMAU#DKN>lB@KB}uuc-ZU(IzHMKa-&bcb z<#5-trdI=WkCp}4`CqzxJ0;Zit9#+`sU?wVqOIFv%kw9j*#)fL`y%Rw%HHsB-}h@L zoXrosV|J&qT-74@!GWd159*s^D`Ly;w^}5fTcW)_=@rW^zT|HGa~U?RW^wBjx_LiE zBtPe1`nj{wDDLNzW(%>KPwQX(n|brel-w}M=M$3G{j`}NsP$8Ex~kf)7iTr>ET4Wn zusHJN97C>OsY{opN&bI2DKYQY^J(klW<8&`ov-rQyt&8vUa#1EuX15;wDX* z-yQW>usd(}`(xJmyZ&FC&9d+3vFHW+ULVu=(8^}MXTd>1FPRU!Ii3D|Z2Z&~Q*^9e zcm18bgPhxKJ{>bGk10MXZGWQpBCpb3#S2f<_Ld&}!gsgiX7c^n->#_cs{L{^aQUB4 zmviUGl-fCvj(uKq|Hqr>_Iza*{_8Nj5&FS*V%e?6 zKjfO1@_U=L)W`q3w|qa-uM6GrHCzJURh*QJSi1jRQkpQSW4`L9Rb6>HkN-?db&}t@ zF<5I)(qu7}rFwbXJ@qUqwMR1a80Q5jMU-4_mFQS!yRN_E_#)*)d}_|tc@v^sG%-?nOlt_J7(bSoH0%dG>ddF>I znX+a|s-ovfS6eNg-m)XA>(7+CxlIb3bbbx5YG&q1?|Viwz5ZWG)!(}FY@fmUvNNY+ zo+oUdc=Tsq(OG7>FD~jf#q)y8FKf>=&s5)A*|s_V{hGO7)}`+)T(x=5M;95p?hKY6Yt<}cIwZ}#BOI0_ zw9D_Vc)Tm+e}iuQ;}a9*ZtZOoP?E7KF<2kHZbjle_o|6|KR>^JLSd$TEyJh9{nmF? zoI3z%HameJab+9gq37Og&Bna46n**k|)GhS{|#F7<$`?nwusmbPe5 zhZFVuOcv)@^;#R+^x3XCJaX@SxI~co@Rh}UmiKFn`7GZ(5au`XuD`G%LEuG?^3o}< zHZ0~7IqkdRr1`Iwpyn)|T!kZA*KC+OSyBTh95?@?ve{)?jOT=Qn_#(>eOA{G>=feY znz8u2uH%~*f&I)M&3o*gDll`Z&De41jQP7Gi+Q*c+)|x**UeejML9E3u2V~=Cv4(gzlXc@H~;QGMk;z+|8ILeafi@_jl6b;WgHpz+;&=$f8c5mcV4{{ zv-K1G15(=BGg+LLtj>0Q|IT{tR`D-U=XGp$q9keC2ZDox_)mCMY-yxLtQx=_0aV^}5~vuN)}7 zXltUok;i6^t_%NV);A^>Gfpcos(oK&8zJCb|NVcj+0h#AiBY_^C4Ws_bT0h;_8_48 zZy;{~_d#aGI}axFh&Sr%T5vwOwxB|wkNfQ5N+XAJB~H=Yf-YISuJb0`jQuYy$l=|= z^XgWv_}_2SZ(L$86`1g26}RZj)$_JqNZmcRZ0Rr4_0b#6pRW;M4f6;-Zx+z9On}=+ z<5R}2+bZ?UY}_X5Pn7KMYc!>?`Cd_eBFNc#q1E!wf{*93c?=%M${Qsp`C7hv(58Q2 z1Jlk{-PnMHi_T#On6xe&l6uDAZ{iimEGgifpv}>wG=)*Kyyvmx3=1bl`A2h7dJZ?A zdBM#6VHv;w3lBx1j^@>O+?^{8lrwlM514)_V7J#$WOj^IVVPCH&ign~Rrk^cxnDon6bohuh{-q#Uvv<%Tw}xATSQed3^x2ovU&H!Rq9o;biTsk%d;MEWzIO3=)`c<7^TrYBx_v8b?rw^1YS3hyIp0Hj~y8eZe$)kz?^@?XK z_WHVkP5RQ#3I8@K$3C}6v$ZMY%HQ&#^_h3G>79rE$(tU_oC;X#B$vQjS#g-})C6|R z)`CyAjZy)>0)oooZ?CrM$1t)n zG&69i>l`0Ona`l3v z#>X~BrUo3`&b4svTa`@_saFMxZ+`x-)#CKVN$mEG^Wk_vSYjUXR&#K{6$ZiN~e3d?OGIQ zDsiYu=4$|($Q&2OZPyOVZ27Q-v*G}cw(8=mLLXesSvu?2zwKyj|5VH2{y0#@{)8V> z*^V}wD-XH++Z8xYb+B8_2u$N!#VkKzL8eF4*HsaYEG+I0Y;!dPrG#%V2HnqKnls0$ z&(P_&^j8Ho<_m9A@^u_dt-n1me>6cZ!+W;ugb!bwrt~Zi(0{@>t3%UxSHmU8&!1%G z)^V(_VO_(n{)8#4Uc=X%BQQ)-fJwIE|0BkyzCJh=g!2hU+b?}#hz)fc612fj#wU{ z=(p8PX3EmcvJmTw3;(!@Kib?Rn_sq!U*Fa8OJSC_RZz;@|LrUq3whEmIDTPT=pdD# z+k579P^xS{$Cl-`Je(pfY)9%H)DHe%D1EKr!S4tE>;AJjU6?)HJoee$1wWnEG%QJX ziG6$VTKnvbFH&9~oTa=M91vQkSY2N8*i_^I@27$)-=`^G7ucn;v0W1g@SPAAm#$^> zfb*9D->e4b1xNVaOh{5uVwUl+E1b@Dy@CI{LMA68+vQ`qJc^7v9r*Y~E1IY0)bnl3 z`Q#Ax;knkC0u~lkHYX)sE)~X;57hb8gl2E|U3Q46(V%f&0V69TQ{@Bg6(8y*muXyk zz;t+)58v^3u+%|l=;EW5}19df&W?o%Rcs6 zl^X(Q8##YH;O$SSWjV;y)!-9yq>V|CDWQG==TZT#ju{=x8#r%$6*v8#tg~~8K*_U+ z30nm+861uu(X_3 z{_72_eC%1zmu37qz{?R4FrlGw;Q=e|gHrOFbwmy@#3=Co`oQxnjY&s<{bK>&(FZYy zo^w8~{}8~aTyY|-;{SobPY!&t%gQnj^qdihVRU2PK8@$r?=a&X!Y(xG&1(pq}tsy5m!)LVGZfL!)z_w7Z;NFL*)Udv`Z=Jjc3zU@wk9G8% zI>g}Xkfn5~@4Rcf96OWE1Fq#M>?fNhiU_cuVBwB8suz&{z;Vxk&&YD}lLEf$3G6lh zUnomH47q5W{vy1S%aNVIghxv~Ywiyok#xKK1U}{IlS+>>ohjfe`@kRDpiq}S7HQyo$Dr_I0e}6~$hl7$LiQxq{TC=c z(!l@nxRTTVmwbl(ds@2xtg$dOG8o0}q!A<5Gv2pKb^`|0tc+$?5O5Q0&*@ zgc6?LlC8@uCxmshuAVueGq8I7PM_2?wk@0q{pD?&gPFG|EbL)lvU{q-p_7HBCj^|* z=gj`P*rAJm`vT>i6EgxFSOYFB%kyIO4Pbuyy~CEX*yuU$EAc7LCqw47&-1UJx=6%< z^_0T=FPVH3HZUEu;L(1-De+%5Q~SdF=*Tq1^lqkAek@#qY+2Lx%~+6il8;wPzU(3Q z(*XX*20Z-^99;`IS~hTTFJAajnOXMNLd_FS%2_$8uLQIi`ZTI$X#Wb)m9mfeHp4h- zF>l#onTd;+ZF2wNz<0P|)yiLM&S$Z$t)EuqHfxqg)r2)WmoT!=)^TDCc-0wnt0etu z<;I6x8{GI#eBe0m#WAmdSM}&pVaqxEg3A)8aG&E^d;S*dEk^;x4UGSOt^d!(q^2M) zad5K5Vau-4sQK9vcKP7}C4g)^Tq;LA&3Dty3q=>z}a1ALo5 z%$dVnzf!1S!~Y9Yr*KW_ofx|SwQAUDEVCM7! zrq#7Sr!Nl5;cb+@<5BT>Ys2nUn>tP|(zrUKUvlQ;$Tf1)1ua)?lHN4)J`3la#ce6O z+qSx_U9oc3ja5tbZCra=W$R|nb^A6Jq_@s)`rdY*fzy6LJL^vIh*Y<`va{Z~FyS?$oWcj5 zcMCYwtTQ8bPSrZSDL!+h{%-+MX_l=9o9~2g=03%@N+D0^0oP3rZd(UNv1BG81-5*T zEfSljU-sbfm~H;)(3bk~Ip=@)4y(m$9y4$r zvflGzwSY>)HkQh@94jZ}-d-Z9uvOqBPnZK^@$8gR?OL5H2G6~9uNXJI}NP2nAz4(;VE5Fb7lq4EmPad1N;{cOs~JLu*dWR zZ}=nLPYG=8i+7!tTFLcr*A9-|$;Q)WOlnU*Bw)0FQ=)_O$Pb3Y7mB+s96Hp_rud>K zavIy^X*}0{Fzl0_vF+8CQ*2u`cXf8YT>X-TDa0W6&W7@N;=I$v^P;;2XN%94x@;@_ zg6p9cBhLYbL)}$OGq=5)y>{J8z9$7NRrNy7*Ag-|n@#&|9bb7(^}BO-zQml5ua?R! zpSkeAfZL1(UO8tN{2uMF&NQC*LqE+e^jd)=cObLNH(t{TsXPKUzZP)Y2uKM{U{fsO zGE-rk{)y++0uF`}mV~BF+=_=2e#-hP8Hz06_;rA9--diEVWz!e{Fw#vLIyl_i#Uxg z~xr}$snzFrZ{9dmGe7Tz~uLSOnN(>S6(-iV|a208h!1q&M7b*Rm$O-#x)4j9>P)q|O0`{t3N@FEGpf z;uoAFC6WhY_~roKP%uqq|H2GTJ7xxoqBKP zZ?##ue)ZXPG5jqX7_EE7ugV18DX>-hd1)0BXYf^C+XZFOLIDXME}j0wVEFgUM;WHH z^1vwtS9un2gtpb6t=g;;ah4&XA?IY%Rfez`o|?0DsxBHaaQ<4rci(}#vVpB^1K0fz zJRdZUDHSp%Jy>_mfwi{xYQiQ)?+bxX1h^{B9*ZvE{kVWXbAi;~Kj%Gf&FU(3dh&tC zcfxh=3t`+}j|o?@Ofce$F1T8ec>Bkj^y>ob>YZAO>8a9t6$=F->fcSjd8+7q(gxo8 zX$29wjCmh;KL+pzFr;hyF5fP|Ui<$6dlv)uB?11A0=&+<>eN&CPC9Tl3UJI5;I&=O zCNp{7K&1xp$PzaA428z+S&9f%i_qo{u&X6BJbLFW~M^U~hbI!!GUmTtkVB1?yJs zT4gQ4abZ_&+w-%bZz><&Iet@#<-`Jh|1#cd0!^nDa2u-LEd9uTal?@b3~RaXnUz_l_ngU4^l@|*h@e2=fa>wf0|&x{22+6K-u2b!)mu(eLl=v~TpvA%)x z$1Ap_OiWG=SqjozWfRyc1-LGL;Jx@^``p94BB`s)|KDTgyd?A8Lt(oEN5nseC>NeJ z$|qT^d6qroy}se)43#J6C$LGip1&V3_i+KQdHCG(3nt92W!d*3{bmHAzP+Jxsl$EV!+@cMG-QIP@`$3^_h>yOrlJm=H-nc4DyS9F(~f?jmrrJj#- z1RNW9egyE#rM_x&dl>NMf+Iule!e$hPJEC5z23hqFr;RN?Yn5T175HmsvI(xc|xT{qh5R{~mD7TN>f?KtAif!Y_fJ%Wp{XU*P@o zfbXAQ-}MLlml!_Z+{Ad!k;8xAgT2x>-|P`w`t;P~^9uhq@a8zU?cU~C$ENT^fOp*n zR>9_~e-rCY1>O8LA^Un6cm2$MHcp{}b*>z7X>+G8<*;e^a`O|vf@tu>|2#bZqzVIg zqW6A3_5T9a6FLot3arb&8s+k%ZZm7EfUjF+S;_6OTFTg{Sb! zIkIp*@tin8KrZ&*>ha$IbAa{LtYT>by83ob0-KkHsO%|pOR zLqt(dH#TpA;PQma3PJ}0xKfWL9TqTs(Q;{q+2dPVT2dzbSaCvFb(&hg#;jY1SDh9R zQn9H}NMvS>c=Ew^(#GpAl8*6uHwA_iudQD+N8d4E!zK}76{jTqm=cdhKTTI}vjz(% z58+wojV`q`m9TtiD}48VhwQ|L)(MVI5k6CL{+wrdb~nrP=}Jbal|BpV76d$)7!)XQ zLEn6XgRrHtntx-9$k&QQ=4lQN%#DVH2Oo2NdwX|(`*}Nyz@vZLRA@gL?l`+lo1oL~I#A9qjFgG-MCn7%qR1Tb-) zF@INW$#4drWuj{!t2-|uEA3JdEj)248AKmNE zS@j+<_P721WV65he-@JqcI}H71nA3ec=TVY{L#%r&W`e}B^!0~qm6>R_5U9C^Syg` zOR&E^tLYVwNCswJV?W=;{Am|1y3bU;CSn?|k zZC3V`mF)6)LR_Eg!voaUp1B!cd^aR2d;QUGmjs#=+~xDX9y~6aKX0S*#vJFp46-%H zI={t4uDp6Bw(|MX@2bLCA5oiZB(~R^9<*aq z+ZS-KU4CxFscysAALn}Q*(_5gy7PTu;xaRkd^$Net@7#Qr^=RTGwair{d}aCcg!+< zZZX^9POoe>tBi&1|E|a{?sv0#xnyl3L*kwbO?a!(*R`0iy z%3i)(ZFkP*&8*TnYm6;+=Wf40&Ft;^>usQUuy-MOtB)SroxlJ8x#0J!H?!?2ILs>^ zlev#Seb2{T!v0l-$JLk5$v!c$e%hX*v*zNzK5gK9Zc}p6^V^*lXWZRwzqWXPe_nDm zzWs01wb=f>Ww&$Z%6_?B{(SF?OPTL&D<1wmQ~Ty&zx%(Xk0z(@`+00yxn0%Eh12)_ zda`o6-R}n*ukWkAz4NMP8>SFl*zlU#@+t=-XIDP-W;h_@d~xhc4+33_1d!H;$n}mtUB`N1MUIcb z&)-SU*wo}~qUdtcw|7^W>J821JHH#>Q1gulbUE3>Bh)@wJO13Bou8jeOY&TPt{1oW z^~VbnH@ltOd|ANh%gO)J`s?TI{v5DJed>bDIs1aP3+8S3OYwu@BXeHZ} zQ7_VK_GpGOhx8BE853^5Fg`P}VP=5y+>EY=ep7#*DO8FPF^`GF7u| zv0-3ZAkDIn%kGuNX9uT1!w0XJg?#>Vh%)mB9B6V-+;OTQT4Bd3Ic}jHRR`@wY(6lo z6?#y1eZ^w0g0qXOj8dF79+%>N?zDCv*EOe#Q0;^D5wosTDCgdI5F@T9>hjmO+7rZx-KOu8uY_?W)}$H7HL zHY$5vj4mb^USMA3vbaZre+HwsQJCx+1*i~<7c1=t^)dNHY;r{eHd zM*b<$aanSG+FNO z(7mH&Lqd~e$l)f|)NbzxiZp;$k@NUWZ5i1nn*r^{Xzqw0V(Lldh$#>H+ z%R3u+FWMd!k4})==#bF9vpzDfK(S%YIW>=iTu}iU+jbq2=Sk$SwrOBhpWShBg4zUb z6GzD(4~}JX2{B3TaFTjlz-|}tlsD~1BWvD8UmG{)nF2YE@++8}Fa3X1z*n1~sLhkW zW`E%!JNw4uwpSnAj1D;1wFEfiUv%o=bU478nsZp@>xJWd>kWI6j>Uz2T^!NCuPef=0>v6B2FDB=VZKtoGkA(LrX{LH=3^A^EQz?8d7yCa^Q~$Ru50 zF~5^IzqsJAct}FZrGJcUHg<*m*BqF2&K>0UI*|~^!`Z0*B+=vdffc=Ludv%6TiB5@ z_Zi2&9c-3|BKkEnnR!c9IGDR0_Ft>txm@nf0vGX)mi{7x!(7XD9iUzt>Uk zW^%^xa2|`&j3d9MTcv$u5IV7d|EmJKrHJ4(0f#1;dkhmCBpMFo@inH?&5}+VI(YIN`Fpuxblu**cMD!5IJ^AkgM{gq_toj;D5H&ykT?Q#~kwOGt~A!|XE zfv4234Q={aES5O7l;{aD4w>*Wm>nB{Z=X~j1wJKTE#z1@TK24+6mIl`43deb}PxKsm zEj*DBC|HI`qzt0mszN=&~Y8d+MkhE#D65X zn=mQ1>CR{rxYp5bozZBd{qnKY3;|}b4GL^NG0n63FKk>Cr^sIx(R?D}0aLw8bAKVP z3X9DFCW&i3tga;<|JQTATy3-CV{goo#A7i5KDPTNrZ%-KU|aqo)%+I&n@-6=zS|1L zseva26a#i#nDL_C&2~y6yXpF!7jFGMKHu=->69G@x!e~pNo6hUt1?jLc=atC-BgOVJ#xmX92JIOBI+uz3(F5O>ihenBIbP1j+AM}qt*^(U_H`_Q+2k7?-z z&J~G*-8+t`yklVG=BZe8dFIMK{y7&}!%d`KO=#C#`>8=;#x}Vb3+_E^dB9z3;2h_) z@uwSG7`C!T(z?i=6m;>W8dyK{``Enh-U zu9$s_RW5)zHYQ!dll`EeT4Y~T(qG0hjpvIjnVPmAKe+9%fYaIydis~l4oU~cHnHd_ zh;x2$kbJR%{r4-QWw`?H82=Z!>&hHs-dz8G^J=@Dp{vVX3 z9DmF(*_YT}p^>q&_2`j8m$*=$FR=^2IH{14dfHt?ShC=IyaeeHk^{{)^_3;5>+l(IT9CT%EQZv;53+!!N1@cc;NjW}R#Fx{1vk;&!*m%>+$o+ZjN4)DEMz}c=L@K%}eSdi|P zLkuAdk>?9o@(wcPDe%9Vz~iFKxcq@!TAHiwiPGw=X4e$>pF6m+C^F_H@QPiR^knYK zZ&MLE5c;eB1IIMimX5TRw#!ca3;6FZ;8;JYm#2aC=u+-muB~|nq6rNJs~5D|KH!Su zXfxbW%uomH9S4IiO=Iu5lBl0>|Gbp2~n@00My z7g(1Ti$$H}2?#J$J5bM5w!!^KftyG{-+ze-a)B-FK@&_HI4@1(>6kgAb0z1l4c?XP z(n6jS#X2WS{p5|GQMoIu;YbtP@(xz6FZ}8=z2qmePna;fJCghM1(Ohl$t@0=MwQby zH%vJ4zkzet%GrkxNNssA*=gszH#g@gFfvLRumm!&b2Ko7H{}1^#%cGVzFux&XZz#Y z2krv9AM%CmG>s0BKmUQXz=<*K0`Fr6PH6{@?+2NVEa2<^z+d9PcmIR(&L7hXUnc%2 z;62o%PU!h2u&J_z{bTy931XX47RSAEJ5dmF z&@=q-&eo%qbIuAo-&rsx{o} zqO(8j>t8O~;mFwHpzEKF+RSDci3l0}dEiPRUy>h{93!bn43wVqs@I1M|u6BVv{44(wvsFsZ7w&yE zNqN@HvPuDI1(wSCle}790>@7Ao!Avqm&$ke0h_x5OVnxRvIO1~&Y8AWGp||oPtRQ8 ze5$YT*~}GI;SarLEsI?DjA3cS%yl7LLT_gYL`CsGZs2rWu(j^>cJ}~gHsfXS0m~$J zuFsq`=ijM048NB#uNFw$9Q{~=J#zI{+XYMVSXuA9oXcM=E;xIU&4>E!_0l_}qjxQP zJx`^9q28c&@`QZusT>6h7}O6iYW|*bw>?nmt9FRz0)yX0OS1}}x$4^laEXbtTq~$l zOkn+ZfmbadYRScE8e3M&XKgtkRa|~^i~nqsK9d1FX`#S?ADfDb$~ckitEW#=d`@Ad`uMMVdm z>|VG0_8~o+cohc5e1lEOx4E(uSVRvT;(s__t;I=8L?J_=>*$5unx?ZDcgHF&;Hdxe zfhVni?{`gDHU5ehKSha;sV$G7{%o`T)-<`1cwkq=!hTeGwy+RMT zCD=IEn+gaeuzmZ%^Wrz#2amq%Qe5vQHaV@DnenJaLYVsv4$NENAnj1XShT*adYQ~kshn4A^%G1dsqTysdsDjZLg?}j$MoA-bB}H~ zzGM0QpGQ|S1f|3{xhy}dUvqkgjiZ17%VqbKoeG@70gO@(LbFzw+`V8@y@Q2cjrZiy z@Wud*BMaIJ-mpz%<-D}Y)HH$jRRVkVN}Y%^t23Ugj@i?6{t@4g4gsk~&Xh-7jWhc9 zEf8x=FyOCu;BkF&Z0~lXOI=IP!2YefIW4H!x#x<{T;sQk z+2&t3J-va=xp|}fN6m#sT8%<1s~faVUTBeCaQ5Q^wuVQBItD!cPdFJuyBEb!TTnFKgprujFDDj3j6Z_etC|IOPW~kT`2K2=wH9V z`{)K10a>R0%gkF76fSqwYujA7@baSJ*Uvg3%XP#BFD5)_T4Z#CZErfq-=bXWbzaN4 zA8GJL7qB=Mu*m;${Sk0cx{z^;+N2%EH?m`$0vKx68Qyqwgy-D>{K)0X`cZR9qni5x*6d4aB>|Jv zK5*QZWiM`0TVJ4hy@9Q8<*8>2yk{-YH`>7Us-fV#!;$+od!x^oiyQDf4d6e@z?N}H zLuoIgcL2}cB|P2%>Pa7X>LYnp)G*fPZ{YoSfN$Xi7N>&SApzX?H(Z{*H`eq4cfSyC z%!G$8w?;DYN-vjW=}}^-EQnq1rd~2B^xcVDO*-p5npp21$a_-2v(ulMD zW$!J5P5quJtjVN%nq%lEDg|rwOvx9L$ey zb)InHE?b$|hZnrH20BI#9B)1-F+Dz8dG^u%u5%gn4Ze39Y|by>cK^dKw87`2Lf@GN zvv&$j{kF#Y68Nqa@@|>RV`**^z4E-yfoE4A@|_RhIkkZAegn_F1N%;Na9+Hia_NJ{ zeFdwn2EDbd&mLHvh;E$K`|o<@N8T0&?%=HFvIiM_0IabOP6|=3HT(b-S9st5cXyox6=W0t3@1Z9)|gT;#oRj_2oxgSNnXJ z-}8o>7QH^UC>$KE814^vT{0b4{|AM+g0X z%AKUZxOC5L9shFo1)JZO$gbR9Z^P!udj13N^AEqQw)6HkI3F_M`Lp4`v)6p<8YVt5 z<6}@%J7V7?eQf9MG*wB<@=uRuDo^j}Vrdf9{l~3%`1+2bk58^LclzHH6ku_@vf}I7 zIlHSq-I*vY);@Dzc8FJ;iUiZC&4q95nywtr;b!et^O-Tx>|M;Bs*hKCH$Qu>=C`UI z|Hh@iUP>)(yow$NY8|p=%=g{d=IPy*$?3XUO>W1@uWxRdrmbr7FlaPhyr}TryptU{ zO|Is7_a1H7@gw2Jj_g7X;XMqjH!ad4%wncC9Gku+xAy;jO=jmSO01F-rbO;mTPv}U zy`Fhx6GvjZ#D@$+&4fU=yeC(>6s8^MDqCwQ&e^|a;^L6Q799$kzkPF^Zzq}7#6P2U zbDOKVBUi#A_2^GgMy#BFRT~yO5H0#p>B9E-!mb5uUNctquD^X`#`;CP4je5Knd>&X zN&5EPP%!HLq}48IqEX=|nP)Olfkn5$zu#i)XZd`Iv5wF?r^&6{e;!Y6;^y1K<-qyl^X0><%wF{WcmKcaPdn?s ze}_-9I5DpEst>SZUphrW*i511g7I3RDHkR=DO|bG?IBxo;e-l#{;0cE9rbt>$r+&zEkGtA2mfJifO6`&q3U={GK_o7bIO zG;K*Eznev3t9aUu#CG{Mi=@tjlNKg*`xdk=Y?(KA#;PXCw-zZA-PLxcO!jxPOr5%( zYav@yTVv&nly!mbbE^~#KRglTI+u_-zaBhaqqghC0_PWrvu8~YYoA+@rYQ7uF8``+ z|I-(*KW3G^cKfqk+3Ppg2VI<9@u={)X8xp3=eDWF3d`r^_ZfW4-uZkPXu{^$@3T@B z4`#oY%3?U1zMuL0?t;Vo?ly%-_nWQyc&xFmrtqZt@;ybT^^e;WpEZBJr}(@*yKTuu zclEs`m;K#sORt8f?=8I^-)>uWGky8qvfKH`ZOiYLKi^w^zn> zN&H9xo9PZm(7vT+d6q;DR|_YhX&35S)Ljy}LU%ZcZM)E_-;&6aYT+z(?LwRRkwm`I z9nNy!E`TR&S}k0Z#4dKYyCexs-Ql8UcCpjHB}rtdg{xNB#jfxpNn%@fxayT%?2czi zmN;tRW;E?$Pr6I8)YTnsX4@|I=IS_dt_orjoZ!%xe?(dOvBhzrKbrmZHpw#n^#x60 zJqMIZdJYQr2{cPe2uXikE&GD;Nw|5}QTde3V!E&>&RzkjK#@ zf$jf}?8ce>?P z2L)#-a;SzJU?~@IJhm(;&FyA& zSWW7%528-)adQ+AW z=MzTxHx3<=4&T%SWF87`X=pk4XC z#}78CWjth;EBGWYV!&*FXJOCf1)40;9IU!;4g{=TGF|?o!!c$Lb|#I4MPk`^H||sU z&}P2dLhkW__mNHwto|32_+!h&D|RZz2?o0|m0V!IJnb0ok6kBOj5ajadUH(|@@NqG z{$YLn_eB19lVUcp0|)hA)K7e1y@Y|y(6u}5O#^?C&7woL6Wg_S%@IudkSrmy>b&tQ zLq7i#kt<9;9C4l$I8h*=QTok~&5L*6*VW%0-tuH$MoH)&-h#%}oe#9#D(3y47}sWP6Q|d} zc)qevc-~=!&w2_S&+N}vo;mzLYwq_yFK#jwb_UlZiqH6dGqsGbG($#D_B)%MlU}8n z?0>uZj~14BAO07zWbED(XK=mb;mIT>H?7P2nfwlN#soBpoG^azR_pe*#o}i! z+g#Yqapm@&dV#$nTR5&*@4fWdMl6JztDyN>Gt&WS38oZAF@dI88~1MC!C-%4?_KXL zADnEs_jJf<+daD_lK64*t{3700%D1sJpuw^DlK9@-up}x83jBV9W&UbcQsbeS}7vY zT+6YmuAq%Qqp|+K)vJA*UKr*Du%}9J2rp`K5!(s@DTfkragz7*d1K&F?atgI>fy}W%Y(b^}b&w#$9m@pK~b5 za)sI#my{)3zDpX|4VX*3+td!Y27hr)c;k|2slSH9&E9Yl$D9KjCy02h;RrUFlAtNp z{lYC|%K^_CH|vNkiU-B~S*9F4?U35ys{fm-dz8duNLePs9_K~;ihq< zEAfjrW)c6(l#mhk6D_l!d)I9&73sBwI8WHvdLdc?E4 z;-t-qz0nUC8~>b~Q*tP9&6d?G4y@&1d=$+7_%s)122@vwmTbUCp*0KdW#SKg@fh~C*?KT#@yaG(|6E;L8Ff94VTB*S9@nEl8%@GfiZuSc-M8qbBq}KGB@5!QNog6xvbWmb;_vf64}Cu?}hPO%t5`Khpm3Ldw zkG2<6&b^RnPYLi{IH|#8M{7pL>GTQKPZ&hSI9iexoX!2n;w2?9IdK9 z>RnfT5Yz}@W%Kk)J;0*4V-vSROLoKg`~a>pjdp>h=duG>c(*v&&p5j+rHyyyg*it| zQg^WBH3aPQWShXmyy=ITZ4Hb5&Mnyjfm$azl{?tVKQL#^-1AkX{bkR&m>quBZ`fYS zv@dHs_fmyDb3^mx4emzSOtBCAvK87)BD^*xFxLBaoL`n1^k1i!&78qSAjd*`25;Vm zwnPEulam=VELe4Jwq<-^HUA*Exnr?*2W#AqX7&pWfsZUXel+`bv}zwH2p^dlq;x`M{N)I-}ik71htOph{ze{PeSYg4rf$h=+A=d?g(yc8DH>?-0 z*?VaAGQ9ZD`Zm!C>qV zk$HjZ$`y8x7;d%z);fjufEQ}Z9d8ufXmh`CAm(pd^XcY#P3cap_GpeK?d~4k32a3c z?JGmsZI>{M8BA5HpUG+8%`TJN_WI1B#2;+M37pd#&IP|Qa7th1K6yNiNUaSf#-pdck3XL8E{`bA|%@_N&oFAJ}|+Y&p1{6BPVbi8r9)Uf6OoBe}J^~UU}9rv1b1H(^9Zc1nj&Di+m=-qFJ+H)5) zo3rjndeJmbLR#o=Tfm3gi#X~TeIBrFpUOVnqP@_eO>Key+5$!a2_|m=R?C3KUK7Wr zfCm2`ES^6eoVk3$S>qw+4Tj6V+XYuNaNcO~p1`cKqT!viCu=~n=L{AhwTG@2Es6mR z+!BrEf4X=ZTDA$seAT^^pAZ!t$#g%KtDPe*?}MmAB14qG@}v!t6F#vTYqZxddvVIb zK$P5_x8LIJGM%(}tMHjMGO0>TN^`nP<25adC zHjczoQURjT?^G@cgaFl8&m9H?mfFIwDQ#K9_gU^2G=bNr3;)EO@-ezc{(NU!r_b2@Nv{R9S` z9c?Rn+frt{s7h!rJkWA`kzqB^+M_b~D*cpD0Gdr7;K0Gh%V9$T?qE3y?s=i}F566Y77j20et#4(di#~`3b25s3 zV7c?RZF%0AY=yS+-KR1$o9aE;79})VB(&!jw0Em9aw)I~zGb@m!MyKXdgcW7NnMxh zJlc+=aY)N%rhQ;d*>~V)H(Q!Rd&&&fcme4$3wEajV`J5piPPA&+g><%ja6?3|0(76 zbdQXb8TG8&RHchPFh^&Jur=JQDoFkx5z(r?vfa|hVq-xg?+gZ0fmY`oOe_a%x<54f z&tNgV;33l1;4HxEJ%iy)HxKU)22q0+J~1Ya4IL9h9(r4_m`X6|PH6Vt(c*6Fxrw3C zq9UiRk8RDnwzbdFlRBC?EIBhHtn(CJzb)Y`;$VqRs&9zAz^=i`>e11b*q!bq!B!d2 zp57tA`Jk<$z`JzM~*Q$FlUoAWs5soNBb_?^$5ZA)a&Y+!QWWVsyIdF5St+67kD zKbzDdGI_4I26&CxnBh;81iw5_t-*?B>!8=4e$6d5M4ai+KB z6-V*JhkNojseNFbUD>v91)t&tR?dDlJqsp93nulM+tMZ8)w>01s80VX#nDp5@ZvQO z=k=8*WWK{sNG9xyGO)Tk}aBv;T}5FIWz zp;T2qcjE%9_=e0(gXR!%CasFr&9dxDcX`b>ux*^i=K6xsGrlRng8kvAINynkK09KQ z8kk;ol{+bD=Ls|$)t~n3{LSVS@Jai4(=%K9;FXM#&l~^WIl`VMz?^&k+r=`~^mO+4 z45oT;oE4PimBsXp&r) z$RV+GO?!p?1dG%a6&vJpMK9LJmtJU9{ZMgAK(A+22lIS;-;N#|3HJY$32j}0zwM)$ zzU=yt&%l3Udr`zv(;H`w2>d^2CY>`YLfccE$t0nzietag3&X)t$?-s!d|Hv0^ux;HWIPf2XX}%II>^WKhaEL zRC3|qbf0(faQSMsj(Q<=KNH2netU`!M+Cfi@lp9ipQ=}Y$is(CENo%3?(V++Ue%}S z{fm!5pB$Q4`IS7{-uyhjXnxg`OMk3`@}hZtU-^AZSl}bSQB~TiZ2}|b{hfs$_vCiI z_-f+(yXxJHnO{8|XYDSGd7^a3f$_3=E&tws ziw``VZIhp&cKWH~nV7vnre?Ee=p5XzFFi)3!fuCTt_Nr4v8%h;+P2Q8pNYko-J@@{)5y*z89OZS~Cxzc^ zbDw~SX!!(|jEKekMgblHcT&#(Wp)zaiW5FktC*8;NcYJD=1!h}o?jSLK3xg+XCsQtQ#st|r z4<2_5h?s~swfz_ASlDlU?}=hWwo}DoKii}~Qy#mA2OS8v-TEcrL1RLYN~3H_p73$j z?~NOdSlo%67^e4b#b*xIjHZG{>FZaNdj+-})SSp|pzz?Z(sYSMhuBniJYZrG@Gv^5 z&ee0^(2Av}6uVUGg>x91U8DjYG%HR&p~%7_)N8=RH2cLWrsN(l9G#~{JjT@ zy#?aZyaibkW*Eb%@Oj-2o<|({PaQGtlU8i`d!DQMp&GA<#sBYJcCT8BO5}b_3GR7e zG(Sm6P2i`Bqs*d-&n|M^IkuRGUtxl8*~yCm64uGJs}}RH_UC7~+y1dpZuwSkRD95< zD8Eonh_&m=ydIWY>jgUOei)#Aumxl=b?{^~Nf|V!$1!x4s4d~q3Rs}7oP zcEqcOlMH-{Cra&(2|SWs!0q(I>X5pT?c!$PNJow-h62_Z8?~NHZj(?+IKVBFDDi8D zn^cOpF;mb3j;w~M{0a*gB{EL2PoB}DD6oM^e8Sq0_J*lzGQRTjdN{DQ3AD;gU}$8j z&w3y)+;LQ8%Yw$hNkU8>6Ar8XThI_V>p^;fQ^M8-3CI3VXjvw+Wk&-e{|xs07mq~~ zbgu7Qm)KtFc}{kF1p{Z+1J0_YiN@LordvNzY~!|h$5`gEz-ZQj;}S|fiSZ8|1mte) zu{3j(`ugCQ(4imw+&>N`?`lZ$zhchkd*Wh)Vn98MX~xL_xu+M7aETbIO8T@*I&nCe{AlD9Ycb5|Kj1#|#fGzOovk|V zzhBr(6m;FHuViWye#pRIq}X35;8ZGed);@AiI z85h_sVxCx5n#17}Y1 zX$CG8PuUEHOevES2j_|SX-v5AHhy{mZ;gVpR{q*Xy)O@Wj}}apT+V!0OrL>m(a}U^ zQ;Fl2SqZ%r35^ZHr;$78kBOGfqRicmw(1XMyV51m>v{#dh#$b z{$HG>!XhWRI`Zl&rX80=g|rS>@%&yOs?x^5EVtnC{#6cqoj*jhV`nNys5wtN650|V z(ip>Lk|_N(V$z#4t?Lql7R)&A^USFIkx$LsydD1yo4?H1*vi$vZuUKk`qR0p8y?>X zD#(loP`AGO)4zX3sPLt?hbJA}a6Kx-B<=4O$r{H=jdL%$yDH=|{an<@wmnUOW4%L< z(zZ_4P9tTGwtwv`A{%*cUQu9HT*)PCWW;l-=?GKKPh2 zoF)JLGdJEN!D~2UqgMZcH#$}68zx9`Y8Y6IJ;|9^XG zqP)u_S2XGudBmP$ag>~LSkGS8xX(Rj0q46N$G9d5_B(8F*!j!Bd4Z}D<5#1L>h<$B zXC5ozOVzMp*}s~-%VTcizk5mdO5a|#>}U+%SbrorM(Y%V=%T>>{4dro&g^Kj*|o9c z+|C9j;U7oTpD-}82m~=so6(~3KP%zU-l+#dC!~B-(Kcv|dAln7{{sf@whx`ICmea# z)$f(6d7~-XBecrJ%*cZ$j$euO?tSO=?!}8`e5^edoH}E$EOLtH`uqRdrK1-z?ajX2 zdQ{W>5xaN&jhT#EjcN%=5+>|F%pV=()rqmPX`E2}#d;E>+!UwsOE2~s%`1Em!!fZq zoqxAGQ`8F$C&i21|F$hwWLq3?WJzz^(zl(<3Zs58_VTpMatoO4?%`mZa9C(g14qgM z;SFc?zcAY#h`RHp+4f7b?Vl!v4~{%X4%pv_+9K2ApmLatr=HpR$6d1t&caI$7751x zo5`TKgZaF}vH!KMTQd2o=k|xZJiL{$cU$4??Y{q6c2rK>QF~kHf}>1;z%*PStz;eq45@(^fe5!EOTs@Je2%3$LfJh3afbQo5X)7 z9nN$1D9%!}`@n2$(kLk#d#Uj7qtuwows*x+4+wc2w%ozQaih^%<1p8bCaV=qJPR7l zCoqdWxPQab^QP;>YQ`v$m9n=3kKR5in|gMx_|;f(rWtk_NAAhaxPP)o)Wf;n<_NQj zRMMXb%}Q@#`g%^i=scvT!1#3M0d?P|CyobJOgX@1ap}3Ikm|~Y4JrSHSbns#EogXE z>ZQG0@1cM$qQ5{hIrl z516fU7Arn#w&QXB+j*9iM|tuSo`r?ZzD|DsUp6n=DcWvx%iN}EN#HHUf9*;I%*!La z+#^`r5Av++ZQ<}}u)T0t_C+uE4F={L4E#Nens=PpPw{fRJ8Uc84Wj& z_;A!u;j^|mY;VJCy@Zk5ror@qv+x{w%N>lzJQ+lF&Y8A2*`5gxu5x*1A<9y9PO#{| z8~+8Sy#h^bJN#@5I+t!Zz$S8^&7em#%gOf4<)EXF#8w5w)+)x$Y>@C1(cN)K`;S_| zJ|nKYN#xFWsa5|d(zD#r&0&3TUw&Nydr zAW^h9EQ&2kCxYo zrMd~lKe1`HiizZHVKTmZ+9OEuWCP>ZrBB>r<{#RV&XT7Z5E{3XF)nIZ z;nSQJmWZxd^Xfz*s}9&*a8|oe#lif@VZj8c`kxJqYCX-RTW0elIN45d=5jq8rYil+<92ITZ~8v53oU7r zBD*pdsX3^+=43IIW+mF5_&`{Hms#R)v ztJQ0AjxxPj^lSO0uYL{l`ex7Z-?sM9l9mGoE0`5GG{nU^^G-Qr*%7+8Yi?`Z(WSqF zm!0BiS4y^XJk(wn(qZ+Rq0o6f3>@PaRNP z(8ZLaIdN5l%GFbAC(Ki`%zRaQNX4a*ss4@b{cme|EKX1R6fC2o^0nazi|rEbjX9^| z-nh(J$>MVE;mV?JnH6-qc3_cdQsEuW@ z*LCHVwKqO7+>}afTXb|A$CL?cg%>$K<=;1Th1Q{`CXFtlCzo#Y$XT-L;ihZX17>sR ztY=@+piuwD+1}!?Z~y~mMT1`l7taj`0fxgd-7iZWS?^2Yah#fh3ud?egbox zEqT}{ooJT7bExL4lB~yf3Ex&m}p3M`ieRSCDLxaf-=CZ2?B(5DWIMS>oqS`69)?ei z-Fe5xanRB6*&;4Sy$8+aEQic$uQ%x&{BZ4%zf}Glh4*e6>p3D0u-Y){?_sgvXv&WG zzuvFIfn&}AW)7x1SC-l?X%OmZ@O;z4aYgHS1WWG>XPFhu7xvB3+Q6(b!_QD;xl0ZM z{{g17wM{G`ZI|XS?`~Z!Q1&cphjYC~#MFZdiHnqmOEZc6Ij zyQ|$_MB1o0^T$YW#~k3WcaM{prvKooU`vy>M$^O@Y{g1BXP&UCCWz^BY}FFkD%Pvc z^P7dcMat^Mq3TTxtS=;3?|3WB=rCH+(aaGhuw9E)V51=W(REYh!>hj*AMw`Pu#USv z-$NiGeD9i%#{Hh=C5)C64j&CVP~_++m^yL7-$zx7Z{IBPzs%Gt6&xmg{jNdst*i51 z$mAc}=HYKr^S|*>=Y~E_!0l^FiH(P@Zi`a!Fu7QfIUrZWnXvMv_E#z`=omLiIA)I zWaf7#d8Aq-GtYi{X4a!Vq1o;N6L-ulR+EFqGu|!SHQ6irVQOu&hWmo>??)y|oX~4_ zG1k`&m0|p*xTZsaIgOQx`9Keg!Hn6FTpTXU+xHxQ{eC=rXz&t8J}3mzl?qo8us0!+ch4gYc>n*BK4c z39UvQU%IAr*gd(Pk}Q>1$HITb)x76weFURQOOtHFN7f_tj6t@|F%x==F)6nmZUHR z^^aNXOTPBoPRRf#jRlTN|FN*HaWZKuiP3kApTK-&uK(4WtST|FE)54h<}S7CIGCbb zX3%zoHKp^?tdF-!U({cAdn@K@Hf_4bwQKKFkF&4iZk50Cm6fGYsiH|qhB2ePjN{EV z)fJ6QD;VzyozCk1#@w-6gU4}YhZ&2{flVj=JF44S7=<<*Sam~g ztH{loSqu^}YICzaS|;q0%m^%5ZdAH?lgBG>nFfZE?~LJAGkWFDSgtr+Z~lVQ!^S~l zO0(UQCY}q&PA4{oU0AY-b@Qy_o*Wt%EjRejIKrq_aY(FS9p?rIDUNi7CtEh@nmKSD zT&?f;F*4nw*W+;&(+Txvfj42{(bKGUIGeOoxoRA`+|R7m!6=&GC}VTjR7AgZ`sP;W z&^VievJA}L2U=n-F&juUD_l9iSwCY3y9T3O^?F-|W}zDgIF3}zH*d7rv&ZViq4Jyq zyJeZfzqwa*tdR{m)R2GTRLn9x1s#U|>@yX7))9xdh|9~d|37rHg88wZi&uZ;azt+Tw3OO8h{rJVHyzw|Taxkn8&Frq03ov?i<(7 zINYgFy*!BVCr2=Mbo$4}e~}&yk`^~h)~0!QHi&9e*sgfNt^6x8qQ!JWvvmT~vbp;b zjxyV4I31g?b4d=f;u`MH+B)YmtWruDb3K_Qbm~27zwTW1&(em;p<#kU>Q{&^r7>@Q7y7SB0w?T9f`~hfgcn z=gae{L7}xV`oHIm%Dx-S6Lb>hb}#oh7RI*j%u8=J*5xg{U1vUSzNXv~epn%P$Bcv( zR~K?lQaf2ckwLNW@DV8mo{1%%eF7YHk}Co(X9Z1R*OTgFXiNxRz_8JgsmXqA z&fSX#nvNvi|F74uvCUsfY-!reAkiY-I7?TBQ+5J}*2vkr@<PjIOzy`U2WJnHOB)ugYtzeUvMQUf(3!{J4hQSuLXm|EOQa?hE}vJi zD6mbkberTesoK1ij4k!+d?p<1t2ig=cJ%(9s7#)M7nSMr>pr}Be$J}gQi)aEi{lHU z_M3(2^3@H2PAvRY&Be#%&ILY~Dm9qc$Re=q#j#n1Ulf-dsP|!D6e@icxUAjSG1ZZ$ z=!WF+uD^Cw3VO8~6E9voEMeg)pTDirP0(PQ2eVN2lfdKR+V-~;CTysw7i0=O?6jhB zQq`TEZ31Qo9GWMj-#C~)Bba}(0$Bt`K--AoeHV_}oI9!urFp)+LJW@_7elyqeG3C&4CPzC>{exAukwheeB) zB)ZF*-+Qv2E%k$?`gD^vpT*pVWV|K|l;8QuHrZr{z+t}lQx~60X-~-QVA7d!arvB- zHv-BLzQ;Bmo7?kUD&wLWbJ~e6M9Y;kISTPEBJA> ze!?&1ZF7QU)Qh;~wEjF_xuPU*#j4etCRyYMv_4RiD>8X$!>hBU$U}GDtt#zxJNt4n zm^J1tG@EI&>wyxlkB?cpXmR*|4MUa(|93o@pds`#rK8?WK$At(Y~~XoQ#FoyCeuCl zU08*@3OZOg*3CHbSMkVq#V*&Vi7cEPPWk^9XVlN^+$DB%z1S`jO(nTEAEcxXO>0k_ z!z_P2!|B$R!?N!i+-;s1@>gB?Jn`F+t&zoEQTu&yIy_8th&un&3K`wZ%)zVe@;nE;x>#T`)~X|uKck?qxwd(NK*h47t_>z zA~K8;ZHtzG7LU0sTFHJrfqga8!3hE%41#`iG@nyxXib0PRWHaBaPeSXpib(E1=^de z*w^}4oT`746@K`|eA5{R=jhrtCnyApZNBtkHm}WFJKmZ$(?^Z{iz6PIKT--ekT2pg zxc5f#{0}y}xE4;uJ{^veFRqgG|;xBf2Ea?`=cHsXpMYcq#HSAjw zge^HXTA24Z>z%MTwAn$SB|2tt&$S!Js(*R9Wj=WnxzWni_3lRA=|ar)+?mpMEtfD& z5{O{c-Wr|dEa%W&9FQ%2N#U_+jiK=EwW{UvjomRXHcd7Cb+GrBg1hDRkH->YzOMUU zF^_-Gb183)1eTlJCx6uxba;0p3AGsq87pKctnV)%ElCJPj@;vjR)`KP)7fP?q zV6~LXK4-A%vFPh3T-Igb=OiJu@?a)rn>_B!X`WvbmGr4Yzzpz;Tn?MV%i$YVq%FazTRcx{!9|~^meVLv) zb;Cu-LBIduD;bUyd$ z0h=+$#KwUC(mrhc2Ao!|Z)CV8NKTZDaMCl|nrWb^EFf}0k@e95-Z}&3+`dVTT8jk5 zEH^K%m)30Q!);kyji>#?H4$G!RygI0{F;_3AV(L?e<=XRJ>{d27EdOa_qu9DbF_tqqzHbnnj#HrY+*nj_yt z-`u|Ku>FCv4N*Um>!p>VqD}IiOq1fdC7YLVipy*VuXaY$hp!gLO*0xx*EdKBS=vrK zHPPttA&p0pj{~;uU%!#dz@S^8BA-fPw>C+W~BY(M@{6*Vw9QsP;6Vn6C($vBMxGniDDd$?0FZ< zL>lWQOA=r7!0}~~~7`Z(96z(h#Idw_2LQCVn>me2=1>Ty4 z5*DxiUpaPYk|R&w0?q{v988xCxS0N{d{)=eRhL(&|Ify>bWaoG3pe=y#w!j?a2Q-k~I}Uu=_~ z%KAXq*wMlCWr4NfWtIiP|1WJ&V=(z|cEHj;iS<>mqohcP-T}M0x-1>VPT}e{-Nq;X zU+b{E=p@@&Y!qu^7k=LHr|`}>LM%r<`x^CF-QQU0a>eGeetmsJ0&|{;vtz#{dx*lD z$vQ2U6(bT@v=}TEA27Nduy<7oa7kd6Q}CX+ndx7jm+_TNF%4#N3`|T@m?Ji`SZ%57 zSmQMDv(wHbXJ!%m%a3d%nOL|)PJR*o|E&0o*$e5QH2afD?sE^h*O$0PP0?YPZ2h!E zrTU8N#@F*s8Qc9Wa`;-m*}?e$zvcormNv;V{erR=&D(TBtV2S0m6?6MhP3l0ym}}o z;jk`3fzRUVpGnV7T>28mbd=fV09(=lxo?dkQxaGYy)m%eQV?XKd}hDQa`+34zX##`ht{47}cL^WHFM&kYU9t{08XE{(og z8vD>CmObR~l`XOSi(@ZejoUvtZZ}ih%+lw3u14P!jelAi|Isv|lQ%*1Qr!8kk%DE3 zTeog9<1sd1VA}9J(q1Vbs+h%qnaRX~S?F-m%&V*k3pOh}*lNIV((YQUtZTyKt%(b# zdPPq2UpR^D+mz`0qOs9o@x{~PS6;18t^StSxS0QtliY=zTdQ&-WskD7Jm8qtkU9IB z=z$kf629wF7=(T>p54|W+u>;Jev-v2f%)4*xmgNqX${&Q2bf(PI6WT9?K{Kf@IZB< zS>Dra+8vzMzPWkf>)B_OasK~tjqlfSSrwjq*75?*?FGEw3k1yzMav_Fol|sQn@fq; zvqdDl;aglNU0x_!-n^%gOH8!b_PTaQljN>bJ$Z?oJ3I=_8^mTLmbz=jK53M-d1xr^ z&i2b;?Jh5=n8s{(PZpI2;%^i=k`}O@nHD(jf%u$8{-g)~555)8oSw&iHg9%#v&KWo zF!PyPj>s-z)LmR&y>ol@-tW~1r&sH4H>ydlsn@)2aK^l5VpwrzSnY0c@69H4yD$Id zn9j7wQSOyuz48*7V~k=g4+I;TEuV&aPBaTx^MEhs0E@?g?I-+L6>=*}{$Q+Mc~s_DqvZcP4;V|XH-%QTM(${h{ZSxp(I$CAJ7q^x z?c%odAFcJ_?o0+xq}MzsDYfXB{*`G`qV%>#=^YQHmK+qn(4abNI*VHalVJi&&JLwm z1-4@i0&9F4iz7Ooe`7RQSfH@5nSZ-MUZPB2MCas2fs_L=dw=vkwCH=fqqXpd`Kupo z*%f^cU6>XflwQKf)ezCoDj9OtiHYatgw*D0UxhO@hgs6<6WHt)urj4icsh+W?~s0w zM4{=I34%YnG=5HYw4CBvIpw*9`}w9Rfioj3&8iB&np-5Wge6T1u3TAjtyr{dTA}6i z(#q+TKi6gLoZfh|+BmbadFPDYpED*}&YW5~bLP&O`eu{=&#|1fv~t$UowL^doVC$% z_SVYTJ9p0B`*Zd|%Q;8uE9actIp^%pITtPGUag#abLZTb<{KAGBI?v}(=CU2D$%T658A?bWKaH+QYQ`)ln(t94JS*1g=d?(MI2 zAFbB^|5~;F=dSgCf30V<-oRSDfphl;-rpMptv8BROK+6iy;1h}Mn&sQs@0n`cW=`D zy~)sevuX8a%iYuG1TyK?FJWz(yuCOmQo_5oTA^(Bj{CnZeQFSxQoZx?1J3XpED;P` zB8mdjqIXU`wPR!ST1^E3rsR7+8s+~ylr-u$9;7h~t~ zM2T4n3tUPZ*ix#uw#iJGA-%6rw$DfL>t>yA$~s&7>)6d%(wqZ?%~{M6nCm6>>{X4K zu=$0CMx%g;qu`wfoZ%;VFFiQyXv4d1cjfr?p!y9)BX%l;E^l)}-!t+sc{jy~Diz&RN^L&I-&~0XGa2 z53Q>G`rz)RGkfp?z{KAKpG}UBF)9 z$)+TCn>F^fw)VB!Mwusy$GDhp9r}CmDcAifhDWUqk9gUNvlt#VJCtQkU{|s$Nt*Z6 zwDEd;B4T@~(8tko=eYlE^?dNad_k9MQlsd-YfcSs?H6@jOppjzm;HZ6qn?D* zyZ?=>8xDxyX_i^TD3szLc)&)yVNG}C+QH?8ZP0~rP_FpXb)8_dg=FtAR=6 zzd??~u8Rl6w>5^;v3%XZ;FN5^+|nSCQLns9_WS2>`6Z0GM*9PIB&?2kCOxlFsE@%q z#X|Q`n)z&K=B8co@24#@ptGZ#ntiac=~h9BL&h8aI21o=CiQ@zTfh^R_WI zv9#LU`S8?nBQco~(S(j~9n7CTKO_9~gG@te4 zh5`+LrzcNV8i)HW*27~F$ExgWM6{n|66fZscl+9GpRc>OS$SROzPbe`Vn0@$k+AUZvEBXc z?Va7#|M~3p{P_6f^lbZnyS=}@zPY`-p1|!<*iPc9sMK`u{IChsjk9h2@ zd#1;DlBxHDrx&<AI8}MAES_Ng^}r!#4xt-? zQw4sSnur!XHfZitT54cC(Xx=^sQ_z4gmS-ioyUO-U3b2?aZIc{DbS(6%jPGK)f<<` zPbTlG|FP&~?9)Jp4)aU4kGWJQ>`>yh{uDF0eUjo82al=yTt16W4U6mS6j*1fa!^^h zb}^4?*A5|G>vt8(Y#h9&#Lk;uJoDm|4!hSe9x)LM5hsD2R~DbMIFjMSskTo+!{4Y( z>MMiluRp5(CQdmX@vGPE6};g+HLo`ve)e@O0S>q=)Pyh-8zw(4Rcv4VvHqK z)Mjir#2RK3>~DEk));VMEvJ#jr|YIhFH{)S?rU6UWtEf(~?y*!-VzHIqXd?B6llAn75w@?$=TfIDLUNsi!!NzoP#6)?TZRZ+1-P zId!G^yl!3U1~vZ-+)N5#{5c2w~*c=Aa&c(WeU-lw7D{Nd`ZGkj+{M87tu*;g*IaxOI% zK5r3iVP9f9Bje5m<{1+fpEs~DtuJ8|Uv5-vXzfRVrBz}o|F?Km#>-`z3MzNcXU+o=o3%`QEez^kJu`SpdHOvQJ8#R<)__Y(Zx zcs1IclwDdho!je~+N2(-0EWnY0!=I{k|u9I;mE}spY81?Es znC0(xFpEuFvHr))Bjpmx_p9U-df%z?DC;Ei;zVlYF zTXGccSa>v<`#_hk%$kM#X(o&j;y0L<%@A~1QXyF8_VSUqaK{N3t)huNIZKpfcRK8} zoM4}vbAqk!08g!lGOr^`qd>+%7pH~^Tu%$?j~h1$9Ct4{toE9D@l%8Lc}oriwCyrD zZn5G6!^OK!?f)4Z`7;{btPC8OL=={>m^rjB-SMbHLi2|C2gjb2BZpj`1n6yzV zuGmIyGLLSS!x`?J2l@YZEZcAJ(MZyVUs+m!$;d>rEm3Uq48?#JX+y!|H){@aDJ*G_ zn76P$y0i=2tuz3!5JJ-Ch&sEYEqd!>eSG$l4XI{X!?38w?B&H6((J zn|WVXNPbPS=K6$XUK$U$E2k`$`4MnT=+%wgIZXYw;vDry%pW=S<+(6RuB&j7I#6lR zGsk%D>oX4oCpGdH*DRLyT>9GLh9h51S+3c=1y1${87`z3Jgk^{s`c=(Lf*6=hlCp^ zTyfLqx_QE@Y}1srYi=9ea(G)}vQ}azgNTb)Q2Pv#rS}6?I!C$sNbI1_8jkWEhg>Cf=iE}(bI=Z~osbk(pMLafOM<8& z=OoJ$hJE><^Kk!9U{IXje(uG}&l=O8BouXc$1HnlELXeqrM(8jjRw9j)c}SJ8@r~v z`89{7CLEZb`k(s&19!y%7B>Dg`Rplud6zdY(0%C{yzA1meIou{H|pK8V{aaMKP}y5 z%TxxP6ASy)W;AY}Z2VI9%g5em0VhG7yu$9?3liIG6!@>tP_q;{*dSru(iNS0e%752 zTxkV|Wq%!Tx3yc;qx&jl?W5e)s`s-T!`Iw6s;kB#cHnsU{%;!lntJ6oPFui{dLb@d7EboS3dBp=hHUixpv$`*5{V{6~V2mT37mnc08OtuRV4PE2Bip zDh{g$h0`5Go%Ak9aLlVHyfme(=)d)3#jC|9nk8d6o``InmG-rzfz{T+Nq9vAo70^` zF7H;bvL2H-);Ll8)TNH*C7Q7>_ihS5ZSd$*P3R$EMV&^jhz-mtQyhhgVt$@IlUKiO z^L*a75l168^FPU9bIy8xVUi)ohJ-~*uXeNq#T_o#xcG>mAk zo~g4pQr&s-oIl_9&tB;tr>wt$Irh_G#ee-$^?RnSnh;U3C7$o|jMFvGKJ?9Yd%A}= zJKuWpwGLxryPvE%EQ+Cf~0M-F06C z=ay~g`!GGfB1%p$%Q#OwViVyLXG+t^lTiCm^W<^B;}ctr?rykRSh2*< zPT+!F?H=8^oEi%_YBwyF5IPWj*l=P`%7TZx#RZy-GTJ#C>n&U@CpB4@$p4!AfTvk< z>yOa>1slGbScpG8*?g*QhrQe=WQ3^arn>iTutlT_o(v| zcoL5LH5OEET~H~+!a75MYe93}lH-lCFB;`7w3j`XcY45E!OF(3z^FN+X})^$ln0jQ z8(7QC%ZviJ2mT zlCgpJ>jU1dh9)fu_QwVB-VO%q8|+SAVBr*HNq14=Fn4WikU8^#KYT}%^bCn3o4npl zP}8<3ixUl~4iWipBzVe!F~`HlYkEWGboTHUl`RE)Uk&(d7>eFG@YP>zU=2UPknd`} zIl-6bVnN{n{;vwWrVpyMXYhoW+FfIdKV`tzv6S;BbJNNdU8_%Y#iSO`oGPn!;6L-e z2F;5LI76PXg_$@OxJoQ^;HY!pJXuib`y|Hb1J@Q|{@V$>Cxm#nPf&cfG|XQ-`gvnb zgEHfU4Iv85t=>(XKOXSi3E-}8Nnq~^P~7sEJ8ef>nL#W2R>mVvj2R8?dF3KXE#W&q zaOjnVzjt5JZx@B;ht1m2Gd{JR>O-8Qla8}e;&Nd4*IX7Zx<`U9>; zgT7r2I`2Mk1+{2gJHUHp0cYg}_8A9wo=hmSdoD2VDR0o%hB5*6#}+(K0%Q{E13dW| zIDaX~o-(Xi`(m;^=M=|Im-QW?Hx9D8KXSXXV9K2jyoVR48ZC?Ys!)5ofH&nE@2NwP zQ=2%?EBx2{<-m7}fvx{T`qN1XwvBoUO40AsvMU3bFFg>QxLM0~l4Ovl^mT)jUjka* z4jwZO^1N%{pQXUe^GIq+f?D5>s{b1pSJgL6Xx=pC@d9q{AUVkkEEg8>KU>gqx`WSE zO!J(oy|x3lhO?9R%q0Ivl>mltE6=I-1o$s4=U%)h`&vTYJBO*U52wyJz_df1xh*5* zzCnu%hvmlyoJI?ZEmlre5a#`_IOXKZIj3(n?roC4^HILUq3i1b{;QF5FD5woH?yV` z@%?J3pMJ-Ir%+*z+eStm2DX-h{>lq17asDiSyJr%Nb%()xv&Rw&uo~ceSqOe1OKlF z{D&VXNeBu~DNy*`$#1;qKd0{jt#uEp{(a!_6>t&x5cX$+yJnDxwgAh*f(gG8=Kedt z6L4VWeMQEE0PbrG_@uNJzDnTT+`xL_gptz&F4MF{e;(BHhaELN^_=s3gWWF!$x{rR z?hj%*lBz2UCb@?3onOG=_#j5%08`fj9<`*olMnLr2QYU!GDd%}_;G=ELPOV?lS^Y? zEzQ5Cz3T$=ymBA@(8>S-j=76CXDr~H)xd5#AxubtIc)*Q+(jH^1uXm@=2Zsz@HaHo z{@{4)sTUJ$bZiS_eO`m!YeANrEcWc5{E`Z6b02Z$J4~~k#278WF`QBHcPfPtHLNFSL@O8x+jvBtGPOkv{54N7I32M4+K{}!EBR8x*6Sbs3-MTQ`Ov6Q`*m}y_O@j2 zZ7gn#jT08Dom%)bTwAe$HH~5Whb;CJ4R#k9*jm(?r##>n(pu;?jVJZAT>Ng+hI;QE zt=T)oSearBxDP+z?tRUD_`+h%-&*1ZJeLx9BeHp)DolO6fMfg0Fr5ar;}3T9-QL-r zy*jcjVcF_kt55IBEoMw%-#y8E(-%j^iYIgL-L$=^u+&vkbR+lfeYbYqRAhYoU~1Tb z-3Pn(oLs$!WgFwV3AHB|@SJQ=4V%E2xO>kF#`@4x52jqJ-h21;-uu5j?mgOF=Dy)U zwQGv`?q{p_y*|C~?dyH-x%Yq6-v8Nq|JUsO-@Et!T)qGI>HU9S@BhzzfKlfFv(Ntn ztT_kRdk%1}Ilz790PmXv{5%H*bq)&q927k>$Jl_`EP+|~0+ZOAgYti5<+kgvs~q~J zaZqB-K^dMy+GpwyxfU|M>fj4D4gENQC!kz5*bI^ay!OnvR19*;V{W%=|C#~Qzub!f;%QBvjg2Ne)_4^l0j{PHk z`NkpL4~)zXEX4t=mI{n26OO#!c*OX^5lNq;fi)t!4aX~Om}Ps8);I1+<1SG*=AF(~#Fy*KW@Af}( z`*_aSK4ACAaNuWPJF_76$pfzXw;Wp}bQt_ToD6kf*w&-1X21{=z+xG|EcPJfVIre= z07F9n>)SWyQa>;oE9_)&U`u^)?z;|aVb7VJXU=!DM7A3qicMypslm@GtMcvw@5|p> zf6cN=9-LujDV(ri>*Xc<`x4G>J9IE?&P5NN6W-G?O`p01yC zsbSBh=L;|CE;z_yz@EKkkl?Ij+;OX|5IYI85y^IkS{V6ZP>W;b9EY&dA8 zdsI>Og0d{5Py$1?19RxxlO_-TGjKXE+WqC1;bACFxEl8Nq$>kM{hjmax{*pZl#>*A z&pfzxMnJVV&CIOz7{d`Jp#qk}7lht?;C)d)nOi(oTl(x}tFu=Xa*ryXy<+Nmsl)e@ z)89+hcdyF4z0|Sxponxt4+R0R!I#2BC!G5`sru9Byyk%a9yre{7(-e<2qTr2P(_g+)4AXnl!i8r^3 za+rMH*kmlc^w9U-vo-e~AH4Uh_tXj-5x0wXJ^uVZo^E(=<=cx=XYQ@=VJe*?_~S1l z>j!3&2@EU+NB-OmE45)#ZaB5r=bm%oIl%^IW&swL7zV!y^;hK`?uZ>=Ryy~{Md40b z?j6Ips`V>Qsu}nxFffTIu$=cbm(*?+QMg+({w!L0mG^$i5Q>gzRSFd0-6y|@AVx_#pv!s0oWA|N+my=To z*c865X;Gj^@VusvvCry-4jeCtyJTj-T$1<9hv!+bU74#dvqvstUB+{VGshe2o{C<0 zxL%M^Ea4P`14H%&2F?OT&AgXW`4|`z80PD+y8GNQ>U+iUWX?PH|GB~wi)VdhuwB5h zYXQ&y=bX_2W&fm1&K_dQ723PGV0&Hz&!q&H_mz4Mg(r;?cz;abj`+Y|zhm-U$pshQ zKD^41z+kVyFvFLD$>26q00WBxqnLs5|Atd66Ij_57@Tt$?$j}`1u(ds(P`#ou$;ia z9>Bopz`*8ko6UfM@qobHri07{@7N~1e-X!^Vt1Qmy~?h8vdX*+9uBv^`rS_9ef)`! zfvMr`j97*@@7^*OFp523dUZ)xtbPF_^M<$k@4kD_$FOdr4!`_IPJug-wzt2|W3Ufk z5EEdmKYVbi9fPz!!&f~97JCMsN9#Y{Wm25L$hv_oa_-u zx%Av8HHJS)wYPZ%u07h%#@6szdcs8oJ%(=iYvuuLtP2z$-_zBK`RP6X_7U6v{c>OD z#k{KH6Zp^apHC&?!UM+^PEoy-8y6lrcL=KbEs41J$hAk(H1Elci;vwWD5}PtILteL zy|T1So5i7eKUs5O9#w%|VgC0mGtcjNcsSf|de02jY>i@d{`;nB=bscxm-)u8lTr|x zH6c;`-p)iFm%OE2uE$s#`Q>^qFVA+qu$D9W3cK;C{gYize0+Vhf(6o%?_H;5)5dt0$cNX#Odl$zJ9{!6xg|yy_-mA~$YtSExBC zv||BlTgT!xj5pI140PG*rAt0IawtzJaN^S5^5GDhrxB;>RH>wssx!nEF?6TD3RIW0 z-gSeC*LTm#bQup7jT0=cR|?x@JdPYl>Nc6Tvu#cEqFJj}t=@EKgOacwN20>U6Gq&+ zoXXj+*Rp#%x;k2t>h40pd`f5@xaG~H=w73p+&mG;_-y&0}Ym}AJ%p;*)e>U z=QdJUz;oZgwzxsZJi3AXgHY_0i&oaglLP<%V=GwC%B`f4vw_Lz?j|lK71bB(nV4iY zIGp60qO`-gzvNqF#^R}EQCX|EPOCr2{pNYY0v1K7(kGTjSxPh$78 zuA@=5LDEmc_btPT6FwZ=a>j`)ye1NjO+PX$5*QflBpx#T=b9p-vFZILjpV+)Zkr!A zavWNAuz`Qp*Vjyj;u9P`HS>h5>EJM1@hjnibG>+N$t7>|+7e#>N7tP5cQS@22u!-z zF5@KU(A}iEXl1*!9+TJ#wxSz~%Q-dVceKjZGRRFzQ1&<1c&IL!mw4(dPlG}ezmRG5 zW|oLvPKWzU918CQ-8dZfO<`6jU=wGs*l=OL+k_m3#g?Xt>6a32-)5SZ$f~1Z#wyto!FY?||1+LNmQpGw zI?g303D5oEqT#l&WaZ<4?G9gdHRn9wICG)h^p3*f#s-Iaj-3bE&6zGLHkuridAGrh zf5Q(3j~eAlCn1T6Tw<;-bbFk5tG!ur7qAGNQ3zo{?i8*EDz=j9Qwx0H0^=XL`h$UB8O(SfL;Y%<38qV z6D(K-z3cBCzRJo~&(Lw1XMsg7BLln1heocJ18vRO2QxmEF$AqsHBz$>;Zzc~Nb2&R zy-~=4*GS=TV&J(5?~(#hkt@C9U$zC(cY}|Iv50kJ*`FMp47Uz@@Gc znavCyXBm0cNd4|AmI=AEWjgDHczzuRU+#c+>|H0F7`q%8RCpG!3H><0ll9`j#VsG$ zlzID_O0KQ>o|3>2q|wCKRqw#!Bg@dN%JSTy&0_}?%RHbYwA-aGjD23AGi|fx2ZnpKvC}T z6~8>q&rVwwYX-)9zHe5Oz@I3><}Gn>?b>Bc56)Q%sD+<98@ z^ql>%IrV>k$1||sQ10FO$UDe;!pX30^AnG}F*os^a87H=!Y+ZepI)8V(`EH>(WPwf z8-FsG#b+$YY%zI~<0ZA=Hq!zQ?i;$NW9khgXMTEWyX(=50?*6F^V&XHSZ=r>$TFk# zY0{CEL5mqn)Kz0S)ZT4PyByCseWeDoi~?iyEdAOO4h=l~Dki#Dq^qF3;e^W(;2i+?h#&=8NFbZr{5 z+IGBgBQWS%r|xSTjwXqBwp+@h90ud|_QasBJ%zN}gd^984Hzpl2=n!R9K zV`#?|jmbw2J&L^l`&s#Z|K;g+zbh3d@NRL?m~h@{@xu%MJ-#erzxATtfpOub2aGHJ zD~ja@+`7bdvSgy!-6MYU!^G!5``|Wr!Vdlu(aitVU-M`>E-0G3&g$OITGNPYOpm>` ziLN&9eGp%k6+HhQLw%c=fREEdy3=xH`6kWPomG+gbr*Za{)TZ~=9sY{jbW?U*~u>TzyZQyoa=#X{Xr*AiAs06%ex)E*o=WF@>zyHb) z_;Wm1CHy0qaqR@2oQ>U?7A7981>HtMOpk0h?pd@Xd}QBnNTztp4o9o{zN!7jhEM#I zES)VB7{yrnlvn@#kP-Ev@y7->u7eAAdj{Km7Ts(z$$YubIp-OkdXf&zDnBYUnt2Zd z3td=|E!ZoR(g8HL+87uEoc>O2VB+3oUH@*)vfnp_6lMts@o7BVyLYzc(S>$`#_JD6JFw+A zu)A~|bnZOl-BI<5?YI_4>ZG~(FPPaRID`(!rrnS=++ZQFfvvQI-Sr1M*Mo-3x0UKS zC!N#oI4|9Hp?Ka!?e0s_9Fh#ot`*J7CpJA@JnvfZel87$%N`6nE;a~D_uQUsJ?nD4 zZFW-k_lf&tH)%3nS={p0vzA2KF>Gmk{+sv1C=+R^=6^ zls`1jl4uW#uxR99%1mId3}8>)!5aUet7HbMQ>J0|oGC9)GQNHw^6vkXv+o(47W(*qjq!i3c&c?lS9VEzl>=8={fhJH5v}Kc zv5O@%xc_LYSka!J(3Y~H?Y|9s>WapaKkNc8nldEVOINg4b+8v5VDVXDU=`(Uoh5g9 zVMBC9+pjg}vklrS9<=e-vQ6>0P|wkrU%*}(z+SU~Sxm#Chl^`O)~xp?>qvfOSRL%i>NO&i7)N%E%z&;LCLDbXy0{6CQn& z$wo&zy7g=uCS)%4GPE-9*~X%=P+-AsMg`_~5!#Fg0vR_g<*D*aJEfcN&7kMoux^s% zq0R<*fu3_6!TiDui(jhoYH0EEG%~!Atu3+#mWADZ|g(+ z=Zbxou@Jk^n!mxdvLRr0LYwl3xxX7*vK88^RV9V}cOWDxe zcFJPwlodR#{uekIB{#6uT7+`+vX>XMS0seh?qJi}aiBu6?Li=WegsqYT#mrqjXD>5 z{=DGJ2%68ja_#XeYu&jTg?8*zI>60b!CS9x!FOL{^0t5pbAN4Jpse~t*-SFDTTDUa z*9`sFD|&w3;xW3QxWtI>*MWWiym?k_ZV(jMxPIaMHHw@66f_w+F5Q2^NRyKx_V%Q+ z4|rzm6rJ7GuXU5B@6{ykrrj>O9a2mT8yx$Zm-fHrSuXbfQ}4v3{n8QBeE)_l;Eh^X zDp0wrq5k*J@T}Vm_7VYk8Buv3*mmz(%O1hxpU{@Qqb=h?t4RWbh%AS2fH)^dw7`T0 z_ZMs_a|DGs7{wMar|QVA3TVqOh}t>zHbZVx9xqe;j<)oI=9n`b`MM0;Cm4_XXuRJe z)08obaYnP{iKd6j9d}kWSx#u;2vA+#wQ`O!gOW$TQGLVwO-aib0%DXJq!}JG87VMt zSlpm|qRG)Dr$Jos<+#@?J7`#_?1zNL(j!IYCH8I2fNlqMWbHEQH9SbApd z-F253I%OGKBDiOt?EO-}^zDP^>WwRv6!;HsnR#E8No>RA#)8HVe;9B5_wBb-XcDqo z^s8KyQ@juUYT#;ncn9wM-f>F{Stoa7R2Hx8nmNqbRGalef;NtLU zt6=a^;EtHk$a3`lpQnv>8Mk>^6S(&!lxXf)?<&qBcIKtwPNoer&PSNfynAICXG7zc zb4@?`&Ie2pda?H2WZsXGiP46JjR69#k=yqR9Dj6T>iV0zu9b2;im2cIN#~JR-fCI4 zN352K44bA-U$RV6&tG=h4TsAsl*H!mNL)6*^O2ZdpU6Fl)*DvJebTHk+=t-u!hp*69AMe)T0uATCnA6UyTuzOTANG39xD6qw^NKQ*=-xhQJ z>i;ZVPaa;6cNh00?~{D!d+|a2*@H7p*(0VgKj}U<_sNBdo)a9s0=pBpAL4#h&O}DZ7tiwcHMl=uV>rva&LzzCLVNnsw9<%_`ma}3 z*l9KV3YHF=mcnjjm6dEacjv=z8)I$?*facbPn#r?Wn~ramikP%H_0ty`zpn(NvU79 zrtAJrk309Y!uv(=yeFY?PZQ%hlHyWZU%c?hOUsbUoFsNC=OLS`9sAUE?I|bHcy2QI zOR&w9YfIX|R{TGqy=q6R+z#F1b?j6BW!4veVEy;i<(f9b*38CFj!`=&YiMW&O}J>e zBsD9x=T*B!)aq@oc24eC8h3j2GLALJOvEG@*Pht0HmqZ{T~wRK{0;wJP0GvO9_KIi zAZ_Qp`wQCyxGLC+GXnP>dzsSEl-K+EFYlWj??TGwT|YAO#-?Xkjgk(&+f+&B$}-PvrJ1^qqG& zsivCudSBj~b9u~*-fXYa{*c=!{$H#!8EwZ2e# zf1%9zLfQL;a_?gb<^LBd@E0k{7b)o%DccvR_!p_h7pdhJsn-{2^cQK)FVb3Hq`kjL z=X{aw{UW{hMf(4X4ET!;<%^B@Tozy)BLZUmD-ea`RYe(*0t# zA8hp=WvTIHY3XG#x@8&SUjz~wvNOuE^ULZgzGdA1W@pZrwZE*uzpU;;TAqHn&G&Cv z{NJQ0>zua_xd3{ZMS=saQ#`5C&|K)|(zc>0< z$d@y=U{g_t&bB6pc@$?Fgh6c`nX3rfh zt~**>GJbgmG}m)l{QAb1yWoArLiMV(;f0I$GkgDN@to1(n(@mu;#b6m7L$gCRrTjr z&#!2?U$uk3kf)(RV?v9|j_N(0@-c)$<39;yTSebNgQVpM`KeLHfyG-ZirnK{md{>w0*5BTi zcmJD>$;(HJc9p%q_V$JsFBgyM2D2--N()Y{FW^`9TT@~9_*kF3bDvJ-+D?t)k4fid zzX+bfF0gKzjc3J%hbNc$FZNqoW%~Nsx_E82g-Mgsdde2-{ol34)0LyM$-P*Yx7y<4 zqhtNb{_ARX1_^Pjd$4Tw^+m^B!UcL11V5VB7pym!+03tBXY=#Zv-9&+4<@xtidDWk8E}tKAk27Xoy-?JOh3!gGaSImb^Uhka zxZg=?<&u0|uZYEylV+`4HoK@Qc1(Ublgfal2qIQz@gsS_zL03l27G|KoHyA@I;%X#0j{rmc+iTN(auobaK+ z@nAEXu#(M>2lwr{>P!-6d2C$d+BL!FPx6Pe^N;r{IJYekXgX**QN#7q;)Fvfr>4vO zjLoPNO7og&l6!B?%b?|6^Gs`-j@%4>;l9YR_m}gkFB%t?8P0b~wF=D&UKPGp>*%h~ z)q(5c_i{~*GfnRX`LVdgPBu{8 { await project.fileExists('.output/test-chrome-mv3-development-dev-build'), ).toBe(true); }); + + it('should throw error when config file not exist', async () => { + const project = new TestProject(); + project.addFile( + 'src/entrypoints/background.ts', + `export default defineBackground( + () => console.log('Hello background'), + );`, + ); + + await expect( + project.build({ configFile: 'foo.config.ts' }), + ).rejects.toThrowError(/not found/); + }); }); diff --git a/packages/wxt/src/core/resolve-config.ts b/packages/wxt/src/core/resolve-config.ts index dc93a319..cb6f40cb 100644 --- a/packages/wxt/src/core/resolve-config.ts +++ b/packages/wxt/src/core/resolve-config.ts @@ -53,6 +53,9 @@ export async function resolveConfig( esmResolve: true, }, }); + if (inlineConfig.configFile && metadata.layers?.length === 0) { + throw Error(`Config file "${inlineConfig.configFile}" not found`); + } userConfig = loadedConfig ?? {}; userConfigMetadata = metadata; } From 330e39159a15779d5d4bc3127d829bf5c87208b3 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 13 Nov 2024 14:04:41 +0000 Subject: [PATCH 023/130] chore(release): @wxt-dev/module-svelte v2.0.0 --- packages/module-svelte/CHANGELOG.md | 21 +++++++++++++++++++++ packages/module-svelte/package.json | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/module-svelte/CHANGELOG.md b/packages/module-svelte/CHANGELOG.md index 9f282ac8..5436a244 100644 --- a/packages/module-svelte/CHANGELOG.md +++ b/packages/module-svelte/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## v2.0.0 + +[⚠️ breaking changes](https://wxt.dev/guide/upgrade-guide/wxt) • [compare changes](https://github.com/wxt-dev/wxt/compare/module-svelte-v1.0.1...module-svelte-v2.0.0) + +### 🚀 Enhancements + +- ⚠️ Svelte 5 support ([#1104](https://github.com/wxt-dev/wxt/pull/1104)) + +### 🏡 Chore + +- Add `oxlint` for linting ([#947](https://github.com/wxt-dev/wxt/pull/947)) +- Upgrade all non-major dependencies ([#1040](https://github.com/wxt-dev/wxt/pull/1040)) + +#### ⚠️ Breaking Changes + +- ⚠️ Svelte 5 support ([#1104](https://github.com/wxt-dev/wxt/pull/1104)) + +### ❤️ Contributors + +- Aaron ([@aklinker1](http://github.com/aklinker1)) + ## v1.0.1 [compare changes](https://github.com/wxt-dev/wxt/compare/module-svelte-v1.0.0...module-svelte-v1.0.1) diff --git a/packages/module-svelte/package.json b/packages/module-svelte/package.json index 9281bd7d..5f60a6db 100644 --- a/packages/module-svelte/package.json +++ b/packages/module-svelte/package.json @@ -17,7 +17,7 @@ "email": "aaronklinker1+wxt@gmail.com" }, "license": "MIT", - "version": "1.0.1", + "version": "2.0.0", "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.mjs", From 2f23556893d135d78ad109eb5279f47d62469a16 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 13 Nov 2024 08:07:15 -0600 Subject: [PATCH 024/130] Update changelog --- packages/module-svelte/CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/module-svelte/CHANGELOG.md b/packages/module-svelte/CHANGELOG.md index 5436a244..c724f471 100644 --- a/packages/module-svelte/CHANGELOG.md +++ b/packages/module-svelte/CHANGELOG.md @@ -15,7 +15,9 @@ #### ⚠️ Breaking Changes -- ⚠️ Svelte 5 support ([#1104](https://github.com/wxt-dev/wxt/pull/1104)) +Upgraded `@sveltejs/vite-plugin-svelte` from v3 to v4. This drops support for Svelte 4 and below. To continue using older versions of Svelte, use v1 of this module. + +To upgrade to svelte 5, just install `svelte@5`. ### ❤️ Contributors @@ -49,4 +51,4 @@ ## v1.0.0 -Initial release 🎉 \ No newline at end of file +Initial release 🎉 From 3481313859f89347aca7944f214043137251c6e3 Mon Sep 17 00:00:00 2001 From: rxliuli Date: Wed, 13 Nov 2024 22:07:37 +0800 Subject: [PATCH 025/130] feat: automatically disable 'Show warning about Self-XSS when pasing code' in new chrome (#1159) --- packages/wxt/src/core/runners/web-ext.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/wxt/src/core/runners/web-ext.ts b/packages/wxt/src/core/runners/web-ext.ts index 09819c3a..98d402e9 100644 --- a/packages/wxt/src/core/runners/web-ext.ts +++ b/packages/wxt/src/core/runners/web-ext.ts @@ -50,7 +50,10 @@ export function createWebExtRunner(): ExtensionRunner { wxtUserConfig?.chromiumPref, DEFAULT_CHROMIUM_PREFS, ), - args: wxtUserConfig?.chromiumArgs, + args: [ + '--unsafely-disable-devtools-self-xss-warnings', + ...(wxtUserConfig?.chromiumArgs ?? []), + ], }), }; From f7d12b74865788c4ffed9b88e010b4d2df1ea1b8 Mon Sep 17 00:00:00 2001 From: Willow <42willow@pm.me> Date: Thu, 14 Nov 2024 01:08:32 +1100 Subject: [PATCH 026/130] docs: add missing cd command (#1163) --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fbe6fdc3..f09f1ccd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,6 +32,7 @@ Here are some helpful commands: ```sh # Build WXT package +cd packages/wxt pnpm build ``` From 5c7b6098a226e881bdd1150a25ba75dde7cc04f4 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 13 Nov 2024 08:19:40 -0600 Subject: [PATCH 027/130] chore(deps): Upgrade all non-major dependencies (#1164) --- package.json | 10 +- packages/auto-icons/package.json | 6 +- packages/i18n/package.json | 12 +- packages/module-react/package.json | 10 +- packages/module-solid/package.json | 6 +- packages/module-svelte/package.json | 4 +- packages/module-vue/package.json | 6 +- packages/unocss/package.json | 8 +- packages/wxt-demo/package.json | 12 +- packages/wxt/package.json | 20 +- pnpm-lock.yaml | 1475 +++++++++++++++------------ templates/react/package.json | 8 +- templates/solid/package.json | 6 +- templates/svelte/package.json | 8 +- templates/vanilla/package.json | 4 +- templates/vue/package.json | 8 +- 16 files changed, 893 insertions(+), 710 deletions(-) diff --git a/package.json b/package.json index 21ea5945..c0e80a8b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "engines": { "node": ">=18.20.3" }, - "packageManager": "pnpm@9.12.0", + "packageManager": "pnpm@9.13.0", "scripts": { "check": "check && pnpm -r --sequential run check", "test": "pnpm -r --sequential run test run", @@ -20,7 +20,7 @@ "@aklinker1/buildc": "^1.1.4", "@aklinker1/check": "^1.4.5", "@types/fs-extra": "^11.0.4", - "@vitest/coverage-v8": "^2.1.2", + "@vitest/coverage-v8": "^2.1.4", "changelogen": "^0.5.7", "consola": "^3.2.3", "fast-glob": "^3.3.2", @@ -34,10 +34,10 @@ "typedoc": "^0.25.4", "typedoc-plugin-markdown": "4.0.0-next.23", "typedoc-vitepress-theme": "1.0.0-next.3", - "typescript": "^5.6.2", - "vitepress": "^1.3.4", + "typescript": "^5.6.3", + "vitepress": "^1.5.0", "vitest-mock-extended": "^2.0.2", - "vue": "^3.5.11", + "vue": "^3.5.12", "wxt": "workspace:*" }, "simple-git-hooks": { diff --git a/packages/auto-icons/package.json b/packages/auto-icons/package.json index 5f2e8ff1..4964ff24 100644 --- a/packages/auto-icons/package.json +++ b/packages/auto-icons/package.json @@ -46,9 +46,9 @@ }, "devDependencies": { "@aklinker1/check": "^1.4.5", - "oxlint": "^0.9.9", - "publint": "^0.2.11", - "typescript": "^5.6.2", + "oxlint": "^0.9.10", + "publint": "^0.2.12", + "typescript": "^5.6.3", "unbuild": "^2.0.0", "wxt": "workspace:*" }, diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 533b3cf7..f22f6c45 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "chokidar": "^3.6.0", - "confbox": "^0.1.7", + "confbox": "^0.1.8", "fast-glob": "^3.3.2" }, "peerDependencies": { @@ -40,12 +40,12 @@ "devDependencies": { "@aklinker1/check": "^1.4.5", "@types/chrome": "^0.0.268", - "@types/node": "^20.16.10", - "oxlint": "^0.9.9", - "publint": "^0.2.11", - "typescript": "^5.6.2", + "@types/node": "^20.17.6", + "oxlint": "^0.9.10", + "publint": "^0.2.12", + "typescript": "^5.6.3", "unbuild": "^2.0.0", - "vitest": "^2.0.0", + "vitest": "^2.1.4", "vitest-plugin-random-seed": "^1.1.0", "wxt": "workspace:*" }, diff --git a/packages/module-react/package.json b/packages/module-react/package.json index cae6ef57..93be2acf 100644 --- a/packages/module-react/package.json +++ b/packages/module-react/package.json @@ -47,16 +47,16 @@ "wxt": ">=0.18.6" }, "dependencies": { - "@vitejs/plugin-react": "^4.3.2" + "@vitejs/plugin-react": "^4.3.3" }, "devDependencies": { "@aklinker1/check": "^1.4.5", - "@types/react": "^18.3.11", - "@types/react-dom": "^18.3.0", - "publint": "^0.2.11", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "publint": "^0.2.12", "react": "^18.3.1", "react-dom": "^18.3.1", - "typescript": "^5.6.2", + "typescript": "^5.6.3", "unbuild": "^2.0.0", "wxt": "workspace:*" } diff --git a/packages/module-solid/package.json b/packages/module-solid/package.json index 81fcb53c..2a49a182 100644 --- a/packages/module-solid/package.json +++ b/packages/module-solid/package.json @@ -51,9 +51,9 @@ }, "devDependencies": { "@aklinker1/check": "^1.4.5", - "publint": "^0.2.11", - "solid-js": "^1.9.1", - "typescript": "^5.6.2", + "publint": "^0.2.12", + "solid-js": "^1.9.3", + "typescript": "^5.6.3", "unbuild": "^2.0.0", "wxt": "workspace:*" } diff --git a/packages/module-svelte/package.json b/packages/module-svelte/package.json index 5f60a6db..d12b55d6 100644 --- a/packages/module-svelte/package.json +++ b/packages/module-svelte/package.json @@ -50,8 +50,8 @@ }, "devDependencies": { "@aklinker1/check": "^1.4.5", - "publint": "^0.2.11", - "typescript": "^5.6.2", + "publint": "^0.2.12", + "typescript": "^5.6.3", "unbuild": "^2.0.0", "wxt": "workspace:*" } diff --git a/packages/module-vue/package.json b/packages/module-vue/package.json index a0e77d38..7eaf6107 100644 --- a/packages/module-vue/package.json +++ b/packages/module-vue/package.json @@ -45,12 +45,12 @@ "wxt": ">=0.18.6" }, "dependencies": { - "@vitejs/plugin-vue": "^5.1.4" + "@vitejs/plugin-vue": "^5.2.0" }, "devDependencies": { "@aklinker1/check": "^1.4.5", - "publint": "^0.2.11", - "typescript": "^5.6.2", + "publint": "^0.2.12", + "typescript": "^5.6.3", "unbuild": "^2.0.0", "wxt": "workspace:*" } diff --git a/packages/unocss/package.json b/packages/unocss/package.json index c50ab9d4..9c6749ff 100644 --- a/packages/unocss/package.json +++ b/packages/unocss/package.json @@ -41,11 +41,11 @@ }, "devDependencies": { "@aklinker1/check": "^1.4.5", - "oxlint": "^0.9.9", - "publint": "^0.2.11", - "typescript": "^5.6.2", + "oxlint": "^0.9.10", + "publint": "^0.2.12", + "typescript": "^5.6.3", "unbuild": "^2.0.0", - "unocss": "^0.63.3", + "unocss": "^0.63.6", "wxt": "workspace:*" }, "dependencies": { diff --git a/packages/wxt-demo/package.json b/packages/wxt-demo/package.json index 5ea0a281..82a51ae9 100644 --- a/packages/wxt-demo/package.json +++ b/packages/wxt-demo/package.json @@ -23,14 +23,14 @@ }, "devDependencies": { "@types/chrome": "^0.0.269", - "@types/react": "^18.3.11", - "@types/react-dom": "^18.3.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", "@wxt-dev/auto-icons": "workspace:*", "@wxt-dev/unocss": "workspace:*", - "sass": "^1.79.4", - "typescript": "^5.6.2", - "unocss": "^0.63.3", - "vitest": "^2.1.2", + "sass": "^1.80.7", + "typescript": "^5.6.3", + "unocss": "^0.63.6", + "vitest": "^2.1.4", "vitest-plugin-random-seed": "^1.1.0", "wxt": "workspace:*" }, diff --git a/packages/wxt/package.json b/packages/wxt/package.json index 11259f9e..965c4f3b 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -91,7 +91,7 @@ "c12": "^1.11.2", "cac": "^6.7.14", "chokidar": "^3.6.0", - "ci-info": "^4.0.0", + "ci-info": "^4.1.0", "consola": "^3.2.3", "defu": "^6.1.4", "dequal": "^2.0.3", @@ -115,15 +115,15 @@ "nypm": "^0.3.12", "ohash": "^1.1.4", "open": "^10.1.0", - "ora": "^8.1.0", + "ora": "^8.1.1", "perfect-debounce": "^1.0.0", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "prompts": "^2.4.2", "publish-browser-extension": "^2.2.2", "scule": "^1.3.0", "unimport": "^3.13.1", - "vite": "^5.4.8", - "vite-node": "^2.1.2", + "vite": "^5.4.11", + "vite-node": "^2.1.4", "web-ext-run": "^0.2.1", "webextension-polyfill": "^0.12.0" }, @@ -132,18 +132,18 @@ "@faker-js/faker": "^8.4.1", "@types/fs-extra": "^11.0.4", "@types/lodash.merge": "^4.6.9", - "@types/node": "^20.16.10", + "@types/node": "^20.17.6", "@types/normalize-path": "^3.0.2", "@types/prompts": "^2.4.9", "extract-zip": "^2.0.1", "happy-dom": "^14.12.3", "lodash.merge": "^4.6.2", - "oxlint": "^0.9.9", - "publint": "^0.2.11", + "oxlint": "^0.9.10", + "publint": "^0.2.12", "tsx": "4.15.7", - "typescript": "^5.6.2", + "typescript": "^5.6.3", "unbuild": "^2.0.0", - "vitest": "^2.1.2", + "vitest": "^2.1.4", "vitest-plugin-random-seed": "^1.1.0" }, "peerDependenciesMeta": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94ccaf1c..7d3ff1e3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,13 +18,13 @@ importers: version: 1.1.4 '@aklinker1/check': specifier: ^1.4.5 - version: 1.4.5(typescript@5.6.2) + version: 1.4.5(typescript@5.6.3) '@types/fs-extra': specifier: ^11.0.4 version: 11.0.4 '@vitest/coverage-v8': - specifier: ^2.1.2 - version: 2.1.2(vitest@2.1.2(@types/node@20.16.10)(happy-dom@14.12.3)(sass@1.79.4)) + specifier: ^2.1.4 + version: 2.1.4(vitest@2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7)) changelogen: specifier: ^0.5.7 version: 0.5.7(magicast@0.3.5) @@ -57,25 +57,25 @@ importers: version: 4.15.7 typedoc: specifier: ^0.25.4 - version: 0.25.4(typescript@5.6.2) + version: 0.25.4(typescript@5.6.3) typedoc-plugin-markdown: specifier: 4.0.0-next.23 - version: 4.0.0-next.23(typedoc@0.25.4(typescript@5.6.2)) + version: 4.0.0-next.23(typedoc@0.25.4(typescript@5.6.3)) typedoc-vitepress-theme: specifier: 1.0.0-next.3 - version: 1.0.0-next.3(typedoc-plugin-markdown@4.0.0-next.23(typedoc@0.25.4(typescript@5.6.2))) + version: 1.0.0-next.3(typedoc-plugin-markdown@4.0.0-next.23(typedoc@0.25.4(typescript@5.6.3))) typescript: - specifier: ^5.6.2 - version: 5.6.2 + specifier: ^5.6.3 + version: 5.6.3 vitepress: - specifier: ^1.3.4 - version: 1.3.4(@algolia/client-search@4.20.0)(@types/node@20.16.10)(@types/react@18.3.11)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.4)(search-insights@2.15.0)(typescript@5.6.2) + specifier: ^1.5.0 + version: 1.5.0(@algolia/client-search@4.20.0)(@types/node@20.17.6)(@types/react@18.3.12)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.80.7)(search-insights@2.15.0)(typescript@5.6.3) vitest-mock-extended: specifier: ^2.0.2 - version: 2.0.2(typescript@5.6.2)(vitest@2.1.2(@types/node@20.16.10)(happy-dom@14.12.3)(sass@1.79.4)) + version: 2.0.2(typescript@5.6.3)(vitest@2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7)) vue: - specifier: ^3.5.11 - version: 3.5.11(typescript@5.6.2) + specifier: ^3.5.12 + version: 3.5.12(typescript@5.6.3) wxt: specifier: workspace:* version: link:packages/wxt @@ -94,19 +94,19 @@ importers: devDependencies: '@aklinker1/check': specifier: ^1.4.5 - version: 1.4.5(typescript@5.6.2) + version: 1.4.5(typescript@5.6.3) oxlint: - specifier: ^0.9.9 - version: 0.9.9 + specifier: ^0.9.10 + version: 0.9.10 publint: - specifier: ^0.2.11 - version: 0.2.11 + specifier: ^0.2.12 + version: 0.2.12 typescript: - specifier: ^5.6.2 - version: 5.6.2 + specifier: ^5.6.3 + version: 5.6.3 unbuild: specifier: ^2.0.0 - version: 2.0.0(sass@1.79.4)(typescript@5.6.2) + version: 2.0.0(sass@1.80.7)(typescript@5.6.3) wxt: specifier: workspace:* version: link:../wxt @@ -117,39 +117,39 @@ importers: specifier: ^3.6.0 version: 3.6.0 confbox: - specifier: ^0.1.7 - version: 0.1.7 + specifier: ^0.1.8 + version: 0.1.8 fast-glob: specifier: ^3.3.2 version: 3.3.2 devDependencies: '@aklinker1/check': specifier: ^1.4.5 - version: 1.4.5(typescript@5.6.2) + version: 1.4.5(typescript@5.6.3) '@types/chrome': specifier: ^0.0.268 version: 0.0.268 '@types/node': - specifier: ^20.16.10 - version: 20.16.10 + specifier: ^20.17.6 + version: 20.17.6 oxlint: - specifier: ^0.9.9 - version: 0.9.9 + specifier: ^0.9.10 + version: 0.9.10 publint: - specifier: ^0.2.11 - version: 0.2.11 + specifier: ^0.2.12 + version: 0.2.12 typescript: - specifier: ^5.6.2 - version: 5.6.2 + specifier: ^5.6.3 + version: 5.6.3 unbuild: specifier: ^2.0.0 - version: 2.0.0(sass@1.79.4)(typescript@5.6.2) + version: 2.0.0(sass@1.80.7)(typescript@5.6.3) vitest: - specifier: ^2.0.0 - version: 2.1.2(@types/node@20.16.10)(happy-dom@14.12.3)(sass@1.79.4) + specifier: ^2.1.4 + version: 2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7) vitest-plugin-random-seed: specifier: ^1.1.0 - version: 1.1.0(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + version: 1.1.0(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) wxt: specifier: workspace:* version: link:../wxt @@ -157,21 +157,21 @@ importers: packages/module-react: dependencies: '@vitejs/plugin-react': - specifier: ^4.3.2 - version: 4.3.2(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + specifier: ^4.3.3 + version: 4.3.3(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) devDependencies: '@aklinker1/check': specifier: ^1.4.5 - version: 1.4.5(typescript@5.6.2) + version: 1.4.5(typescript@5.6.3) '@types/react': - specifier: ^18.3.11 - version: 18.3.11 + specifier: ^18.3.12 + version: 18.3.12 '@types/react-dom': - specifier: ^18.3.0 - version: 18.3.0 + specifier: ^18.3.1 + version: 18.3.1 publint: - specifier: ^0.2.11 - version: 0.2.11 + specifier: ^0.2.12 + version: 0.2.12 react: specifier: ^18.3.1 version: 18.3.1 @@ -179,11 +179,11 @@ importers: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) typescript: - specifier: ^5.6.2 - version: 5.6.2 + specifier: ^5.6.3 + version: 5.6.3 unbuild: specifier: ^2.0.0 - version: 2.0.0(sass@1.79.4)(typescript@5.6.2) + version: 2.0.0(sass@1.80.7)(typescript@5.6.3) wxt: specifier: workspace:* version: link:../wxt @@ -192,23 +192,23 @@ importers: dependencies: vite-plugin-solid: specifier: ^2.10.2 - version: 2.10.2(solid-js@1.9.1)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + version: 2.10.2(solid-js@1.9.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) devDependencies: '@aklinker1/check': specifier: ^1.4.5 - version: 1.4.5(typescript@5.6.2) + version: 1.4.5(typescript@5.6.3) publint: - specifier: ^0.2.11 - version: 0.2.11 + specifier: ^0.2.12 + version: 0.2.12 solid-js: - specifier: ^1.9.1 - version: 1.9.1 + specifier: ^1.9.3 + version: 1.9.3 typescript: - specifier: ^5.6.2 - version: 5.6.2 + specifier: ^5.6.3 + version: 5.6.3 unbuild: specifier: ^2.0.0 - version: 2.0.0(sass@1.79.4)(typescript@5.6.2) + version: 2.0.0(sass@1.80.7)(typescript@5.6.3) wxt: specifier: workspace:* version: link:../wxt @@ -217,23 +217,23 @@ importers: dependencies: '@sveltejs/vite-plugin-svelte': specifier: ^4.0.0 - version: 4.0.0(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + version: 4.0.0(svelte@5.1.6)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) svelte: specifier: '>=5' version: 5.1.6 devDependencies: '@aklinker1/check': specifier: ^1.4.5 - version: 1.4.5(typescript@5.6.2) + version: 1.4.5(typescript@5.6.3) publint: - specifier: ^0.2.11 - version: 0.2.11 + specifier: ^0.2.12 + version: 0.2.12 typescript: - specifier: ^5.6.2 - version: 5.6.2 + specifier: ^5.6.3 + version: 5.6.3 unbuild: specifier: ^2.0.0 - version: 2.0.0(sass@1.79.4)(typescript@5.6.2) + version: 2.0.0(sass@1.80.7)(typescript@5.6.3) wxt: specifier: workspace:* version: link:../wxt @@ -241,21 +241,21 @@ importers: packages/module-vue: dependencies: '@vitejs/plugin-vue': - specifier: ^5.1.4 - version: 5.1.4(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))(vue@3.5.11(typescript@5.6.2)) + specifier: ^5.2.0 + version: 5.2.0(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))(vue@3.5.12(typescript@5.6.3)) devDependencies: '@aklinker1/check': specifier: ^1.4.5 - version: 1.4.5(typescript@5.6.2) + version: 1.4.5(typescript@5.6.3) publint: - specifier: ^0.2.11 - version: 0.2.11 + specifier: ^0.2.12 + version: 0.2.12 typescript: - specifier: ^5.6.2 - version: 5.6.2 + specifier: ^5.6.3 + version: 5.6.3 unbuild: specifier: ^2.0.0 - version: 2.0.0(sass@1.79.4)(typescript@5.6.2) + version: 2.0.0(sass@1.80.7)(typescript@5.6.3) wxt: specifier: workspace:* version: link:../wxt @@ -271,22 +271,22 @@ importers: devDependencies: '@aklinker1/check': specifier: ^1.4.5 - version: 1.4.5(typescript@5.6.2) + version: 1.4.5(typescript@5.6.3) oxlint: - specifier: ^0.9.9 - version: 0.9.9 + specifier: ^0.9.10 + version: 0.9.10 publint: - specifier: ^0.2.11 - version: 0.2.11 + specifier: ^0.2.12 + version: 0.2.12 typescript: - specifier: ^5.6.2 - version: 5.6.2 + specifier: ^5.6.3 + version: 5.6.3 unbuild: specifier: ^2.0.0 - version: 2.0.0(sass@1.79.4)(typescript@5.6.2) + version: 2.0.0(sass@1.80.7)(typescript@5.6.3) unocss: - specifier: ^0.63.3 - version: 0.63.4(postcss@8.4.47)(rollup@4.24.0)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + specifier: ^0.63.6 + version: 0.63.6(postcss@8.4.47)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) wxt: specifier: workspace:* version: link:../wxt @@ -324,8 +324,8 @@ importers: specifier: ^3.6.0 version: 3.6.0 ci-info: - specifier: ^4.0.0 - version: 4.0.0 + specifier: ^4.1.0 + version: 4.1.0 consola: specifier: ^3.2.3 version: 3.2.3 @@ -396,14 +396,14 @@ importers: specifier: ^10.1.0 version: 10.1.0 ora: - specifier: ^8.1.0 - version: 8.1.0 + specifier: ^8.1.1 + version: 8.1.1 perfect-debounce: specifier: ^1.0.0 version: 1.0.0 picocolors: - specifier: ^1.1.0 - version: 1.1.0 + specifier: ^1.1.1 + version: 1.1.1 prompts: specifier: ^2.4.2 version: 2.4.2 @@ -417,11 +417,11 @@ importers: specifier: ^3.13.1 version: 3.13.1(rollup@4.24.0)(webpack-sources@3.2.3) vite: - specifier: ^5.4.8 - version: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + specifier: ^5.4.11 + version: 5.4.11(@types/node@20.17.6)(sass@1.80.7) vite-node: - specifier: ^2.1.2 - version: 2.1.2(@types/node@20.16.10)(sass@1.79.4) + specifier: ^2.1.4 + version: 2.1.4(@types/node@20.17.6)(sass@1.80.7) web-ext-run: specifier: ^0.2.1 version: 0.2.1 @@ -431,7 +431,7 @@ importers: devDependencies: '@aklinker1/check': specifier: ^1.4.5 - version: 1.4.5(typescript@5.6.2) + version: 1.4.5(typescript@5.6.3) '@faker-js/faker': specifier: ^8.4.1 version: 8.4.1 @@ -442,8 +442,8 @@ importers: specifier: ^4.6.9 version: 4.6.9 '@types/node': - specifier: ^20.16.10 - version: 20.16.10 + specifier: ^20.17.6 + version: 20.17.6 '@types/normalize-path': specifier: ^3.0.2 version: 3.0.2 @@ -460,26 +460,26 @@ importers: specifier: ^4.6.2 version: 4.6.2 oxlint: - specifier: ^0.9.9 - version: 0.9.9 + specifier: ^0.9.10 + version: 0.9.10 publint: - specifier: ^0.2.11 - version: 0.2.11 + specifier: ^0.2.12 + version: 0.2.12 tsx: specifier: 4.15.7 version: 4.15.7 typescript: - specifier: ^5.6.2 - version: 5.6.2 + specifier: ^5.6.3 + version: 5.6.3 unbuild: specifier: ^2.0.0 - version: 2.0.0(sass@1.79.4)(typescript@5.6.2) + version: 2.0.0(sass@1.80.7)(typescript@5.6.3) vitest: - specifier: ^2.1.2 - version: 2.1.2(@types/node@20.16.10)(happy-dom@14.12.3)(sass@1.79.4) + specifier: ^2.1.4 + version: 2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7) vitest-plugin-random-seed: specifier: ^1.1.0 - version: 1.1.0(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + version: 1.1.0(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) packages/wxt-demo: dependencies: @@ -497,11 +497,11 @@ importers: specifier: ^0.0.269 version: 0.0.269 '@types/react': - specifier: ^18.3.11 - version: 18.3.11 + specifier: ^18.3.12 + version: 18.3.12 '@types/react-dom': - specifier: ^18.3.0 - version: 18.3.0 + specifier: ^18.3.1 + version: 18.3.1 '@wxt-dev/auto-icons': specifier: workspace:* version: link:../auto-icons @@ -509,20 +509,20 @@ importers: specifier: workspace:* version: link:../unocss sass: - specifier: ^1.79.4 - version: 1.79.4 + specifier: ^1.80.7 + version: 1.80.7 typescript: - specifier: ^5.6.2 - version: 5.6.2 + specifier: ^5.6.3 + version: 5.6.3 unocss: - specifier: ^0.63.3 - version: 0.63.4(postcss@8.4.47)(rollup@4.24.0)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + specifier: ^0.63.6 + version: 0.63.6(postcss@8.4.47)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) vitest: - specifier: ^2.1.2 - version: 2.1.2(@types/node@20.16.10)(happy-dom@14.12.3)(sass@1.79.4) + specifier: ^2.1.4 + version: 2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7) vitest-plugin-random-seed: specifier: ^1.1.0 - version: 1.1.0(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + version: 1.1.0(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) wxt: specifier: workspace:* version: link:../wxt @@ -611,10 +611,6 @@ packages: '@algolia/transporter@4.20.0': resolution: {integrity: sha512-Lsii1pGWOAISbzeyuf+r/GPhvHMPHSPrTDWNcIzOE1SG1inlJHICaVe2ikuoRjcpgxZNU54Jl+if15SUCsaTUg==} - '@ampproject/remapping@2.2.1': - resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} - engines: {node: '>=6.0.0'} - '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -1299,6 +1295,9 @@ packages: resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} + '@iconify-json/simple-icons@1.2.11': + resolution: {integrity: sha512-AHCGDtBRqP+JzAbBzgO8uN/08CXxEmuaC6lQQZ3b5burKhRU12AJnJczwbUw2K5Mb/U85EpSUNhYMG3F28b8NA==} + '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -1451,46 +1450,128 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@oxlint/darwin-arm64@0.9.9': - resolution: {integrity: sha512-My2KfUByjRl49p0rr/Glx9Y/hjney1uFk0JXNjwHqYToHqO9fY/IZ6XT1fdw9sX+1hdpq9bmj88rnkpvu0/cRw==} + '@oxlint/darwin-arm64@0.9.10': + resolution: {integrity: sha512-eOXKZYq5bnCSgDefgM5bzAg+4Fc//Rc4yjgKN8iDWUARweCaChiQXb6TXX8MfEfs6qayEMy6yVj0pqoFz0B1aw==} cpu: [arm64] os: [darwin] - '@oxlint/darwin-x64@0.9.9': - resolution: {integrity: sha512-k0r0t+MAzrk8yWs0nxyD9Skfb+Ozmu6HRMTJIsUTLV4AKMt9CZBlLVS0OXzXHi72AOHz3UUve5rXxevVVY9lHQ==} + '@oxlint/darwin-x64@0.9.10': + resolution: {integrity: sha512-UeYICDvLUaUOcY+0ugZUEmBMRLP+x8iTgL7TeY6BlpGw2ahbtUOTbyIIRWtr/0O++TnjZ+v8TzhJ9crw6Ij6dg==} cpu: [x64] os: [darwin] - '@oxlint/linux-arm64-gnu@0.9.9': - resolution: {integrity: sha512-atiotr1pN3rr0i7Ww3SpOEOvkFex97S8GwYmiTlSng0kp+FSIZD6Kjlr9k3oayf9RZUDRuAE4WptwV1KYLvIDw==} + '@oxlint/linux-arm64-gnu@0.9.10': + resolution: {integrity: sha512-0Zn+vqHhrZyufFBfq9WOgiIool0gCR14BLsdS+0Dwd9o+kNxPGA5q7erQFkiC4rpkxtfBHeD3iIKMMt7d29Kyw==} cpu: [arm64] os: [linux] - '@oxlint/linux-arm64-musl@0.9.9': - resolution: {integrity: sha512-vmd6Eog6WiudMTT0Fa8u5N+47caaWF2KVUHOSEV/d/WauVx+ZOR9z1LEM+54AZwn3Z2TbtFOJDiz7iDukZPbfw==} + '@oxlint/linux-arm64-musl@0.9.10': + resolution: {integrity: sha512-tkQcWpYwF42bA/uRaV2iMFePHkBjTTgomOgeEaiw6XOSJX4nBEqGIIboqqLBWT4JnKCf/L+IG3y/e1MflhKByw==} cpu: [arm64] os: [linux] - '@oxlint/linux-x64-gnu@0.9.9': - resolution: {integrity: sha512-AUmSRerK4VXIMcTYYk25KGoOU2/z+NGItUhI6nJgMFktrbF8MUD6hlf3vaQZNle454z7FDJNSjARM0bB+xpBiQ==} + '@oxlint/linux-x64-gnu@0.9.10': + resolution: {integrity: sha512-JHbkMUnibqaSMBvLHyqTL5cWxcGW+jw+Ppt2baLISpvo34a6fBR+PI7v/A92sEDWe0W1rPhypzCwA8mKpkQ3DA==} cpu: [x64] os: [linux] - '@oxlint/linux-x64-musl@0.9.9': - resolution: {integrity: sha512-jQYIx5KUYbpXbXBFPgIwRPLSm3AO5wa+32BQYASIOCPcsPywV+HJKbQpzmWqXDYel6hrifmIxspgxHhGMlHJ1Q==} + '@oxlint/linux-x64-musl@0.9.10': + resolution: {integrity: sha512-aBBwN7bQzidwHwEXr7BAdVvMTLWstCy5gikerjLnGDeCSXX9r+o6+yUzTOqZvOo66E+XBgOJaVbY8rsL1MLE0g==} cpu: [x64] os: [linux] - '@oxlint/win32-arm64@0.9.9': - resolution: {integrity: sha512-HSOztIKmiivfUAoxGx4qyerYV+aAXGvKbwWf8j4RorAEg2WWBdhVe9XHoSdqgYsOBi1515+YXxXiSRX3F/0xAg==} + '@oxlint/win32-arm64@0.9.10': + resolution: {integrity: sha512-LXDnk7vKHT3IY6G1jq0O7+XMhtcHOYuxLGIx4KP+4xS6vKgBY+Bsq4xV3AtmtKlvnXkP5FxHpfLmcEtm5AWysA==} cpu: [arm64] os: [win32] - '@oxlint/win32-x64@0.9.9': - resolution: {integrity: sha512-T1/tNxqoYd/MMqi1dhSVzAVL0ZINvXDBEQWm6OCSrrjRM6c9UQydTzsgLWfvm9uHWngcMuRGXhN3F+D6KEYs3w==} + '@oxlint/win32-x64@0.9.10': + resolution: {integrity: sha512-w5XRAV4bhgwenjjpGYZGglqzG9Wv/sI+cjQWJBQsvfDXsr2w4vOBXzt1j3/Z3EcSqf4KtkCa/IIuAhQyeShUbA==} cpu: [x64] os: [win32] + '@parcel/watcher-android-arm64@2.5.0': + resolution: {integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.0': + resolution: {integrity: sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.0': + resolution: {integrity: sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.0': + resolution: {integrity: sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.0': + resolution: {integrity: sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.0': + resolution: {integrity: sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.0': + resolution: {integrity: sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.0': + resolution: {integrity: sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.0': + resolution: {integrity: sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.0': + resolution: {integrity: sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.0': + resolution: {integrity: sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.0': + resolution: {integrity: sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.0': + resolution: {integrity: sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.0': + resolution: {integrity: sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==} + engines: {node: '>= 10.0.0'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -1653,23 +1734,23 @@ packages: cpu: [x64] os: [win32] - '@shikijs/core@1.21.0': - resolution: {integrity: sha512-zAPMJdiGuqXpZQ+pWNezQAk5xhzRXBNiECFPcJLtUdsFM3f//G95Z15EHTnHchYycU8kIIysqGgxp8OVSj1SPQ==} + '@shikijs/core@1.22.2': + resolution: {integrity: sha512-bvIQcd8BEeR1yFvOYv6HDiyta2FFVePbzeowf5pPS1avczrPK+cjmaxxh0nx5QzbON7+Sv0sQfQVciO7bN72sg==} - '@shikijs/engine-javascript@1.21.0': - resolution: {integrity: sha512-jxQHNtVP17edFW4/0vICqAVLDAxmyV31MQJL4U/Kg+heQALeKYVOWo0sMmEZ18FqBt+9UCdyqGKYE7bLRtk9mg==} + '@shikijs/engine-javascript@1.22.2': + resolution: {integrity: sha512-iOvql09ql6m+3d1vtvP8fLCVCK7BQD1pJFmHIECsujB0V32BJ0Ab6hxk1ewVSMFA58FI0pR2Had9BKZdyQrxTw==} - '@shikijs/engine-oniguruma@1.21.0': - resolution: {integrity: sha512-AIZ76XocENCrtYzVU7S4GY/HL+tgHGbVU+qhiDyNw1qgCA5OSi4B4+HY4BtAoJSMGuD/L5hfTzoRVbzEm2WTvg==} + '@shikijs/engine-oniguruma@1.22.2': + resolution: {integrity: sha512-GIZPAGzQOy56mGvWMoZRPggn0dTlBf1gutV5TdceLCZlFNqWmuc7u+CzD0Gd9vQUTgLbrt0KLzz6FNprqYAxlA==} - '@shikijs/transformers@1.21.0': - resolution: {integrity: sha512-aA+XGGSzipcvqdsOYL8l6Q2RYiMuJNdhdt9eZnkJmW+wjSOixN/I7dBq3fISwvEMDlawrtuXM3eybLCEC+Fjlg==} + '@shikijs/transformers@1.22.2': + resolution: {integrity: sha512-8f78OiBa6pZDoZ53lYTmuvpFPlWtevn23bzG+azpPVvZg7ITax57o/K3TC91eYL3OMJOO0onPbgnQyZjRos8XQ==} - '@shikijs/types@1.21.0': - resolution: {integrity: sha512-tzndANDhi5DUndBtpojEq/42+dpUF2wS7wdCDQaFtIXm3Rd1QkrcVgSSRLOvEwexekihOXfbYJINW37g96tJRw==} + '@shikijs/types@1.22.2': + resolution: {integrity: sha512-NCWDa6LGZqTuzjsGfXOBWfjS/fDIbDdmVDug+7ykVe1IKT4c1gakrvlfFYp5NhAXH/lyqLM8wsAPo5wNy73Feg==} - '@shikijs/vscode-textmate@9.2.2': - resolution: {integrity: sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg==} + '@shikijs/vscode-textmate@9.3.0': + resolution: {integrity: sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==} '@sindresorhus/is@5.4.1': resolution: {integrity: sha512-axlrvsHlHlFmKKMEg4VyvMzFr93JWJj4eIfXY1STVuO2fsImCa7ncaiG5gC8HKOX590AW5RtRsC41/B+OfrSqw==} @@ -1767,8 +1848,8 @@ packages: '@types/node@20.10.3': resolution: {integrity: sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==} - '@types/node@20.16.10': - resolution: {integrity: sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==} + '@types/node@20.17.6': + resolution: {integrity: sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==} '@types/normalize-path@3.0.2': resolution: {integrity: sha512-DO++toKYPaFn0Z8hQ7Tx+3iT9t77IJo/nDiqTXilgEP+kPNIYdpS9kh3fXuc53ugqwp9pxC1PVjCpV1tQDyqMA==} @@ -1779,11 +1860,11 @@ packages: '@types/prop-types@15.7.9': resolution: {integrity: sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==} - '@types/react-dom@18.3.0': - resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react-dom@18.3.1': + resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==} - '@types/react@18.3.11': - resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==} + '@types/react@18.3.12': + resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -1803,116 +1884,115 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - '@unocss/astro@0.63.4': - resolution: {integrity: sha512-qu1uMDUT8lXU3mm5EjZpnizvjSYtfY0TTDivR5QNm1i3Xd+ErHfdfOpXdJ2mYvxv+X7C570//KUugkTI3Mb3kQ==} + '@unocss/astro@0.63.6': + resolution: {integrity: sha512-5Fjlv6dpQo6o2PUAcEv8p24G8rn8Op79xLFofq2V+iA/Q32G9/UsxTLOpj+yc+q0YdJrFfDCT2X/3pvVY8Db5g==} peerDependencies: vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 peerDependenciesMeta: vite: optional: true - '@unocss/cli@0.63.4': - resolution: {integrity: sha512-kBWEiVW7KWfjptAJsk38w9dVqOmrO2/z0WADFnlX2RuKNDoCn422Rus6tFB12wZsEujC9eFM34P2nnU7IWWtlQ==} + '@unocss/cli@0.63.6': + resolution: {integrity: sha512-OZb8hO0x4nCJjFd3Gq3km78YnyMAdq282D+BLiDE6IhQ5WHCVL7fyhfgIVL6xwxISDVxiyITwNb72ky0MEutPg==} engines: {node: '>=14'} hasBin: true - '@unocss/config@0.63.4': - resolution: {integrity: sha512-LfAzM8z0r2comUW94KaSo4JaaEZjPkvrfyVWfO/hyaXa+/xSVIkCTW7+lfWh77hrg1e2SUY1HEvIFBg9Jvb1xQ==} + '@unocss/config@0.63.6': + resolution: {integrity: sha512-+4Lt5uTwRgu1z7vhOUzDf+mL+BQYdaa/Z8NMT2Fiqb37tcjEKvmwaUHdfE22Vif1luDgC6xqFsn6qqFtOxhoWQ==} engines: {node: '>=14'} - '@unocss/core@0.63.4': - resolution: {integrity: sha512-VB4DJ5DsRWpX64si5tWYRXf1n5UkYQqe2s1V22qFiWmXa7Ec+Vf9s3cxWZmoWFC5P9RQiwM9kAqxdg1G+elVkQ==} + '@unocss/core@0.63.6': + resolution: {integrity: sha512-Q4QPgJ271Up89+vIqqOKgtdCKkFpHqvHN8W1LUlKPqtYnOvVYaOIVNAZowaIdEhPuc83yLc6Tg2+7riK18QKEw==} - '@unocss/extractor-arbitrary-variants@0.63.4': - resolution: {integrity: sha512-gI/+2Nv+cH/ZoOc/4X7RLD9CuBXH51jfwGJ1xRveS7tj+EBs8VshP7Vhbn6Jyp69E00wt4hyzjviDoGqcIA8bA==} + '@unocss/extractor-arbitrary-variants@0.63.6': + resolution: {integrity: sha512-HJX0oAa9uzwKYoU8CoJdP1gxjuqFmOLxyZmITjStAmZNZpIxlz2wz4VrHmqml2dkvx/mifGGGc/GxZpQ36D12Q==} - '@unocss/inspector@0.63.4': - resolution: {integrity: sha512-NHvOTScsMrh6oMmwGMrqB1q1RCFTHZCIK0Vwp8hL8/gmNlza2Kd2cQ/WYSEsjW132xeLCOqTME5qny1gpG6SpA==} + '@unocss/inspector@0.63.6': + resolution: {integrity: sha512-DQDJnhtzdHIQXD2vCdj5ytFnHfQCWJGPmrHJHXxzkTYn8nIovV1roVl1ITLxkDIIYK9bdYneg2imQN5JCZhHmQ==} - '@unocss/postcss@0.63.4': - resolution: {integrity: sha512-JnSAV1hAZumkm0KZGXYqWsP2I7wnOdr+oeDckHKLdZR2mHNVbDm46H8XGbie55t/gPftaLSsMbaPvRjU2Fclqg==} + '@unocss/postcss@0.63.6': + resolution: {integrity: sha512-XI6U1jMwbQoSHVWpZZu3Cxp3t1PVj5VOj+IYtz7xmcWP9GVK+eyETo/xyB0l4muD4emXfSrhNDrFYzSyIyF5cg==} engines: {node: '>=14'} peerDependencies: postcss: ^8.4.21 - '@unocss/preset-attributify@0.63.4': - resolution: {integrity: sha512-Q2DT4oVdxaL7XxD9sDP3adb5tnYr05sCxCxPhv3ch8brU7uvwbyqkiEw105pWbj0Hb3i/0kD4iq7lVMZYRH5nw==} + '@unocss/preset-attributify@0.63.6': + resolution: {integrity: sha512-sHH17mfl/THHLxCLAHqPdUniCNMFjAxBHFDZYgGi83azuarF2evI5Mtc3Qsj3nzoSQwTPmK2VY3XYUUrpPDGWQ==} - '@unocss/preset-icons@0.63.4': - resolution: {integrity: sha512-V7JV2xvEGeNVjP6HT4IG/BY/HgajJt9CLT2sgKbaVCU9hNOuBs1YTOxua0KLynbTYwr5F5cDMuE/9slQYinZmg==} + '@unocss/preset-icons@0.63.6': + resolution: {integrity: sha512-fRU44wXABnMPT/9zhKBNSUeDJlOxJhUJP9W3FSRnc+ktjAifJIj0xpUKtEqxL46QMq825Bsj2yDSquzP+XYGnQ==} - '@unocss/preset-mini@0.63.4': - resolution: {integrity: sha512-sim1/uy/XaVzdnMdepXdbdacXF5QNkPDnl4PYBWTyGuT5yKFpuipWpJDS5zZH5W6PYzKdcDA3YiaJ0S5CiUWpQ==} + '@unocss/preset-mini@0.63.6': + resolution: {integrity: sha512-pZDZbSuxabHSwPIy3zCgQ4MNdVCSHvOvZecreH+v96R1oOhquiwU8WiSbkxvZiKiLQJd7JUVW87E1pAzr5ZGGQ==} - '@unocss/preset-tagify@0.63.4': - resolution: {integrity: sha512-RQkeSCKrGAowomjh8/chlnVWWOFlC+QkHB1oY5isRXNO2HStESZljyL/MisRpgjj0ubPiocoFCI2hRzXT/HrSg==} + '@unocss/preset-tagify@0.63.6': + resolution: {integrity: sha512-3lKhk4MW3RqJBwIvBXHj0H0/kHkFlKtCIBQFiBcCJh8TXOID8IZ0iVjuGwdlk63VTizI/wnsNDOVpj6YcjRRlw==} - '@unocss/preset-typography@0.63.4': - resolution: {integrity: sha512-PtRXDqF8dW1GYDxiF1Opl+M5fhZeKx63bhvtXXf3iHjVzPDSHB6w1kTElh6vIWeLDNM9GZbbJyB5f2C8DBjibw==} + '@unocss/preset-typography@0.63.6': + resolution: {integrity: sha512-AXmBVnbV54gUIv5kbywjZek9ZlKRwJfBDVMtWOcLOjN3AHirGx1W2oq2UzNkfYZ2leof/Y2BocxeTwGCCRhqDQ==} - '@unocss/preset-uno@0.63.4': - resolution: {integrity: sha512-VMc2R0XRMjXA5u5HnP0SkiWtc8EnEJvipNPKsWBuyyVb0QrsIXtF5z3l3cuZmD6V7m/o9s81yshL0gFOBpF7iQ==} + '@unocss/preset-uno@0.63.6': + resolution: {integrity: sha512-67PbHyVgAe9Rz0Rhyl3zBibFuGmqQMRPMkRjNYrwmmtNydpQYsXbfnDs0p8mZFp6uO2o3Jkh7urqEtixHHvq0Q==} - '@unocss/preset-web-fonts@0.63.4': - resolution: {integrity: sha512-XuU4dNwTQ0ULlYpQFSKk2JRYACTzpIzpPGP5ZnqdwBxEQH5JhXx4mEmaOhu1OH3c2hZURAkdQvBzYWia4oZ6og==} + '@unocss/preset-web-fonts@0.63.6': + resolution: {integrity: sha512-ko1aHDax0u5CQi1BXggv6uW5Vq/LQRWwzOxqBFTh1JlGHPZTw4CdVJkYnlpt3WEW+FPUzZYjhKmMmQY7KtOTng==} - '@unocss/preset-wind@0.63.4': - resolution: {integrity: sha512-8fTUp6ZxH9YiScz4nZ1tRqprayrlQSfguzkjxDvOrwazfNcmxvHSZfC9dtpEmY+QssM1zHH0mmWmWgQYwU9Zdw==} + '@unocss/preset-wind@0.63.6': + resolution: {integrity: sha512-W3oZ2TXSqStNE+X++kcspRTF2Szu2ej6NW5Kiyy6WQn/+ZD77AF4VtvzHtzFVZ2QKpEIovGBpU5tywooHbB7hw==} - '@unocss/reset@0.63.4': - resolution: {integrity: sha512-7lnVH9zuVMekY0IUtcQRrbEqlkhvyGixgzHSWPBF/JA/Pto18bhd+cMeZhuz4eHRbN274bANX+//I+Ilfo7SSg==} + '@unocss/reset@0.63.6': + resolution: {integrity: sha512-gq73RSZj54MOloqrivkoMPXCqNG2WpIyBT1AYlF76uKxEEbUD41E8uBUhLSKs7gFgF01yQJLRaIuyN1yw09pbQ==} - '@unocss/rule-utils@0.63.4': - resolution: {integrity: sha512-7yRWF881ymxnMcCJSiI/1kMI8uwRqRi3l5XnV+JSGjjF2fDr1POUQjSLaA4s7ZfdEgmjagdLK3F5xqkfMMECNA==} + '@unocss/rule-utils@0.63.6': + resolution: {integrity: sha512-moeDEq5d9mB8gSYeoqHMkXWWekaFFdhg7QCuwwCbxCc+NPMOgGkmfAoafz+y2tdvK7pEuT191RWOiHQ0MkA5oQ==} engines: {node: '>=14'} - '@unocss/transformer-attributify-jsx@0.63.4': - resolution: {integrity: sha512-5cO9BY/Bga6YmbTch1Neg+E46HerJp5wLxPkIcFCDNsqy2MsB97jsFG1dO0jDUg43E26MRI19tg1eqrWL6sTYg==} + '@unocss/transformer-attributify-jsx@0.63.6': + resolution: {integrity: sha512-/RU09MF+hJK7cFbLJ+8vloCGyhn6Oys8R6gey0auB0+nw/ucUXoLQKWgUqo9taQlLuYOiehdkYjQSdWn5lyA/Q==} - '@unocss/transformer-compile-class@0.63.4': - resolution: {integrity: sha512-ta6mqq2S5OWcfBzzYnaiMt3ekn2ECNZTqzzqMglnIKPkE+GmqUmmRavRnpc+NGobuqMRcI4F6x8MSSHf4MV0jw==} + '@unocss/transformer-compile-class@0.63.6': + resolution: {integrity: sha512-zzAqs8adnTUOLA88RgcToadcrz9gjxrZk6IrcmMqMmWqk0MOWNQHIN0RzKa/yaw4QhO2xuGyIz4/WHyXuCXMQg==} - '@unocss/transformer-directives@0.63.4': - resolution: {integrity: sha512-N/dNhmn3e9/Z4IvAujxCdwhNMfx2SihPA2/7GFSMMRi7F0Hn/o2hOqQquRqIJbQwIvi6bJtKwyasxjDoUhJqBA==} + '@unocss/transformer-directives@0.63.6': + resolution: {integrity: sha512-XcNOwLRbfrJSU6YXyLgiMzAigSzjIdvHwS3lLCZ2n6DWuLmTuXBfvVtRxeJ+aflNkhpQNKONCClC4s6I2r53uw==} - '@unocss/transformer-variant-group@0.63.4': - resolution: {integrity: sha512-uEHltdfR0Y1nvs1eqHwsgevRFhZkLmA/MsaMEfNblDJ6CLHe/ACNmMoLX1Mcuq/lAPs0X6jGnKudk4QTrCv15Q==} + '@unocss/transformer-variant-group@0.63.6': + resolution: {integrity: sha512-ebYSjZnZrtcJYjmAEDwGVwPuaQ9EVWKNDDJFFSusP8k/6PjJoHDh0qkj+hdPPDhYn81yzJQalU1eSUSlfC30VA==} - '@unocss/vite@0.63.4': - resolution: {integrity: sha512-YK0L177GD8Kx+JtfiCJy4YyBYckAXo4ogC8LZ+pYVNXDMN+F+XItpGI/ofLRaGIaewNg+MJgGY+CQZceABEAfg==} + '@unocss/vite@0.63.6': + resolution: {integrity: sha512-gxK3gtvYQH5S/qtuvsY4M0S+KJPZnYrOQI/Gopufx+b2qgmwZ/TSAe66gWeKYfe3DfQsmA3PPh/GXpkK+/FnHg==} peerDependencies: vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 - '@vitejs/plugin-react@4.3.2': - resolution: {integrity: sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==} + '@vitejs/plugin-react@4.3.3': + resolution: {integrity: sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 - '@vitejs/plugin-vue@5.1.4': - resolution: {integrity: sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==} + '@vitejs/plugin-vue@5.2.0': + resolution: {integrity: sha512-7n7KdUEtx/7Yl7I/WVAMZ1bEb0eVvXF3ummWTeLcs/9gvo9pJhuLdouSXGjdZ/MKD1acf1I272+X0RMua4/R3g==} engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: vite: ^5.0.0 vue: ^3.2.25 - '@vitest/coverage-v8@2.1.2': - resolution: {integrity: sha512-b7kHrFrs2urS0cOk5N10lttI8UdJ/yP3nB4JYTREvR5o18cR99yPpK4gK8oQgI42BVv0ILWYUSYB7AXkAUDc0g==} + '@vitest/coverage-v8@2.1.4': + resolution: {integrity: sha512-FPKQuJfR6VTfcNMcGpqInmtJuVXFSCd9HQltYncfR01AzXhLucMEtQ5SinPdZxsT5x/5BK7I5qFJ5/ApGCmyTQ==} peerDependencies: - '@vitest/browser': 2.1.2 - vitest: 2.1.2 + '@vitest/browser': 2.1.4 + vitest: 2.1.4 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@2.1.2': - resolution: {integrity: sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==} + '@vitest/expect@2.1.4': + resolution: {integrity: sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==} - '@vitest/mocker@2.1.2': - resolution: {integrity: sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==} + '@vitest/mocker@2.1.4': + resolution: {integrity: sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==} peerDependencies: - '@vitest/spy': 2.1.2 - msw: ^2.3.5 + msw: ^2.4.9 vite: ^5.0.0 peerDependenciesMeta: msw: @@ -1920,58 +2000,58 @@ packages: vite: optional: true - '@vitest/pretty-format@2.1.2': - resolution: {integrity: sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==} + '@vitest/pretty-format@2.1.4': + resolution: {integrity: sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==} - '@vitest/runner@2.1.2': - resolution: {integrity: sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==} + '@vitest/runner@2.1.4': + resolution: {integrity: sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==} - '@vitest/snapshot@2.1.2': - resolution: {integrity: sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==} + '@vitest/snapshot@2.1.4': + resolution: {integrity: sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==} - '@vitest/spy@2.1.2': - resolution: {integrity: sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==} + '@vitest/spy@2.1.4': + resolution: {integrity: sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==} - '@vitest/utils@2.1.2': - resolution: {integrity: sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==} + '@vitest/utils@2.1.4': + resolution: {integrity: sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==} - '@vue/compiler-core@3.5.11': - resolution: {integrity: sha512-PwAdxs7/9Hc3ieBO12tXzmTD+Ln4qhT/56S+8DvrrZ4kLDn4Z/AMUr8tXJD0axiJBS0RKIoNaR0yMuQB9v9Udg==} + '@vue/compiler-core@3.5.12': + resolution: {integrity: sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==} - '@vue/compiler-dom@3.5.11': - resolution: {integrity: sha512-pyGf8zdbDDRkBrEzf8p7BQlMKNNF5Fk/Cf/fQ6PiUz9at4OaUfyXW0dGJTo2Vl1f5U9jSLCNf0EZJEogLXoeew==} + '@vue/compiler-dom@3.5.12': + resolution: {integrity: sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==} - '@vue/compiler-sfc@3.5.11': - resolution: {integrity: sha512-gsbBtT4N9ANXXepprle+X9YLg2htQk1sqH/qGJ/EApl+dgpUBdTv3yP7YlR535uHZY3n6XaR0/bKo0BgwwDniw==} + '@vue/compiler-sfc@3.5.12': + resolution: {integrity: sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==} - '@vue/compiler-ssr@3.5.11': - resolution: {integrity: sha512-P4+GPjOuC2aFTk1Z4WANvEhyOykcvEd5bIj2KVNGKGfM745LaXGr++5njpdBTzVz5pZifdlR1kpYSJJpIlSePA==} + '@vue/compiler-ssr@3.5.12': + resolution: {integrity: sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==} - '@vue/devtools-api@7.4.6': - resolution: {integrity: sha512-XipBV5k0/IfTr0sNBDTg7OBUCp51cYMMXyPxLXJZ4K/wmUeMqt8cVdr2ZZGOFq+si/jTyCYnNxeKoyev5DOUUA==} + '@vue/devtools-api@7.6.4': + resolution: {integrity: sha512-5AaJ5ELBIuevmFMZYYLuOO9HUuY/6OlkOELHE7oeDhy4XD/hSODIzktlsvBOsn+bto3aD0psj36LGzwVu5Ip8w==} - '@vue/devtools-kit@7.4.6': - resolution: {integrity: sha512-NbYBwPWgEic1AOd9bWExz9weBzFdjiIfov0yRn4DrRfR+EQJCI9dn4I0XS7IxYGdkmUJi8mFW42LLk18WsGqew==} + '@vue/devtools-kit@7.6.4': + resolution: {integrity: sha512-Zs86qIXXM9icU0PiGY09PQCle4TI750IPLmAJzW5Kf9n9t5HzSYf6Rz6fyzSwmfMPiR51SUKJh9sXVZu78h2QA==} - '@vue/devtools-shared@7.4.6': - resolution: {integrity: sha512-rPeSBzElnHYMB05Cc056BQiJpgocQjY8XVulgni+O9a9Gr9tNXgPteSzFFD+fT/iWMxNuUgGKs9CuW5DZewfIg==} + '@vue/devtools-shared@7.6.4': + resolution: {integrity: sha512-nD6CUvBEel+y7zpyorjiUocy0nh77DThZJ0k1GRnJeOmY3ATq2fWijEp7wk37gb023Cb0R396uYh5qMSBQ5WFg==} - '@vue/reactivity@3.5.11': - resolution: {integrity: sha512-Nqo5VZEn8MJWlCce8XoyVqHZbd5P2NH+yuAaFzuNSR96I+y1cnuUiq7xfSG+kyvLSiWmaHTKP1r3OZY4mMD50w==} + '@vue/reactivity@3.5.12': + resolution: {integrity: sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==} - '@vue/runtime-core@3.5.11': - resolution: {integrity: sha512-7PsxFGqwfDhfhh0OcDWBG1DaIQIVOLgkwA5q6MtkPiDFjp5gohVnJEahSktwSFLq7R5PtxDKy6WKURVN1UDbzA==} + '@vue/runtime-core@3.5.12': + resolution: {integrity: sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==} - '@vue/runtime-dom@3.5.11': - resolution: {integrity: sha512-GNghjecT6IrGf0UhuYmpgaOlN7kxzQBhxWEn08c/SQDxv1yy4IXI1bn81JgEpQ4IXjRxWtPyI8x0/7TF5rPfYQ==} + '@vue/runtime-dom@3.5.12': + resolution: {integrity: sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==} - '@vue/server-renderer@3.5.11': - resolution: {integrity: sha512-cVOwYBxR7Wb1B1FoxYvtjJD8X/9E5nlH4VSkJy2uMA1MzYNdzAAB//l8nrmN9py/4aP+3NjWukf9PZ3TeWULaA==} + '@vue/server-renderer@3.5.12': + resolution: {integrity: sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==} peerDependencies: - vue: 3.5.11 + vue: 3.5.12 - '@vue/shared@3.5.11': - resolution: {integrity: sha512-W8GgysJVnFo81FthhzurdRAWP/byq3q2qIw70e0JWblzVhjgOMiC2GyovXrZTFQJnFVryYaKGP3Tc9vYzYm6PQ==} + '@vue/shared@3.5.12': + resolution: {integrity: sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==} '@vueuse/core@11.1.0': resolution: {integrity: sha512-P6dk79QYA6sKQnghrUz/1tHi0n9mrb/iO1WTMk/ElLmTyNqgDeSZ3wcDf6fRBGzRJbeG1dxzEOvLENMjr+E3fg==} @@ -2151,8 +2231,8 @@ packages: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} - birpc@0.2.17: - resolution: {integrity: sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg==} + birpc@0.2.19: + resolution: {integrity: sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==} bl@5.1.0: resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} @@ -2259,8 +2339,8 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai@5.1.1: - resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} + chai@5.1.2: + resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} engines: {node: '>=12'} chalk@2.4.2: @@ -2314,6 +2394,10 @@ packages: resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} engines: {node: '>=8'} + ci-info@4.1.0: + resolution: {integrity: sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==} + engines: {node: '>=8'} + citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} @@ -2408,8 +2492,8 @@ packages: resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} engines: {'0': node >= 0.8} - confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -2521,15 +2605,6 @@ packages: supports-color: optional: true - debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.3.7: resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} @@ -2600,6 +2675,11 @@ packages: destr@2.0.3: resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==} + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + detect-libc@2.0.3: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} @@ -2744,6 +2824,10 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + expect-type@1.1.0: + resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} + engines: {node: '>=12.0.0'} + extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -2779,8 +2863,8 @@ packages: resolution: {integrity: sha512-I9rAm1w8U3CdhgO4EzTJsCvgcbvynZn9lOySkZf78wUdUIQH2w9QOKf3pAX+THt2XMSSR3kJSuM8P7bYux9j8g==} hasBin: true - focus-trap@7.5.4: - resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==} + focus-trap@7.6.1: + resolution: {integrity: sha512-nB8y4nQl8PshahLpGKZOq1sb0xrMVFSn6at7u/qOsBZTlZRzaapISGENcB6mOkoezbClZyiMwEF/dGY8AZ00rA==} foreground-child@3.2.1: resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} @@ -2994,8 +3078,8 @@ packages: immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - immutable@4.3.4: - resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} + immutable@5.0.2: + resolution: {integrity: sha512-1NU7hWZDkV7hJ4PJ9dur9gTNQ4ePNPN4k9/0YhwjzykTi/+3Q5pF93YU5QoVj8BuOnhLgaY8gs0U2pj4kSYVcw==} import-lazy@4.0.0: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} @@ -3342,6 +3426,9 @@ packages: loupe@3.1.1: resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} + loupe@3.1.2: + resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} + lowercase-keys@3.0.0: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3577,6 +3664,9 @@ packages: resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==} hasBin: true + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-fetch-native@1.6.4: resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==} @@ -3678,16 +3768,16 @@ packages: resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - ora@8.1.0: - resolution: {integrity: sha512-GQEkNkH/GHOhPFXcqZs3IDahXEQcQxsSjEkK4KvEEST4t7eNzoMjxTzef+EZ+JluDEV+Raoi3WQ2CflnRdSVnQ==} + ora@8.1.1: + resolution: {integrity: sha512-YWielGi1XzG1UTvOaCFaNgEnuhZVMSHYkW/FQ7UX8O26PtlpdM84c0f7wLPlkvx2RfiQmnzd61d/MGxmpQeJPw==} engines: {node: '>=18'} os-shim@0.1.3: resolution: {integrity: sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==} engines: {node: '>= 0.4.0'} - oxlint@0.9.9: - resolution: {integrity: sha512-EqUmSKgnU7KNCi29uFe5MQnUZ0GH569Inkry/e84k4lwVRv1QuDr4DHLpu0cXBN2atwhKf1Szk/3uxmB2qgPbw==} + oxlint@0.9.10: + resolution: {integrity: sha512-bKiiFN7Hnoaist/rditTRBXz+GXKYuLd53/NB7Q6zHB/bifELJarSoRLkAUGElIJKl4PSr3lTh1g6zehh+rX0g==} engines: {node: '>=14.*'} hasBin: true @@ -3757,8 +3847,8 @@ packages: perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -3994,8 +4084,8 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - publint@0.2.11: - resolution: {integrity: sha512-/kxbd+sD/uEG515N/ZYpC6gYs8h89cQ4UIsAq1y6VT4qlNh8xmiSwcP2xU2MbzXFl8J0l2IdONKFweLfYoqhcA==} + publint@0.2.12: + resolution: {integrity: sha512-YNeUtCVeM4j9nDiTT2OPczmlyzOkIXNtdDZnSuajAxS/nZ6j3t7Vs9SUB4euQNddiltIwu7Tdd3s+hr08fAsMw==} engines: {node: '>=16'} hasBin: true @@ -4147,8 +4237,8 @@ packages: safe-json-stringify@1.2.0: resolution: {integrity: sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==} - sass@1.79.4: - resolution: {integrity: sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==} + sass@1.80.7: + resolution: {integrity: sha512-MVWvN0u5meytrSjsU7AWsbhoXi1sc58zADXFllfZzbsBT1GHjjar6JwBINYPRrkx/zqnQ6uqbQuHgE95O+C+eQ==} engines: {node: '>=14.0.0'} hasBin: true @@ -4220,8 +4310,8 @@ packages: shiki@0.14.5: resolution: {integrity: sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==} - shiki@1.21.0: - resolution: {integrity: sha512-apCH5BoWTrmHDPGgg3RF8+HAAbEL/CdbYr8rMw7eIrdhCkZHdVGat5mMNlRtd1erNG01VPMIKHNQ0Pj2HMAiog==} + shiki@1.22.2: + resolution: {integrity: sha512-3IZau0NdGKXhH2bBlUk4w1IHNxPh6A5B2sUpyY+8utLu2j/h1QpFkAaUA1bAMxOWWGtTWcAh531vnS4NJKS/lA==} siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -4259,8 +4349,8 @@ packages: resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} engines: {node: '>=18'} - solid-js@1.9.1: - resolution: {integrity: sha512-Gd6QWRFfO2XKKZqVK4YwbhWZkr0jWw1dYHOt+VYebomeyikGP0SuMflf42XcDuU9HAEYDArFJIYsBNjlE7iZsw==} + solid-js@1.9.3: + resolution: {integrity: sha512-5ba3taPoZGt9GY3YlsCB24kCg0Lv/rie/HTD4kG6h4daZZz7+yK02xn8Vx8dLYBc9i6Ps5JwAbEiqjmKaLB3Ag==} solid-refresh@0.6.3: resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==} @@ -4424,20 +4514,23 @@ packages: tinyexec@0.3.0: resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + tinyexec@0.3.1: + resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} + tinyglobby@0.2.9: resolution: {integrity: sha512-8or1+BGEdk1Zkkw2ii16qSS7uVrQJPre5A9o/XkWPATkk23FZh/15BKFxPnlTy6vkljZxLqYCzzBMj30ZrSvjw==} engines: {node: '>=12.0.0'} - tinypool@1.0.0: - resolution: {integrity: sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==} + tinypool@1.0.1: + resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} engines: {node: ^18.0.0 || >=20.0.0} tinyrainbow@1.2.0: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} - tinyspy@3.0.0: - resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==} + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} titleize@3.0.0: @@ -4519,8 +4612,8 @@ packages: peerDependencies: typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x - typescript@5.6.2: - resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + typescript@5.6.3: + resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} engines: {node: '>=14.17'} hasBin: true @@ -4581,11 +4674,11 @@ packages: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} - unocss@0.63.4: - resolution: {integrity: sha512-MQ/ktuJ2MoXBsd117DEONFubJRQN6Og4mQJLbT+0nna2aTW4jYJESJ479mJYWq/ajonxEaM+zrf8M92VIWxzEw==} + unocss@0.63.6: + resolution: {integrity: sha512-OKJJKEFWVz+Lsf3JdOgRiRtL+QOUQRBov89taUcCPFPZtrhP6pPVFCZHD9qMvY4IChMX7dzalQax3ZXJ3hbtkQ==} engines: {node: '>=14'} peerDependencies: - '@unocss/webpack': 0.63.4 + '@unocss/webpack': 0.63.6 vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 peerDependenciesMeta: '@unocss/webpack': @@ -4642,8 +4735,8 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite-node@2.1.2: - resolution: {integrity: sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==} + vite-node@2.1.4: + resolution: {integrity: sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -4657,8 +4750,8 @@ packages: '@testing-library/jest-dom': optional: true - vite@5.4.8: - resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} + vite@5.4.11: + resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -4704,8 +4797,8 @@ packages: vite: optional: true - vitepress@1.3.4: - resolution: {integrity: sha512-I1/F6OW1xl3kW4PaIMC6snxjWgf3qfziq2aqsDoFc/Gt41WbcRv++z8zjw8qGRIJ+I4bUW7ZcKFDHHN/jkH9DQ==} + vitepress@1.5.0: + resolution: {integrity: sha512-q4Q/G2zjvynvizdB3/bupdYkCJe2umSAMv9Ju4d92E6/NXJ59z70xB0q5p/4lpRyAwflDsbwy1mLV9Q5+nlB+g==} hasBin: true peerDependencies: markdown-it-mathjax3: ^4 @@ -4727,15 +4820,15 @@ packages: peerDependencies: vite: ^4.0.0 || ^5.0.0 - vitest@2.1.2: - resolution: {integrity: sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==} + vitest@2.1.4: + resolution: {integrity: sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.1.2 - '@vitest/ui': 2.1.2 + '@vitest/browser': 2.1.4 + '@vitest/ui': 2.1.4 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -4769,8 +4862,11 @@ packages: '@vue/composition-api': optional: true - vue@3.5.11: - resolution: {integrity: sha512-/8Wurrd9J3lb72FTQS7gRMNQD4nztTtKPmuDuPuhqXmmpD6+skVjAeahNpVzsuky6Sy9gy7wn8UadqPtt9SQIg==} + vue-flow-layout@0.0.5: + resolution: {integrity: sha512-lZlqQ/Se1trGMtBMneZDWaiQiQBuxU8ivZ+KpJMem5zKROFpzuPq9KqyWABbSYbxq0qhqZs1I4DBwrY041rtOA==} + + vue@3.5.12: + resolution: {integrity: sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -4932,12 +5028,12 @@ snapshots: proper-lockfile: 4.1.2 yaml: 2.5.1 - '@aklinker1/check@1.4.5(typescript@5.6.2)': + '@aklinker1/check@1.4.5(typescript@5.6.3)': dependencies: '@antfu/utils': 0.7.7 ci-info: 4.0.0 citty: 0.1.6 - typescript: 5.6.2 + typescript: 5.6.3 '@aklinker1/rollup-plugin-visualizer@5.12.0(rollup@4.24.0)': dependencies: @@ -5038,11 +5134,6 @@ snapshots: '@algolia/logger-common': 4.20.0 '@algolia/requester-common': 4.20.0 - '@ampproject/remapping@2.2.1': - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.5 @@ -5060,12 +5151,12 @@ snapshots: '@babel/code-frame@7.24.7': dependencies: '@babel/highlight': 7.24.7 - picocolors: 1.1.0 + picocolors: 1.1.1 '@babel/code-frame@7.25.7': dependencies: '@babel/highlight': 7.25.7 - picocolors: 1.1.0 + picocolors: 1.1.1 '@babel/compat-data@7.24.7': {} @@ -5073,7 +5164,7 @@ snapshots: '@babel/core@7.24.7': dependencies: - '@ampproject/remapping': 2.2.1 + '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.24.7 '@babel/generator': 7.24.7 '@babel/helper-compilation-targets': 7.24.7 @@ -5084,7 +5175,7 @@ snapshots: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 convert-source-map: 2.0.0 - debug: 4.3.4 + debug: 4.3.7 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -5104,7 +5195,7 @@ snapshots: '@babel/traverse': 7.25.7 '@babel/types': 7.25.7 convert-source-map: 2.0.0 - debug: 4.3.5 + debug: 4.3.7 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -5147,12 +5238,12 @@ snapshots: '@babel/helper-function-name@7.24.7': dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.25.6 + '@babel/template': 7.25.7 + '@babel/types': 7.25.7 '@babel/helper-hoist-variables@7.24.7': dependencies: - '@babel/types': 7.25.6 + '@babel/types': 7.25.7 '@babel/helper-module-imports@7.18.6': dependencies: @@ -5242,14 +5333,14 @@ snapshots: '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 - picocolors: 1.1.0 + picocolors: 1.1.1 '@babel/highlight@7.25.7': dependencies: '@babel/helper-validator-identifier': 7.25.7 chalk: 2.4.2 js-tokens: 4.0.0 - picocolors: 1.1.0 + picocolors: 1.1.1 '@babel/parser@7.24.7': dependencies: @@ -5306,7 +5397,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.25.6 '@babel/types': 7.25.6 - debug: 4.3.5 + debug: 4.3.7 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -5318,7 +5409,7 @@ snapshots: '@babel/parser': 7.25.7 '@babel/template': 7.25.7 '@babel/types': 7.25.7 - debug: 4.3.5 + debug: 4.3.7 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -5353,7 +5444,7 @@ snapshots: '@devicefarmer/adbkit-monkey': 1.2.1 bluebird: 3.7.2 commander: 9.5.0 - debug: 4.3.5 + debug: 4.3.7 node-forge: 1.3.1 split: 1.0.1 transitivePeerDependencies: @@ -5361,9 +5452,9 @@ snapshots: '@docsearch/css@3.6.2': {} - '@docsearch/js@3.6.2(@algolia/client-search@4.20.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)': + '@docsearch/js@3.6.2(@algolia/client-search@4.20.0)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)': dependencies: - '@docsearch/react': 3.6.2(@algolia/client-search@4.20.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0) + '@docsearch/react': 3.6.2(@algolia/client-search@4.20.0)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0) preact: 10.18.1 transitivePeerDependencies: - '@algolia/client-search' @@ -5372,14 +5463,14 @@ snapshots: - react-dom - search-insights - '@docsearch/react@3.6.2(@algolia/client-search@4.20.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)': + '@docsearch/react@3.6.2(@algolia/client-search@4.20.0)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0)': dependencies: '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0)(search-insights@2.15.0) '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0) '@docsearch/css': 3.6.2 algoliasearch: 4.20.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.12 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) search-insights: 2.15.0 @@ -5603,6 +5694,10 @@ snapshots: '@faker-js/faker@8.4.1': {} + '@iconify-json/simple-icons@1.2.11': + dependencies: + '@iconify/types': 2.0.0 + '@iconify/types@2.0.0': {} '@iconify/utils@2.1.33': @@ -5734,28 +5829,89 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - '@oxlint/darwin-arm64@0.9.9': + '@oxlint/darwin-arm64@0.9.10': optional: true - '@oxlint/darwin-x64@0.9.9': + '@oxlint/darwin-x64@0.9.10': optional: true - '@oxlint/linux-arm64-gnu@0.9.9': + '@oxlint/linux-arm64-gnu@0.9.10': optional: true - '@oxlint/linux-arm64-musl@0.9.9': + '@oxlint/linux-arm64-musl@0.9.10': optional: true - '@oxlint/linux-x64-gnu@0.9.9': + '@oxlint/linux-x64-gnu@0.9.10': optional: true - '@oxlint/linux-x64-musl@0.9.9': + '@oxlint/linux-x64-musl@0.9.10': optional: true - '@oxlint/win32-arm64@0.9.9': + '@oxlint/win32-arm64@0.9.10': optional: true - '@oxlint/win32-x64@0.9.9': + '@oxlint/win32-x64@0.9.10': + optional: true + + '@parcel/watcher-android-arm64@2.5.0': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.0': + optional: true + + '@parcel/watcher-darwin-x64@2.5.0': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.0': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.0': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.0': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.0': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.0': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.0': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.0': + optional: true + + '@parcel/watcher-win32-arm64@2.5.0': + optional: true + + '@parcel/watcher-win32-ia32@2.5.0': + optional: true + + '@parcel/watcher-win32-x64@2.5.0': + optional: true + + '@parcel/watcher@2.5.0': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.0 + '@parcel/watcher-darwin-arm64': 2.5.0 + '@parcel/watcher-darwin-x64': 2.5.0 + '@parcel/watcher-freebsd-x64': 2.5.0 + '@parcel/watcher-linux-arm-glibc': 2.5.0 + '@parcel/watcher-linux-arm-musl': 2.5.0 + '@parcel/watcher-linux-arm64-glibc': 2.5.0 + '@parcel/watcher-linux-arm64-musl': 2.5.0 + '@parcel/watcher-linux-x64-glibc': 2.5.0 + '@parcel/watcher-linux-x64-musl': 2.5.0 + '@parcel/watcher-win32-arm64': 2.5.0 + '@parcel/watcher-win32-ia32': 2.5.0 + '@parcel/watcher-win32-x64': 2.5.0 optional: true '@pkgjs/parseargs@0.11.0': @@ -5880,58 +6036,58 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.24.0': optional: true - '@shikijs/core@1.21.0': + '@shikijs/core@1.22.2': dependencies: - '@shikijs/engine-javascript': 1.21.0 - '@shikijs/engine-oniguruma': 1.21.0 - '@shikijs/types': 1.21.0 - '@shikijs/vscode-textmate': 9.2.2 + '@shikijs/engine-javascript': 1.22.2 + '@shikijs/engine-oniguruma': 1.22.2 + '@shikijs/types': 1.22.2 + '@shikijs/vscode-textmate': 9.3.0 '@types/hast': 3.0.4 hast-util-to-html: 9.0.3 - '@shikijs/engine-javascript@1.21.0': + '@shikijs/engine-javascript@1.22.2': dependencies: - '@shikijs/types': 1.21.0 - '@shikijs/vscode-textmate': 9.2.2 + '@shikijs/types': 1.22.2 + '@shikijs/vscode-textmate': 9.3.0 oniguruma-to-js: 0.4.3 - '@shikijs/engine-oniguruma@1.21.0': + '@shikijs/engine-oniguruma@1.22.2': dependencies: - '@shikijs/types': 1.21.0 - '@shikijs/vscode-textmate': 9.2.2 + '@shikijs/types': 1.22.2 + '@shikijs/vscode-textmate': 9.3.0 - '@shikijs/transformers@1.21.0': + '@shikijs/transformers@1.22.2': dependencies: - shiki: 1.21.0 + shiki: 1.22.2 - '@shikijs/types@1.21.0': + '@shikijs/types@1.22.2': dependencies: - '@shikijs/vscode-textmate': 9.2.2 + '@shikijs/vscode-textmate': 9.3.0 '@types/hast': 3.0.4 - '@shikijs/vscode-textmate@9.2.2': {} + '@shikijs/vscode-textmate@9.3.0': {} '@sindresorhus/is@5.4.1': {} - '@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.0(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)))(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.0(svelte@5.1.6)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)))(svelte@5.1.6)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.0(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + '@sveltejs/vite-plugin-svelte': 4.0.0(svelte@5.1.6)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) debug: 4.3.7 svelte: 5.1.6 - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.0(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))': + '@sveltejs/vite-plugin-svelte@4.0.0(svelte@5.1.6)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.0(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)))(svelte@5.1.6)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + '@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.0(svelte@5.1.6)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)))(svelte@5.1.6)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) debug: 4.3.7 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.12 svelte: 5.1.6 - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) - vitefu: 1.0.3(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) + vitefu: 1.0.3(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) transitivePeerDependencies: - supports-color @@ -5997,7 +6153,7 @@ snapshots: '@types/jsonfile@6.1.3': dependencies: - '@types/node': 20.16.10 + '@types/node': 20.17.6 '@types/linkify-it@5.0.0': {} @@ -6024,7 +6180,7 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/node@20.16.10': + '@types/node@20.17.6': dependencies: undici-types: 6.19.8 @@ -6032,16 +6188,16 @@ snapshots: '@types/prompts@2.4.9': dependencies: - '@types/node': 20.16.10 + '@types/node': 20.17.6 kleur: 3.0.3 '@types/prop-types@15.7.9': {} - '@types/react-dom@18.3.0': + '@types/react-dom@18.3.1': dependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.12 - '@types/react@18.3.11': + '@types/react@18.3.12': dependencies: '@types/prop-types': 15.7.9 csstype: 3.1.3 @@ -6056,34 +6212,35 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 20.16.10 + '@types/node': 20.17.6 optional: true '@ungap/structured-clone@1.2.0': {} - '@unocss/astro@0.63.4(rollup@4.24.0)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))': + '@unocss/astro@0.63.6(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))': dependencies: - '@unocss/core': 0.63.4 - '@unocss/reset': 0.63.4 - '@unocss/vite': 0.63.4(rollup@4.24.0)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + '@unocss/core': 0.63.6 + '@unocss/reset': 0.63.6 + '@unocss/vite': 0.63.6(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) optionalDependencies: - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) transitivePeerDependencies: - rollup - supports-color + - typescript - '@unocss/cli@0.63.4(rollup@4.24.0)': + '@unocss/cli@0.63.6(rollup@4.24.0)': dependencies: '@ampproject/remapping': 2.3.0 '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - '@unocss/config': 0.63.4 - '@unocss/core': 0.63.4 - '@unocss/preset-uno': 0.63.4 + '@unocss/config': 0.63.6 + '@unocss/core': 0.63.6 + '@unocss/preset-uno': 0.63.6 cac: 6.7.14 chokidar: 3.6.0 colorette: 2.0.20 consola: 3.2.3 - magic-string: 0.30.11 + magic-string: 0.30.12 pathe: 1.1.2 perfect-debounce: 1.0.0 tinyglobby: 0.2.9 @@ -6091,139 +6248,143 @@ snapshots: - rollup - supports-color - '@unocss/config@0.63.4': + '@unocss/config@0.63.6': dependencies: - '@unocss/core': 0.63.4 + '@unocss/core': 0.63.6 unconfig: 0.5.5 transitivePeerDependencies: - supports-color - '@unocss/core@0.63.4': {} + '@unocss/core@0.63.6': {} - '@unocss/extractor-arbitrary-variants@0.63.4': + '@unocss/extractor-arbitrary-variants@0.63.6': dependencies: - '@unocss/core': 0.63.4 + '@unocss/core': 0.63.6 - '@unocss/inspector@0.63.4': + '@unocss/inspector@0.63.6(typescript@5.6.3)': dependencies: - '@unocss/core': 0.63.4 - '@unocss/rule-utils': 0.63.4 + '@unocss/core': 0.63.6 + '@unocss/rule-utils': 0.63.6 gzip-size: 6.0.0 sirv: 2.0.4 + vue-flow-layout: 0.0.5(typescript@5.6.3) + transitivePeerDependencies: + - typescript - '@unocss/postcss@0.63.4(postcss@8.4.47)': + '@unocss/postcss@0.63.6(postcss@8.4.47)': dependencies: - '@unocss/config': 0.63.4 - '@unocss/core': 0.63.4 - '@unocss/rule-utils': 0.63.4 + '@unocss/config': 0.63.6 + '@unocss/core': 0.63.6 + '@unocss/rule-utils': 0.63.6 css-tree: 3.0.0 postcss: 8.4.47 tinyglobby: 0.2.9 transitivePeerDependencies: - supports-color - '@unocss/preset-attributify@0.63.4': + '@unocss/preset-attributify@0.63.6': dependencies: - '@unocss/core': 0.63.4 + '@unocss/core': 0.63.6 - '@unocss/preset-icons@0.63.4': + '@unocss/preset-icons@0.63.6': dependencies: '@iconify/utils': 2.1.33 - '@unocss/core': 0.63.4 + '@unocss/core': 0.63.6 ofetch: 1.4.0 transitivePeerDependencies: - supports-color - '@unocss/preset-mini@0.63.4': + '@unocss/preset-mini@0.63.6': dependencies: - '@unocss/core': 0.63.4 - '@unocss/extractor-arbitrary-variants': 0.63.4 - '@unocss/rule-utils': 0.63.4 + '@unocss/core': 0.63.6 + '@unocss/extractor-arbitrary-variants': 0.63.6 + '@unocss/rule-utils': 0.63.6 - '@unocss/preset-tagify@0.63.4': + '@unocss/preset-tagify@0.63.6': dependencies: - '@unocss/core': 0.63.4 + '@unocss/core': 0.63.6 - '@unocss/preset-typography@0.63.4': + '@unocss/preset-typography@0.63.6': dependencies: - '@unocss/core': 0.63.4 - '@unocss/preset-mini': 0.63.4 + '@unocss/core': 0.63.6 + '@unocss/preset-mini': 0.63.6 - '@unocss/preset-uno@0.63.4': + '@unocss/preset-uno@0.63.6': dependencies: - '@unocss/core': 0.63.4 - '@unocss/preset-mini': 0.63.4 - '@unocss/preset-wind': 0.63.4 - '@unocss/rule-utils': 0.63.4 + '@unocss/core': 0.63.6 + '@unocss/preset-mini': 0.63.6 + '@unocss/preset-wind': 0.63.6 + '@unocss/rule-utils': 0.63.6 - '@unocss/preset-web-fonts@0.63.4': + '@unocss/preset-web-fonts@0.63.6': dependencies: - '@unocss/core': 0.63.4 + '@unocss/core': 0.63.6 ofetch: 1.4.0 - '@unocss/preset-wind@0.63.4': + '@unocss/preset-wind@0.63.6': dependencies: - '@unocss/core': 0.63.4 - '@unocss/preset-mini': 0.63.4 - '@unocss/rule-utils': 0.63.4 + '@unocss/core': 0.63.6 + '@unocss/preset-mini': 0.63.6 + '@unocss/rule-utils': 0.63.6 - '@unocss/reset@0.63.4': {} + '@unocss/reset@0.63.6': {} - '@unocss/rule-utils@0.63.4': + '@unocss/rule-utils@0.63.6': dependencies: - '@unocss/core': 0.63.4 - magic-string: 0.30.11 + '@unocss/core': 0.63.6 + magic-string: 0.30.12 - '@unocss/transformer-attributify-jsx@0.63.4': + '@unocss/transformer-attributify-jsx@0.63.6': dependencies: - '@unocss/core': 0.63.4 + '@unocss/core': 0.63.6 - '@unocss/transformer-compile-class@0.63.4': + '@unocss/transformer-compile-class@0.63.6': dependencies: - '@unocss/core': 0.63.4 + '@unocss/core': 0.63.6 - '@unocss/transformer-directives@0.63.4': + '@unocss/transformer-directives@0.63.6': dependencies: - '@unocss/core': 0.63.4 - '@unocss/rule-utils': 0.63.4 + '@unocss/core': 0.63.6 + '@unocss/rule-utils': 0.63.6 css-tree: 3.0.0 - '@unocss/transformer-variant-group@0.63.4': + '@unocss/transformer-variant-group@0.63.6': dependencies: - '@unocss/core': 0.63.4 + '@unocss/core': 0.63.6 - '@unocss/vite@0.63.4(rollup@4.24.0)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))': + '@unocss/vite@0.63.6(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))': dependencies: '@ampproject/remapping': 2.3.0 '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - '@unocss/config': 0.63.4 - '@unocss/core': 0.63.4 - '@unocss/inspector': 0.63.4 + '@unocss/config': 0.63.6 + '@unocss/core': 0.63.6 + '@unocss/inspector': 0.63.6(typescript@5.6.3) chokidar: 3.6.0 - magic-string: 0.30.11 + magic-string: 0.30.12 tinyglobby: 0.2.9 - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) transitivePeerDependencies: - rollup - supports-color + - typescript - '@vitejs/plugin-react@4.3.2(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))': + '@vitejs/plugin-react@4.3.3(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))': dependencies: '@babel/core': 7.25.7 '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.7) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.7) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.1.4(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))(vue@3.5.11(typescript@5.6.2))': + '@vitejs/plugin-vue@5.2.0(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))(vue@3.5.12(typescript@5.6.3))': dependencies: - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) - vue: 3.5.11(typescript@5.6.2) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) + vue: 3.5.12(typescript@5.6.3) - '@vitest/coverage-v8@2.1.2(vitest@2.1.2(@types/node@20.16.10)(happy-dom@14.12.3)(sass@1.79.4))': + '@vitest/coverage-v8@2.1.4(vitest@2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -6232,153 +6393,153 @@ snapshots: istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.1.7 - magic-string: 0.30.11 + magic-string: 0.30.12 magicast: 0.3.5 std-env: 3.7.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 - vitest: 2.1.2(@types/node@20.16.10)(happy-dom@14.12.3)(sass@1.79.4) + vitest: 2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7) transitivePeerDependencies: - supports-color - '@vitest/expect@2.1.2': + '@vitest/expect@2.1.4': dependencies: - '@vitest/spy': 2.1.2 - '@vitest/utils': 2.1.2 - chai: 5.1.1 + '@vitest/spy': 2.1.4 + '@vitest/utils': 2.1.4 + chai: 5.1.2 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))': + '@vitest/mocker@2.1.4(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))': dependencies: - '@vitest/spy': 2.1.2 + '@vitest/spy': 2.1.4 estree-walker: 3.0.3 - magic-string: 0.30.11 + magic-string: 0.30.12 optionalDependencies: - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) - '@vitest/pretty-format@2.1.2': + '@vitest/pretty-format@2.1.4': dependencies: tinyrainbow: 1.2.0 - '@vitest/runner@2.1.2': + '@vitest/runner@2.1.4': dependencies: - '@vitest/utils': 2.1.2 + '@vitest/utils': 2.1.4 pathe: 1.1.2 - '@vitest/snapshot@2.1.2': + '@vitest/snapshot@2.1.4': dependencies: - '@vitest/pretty-format': 2.1.2 - magic-string: 0.30.11 + '@vitest/pretty-format': 2.1.4 + magic-string: 0.30.12 pathe: 1.1.2 - '@vitest/spy@2.1.2': + '@vitest/spy@2.1.4': dependencies: - tinyspy: 3.0.0 + tinyspy: 3.0.2 - '@vitest/utils@2.1.2': + '@vitest/utils@2.1.4': dependencies: - '@vitest/pretty-format': 2.1.2 - loupe: 3.1.1 + '@vitest/pretty-format': 2.1.4 + loupe: 3.1.2 tinyrainbow: 1.2.0 - '@vue/compiler-core@3.5.11': + '@vue/compiler-core@3.5.12': dependencies: - '@babel/parser': 7.25.6 - '@vue/shared': 3.5.11 + '@babel/parser': 7.25.7 + '@vue/shared': 3.5.12 entities: 4.5.0 estree-walker: 2.0.2 - source-map-js: 1.2.0 + source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.11': + '@vue/compiler-dom@3.5.12': dependencies: - '@vue/compiler-core': 3.5.11 - '@vue/shared': 3.5.11 + '@vue/compiler-core': 3.5.12 + '@vue/shared': 3.5.12 - '@vue/compiler-sfc@3.5.11': + '@vue/compiler-sfc@3.5.12': dependencies: - '@babel/parser': 7.25.6 - '@vue/compiler-core': 3.5.11 - '@vue/compiler-dom': 3.5.11 - '@vue/compiler-ssr': 3.5.11 - '@vue/shared': 3.5.11 + '@babel/parser': 7.25.7 + '@vue/compiler-core': 3.5.12 + '@vue/compiler-dom': 3.5.12 + '@vue/compiler-ssr': 3.5.12 + '@vue/shared': 3.5.12 estree-walker: 2.0.2 - magic-string: 0.30.11 + magic-string: 0.30.12 postcss: 8.4.47 - source-map-js: 1.2.0 + source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.11': + '@vue/compiler-ssr@3.5.12': dependencies: - '@vue/compiler-dom': 3.5.11 - '@vue/shared': 3.5.11 + '@vue/compiler-dom': 3.5.12 + '@vue/shared': 3.5.12 - '@vue/devtools-api@7.4.6': + '@vue/devtools-api@7.6.4': dependencies: - '@vue/devtools-kit': 7.4.6 + '@vue/devtools-kit': 7.6.4 - '@vue/devtools-kit@7.4.6': + '@vue/devtools-kit@7.6.4': dependencies: - '@vue/devtools-shared': 7.4.6 - birpc: 0.2.17 + '@vue/devtools-shared': 7.6.4 + birpc: 0.2.19 hookable: 5.5.3 mitt: 3.0.1 perfect-debounce: 1.0.0 speakingurl: 14.0.1 superjson: 2.2.1 - '@vue/devtools-shared@7.4.6': + '@vue/devtools-shared@7.6.4': dependencies: rfdc: 1.4.1 - '@vue/reactivity@3.5.11': + '@vue/reactivity@3.5.12': dependencies: - '@vue/shared': 3.5.11 + '@vue/shared': 3.5.12 - '@vue/runtime-core@3.5.11': + '@vue/runtime-core@3.5.12': dependencies: - '@vue/reactivity': 3.5.11 - '@vue/shared': 3.5.11 + '@vue/reactivity': 3.5.12 + '@vue/shared': 3.5.12 - '@vue/runtime-dom@3.5.11': + '@vue/runtime-dom@3.5.12': dependencies: - '@vue/reactivity': 3.5.11 - '@vue/runtime-core': 3.5.11 - '@vue/shared': 3.5.11 + '@vue/reactivity': 3.5.12 + '@vue/runtime-core': 3.5.12 + '@vue/shared': 3.5.12 csstype: 3.1.3 - '@vue/server-renderer@3.5.11(vue@3.5.11(typescript@5.6.2))': + '@vue/server-renderer@3.5.12(vue@3.5.12(typescript@5.6.3))': dependencies: - '@vue/compiler-ssr': 3.5.11 - '@vue/shared': 3.5.11 - vue: 3.5.11(typescript@5.6.2) + '@vue/compiler-ssr': 3.5.12 + '@vue/shared': 3.5.12 + vue: 3.5.12(typescript@5.6.3) - '@vue/shared@3.5.11': {} + '@vue/shared@3.5.12': {} - '@vueuse/core@11.1.0(vue@3.5.11(typescript@5.6.2))': + '@vueuse/core@11.1.0(vue@3.5.12(typescript@5.6.3))': dependencies: '@types/web-bluetooth': 0.0.20 '@vueuse/metadata': 11.1.0 - '@vueuse/shared': 11.1.0(vue@3.5.11(typescript@5.6.2)) - vue-demi: 0.14.10(vue@3.5.11(typescript@5.6.2)) + '@vueuse/shared': 11.1.0(vue@3.5.12(typescript@5.6.3)) + vue-demi: 0.14.10(vue@3.5.12(typescript@5.6.3)) transitivePeerDependencies: - '@vue/composition-api' - vue - '@vueuse/integrations@11.1.0(focus-trap@7.5.4)(vue@3.5.11(typescript@5.6.2))': + '@vueuse/integrations@11.1.0(focus-trap@7.6.1)(vue@3.5.12(typescript@5.6.3))': dependencies: - '@vueuse/core': 11.1.0(vue@3.5.11(typescript@5.6.2)) - '@vueuse/shared': 11.1.0(vue@3.5.11(typescript@5.6.2)) - vue-demi: 0.14.10(vue@3.5.11(typescript@5.6.2)) + '@vueuse/core': 11.1.0(vue@3.5.12(typescript@5.6.3)) + '@vueuse/shared': 11.1.0(vue@3.5.12(typescript@5.6.3)) + vue-demi: 0.14.10(vue@3.5.12(typescript@5.6.3)) optionalDependencies: - focus-trap: 7.5.4 + focus-trap: 7.6.1 transitivePeerDependencies: - '@vue/composition-api' - vue '@vueuse/metadata@11.1.0': {} - '@vueuse/shared@11.1.0(vue@3.5.11(typescript@5.6.2))': + '@vueuse/shared@11.1.0(vue@3.5.12(typescript@5.6.3))': dependencies: - vue-demi: 0.14.10(vue@3.5.11(typescript@5.6.2)) + vue-demi: 0.14.10(vue@3.5.12(typescript@5.6.3)) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -6475,7 +6636,7 @@ snapshots: caniuse-lite: 1.0.30001633 fraction.js: 4.3.7 normalize-range: 0.1.2 - picocolors: 1.1.0 + picocolors: 1.1.1 postcss: 8.4.39 postcss-value-parser: 4.2.0 @@ -6503,7 +6664,7 @@ snapshots: binary-extensions@2.2.0: {} - birpc@0.2.17: {} + birpc@0.2.19: {} bl@5.1.0: dependencies: @@ -6591,7 +6752,7 @@ snapshots: c12@1.11.2(magicast@0.3.5): dependencies: chokidar: 3.6.0 - confbox: 0.1.7 + confbox: 0.1.8 defu: 6.1.4 dotenv: 16.4.5 giget: 1.2.3 @@ -6634,7 +6795,7 @@ snapshots: ccount@2.0.1: {} - chai@5.1.1: + chai@5.1.2: dependencies: assertion-error: 2.0.1 check-error: 2.1.1 @@ -6700,7 +6861,7 @@ snapshots: chrome-launcher@1.1.0: dependencies: - '@types/node': 20.16.10 + '@types/node': 20.17.6 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 2.0.1 @@ -6711,6 +6872,8 @@ snapshots: ci-info@4.0.0: {} + ci-info@4.1.0: {} + citty@0.1.6: dependencies: consola: 3.2.3 @@ -6804,7 +6967,7 @@ snapshots: readable-stream: 2.3.8 typedarray: 0.0.6 - confbox@0.1.7: {} + confbox@0.1.8: {} config-chain@1.1.13: dependencies: @@ -6856,12 +7019,12 @@ snapshots: css-tree@2.2.1: dependencies: mdn-data: 2.0.28 - source-map-js: 1.2.0 + source-map-js: 1.2.1 css-tree@2.3.1: dependencies: mdn-data: 2.0.30 - source-map-js: 1.2.0 + source-map-js: 1.2.1 css-tree@3.0.0: dependencies: @@ -6934,10 +7097,6 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.3.5: - dependencies: - ms: 2.1.2 - debug@4.3.7: dependencies: ms: 2.1.3 @@ -6989,6 +7148,9 @@ snapshots: destr@2.0.3: {} + detect-libc@1.0.3: + optional: true + detect-libc@2.0.3: {} devlop@1.1.0: @@ -7200,6 +7362,8 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + expect-type@1.1.0: {} + extract-zip@2.0.1: dependencies: debug: 4.3.4 @@ -7244,7 +7408,7 @@ snapshots: minimist: 1.2.8 xml2js: 0.5.0 - focus-trap@7.5.4: + focus-trap@7.6.1: dependencies: tabbable: 6.2.0 @@ -7473,7 +7637,7 @@ snapshots: immediate@3.0.6: {} - immutable@4.3.4: {} + immutable@5.0.2: {} import-lazy@4.0.0: {} @@ -7798,6 +7962,8 @@ snapshots: dependencies: get-func-name: 2.0.2 + loupe@3.1.2: {} + lowercase-keys@3.0.0: {} lru-cache@10.4.3: {} @@ -7951,7 +8117,7 @@ snapshots: mkdirp@3.0.1: {} - mkdist@1.5.4(sass@1.79.4)(typescript@5.6.2): + mkdist@1.5.4(sass@1.80.7)(typescript@5.6.3): dependencies: autoprefixer: 10.4.19(postcss@8.4.39) citty: 0.1.6 @@ -7967,8 +8133,8 @@ snapshots: postcss-nested: 6.0.1(postcss@8.4.39) semver: 7.6.2 optionalDependencies: - sass: 1.79.4 - typescript: 5.6.2 + sass: 1.80.7 + typescript: 5.6.3 mlly@1.7.1: dependencies: @@ -8020,6 +8186,9 @@ snapshots: ncp@2.0.0: optional: true + node-addon-api@7.1.1: + optional: true + node-fetch-native@1.6.4: {} node-forge@1.3.1: {} @@ -8139,7 +8308,7 @@ snapshots: strip-ansi: 7.1.0 wcwidth: 1.0.1 - ora@8.1.0: + ora@8.1.1: dependencies: chalk: 5.3.0 cli-cursor: 5.0.0 @@ -8153,16 +8322,16 @@ snapshots: os-shim@0.1.3: {} - oxlint@0.9.9: + oxlint@0.9.10: optionalDependencies: - '@oxlint/darwin-arm64': 0.9.9 - '@oxlint/darwin-x64': 0.9.9 - '@oxlint/linux-arm64-gnu': 0.9.9 - '@oxlint/linux-arm64-musl': 0.9.9 - '@oxlint/linux-x64-gnu': 0.9.9 - '@oxlint/linux-x64-musl': 0.9.9 - '@oxlint/win32-arm64': 0.9.9 - '@oxlint/win32-x64': 0.9.9 + '@oxlint/darwin-arm64': 0.9.10 + '@oxlint/darwin-x64': 0.9.10 + '@oxlint/linux-arm64-gnu': 0.9.10 + '@oxlint/linux-arm64-musl': 0.9.10 + '@oxlint/linux-x64-gnu': 0.9.10 + '@oxlint/linux-x64-musl': 0.9.10 + '@oxlint/win32-arm64': 0.9.10 + '@oxlint/win32-x64': 0.9.10 p-cancelable@3.0.0: {} @@ -8219,7 +8388,7 @@ snapshots: perfect-debounce@1.0.0: {} - picocolors@1.1.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -8229,13 +8398,13 @@ snapshots: pkg-types@1.1.3: dependencies: - confbox: 0.1.7 + confbox: 0.1.8 mlly: 1.7.1 pathe: 1.1.2 pkg-types@1.2.0: dependencies: - confbox: 0.1.7 + confbox: 0.1.8 mlly: 1.7.1 pathe: 1.1.2 @@ -8403,13 +8572,13 @@ snapshots: postcss@8.4.39: dependencies: nanoid: 3.3.7 - picocolors: 1.1.0 + picocolors: 1.1.1 source-map-js: 1.2.0 postcss@8.4.47: dependencies: nanoid: 3.3.7 - picocolors: 1.1.0 + picocolors: 1.1.1 source-map-js: 1.2.1 preact@10.18.1: {} @@ -8439,10 +8608,10 @@ snapshots: proto-list@1.2.4: {} - publint@0.2.11: + publint@0.2.12: dependencies: npm-packlist: 5.1.3 - picocolors: 1.1.0 + picocolors: 1.1.1 sade: 1.8.1 publish-browser-extension@2.2.2: @@ -8573,11 +8742,11 @@ snapshots: glob: 6.0.4 optional: true - rollup-plugin-dts@6.1.1(rollup@3.29.4)(typescript@5.6.2): + rollup-plugin-dts@6.1.1(rollup@3.29.4)(typescript@5.6.3): dependencies: magic-string: 0.30.10 rollup: 3.29.4 - typescript: 5.6.2 + typescript: 5.6.3 optionalDependencies: '@babel/code-frame': 7.24.7 @@ -8626,11 +8795,13 @@ snapshots: safe-json-stringify@1.2.0: optional: true - sass@1.79.4: + sass@1.80.7: dependencies: chokidar: 4.0.1 - immutable: 4.3.4 + immutable: 5.0.2 source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.0 sax@1.2.4: {} @@ -8708,13 +8879,13 @@ snapshots: vscode-oniguruma: 1.7.0 vscode-textmate: 8.0.0 - shiki@1.21.0: + shiki@1.22.2: dependencies: - '@shikijs/core': 1.21.0 - '@shikijs/engine-javascript': 1.21.0 - '@shikijs/engine-oniguruma': 1.21.0 - '@shikijs/types': 1.21.0 - '@shikijs/vscode-textmate': 9.2.2 + '@shikijs/core': 1.22.2 + '@shikijs/engine-javascript': 1.22.2 + '@shikijs/engine-oniguruma': 1.22.2 + '@shikijs/types': 1.22.2 + '@shikijs/vscode-textmate': 9.3.0 '@types/hast': 3.0.4 siginfo@2.0.0: {} @@ -8749,18 +8920,18 @@ snapshots: ansi-styles: 6.2.1 is-fullwidth-code-point: 5.0.0 - solid-js@1.9.1: + solid-js@1.9.3: dependencies: csstype: 3.1.3 seroval: 1.1.0 seroval-plugins: 1.1.0(seroval@1.1.0) - solid-refresh@0.6.3(solid-js@1.9.1): + solid-refresh@0.6.3(solid-js@1.9.3): dependencies: '@babel/generator': 7.24.7 '@babel/helper-module-imports': 7.24.7 '@babel/types': 7.24.7 - solid-js: 1.9.1 + solid-js: 1.9.3 transitivePeerDependencies: - supports-color @@ -8895,7 +9066,7 @@ snapshots: css-tree: 2.3.1 css-what: 6.1.0 csso: 5.0.5 - picocolors: 1.1.0 + picocolors: 1.1.1 tabbable@6.2.0: {} @@ -8928,16 +9099,18 @@ snapshots: tinyexec@0.3.0: {} + tinyexec@0.3.1: {} + tinyglobby@0.2.9: dependencies: fdir: 6.4.2(picomatch@4.0.2) picomatch: 4.0.2 - tinypool@1.0.0: {} + tinypool@1.0.1: {} tinyrainbow@1.2.0: {} - tinyspy@3.0.0: {} + tinyspy@3.0.2: {} titleize@3.0.0: {} @@ -8953,9 +9126,9 @@ snapshots: trim-lines@3.0.1: {} - ts-essentials@10.0.1(typescript@5.6.2): + ts-essentials@10.0.1(typescript@5.6.3): optionalDependencies: - typescript: 5.6.2 + typescript: 5.6.3 tslib@2.6.0: {} @@ -8985,23 +9158,23 @@ snapshots: typedarray@0.0.6: {} - typedoc-plugin-markdown@4.0.0-next.23(typedoc@0.25.4(typescript@5.6.2)): + typedoc-plugin-markdown@4.0.0-next.23(typedoc@0.25.4(typescript@5.6.3)): dependencies: - typedoc: 0.25.4(typescript@5.6.2) + typedoc: 0.25.4(typescript@5.6.3) - typedoc-vitepress-theme@1.0.0-next.3(typedoc-plugin-markdown@4.0.0-next.23(typedoc@0.25.4(typescript@5.6.2))): + typedoc-vitepress-theme@1.0.0-next.3(typedoc-plugin-markdown@4.0.0-next.23(typedoc@0.25.4(typescript@5.6.3))): dependencies: - typedoc-plugin-markdown: 4.0.0-next.23(typedoc@0.25.4(typescript@5.6.2)) + typedoc-plugin-markdown: 4.0.0-next.23(typedoc@0.25.4(typescript@5.6.3)) - typedoc@0.25.4(typescript@5.6.2): + typedoc@0.25.4(typescript@5.6.3): dependencies: lunr: 2.3.9 marked: 4.3.0 minimatch: 9.0.3 shiki: 0.14.5 - typescript: 5.6.2 + typescript: 5.6.3 - typescript@5.6.2: {} + typescript@5.6.3: {} ufo@1.5.3: {} @@ -9009,7 +9182,7 @@ snapshots: uhyphen@0.2.0: {} - unbuild@2.0.0(sass@1.79.4)(typescript@5.6.2): + unbuild@2.0.0(sass@1.80.7)(typescript@5.6.3): dependencies: '@rollup/plugin-alias': 5.1.0(rollup@3.29.4) '@rollup/plugin-commonjs': 25.0.8(rollup@3.29.4) @@ -9026,17 +9199,17 @@ snapshots: hookable: 5.5.3 jiti: 1.21.6 magic-string: 0.30.10 - mkdist: 1.5.4(sass@1.79.4)(typescript@5.6.2) + mkdist: 1.5.4(sass@1.80.7)(typescript@5.6.3) mlly: 1.7.1 pathe: 1.1.2 pkg-types: 1.1.3 pretty-bytes: 6.1.1 rollup: 3.29.4 - rollup-plugin-dts: 6.1.1(rollup@3.29.4)(typescript@5.6.2) + rollup-plugin-dts: 6.1.1(rollup@3.29.4)(typescript@5.6.3) scule: 1.3.0 untyped: 1.4.2 optionalDependencies: - typescript: 5.6.2 + typescript: 5.6.3 transitivePeerDependencies: - sass - supports-color @@ -9104,31 +9277,32 @@ snapshots: universalify@2.0.0: {} - unocss@0.63.4(postcss@8.4.47)(rollup@4.24.0)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)): + unocss@0.63.6(postcss@8.4.47)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)): dependencies: - '@unocss/astro': 0.63.4(rollup@4.24.0)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) - '@unocss/cli': 0.63.4(rollup@4.24.0) - '@unocss/core': 0.63.4 - '@unocss/postcss': 0.63.4(postcss@8.4.47) - '@unocss/preset-attributify': 0.63.4 - '@unocss/preset-icons': 0.63.4 - '@unocss/preset-mini': 0.63.4 - '@unocss/preset-tagify': 0.63.4 - '@unocss/preset-typography': 0.63.4 - '@unocss/preset-uno': 0.63.4 - '@unocss/preset-web-fonts': 0.63.4 - '@unocss/preset-wind': 0.63.4 - '@unocss/transformer-attributify-jsx': 0.63.4 - '@unocss/transformer-compile-class': 0.63.4 - '@unocss/transformer-directives': 0.63.4 - '@unocss/transformer-variant-group': 0.63.4 - '@unocss/vite': 0.63.4(rollup@4.24.0)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + '@unocss/astro': 0.63.6(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) + '@unocss/cli': 0.63.6(rollup@4.24.0) + '@unocss/core': 0.63.6 + '@unocss/postcss': 0.63.6(postcss@8.4.47) + '@unocss/preset-attributify': 0.63.6 + '@unocss/preset-icons': 0.63.6 + '@unocss/preset-mini': 0.63.6 + '@unocss/preset-tagify': 0.63.6 + '@unocss/preset-typography': 0.63.6 + '@unocss/preset-uno': 0.63.6 + '@unocss/preset-web-fonts': 0.63.6 + '@unocss/preset-wind': 0.63.6 + '@unocss/transformer-attributify-jsx': 0.63.6 + '@unocss/transformer-compile-class': 0.63.6 + '@unocss/transformer-directives': 0.63.6 + '@unocss/transformer-variant-group': 0.63.6 + '@unocss/vite': 0.63.6(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) optionalDependencies: - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) transitivePeerDependencies: - postcss - rollup - supports-color + - typescript unplugin@1.14.1(webpack-sources@3.2.3): dependencies: @@ -9155,13 +9329,13 @@ snapshots: dependencies: browserslist: 4.23.1 escalade: 3.1.2 - picocolors: 1.1.0 + picocolors: 1.1.1 update-browserslist-db@1.1.1(browserslist@4.24.0): dependencies: browserslist: 4.24.0 escalade: 3.2.0 - picocolors: 1.1.0 + picocolors: 1.1.1 update-notifier@6.0.2: dependencies: @@ -9196,12 +9370,12 @@ snapshots: '@types/unist': 3.0.2 vfile-message: 4.0.2 - vite-node@2.1.2(@types/node@20.16.10)(sass@1.79.4): + vite-node@2.1.4(@types/node@20.17.6)(sass@1.80.7): dependencies: cac: 6.7.14 debug: 4.3.7 pathe: 1.1.2 - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) transitivePeerDependencies: - '@types/node' - less @@ -9213,55 +9387,57 @@ snapshots: - supports-color - terser - vite-plugin-solid@2.10.2(solid-js@1.9.1)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)): + vite-plugin-solid@2.10.2(solid-js@1.9.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)): dependencies: '@babel/core': 7.24.7 '@types/babel__core': 7.20.5 babel-preset-solid: 1.8.17(@babel/core@7.24.7) merge-anything: 5.1.7 - solid-js: 1.9.1 - solid-refresh: 0.6.3(solid-js@1.9.1) - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) - vitefu: 0.2.5(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) + solid-js: 1.9.3 + solid-refresh: 0.6.3(solid-js@1.9.3) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) + vitefu: 0.2.5(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) transitivePeerDependencies: - supports-color - vite@5.4.8(@types/node@20.16.10)(sass@1.79.4): + vite@5.4.11(@types/node@20.17.6)(sass@1.80.7): dependencies: esbuild: 0.21.5 postcss: 8.4.47 rollup: 4.24.0 optionalDependencies: - '@types/node': 20.16.10 + '@types/node': 20.17.6 fsevents: 2.3.3 - sass: 1.79.4 + sass: 1.80.7 - vitefu@0.2.5(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)): + vitefu@0.2.5(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)): optionalDependencies: - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) - vitefu@1.0.3(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)): + vitefu@1.0.3(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)): optionalDependencies: - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) - vitepress@1.3.4(@algolia/client-search@4.20.0)(@types/node@20.16.10)(@types/react@18.3.11)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.4)(search-insights@2.15.0)(typescript@5.6.2): + vitepress@1.5.0(@algolia/client-search@4.20.0)(@types/node@20.17.6)(@types/react@18.3.12)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.80.7)(search-insights@2.15.0)(typescript@5.6.3): dependencies: '@docsearch/css': 3.6.2 - '@docsearch/js': 3.6.2(@algolia/client-search@4.20.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0) - '@shikijs/core': 1.21.0 - '@shikijs/transformers': 1.21.0 + '@docsearch/js': 3.6.2(@algolia/client-search@4.20.0)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0) + '@iconify-json/simple-icons': 1.2.11 + '@shikijs/core': 1.22.2 + '@shikijs/transformers': 1.22.2 + '@shikijs/types': 1.22.2 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.1.4(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4))(vue@3.5.11(typescript@5.6.2)) - '@vue/devtools-api': 7.4.6 - '@vue/shared': 3.5.11 - '@vueuse/core': 11.1.0(vue@3.5.11(typescript@5.6.2)) - '@vueuse/integrations': 11.1.0(focus-trap@7.5.4)(vue@3.5.11(typescript@5.6.2)) - focus-trap: 7.5.4 + '@vitejs/plugin-vue': 5.2.0(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))(vue@3.5.12(typescript@5.6.3)) + '@vue/devtools-api': 7.6.4 + '@vue/shared': 3.5.12 + '@vueuse/core': 11.1.0(vue@3.5.12(typescript@5.6.3)) + '@vueuse/integrations': 11.1.0(focus-trap@7.6.1)(vue@3.5.12(typescript@5.6.3)) + focus-trap: 7.6.1 mark.js: 8.11.1 minisearch: 7.1.0 - shiki: 1.21.0 - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) - vue: 3.5.11(typescript@5.6.2) + shiki: 1.22.2 + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) + vue: 3.5.12(typescript@5.6.3) optionalDependencies: postcss: 8.4.47 transitivePeerDependencies: @@ -9292,39 +9468,40 @@ snapshots: - typescript - universal-cookie - vitest-mock-extended@2.0.2(typescript@5.6.2)(vitest@2.1.2(@types/node@20.16.10)(happy-dom@14.12.3)(sass@1.79.4)): + vitest-mock-extended@2.0.2(typescript@5.6.3)(vitest@2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7)): dependencies: - ts-essentials: 10.0.1(typescript@5.6.2) - typescript: 5.6.2 - vitest: 2.1.2(@types/node@20.16.10)(happy-dom@14.12.3)(sass@1.79.4) + ts-essentials: 10.0.1(typescript@5.6.3) + typescript: 5.6.3 + vitest: 2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7) - vitest-plugin-random-seed@1.1.0(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)): + vitest-plugin-random-seed@1.1.0(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)): dependencies: - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) - vitest@2.1.2(@types/node@20.16.10)(happy-dom@14.12.3)(sass@1.79.4): + vitest@2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7): dependencies: - '@vitest/expect': 2.1.2 - '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.4.8(@types/node@20.16.10)(sass@1.79.4)) - '@vitest/pretty-format': 2.1.2 - '@vitest/runner': 2.1.2 - '@vitest/snapshot': 2.1.2 - '@vitest/spy': 2.1.2 - '@vitest/utils': 2.1.2 - chai: 5.1.1 + '@vitest/expect': 2.1.4 + '@vitest/mocker': 2.1.4(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) + '@vitest/pretty-format': 2.1.4 + '@vitest/runner': 2.1.4 + '@vitest/snapshot': 2.1.4 + '@vitest/spy': 2.1.4 + '@vitest/utils': 2.1.4 + chai: 5.1.2 debug: 4.3.7 - magic-string: 0.30.11 + expect-type: 1.1.0 + magic-string: 0.30.12 pathe: 1.1.2 std-env: 3.7.0 tinybench: 2.9.0 - tinyexec: 0.3.0 - tinypool: 1.0.0 + tinyexec: 0.3.1 + tinypool: 1.0.1 tinyrainbow: 1.2.0 - vite: 5.4.8(@types/node@20.16.10)(sass@1.79.4) - vite-node: 2.1.2(@types/node@20.16.10)(sass@1.79.4) + vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) + vite-node: 2.1.4(@types/node@20.17.6)(sass@1.80.7) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 20.16.10 + '@types/node': 20.17.6 happy-dom: 14.12.3 transitivePeerDependencies: - less @@ -9341,19 +9518,25 @@ snapshots: vscode-textmate@8.0.0: {} - vue-demi@0.14.10(vue@3.5.11(typescript@5.6.2)): + vue-demi@0.14.10(vue@3.5.12(typescript@5.6.3)): dependencies: - vue: 3.5.11(typescript@5.6.2) + vue: 3.5.12(typescript@5.6.3) - vue@3.5.11(typescript@5.6.2): + vue-flow-layout@0.0.5(typescript@5.6.3): dependencies: - '@vue/compiler-dom': 3.5.11 - '@vue/compiler-sfc': 3.5.11 - '@vue/runtime-dom': 3.5.11 - '@vue/server-renderer': 3.5.11(vue@3.5.11(typescript@5.6.2)) - '@vue/shared': 3.5.11 + vue: 3.5.12(typescript@5.6.3) + transitivePeerDependencies: + - typescript + + vue@3.5.12(typescript@5.6.3): + dependencies: + '@vue/compiler-dom': 3.5.12 + '@vue/compiler-sfc': 3.5.12 + '@vue/runtime-dom': 3.5.12 + '@vue/server-renderer': 3.5.12(vue@3.5.12(typescript@5.6.3)) + '@vue/shared': 3.5.12 optionalDependencies: - typescript: 5.6.2 + typescript: 5.6.3 watchpack@2.4.1: dependencies: diff --git a/templates/react/package.json b/templates/react/package.json index 5943cd45..d35d9e58 100644 --- a/templates/react/package.json +++ b/templates/react/package.json @@ -20,10 +20,10 @@ }, "devDependencies": { "@types/chrome": "^0.0.269", - "@types/react": "^18.3.11", - "@types/react-dom": "^18.3.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", "@wxt-dev/module-react": "^1.1.1", - "typescript": "^5.6.2", - "wxt": "^0.19.11" + "typescript": "^5.6.3", + "wxt": "^0.19.13" } } diff --git a/templates/solid/package.json b/templates/solid/package.json index 7d0044d6..4939a500 100644 --- a/templates/solid/package.json +++ b/templates/solid/package.json @@ -15,12 +15,12 @@ "postinstall": "wxt prepare" }, "dependencies": { - "solid-js": "^1.9.1" + "solid-js": "^1.9.3" }, "devDependencies": { "@types/chrome": "^0.0.269", "@wxt-dev/module-solid": "^1.1.2", - "typescript": "^5.6.2", - "wxt": "^0.19.11" + "typescript": "^5.6.3", + "wxt": "^0.19.13" } } diff --git a/templates/svelte/package.json b/templates/svelte/package.json index 537940ed..51d3e750 100644 --- a/templates/svelte/package.json +++ b/templates/svelte/package.json @@ -15,13 +15,13 @@ "postinstall": "wxt prepare" }, "devDependencies": { - "@types/chrome": "^0.0.269", "@tsconfig/svelte": "^5.0.4", + "@types/chrome": "^0.0.269", "@wxt-dev/module-svelte": "^1.0.1", "svelte": "^4.2.19", "svelte-check": "^3.8.6", - "tslib": "^2.7.0", - "typescript": "^5.6.2", - "wxt": "^0.19.11" + "tslib": "^2.8.1", + "typescript": "^5.6.3", + "wxt": "^0.19.13" } } diff --git a/templates/vanilla/package.json b/templates/vanilla/package.json index a8e373e8..09a0d1b1 100644 --- a/templates/vanilla/package.json +++ b/templates/vanilla/package.json @@ -16,7 +16,7 @@ }, "devDependencies": { "@types/chrome": "^0.0.269", - "typescript": "^5.6.2", - "wxt": "^0.19.11" + "typescript": "^5.6.3", + "wxt": "^0.19.13" } } diff --git a/templates/vue/package.json b/templates/vue/package.json index 236095ca..551bf8fb 100644 --- a/templates/vue/package.json +++ b/templates/vue/package.json @@ -15,13 +15,13 @@ "postinstall": "wxt prepare" }, "dependencies": { - "vue": "^3.5.11" + "vue": "^3.5.12" }, "devDependencies": { "@types/chrome": "^0.0.269", "@wxt-dev/module-vue": "^1.0.1", - "typescript": "^5.6.2", - "vue-tsc": "^2.1.6", - "wxt": "^0.19.11" + "typescript": "^5.6.3", + "vue-tsc": "^2.1.10", + "wxt": "^0.19.13" } } From 67f35615a227896314bcb922d9af9a888d7def80 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 13 Nov 2024 08:32:29 -0600 Subject: [PATCH 028/130] chore(deps): Bump dev and non-breaking major dependencies (#1167) --- package.json | 2 +- packages/auto-icons/package.json | 2 +- packages/i18n/package.json | 4 +- packages/unocss/package.json | 4 +- packages/wxt-demo/package.json | 4 +- packages/wxt/package.json | 12 +- packages/wxt/src/browser/index.ts | 1 - .../wxt/src/core/utils/content-scripts.ts | 3 - packages/wxt/src/storage.ts | 4 +- pnpm-lock.yaml | 531 +++++++++--------- taze.config.ts | 2 + templates/react/package.json | 2 +- templates/solid/package.json | 2 +- templates/svelte/package.json | 8 +- templates/vanilla/package.json | 2 +- templates/vue/package.json | 2 +- 16 files changed, 298 insertions(+), 287 deletions(-) diff --git a/package.json b/package.json index c0e80a8b..53264f64 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "fs-extra": "^11.2.0", "lint-staged": "^15.2.10", "markdown-it-footnote": "^4.0.0", - "nano-spawn": "^0.1.0", + "nano-spawn": "^0.2.0", "prettier": "^3.3.3", "simple-git-hooks": "^2.11.1", "tsx": "4.15.7", diff --git a/packages/auto-icons/package.json b/packages/auto-icons/package.json index 4964ff24..a36873ac 100644 --- a/packages/auto-icons/package.json +++ b/packages/auto-icons/package.json @@ -46,7 +46,7 @@ }, "devDependencies": { "@aklinker1/check": "^1.4.5", - "oxlint": "^0.9.10", + "oxlint": "^0.11.1", "publint": "^0.2.12", "typescript": "^5.6.3", "unbuild": "^2.0.0", diff --git a/packages/i18n/package.json b/packages/i18n/package.json index f22f6c45..fc18577d 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -39,9 +39,9 @@ }, "devDependencies": { "@aklinker1/check": "^1.4.5", - "@types/chrome": "^0.0.268", + "@types/chrome": "^0.0.280", "@types/node": "^20.17.6", - "oxlint": "^0.9.10", + "oxlint": "^0.11.1", "publint": "^0.2.12", "typescript": "^5.6.3", "unbuild": "^2.0.0", diff --git a/packages/unocss/package.json b/packages/unocss/package.json index 9c6749ff..5548de5a 100644 --- a/packages/unocss/package.json +++ b/packages/unocss/package.json @@ -41,11 +41,11 @@ }, "devDependencies": { "@aklinker1/check": "^1.4.5", - "oxlint": "^0.9.10", + "oxlint": "^0.11.1", "publint": "^0.2.12", "typescript": "^5.6.3", "unbuild": "^2.0.0", - "unocss": "^0.63.6", + "unocss": "^0.64.0", "wxt": "workspace:*" }, "dependencies": { diff --git a/packages/wxt-demo/package.json b/packages/wxt-demo/package.json index 82a51ae9..7b218e99 100644 --- a/packages/wxt-demo/package.json +++ b/packages/wxt-demo/package.json @@ -22,14 +22,14 @@ "react-dom": "^18.3.1" }, "devDependencies": { - "@types/chrome": "^0.0.269", + "@types/chrome": "^0.0.280", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@wxt-dev/auto-icons": "workspace:*", "@wxt-dev/unocss": "workspace:*", "sass": "^1.80.7", "typescript": "^5.6.3", - "unocss": "^0.63.6", + "unocss": "^0.64.0", "vitest": "^2.1.4", "vitest-plugin-random-seed": "^1.1.0", "wxt": "workspace:*" diff --git a/packages/wxt/package.json b/packages/wxt/package.json index 965c4f3b..2e430b03 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -82,8 +82,8 @@ }, "dependencies": { "@aklinker1/rollup-plugin-visualizer": "5.12.0", - "@types/chrome": "^0.0.269", - "@types/webextension-polyfill": "^0.10.7", + "@types/chrome": "^0.0.280", + "@types/webextension-polyfill": "^0.12.1", "@webext-core/fake-browser": "^1.3.1", "@webext-core/isolated-element": "^1.1.2", "@webext-core/match-patterns": "^1.0.3", @@ -110,7 +110,7 @@ "linkedom": "^0.18.5", "magicast": "^0.3.5", "minimatch": "^10.0.1", - "nano-spawn": "^0.1.0", + "nano-spawn": "^0.2.0", "normalize-path": "^3.0.0", "nypm": "^0.3.12", "ohash": "^1.1.4", @@ -129,16 +129,16 @@ }, "devDependencies": { "@aklinker1/check": "^1.4.5", - "@faker-js/faker": "^8.4.1", + "@faker-js/faker": "^9.2.0", "@types/fs-extra": "^11.0.4", "@types/lodash.merge": "^4.6.9", "@types/node": "^20.17.6", "@types/normalize-path": "^3.0.2", "@types/prompts": "^2.4.9", "extract-zip": "^2.0.1", - "happy-dom": "^14.12.3", + "happy-dom": "^15.11.4", "lodash.merge": "^4.6.2", - "oxlint": "^0.9.10", + "oxlint": "^0.11.1", "publint": "^0.2.12", "tsx": "4.15.7", "typescript": "^5.6.3", diff --git a/packages/wxt/src/browser/index.ts b/packages/wxt/src/browser/index.ts index 3eb44e11..8e21e1fb 100644 --- a/packages/wxt/src/browser/index.ts +++ b/packages/wxt/src/browser/index.ts @@ -80,7 +80,6 @@ export type { Theme, TopSites, Types, - Urlbar, UserScripts, WebNavigation, WebRequest, diff --git a/packages/wxt/src/core/utils/content-scripts.ts b/packages/wxt/src/core/utils/content-scripts.ts index d9b1425d..f1c7dabf 100644 --- a/packages/wxt/src/core/utils/content-scripts.ts +++ b/packages/wxt/src/core/utils/content-scripts.ts @@ -29,7 +29,6 @@ export function hashContentScriptOptions( match_about_blank: false, run_at: 'document_idle', all_frames: false, - // @ts-expect-error - not in type match_origin_as_fallback: false, world: 'ISOLATED', ...simplifiedOptions, @@ -62,7 +61,6 @@ export function mapWxtOptionsToContentScript( css, js, - // @ts-expect-error: untyped chrome options match_origin_as_fallback: options.matchOriginAsFallback, world: options.world, }; @@ -80,7 +78,6 @@ export function mapWxtOptionsToRegisteredContentScript( runAt: options.runAt, js, css, - // @ts-expect-error: Chrome accepts this, not typed in webextension-polyfill (https://developer.chrome.com/docs/extensions/reference/scripting/#type-RegisteredContentScript) world: options.world, }; } diff --git a/packages/wxt/src/storage.ts b/packages/wxt/src/storage.ts index 7491d84c..bad01977 100644 --- a/packages/wxt/src/storage.ts +++ b/packages/wxt/src/storage.ts @@ -500,7 +500,7 @@ function createDriver(storageArea: StorageArea): WxtStorageDriver { return { getItem: async (key) => { const res = await getStorageArea().get(key); - return res[key]; + return res[key] as any; }, getItems: async (keys) => { const result = await getStorageArea().get(keys); @@ -537,7 +537,7 @@ function createDriver(storageArea: StorageArea): WxtStorageDriver { }, watch(key, cb) { const listener = (changes: Storage.StorageAreaOnChangedChangesType) => { - const change = changes[key]; + const change = changes[key] as { newValue: any; oldValue: any }; if (change == null) return; if (dequal(change.newValue, change.oldValue)) return; cb(change.newValue ?? null, change.oldValue ?? null); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d3ff1e3..c25bff72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,7 +24,7 @@ importers: version: 11.0.4 '@vitest/coverage-v8': specifier: ^2.1.4 - version: 2.1.4(vitest@2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7)) + version: 2.1.4(vitest@2.1.4(@types/node@20.17.6)(happy-dom@15.11.4)(sass@1.80.7)) changelogen: specifier: ^0.5.7 version: 0.5.7(magicast@0.3.5) @@ -44,8 +44,8 @@ importers: specifier: ^4.0.0 version: 4.0.0(patch_hash=ue34jdgdx43siqdj557feoepzq) nano-spawn: - specifier: ^0.1.0 - version: 0.1.0 + specifier: ^0.2.0 + version: 0.2.0 prettier: specifier: ^3.3.3 version: 3.3.3 @@ -72,7 +72,7 @@ importers: version: 1.5.0(@algolia/client-search@4.20.0)(@types/node@20.17.6)(@types/react@18.3.12)(postcss@8.4.47)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.80.7)(search-insights@2.15.0)(typescript@5.6.3) vitest-mock-extended: specifier: ^2.0.2 - version: 2.0.2(typescript@5.6.3)(vitest@2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7)) + version: 2.0.2(typescript@5.6.3)(vitest@2.1.4(@types/node@20.17.6)(happy-dom@15.11.4)(sass@1.80.7)) vue: specifier: ^3.5.12 version: 3.5.12(typescript@5.6.3) @@ -96,8 +96,8 @@ importers: specifier: ^1.4.5 version: 1.4.5(typescript@5.6.3) oxlint: - specifier: ^0.9.10 - version: 0.9.10 + specifier: ^0.11.1 + version: 0.11.1 publint: specifier: ^0.2.12 version: 0.2.12 @@ -127,14 +127,14 @@ importers: specifier: ^1.4.5 version: 1.4.5(typescript@5.6.3) '@types/chrome': - specifier: ^0.0.268 - version: 0.0.268 + specifier: ^0.0.280 + version: 0.0.280 '@types/node': specifier: ^20.17.6 version: 20.17.6 oxlint: - specifier: ^0.9.10 - version: 0.9.10 + specifier: ^0.11.1 + version: 0.11.1 publint: specifier: ^0.2.12 version: 0.2.12 @@ -146,7 +146,7 @@ importers: version: 2.0.0(sass@1.80.7)(typescript@5.6.3) vitest: specifier: ^2.1.4 - version: 2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7) + version: 2.1.4(@types/node@20.17.6)(happy-dom@15.11.4)(sass@1.80.7) vitest-plugin-random-seed: specifier: ^1.1.0 version: 1.1.0(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) @@ -273,8 +273,8 @@ importers: specifier: ^1.4.5 version: 1.4.5(typescript@5.6.3) oxlint: - specifier: ^0.9.10 - version: 0.9.10 + specifier: ^0.11.1 + version: 0.11.1 publint: specifier: ^0.2.12 version: 0.2.12 @@ -285,8 +285,8 @@ importers: specifier: ^2.0.0 version: 2.0.0(sass@1.80.7)(typescript@5.6.3) unocss: - specifier: ^0.63.6 - version: 0.63.6(postcss@8.4.47)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) + specifier: ^0.64.0 + version: 0.64.0(postcss@8.4.47)(rollup@4.24.0)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))(vue@3.5.12(typescript@5.6.3)) wxt: specifier: workspace:* version: link:../wxt @@ -297,11 +297,11 @@ importers: specifier: 5.12.0 version: 5.12.0(rollup@4.24.0) '@types/chrome': - specifier: ^0.0.269 - version: 0.0.269 + specifier: ^0.0.280 + version: 0.0.280 '@types/webextension-polyfill': - specifier: ^0.10.7 - version: 0.10.7 + specifier: ^0.12.1 + version: 0.12.1 '@webext-core/fake-browser': specifier: ^1.3.1 version: 1.3.1 @@ -381,8 +381,8 @@ importers: specifier: ^10.0.1 version: 10.0.1 nano-spawn: - specifier: ^0.1.0 - version: 0.1.0 + specifier: ^0.2.0 + version: 0.2.0 normalize-path: specifier: ^3.0.0 version: 3.0.0 @@ -433,8 +433,8 @@ importers: specifier: ^1.4.5 version: 1.4.5(typescript@5.6.3) '@faker-js/faker': - specifier: ^8.4.1 - version: 8.4.1 + specifier: ^9.2.0 + version: 9.2.0 '@types/fs-extra': specifier: ^11.0.4 version: 11.0.4 @@ -454,14 +454,14 @@ importers: specifier: ^2.0.1 version: 2.0.1 happy-dom: - specifier: ^14.12.3 - version: 14.12.3 + specifier: ^15.11.4 + version: 15.11.4 lodash.merge: specifier: ^4.6.2 version: 4.6.2 oxlint: - specifier: ^0.9.10 - version: 0.9.10 + specifier: ^0.11.1 + version: 0.11.1 publint: specifier: ^0.2.12 version: 0.2.12 @@ -476,7 +476,7 @@ importers: version: 2.0.0(sass@1.80.7)(typescript@5.6.3) vitest: specifier: ^2.1.4 - version: 2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7) + version: 2.1.4(@types/node@20.17.6)(happy-dom@15.11.4)(sass@1.80.7) vitest-plugin-random-seed: specifier: ^1.1.0 version: 1.1.0(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) @@ -494,8 +494,8 @@ importers: version: 18.3.1(react@18.3.1) devDependencies: '@types/chrome': - specifier: ^0.0.269 - version: 0.0.269 + specifier: ^0.0.280 + version: 0.0.280 '@types/react': specifier: ^18.3.12 version: 18.3.12 @@ -515,11 +515,11 @@ importers: specifier: ^5.6.3 version: 5.6.3 unocss: - specifier: ^0.63.6 - version: 0.63.6(postcss@8.4.47)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) + specifier: ^0.64.0 + version: 0.64.0(postcss@8.4.47)(rollup@4.24.0)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))(vue@3.5.12(typescript@5.6.3)) vitest: specifier: ^2.1.4 - version: 2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7) + version: 2.1.4(@types/node@20.17.6)(happy-dom@15.11.4)(sass@1.80.7) vitest-plugin-random-seed: specifier: ^1.1.0 version: 1.1.0(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) @@ -1291,9 +1291,9 @@ packages: cpu: [x64] os: [win32] - '@faker-js/faker@8.4.1': - resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} + '@faker-js/faker@9.2.0': + resolution: {integrity: sha512-ulqQu4KMr1/sTFIYvqSdegHT8NIkt66tFAkugGnHA+1WAfEn6hMzNR+svjXGFRVLnapxvej67Z/LwchFrnLBUg==} + engines: {node: '>=18.0.0', npm: '>=9.0.0'} '@iconify-json/simple-icons@1.2.11': resolution: {integrity: sha512-AHCGDtBRqP+JzAbBzgO8uN/08CXxEmuaC6lQQZ3b5burKhRU12AJnJczwbUw2K5Mb/U85EpSUNhYMG3F28b8NA==} @@ -1450,43 +1450,43 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@oxlint/darwin-arm64@0.9.10': - resolution: {integrity: sha512-eOXKZYq5bnCSgDefgM5bzAg+4Fc//Rc4yjgKN8iDWUARweCaChiQXb6TXX8MfEfs6qayEMy6yVj0pqoFz0B1aw==} + '@oxlint/darwin-arm64@0.11.1': + resolution: {integrity: sha512-S+cHn49fT+qSJXhQ3Z4EG/5ENp2dAUbS2sMNkhgkLqlO8aYl0TR9R7omU3vpU/beu8ePnV+mdVlJYGjsPIMGtg==} cpu: [arm64] os: [darwin] - '@oxlint/darwin-x64@0.9.10': - resolution: {integrity: sha512-UeYICDvLUaUOcY+0ugZUEmBMRLP+x8iTgL7TeY6BlpGw2ahbtUOTbyIIRWtr/0O++TnjZ+v8TzhJ9crw6Ij6dg==} + '@oxlint/darwin-x64@0.11.1': + resolution: {integrity: sha512-LPuF0D8uu30KIVEeVuGwIPwHwJRQ1i1otwFFH7tRsNXPgMgZJ4VgriyH22i6RWwBtclJoCSBLtGK6gLZ0oZBvw==} cpu: [x64] os: [darwin] - '@oxlint/linux-arm64-gnu@0.9.10': - resolution: {integrity: sha512-0Zn+vqHhrZyufFBfq9WOgiIool0gCR14BLsdS+0Dwd9o+kNxPGA5q7erQFkiC4rpkxtfBHeD3iIKMMt7d29Kyw==} + '@oxlint/linux-arm64-gnu@0.11.1': + resolution: {integrity: sha512-CYBE+GRIPs5e+raD2pdicuBn6Y6E1xAnyWQ/kHE4GEWDAQZY0Um2VYEUTGH2ObwJ3uXr6jeJ16HOKJvr0S8a8w==} cpu: [arm64] os: [linux] - '@oxlint/linux-arm64-musl@0.9.10': - resolution: {integrity: sha512-tkQcWpYwF42bA/uRaV2iMFePHkBjTTgomOgeEaiw6XOSJX4nBEqGIIboqqLBWT4JnKCf/L+IG3y/e1MflhKByw==} + '@oxlint/linux-arm64-musl@0.11.1': + resolution: {integrity: sha512-iYXF5N5Gv+lc2wt90kxXy/W0cn7IEWu3UPzewIjPGDH8ajDckvGzZx6pTGYJnTyMh7U6hUKwOBFPVLMWI7UwKQ==} cpu: [arm64] os: [linux] - '@oxlint/linux-x64-gnu@0.9.10': - resolution: {integrity: sha512-JHbkMUnibqaSMBvLHyqTL5cWxcGW+jw+Ppt2baLISpvo34a6fBR+PI7v/A92sEDWe0W1rPhypzCwA8mKpkQ3DA==} + '@oxlint/linux-x64-gnu@0.11.1': + resolution: {integrity: sha512-D0tT8X0CsK/bpdkGdLSmsGftG3VndjyAUJuNGt56JYn0UfuPDkhQcLgUlkANHzNRXJ84tLQKhpf/MUDUHPB5cg==} cpu: [x64] os: [linux] - '@oxlint/linux-x64-musl@0.9.10': - resolution: {integrity: sha512-aBBwN7bQzidwHwEXr7BAdVvMTLWstCy5gikerjLnGDeCSXX9r+o6+yUzTOqZvOo66E+XBgOJaVbY8rsL1MLE0g==} + '@oxlint/linux-x64-musl@0.11.1': + resolution: {integrity: sha512-WekaLYk8WLT7Di8+nyPvtqs9OlMoO6KjFDMlqqLDWQTk9ffjn8e76PCRigF3w39jQ70qP3c8k8cNKNw5ROuFcg==} cpu: [x64] os: [linux] - '@oxlint/win32-arm64@0.9.10': - resolution: {integrity: sha512-LXDnk7vKHT3IY6G1jq0O7+XMhtcHOYuxLGIx4KP+4xS6vKgBY+Bsq4xV3AtmtKlvnXkP5FxHpfLmcEtm5AWysA==} + '@oxlint/win32-arm64@0.11.1': + resolution: {integrity: sha512-/CN/bFtI33vB8uemOkZxlNRf6Q7CftP2pSO7a6Q2niG4NC99YRPj7ctXcPF0jGR0NQUhGZk7ajM4G/0MKcRdag==} cpu: [arm64] os: [win32] - '@oxlint/win32-x64@0.9.10': - resolution: {integrity: sha512-w5XRAV4bhgwenjjpGYZGglqzG9Wv/sI+cjQWJBQsvfDXsr2w4vOBXzt1j3/Z3EcSqf4KtkCa/IIuAhQyeShUbA==} + '@oxlint/win32-x64@0.11.1': + resolution: {integrity: sha512-0hLl0z6adYTvLIOPC5uyo+EAwNITkzi4AY4xImykQW8H89GhiV9Xl8MPJeZQMWSz7ajI1I2+hRsvA0QAzeBsxA==} cpu: [x64] os: [win32] @@ -1654,6 +1654,15 @@ packages: rollup: optional: true + '@rollup/pluginutils@5.1.3': + resolution: {integrity: sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/rollup-android-arm-eabi@4.24.0': resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} cpu: [arm] @@ -1791,11 +1800,8 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - '@types/chrome@0.0.268': - resolution: {integrity: sha512-7N1QH9buudSJ7sI8Pe4mBHJr5oZ48s0hcanI9w3wgijAlv1OZNUZve9JR4x42dn5lJ5Sm87V1JNfnoh10EnQlA==} - - '@types/chrome@0.0.269': - resolution: {integrity: sha512-vF7x8YywnhXX2F06njQ/OE7a3Qeful43C5GUOsUksXWk89WoSFUU3iLeZW8lDpVO9atm8iZIEiLQTRC3H7NOXQ==} + '@types/chrome@0.0.280': + resolution: {integrity: sha512-AotSmZrL9bcZDDmSI1D9dE7PGbhOur5L0cKxXd7IqbVizQWCY4gcvupPUVsQ4FfDj3V2tt/iOpomT9EY0s+w1g==} '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} @@ -1875,8 +1881,8 @@ packages: '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} - '@types/webextension-polyfill@0.10.7': - resolution: {integrity: sha512-10ql7A0qzBmFB+F+qAke/nP1PIonS0TXZAOMVOxEUsm+lGSW6uwVcISFNa0I4Oyj0884TZVWGGMIWeXOVSNFHw==} + '@types/webextension-polyfill@0.12.1': + resolution: {integrity: sha512-xPTFWwQ8BxPevPF2IKsf4hpZNss4LxaOLZXypQH4E63BDLmcwX/RMGdI4tB4VO4Nb6xDBH3F/p4gz4wvof1o9w==} '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} @@ -1884,83 +1890,83 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - '@unocss/astro@0.63.6': - resolution: {integrity: sha512-5Fjlv6dpQo6o2PUAcEv8p24G8rn8Op79xLFofq2V+iA/Q32G9/UsxTLOpj+yc+q0YdJrFfDCT2X/3pvVY8Db5g==} + '@unocss/astro@0.64.0': + resolution: {integrity: sha512-4Ijf3cQblSjdC3XV4SvzkEj17z6gNsuMGy7M+TvNN4cZhGLWQCIChtHR525ESGxJ4kdZ6FoIUoxmLdWHMOpX4Q==} peerDependencies: vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 peerDependenciesMeta: vite: optional: true - '@unocss/cli@0.63.6': - resolution: {integrity: sha512-OZb8hO0x4nCJjFd3Gq3km78YnyMAdq282D+BLiDE6IhQ5WHCVL7fyhfgIVL6xwxISDVxiyITwNb72ky0MEutPg==} + '@unocss/cli@0.64.0': + resolution: {integrity: sha512-xfY/qm7vr/4Qaf+CcQHuBJSg5ApZBvvGVD1zwyGFgfhfOFYR1hI3DS2zl75zav6btEwwXsjY7AUv6uYGF4M7dA==} engines: {node: '>=14'} hasBin: true - '@unocss/config@0.63.6': - resolution: {integrity: sha512-+4Lt5uTwRgu1z7vhOUzDf+mL+BQYdaa/Z8NMT2Fiqb37tcjEKvmwaUHdfE22Vif1luDgC6xqFsn6qqFtOxhoWQ==} + '@unocss/config@0.64.0': + resolution: {integrity: sha512-L97x4vEk7jNG5ptZY5Xp0xgEk//tbMpQVm2BzfyL7w+Hg8X3AV4YjFL6hysHvpYiTdUCVaZg+S0s3b7wuj8Mqw==} engines: {node: '>=14'} - '@unocss/core@0.63.6': - resolution: {integrity: sha512-Q4QPgJ271Up89+vIqqOKgtdCKkFpHqvHN8W1LUlKPqtYnOvVYaOIVNAZowaIdEhPuc83yLc6Tg2+7riK18QKEw==} + '@unocss/core@0.64.0': + resolution: {integrity: sha512-Qb8wWPYNlTagCdJGzULew+e3NMM8Bd7fr38lDLgrMj+njop+wzkSe1ZZOyMMH9yHSq/Rznn5eCjnyzyHwxGslQ==} - '@unocss/extractor-arbitrary-variants@0.63.6': - resolution: {integrity: sha512-HJX0oAa9uzwKYoU8CoJdP1gxjuqFmOLxyZmITjStAmZNZpIxlz2wz4VrHmqml2dkvx/mifGGGc/GxZpQ36D12Q==} + '@unocss/extractor-arbitrary-variants@0.64.0': + resolution: {integrity: sha512-oVB8l8zM+x0MQJTkraRcsrfJnWEwyPVgMgtzmNUm//HqV+xTrjZCNtOqHFNIZdj/+w0gkErGQLxzRwyPjlHq4g==} - '@unocss/inspector@0.63.6': - resolution: {integrity: sha512-DQDJnhtzdHIQXD2vCdj5ytFnHfQCWJGPmrHJHXxzkTYn8nIovV1roVl1ITLxkDIIYK9bdYneg2imQN5JCZhHmQ==} + '@unocss/inspector@0.64.0': + resolution: {integrity: sha512-aFEfxEuPOpbPNH3j1CLLnN7ZyZkc64XoxZbz7RbG20Wy5oJxonOnlu+Wikz9SfGvIyF16MVAMCkHu12WFRRC+g==} - '@unocss/postcss@0.63.6': - resolution: {integrity: sha512-XI6U1jMwbQoSHVWpZZu3Cxp3t1PVj5VOj+IYtz7xmcWP9GVK+eyETo/xyB0l4muD4emXfSrhNDrFYzSyIyF5cg==} + '@unocss/postcss@0.64.0': + resolution: {integrity: sha512-OMDhAUDEzbb7i+fcYEYNxwdWJLSYklMrFGSC60ADK96UPX/B9S0z1pBz7N34DRPPIzg6shO6NQfDHOaxLelAeg==} engines: {node: '>=14'} peerDependencies: postcss: ^8.4.21 - '@unocss/preset-attributify@0.63.6': - resolution: {integrity: sha512-sHH17mfl/THHLxCLAHqPdUniCNMFjAxBHFDZYgGi83azuarF2evI5Mtc3Qsj3nzoSQwTPmK2VY3XYUUrpPDGWQ==} + '@unocss/preset-attributify@0.64.0': + resolution: {integrity: sha512-3T1mktq5rAQxHXtdLkjjj1UOjPwy9iGbVUChvxyaGV5oOsj1mvfe1oetxz8HqAVQak8MtvsJzFzvuuQQln/6OA==} - '@unocss/preset-icons@0.63.6': - resolution: {integrity: sha512-fRU44wXABnMPT/9zhKBNSUeDJlOxJhUJP9W3FSRnc+ktjAifJIj0xpUKtEqxL46QMq825Bsj2yDSquzP+XYGnQ==} + '@unocss/preset-icons@0.64.0': + resolution: {integrity: sha512-jhozA4r583agZZpKttdootaWfvQ29lY/kHxNU1Ah2xeRQcVXXEh7M3cG0bo9HSIX9/BgXSk5rWQlqSPIqFl4Lw==} - '@unocss/preset-mini@0.63.6': - resolution: {integrity: sha512-pZDZbSuxabHSwPIy3zCgQ4MNdVCSHvOvZecreH+v96R1oOhquiwU8WiSbkxvZiKiLQJd7JUVW87E1pAzr5ZGGQ==} + '@unocss/preset-mini@0.64.0': + resolution: {integrity: sha512-bc7zanalVQUrETJ06eyS7y/lhceRlY8kBG/lRCV/dYmKl4Ho/s57LrpZH0G63OcO6IfWIjwoZHVC8/RHAqnYvQ==} - '@unocss/preset-tagify@0.63.6': - resolution: {integrity: sha512-3lKhk4MW3RqJBwIvBXHj0H0/kHkFlKtCIBQFiBcCJh8TXOID8IZ0iVjuGwdlk63VTizI/wnsNDOVpj6YcjRRlw==} + '@unocss/preset-tagify@0.64.0': + resolution: {integrity: sha512-WlRQXYgtVzJpVlZ+itXhrQyvMj6XW1InNIfvAHMorr5BGvMGETLRnuWwYYhGg2YDF/g+/EucU5PQmk9UkurBzg==} - '@unocss/preset-typography@0.63.6': - resolution: {integrity: sha512-AXmBVnbV54gUIv5kbywjZek9ZlKRwJfBDVMtWOcLOjN3AHirGx1W2oq2UzNkfYZ2leof/Y2BocxeTwGCCRhqDQ==} + '@unocss/preset-typography@0.64.0': + resolution: {integrity: sha512-hMKxhHTRUjvwB0gcdWOh6zWWolH9pvIvgB4p2GaFT1vKyFD0wkTZ/7S/Q3OMKJyevSKHyIgKd+PhNGKTx5FuQQ==} - '@unocss/preset-uno@0.63.6': - resolution: {integrity: sha512-67PbHyVgAe9Rz0Rhyl3zBibFuGmqQMRPMkRjNYrwmmtNydpQYsXbfnDs0p8mZFp6uO2o3Jkh7urqEtixHHvq0Q==} + '@unocss/preset-uno@0.64.0': + resolution: {integrity: sha512-gUmuL8anty551r/Q2XU5wc0aNZ+te4yydnamXHSUv3EkX6PCphOaiWsQ5f95fj26G8EYH9fLBvxqXurFBPM7og==} - '@unocss/preset-web-fonts@0.63.6': - resolution: {integrity: sha512-ko1aHDax0u5CQi1BXggv6uW5Vq/LQRWwzOxqBFTh1JlGHPZTw4CdVJkYnlpt3WEW+FPUzZYjhKmMmQY7KtOTng==} + '@unocss/preset-web-fonts@0.64.0': + resolution: {integrity: sha512-qraIhS0tCFHvdPQnzGTfi/dggwyboWPU8UQn8oLMsmPKogNPsYQfjrtTZs8X6F1KNaPV18c6saaWYvVZ8tXPoA==} - '@unocss/preset-wind@0.63.6': - resolution: {integrity: sha512-W3oZ2TXSqStNE+X++kcspRTF2Szu2ej6NW5Kiyy6WQn/+ZD77AF4VtvzHtzFVZ2QKpEIovGBpU5tywooHbB7hw==} + '@unocss/preset-wind@0.64.0': + resolution: {integrity: sha512-cJbZI4etFrIIQoC1VhRqyEZU5fUaYqOH3uIt5lM3osxBdAvHds7SPjLRbdR612US7JbuPeFhMMRnA1EYoo39sQ==} - '@unocss/reset@0.63.6': - resolution: {integrity: sha512-gq73RSZj54MOloqrivkoMPXCqNG2WpIyBT1AYlF76uKxEEbUD41E8uBUhLSKs7gFgF01yQJLRaIuyN1yw09pbQ==} + '@unocss/reset@0.64.0': + resolution: {integrity: sha512-75SiDtRX/mtg/7GWeoLfDfdWF4z59zF1XesL46FNd2hDZL36a+SZHIKB/J+PPzLyX9irqm3mAETS2PNfynuJpA==} - '@unocss/rule-utils@0.63.6': - resolution: {integrity: sha512-moeDEq5d9mB8gSYeoqHMkXWWekaFFdhg7QCuwwCbxCc+NPMOgGkmfAoafz+y2tdvK7pEuT191RWOiHQ0MkA5oQ==} + '@unocss/rule-utils@0.64.0': + resolution: {integrity: sha512-R5b/uspq6XsmpEqhxSzOOePHsS+pdxya+0pkQw7m6thsUxNDL7kVDpBiz2iNX5lnwagvhyhUWYu85a8XmZ8ymw==} engines: {node: '>=14'} - '@unocss/transformer-attributify-jsx@0.63.6': - resolution: {integrity: sha512-/RU09MF+hJK7cFbLJ+8vloCGyhn6Oys8R6gey0auB0+nw/ucUXoLQKWgUqo9taQlLuYOiehdkYjQSdWn5lyA/Q==} + '@unocss/transformer-attributify-jsx@0.64.0': + resolution: {integrity: sha512-/kG7NFmqMCftK5DJUgMUbe9SWRJt20Z55o36aaCkBcEsrTSYBmWYDyIJPZa3TxsjO8H1qDekRVu7CgDxwlxMEQ==} - '@unocss/transformer-compile-class@0.63.6': - resolution: {integrity: sha512-zzAqs8adnTUOLA88RgcToadcrz9gjxrZk6IrcmMqMmWqk0MOWNQHIN0RzKa/yaw4QhO2xuGyIz4/WHyXuCXMQg==} + '@unocss/transformer-compile-class@0.64.0': + resolution: {integrity: sha512-p1LZG2AUsD0FrkCSo1JOsWVQ+sEMcgnVCm6XtCgxBraV3nPFeZUyxmj9yEkt0HhfYkMTvdT155c3rDhbwP8AFw==} - '@unocss/transformer-directives@0.63.6': - resolution: {integrity: sha512-XcNOwLRbfrJSU6YXyLgiMzAigSzjIdvHwS3lLCZ2n6DWuLmTuXBfvVtRxeJ+aflNkhpQNKONCClC4s6I2r53uw==} + '@unocss/transformer-directives@0.64.0': + resolution: {integrity: sha512-+e2bDEQMEsfq4KZ2R+GQNrEv0bL3E1KbXGPQXUiMGitmZzzagDfIBk9VTP3gNhU+hgTaWtjXlReeap1eSmwKGQ==} - '@unocss/transformer-variant-group@0.63.6': - resolution: {integrity: sha512-ebYSjZnZrtcJYjmAEDwGVwPuaQ9EVWKNDDJFFSusP8k/6PjJoHDh0qkj+hdPPDhYn81yzJQalU1eSUSlfC30VA==} + '@unocss/transformer-variant-group@0.64.0': + resolution: {integrity: sha512-c4CN+W8ShBhGIma3KHHcBe7CRljRwZ0f5UamRrUIMs28a2jfa1TlPlr/4Ke5b6icr0mwTGajJEUaPanOK0Fp1A==} - '@unocss/vite@0.63.6': - resolution: {integrity: sha512-gxK3gtvYQH5S/qtuvsY4M0S+KJPZnYrOQI/Gopufx+b2qgmwZ/TSAe66gWeKYfe3DfQsmA3PPh/GXpkK+/FnHg==} + '@unocss/vite@0.64.0': + resolution: {integrity: sha512-QrfXlI8YcIaqQc4WRVrLbCho8eEi5pjs1/C8AwnUHGximEDN6MZNUk0htjo4QZ+50IA2b4RrYdz1N3875bJoFg==} peerDependencies: vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 @@ -2544,8 +2550,8 @@ packages: resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - css-tree@3.0.0: - resolution: {integrity: sha512-o88DVQ6GzsABn1+6+zo2ct801dBO5OASVyxbbvA2W20ue2puSh/VOuqUj90eUeMSX/xqGqBmOKiRQN7tJOuBXw==} + css-tree@3.0.1: + resolution: {integrity: sha512-8Fxxv+tGhORlshCdCwnNJytvlvq46sOLSYEx2ZIGurahWvMucSRnyjPA3AmrMq4VPRYbHVpWj5VkiVasrM2H4Q==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} css-what@6.1.0: @@ -2998,9 +3004,9 @@ packages: resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} engines: {node: '>=10'} - happy-dom@14.12.3: - resolution: {integrity: sha512-vsYlEs3E9gLwA1Hp+w3qzu+RUDFf4VTT8cyKqVICoZ2k7WM++Qyd2LwzyTi5bqMJFiIC/vNpTDYuxdreENRK/g==} - engines: {node: '>=16.0.0'} + happy-dom@15.11.4: + resolution: {integrity: sha512-AU6tzh3ADd28vSmXahgLsGyGGihXPGeKH0owDn9lhHolB6vIwEhag//T+TBzDoAcHhmVEwlxwSgtW1KZep+1MA==} + engines: {node: '>=18.0.0'} has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} @@ -3484,8 +3490,8 @@ packages: mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} - mdn-data@2.10.0: - resolution: {integrity: sha512-qq7C3EtK3yJXMwz1zAab65pjl+UhohqMOctTgcqjLOWABqmwj+me02LSsCuEUxnst9X1lCBpoE0WArGKgdGDzw==} + mdn-data@2.12.1: + resolution: {integrity: sha512-rsfnCbOHjqrhWxwt5/wtSLzpoKTzW7OXdT5lLOIH1OTYhWu9rRJveGq0sKvDZODABH7RX+uoR+DYcpFnq4Tf6Q==} merge-anything@5.1.7: resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} @@ -3651,8 +3657,8 @@ packages: nan@2.17.0: resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==} - nano-spawn@0.1.0: - resolution: {integrity: sha512-Q0fYRut0GGSoysqrS6hF1jlQEc3yOglnGXwsS+DcoaLwu6NCkUxOB8om08hvUF0V+bNRjExzKM3v8ATbfrLWyQ==} + nano-spawn@0.2.0: + resolution: {integrity: sha512-IjZBIOLxSlxu+m/kacg9JuP93oUpRemeV0mEuCy64nzBKKIL9m0aLJHtVPcVuzJDHFhElzjpwbW4a3tMzgKoZQ==} engines: {node: '>=18.19'} nanoid@3.3.7: @@ -3731,6 +3737,9 @@ packages: ofetch@1.4.0: resolution: {integrity: sha512-MuHgsEhU6zGeX+EMh+8mSMrYTnsqJQQrpM00Q6QHMKNqQ0bKy0B43tk8tL1wg+CnsSTy1kg4Ir2T5Ig6rD+dfQ==} + ofetch@1.4.1: + resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} + ohash@1.1.4: resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==} @@ -3776,8 +3785,8 @@ packages: resolution: {integrity: sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==} engines: {node: '>= 0.4.0'} - oxlint@0.9.10: - resolution: {integrity: sha512-bKiiFN7Hnoaist/rditTRBXz+GXKYuLd53/NB7Q6zHB/bifELJarSoRLkAUGElIJKl4PSr3lTh1g6zehh+rX0g==} + oxlint@0.11.1: + resolution: {integrity: sha512-yVTkBmSvn1mo69vxBdNASOGFd1oqWzpaIWPFPIXNAHxgrW7FjotKuJ71j/pqtZH/sVSRWTpQFdmBa3CIuBEILg==} engines: {node: '>=14.*'} hasBin: true @@ -4511,14 +4520,11 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.0: - resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} - tinyexec@0.3.1: resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} - tinyglobby@0.2.9: - resolution: {integrity: sha512-8or1+BGEdk1Zkkw2ii16qSS7uVrQJPre5A9o/XkWPATkk23FZh/15BKFxPnlTy6vkljZxLqYCzzBMj30ZrSvjw==} + tinyglobby@0.2.10: + resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} engines: {node: '>=12.0.0'} tinypool@1.0.1: @@ -4674,11 +4680,11 @@ packages: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} - unocss@0.63.6: - resolution: {integrity: sha512-OKJJKEFWVz+Lsf3JdOgRiRtL+QOUQRBov89taUcCPFPZtrhP6pPVFCZHD9qMvY4IChMX7dzalQax3ZXJ3hbtkQ==} + unocss@0.64.0: + resolution: {integrity: sha512-wiEFRjGXSogzf/4+KICXjFDgSGloSCV1Ka2Dct/8Z8U+iwRqeVpHGVQcGjBFg9Uh0DH1fSVBbis2aPuIkT0nEA==} engines: {node: '>=14'} peerDependencies: - '@unocss/webpack': 0.63.6 + '@unocss/webpack': 0.64.0 vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 peerDependenciesMeta: '@unocss/webpack': @@ -4862,8 +4868,10 @@ packages: '@vue/composition-api': optional: true - vue-flow-layout@0.0.5: - resolution: {integrity: sha512-lZlqQ/Se1trGMtBMneZDWaiQiQBuxU8ivZ+KpJMem5zKROFpzuPq9KqyWABbSYbxq0qhqZs1I4DBwrY041rtOA==} + vue-flow-layout@0.1.1: + resolution: {integrity: sha512-JdgRRUVrN0Y2GosA0M68DEbKlXMqJ7FQgsK8CjQD2vxvNSqAU6PZEpi4cfcTVtfM2GVOMjHo7GKKLbXxOBqDqA==} + peerDependencies: + vue: ^3.4.37 vue@3.5.12: resolution: {integrity: sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==} @@ -5142,7 +5150,7 @@ snapshots: '@antfu/install-pkg@0.4.1': dependencies: package-manager-detector: 0.2.2 - tinyexec: 0.3.0 + tinyexec: 0.3.1 '@antfu/utils@0.7.10': {} @@ -5692,7 +5700,7 @@ snapshots: '@esbuild/win32-x64@0.23.0': optional: true - '@faker-js/faker@8.4.1': {} + '@faker-js/faker@9.2.0': {} '@iconify-json/simple-icons@1.2.11': dependencies: @@ -5829,28 +5837,28 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - '@oxlint/darwin-arm64@0.9.10': + '@oxlint/darwin-arm64@0.11.1': optional: true - '@oxlint/darwin-x64@0.9.10': + '@oxlint/darwin-x64@0.11.1': optional: true - '@oxlint/linux-arm64-gnu@0.9.10': + '@oxlint/linux-arm64-gnu@0.11.1': optional: true - '@oxlint/linux-arm64-musl@0.9.10': + '@oxlint/linux-arm64-musl@0.11.1': optional: true - '@oxlint/linux-x64-gnu@0.9.10': + '@oxlint/linux-x64-gnu@0.11.1': optional: true - '@oxlint/linux-x64-musl@0.9.10': + '@oxlint/linux-x64-musl@0.11.1': optional: true - '@oxlint/win32-arm64@0.9.10': + '@oxlint/win32-arm64@0.11.1': optional: true - '@oxlint/win32-x64@0.9.10': + '@oxlint/win32-x64@0.11.1': optional: true '@parcel/watcher-android-arm64@2.5.0': @@ -5988,6 +5996,14 @@ snapshots: optionalDependencies: rollup: 4.24.0 + '@rollup/pluginutils@5.1.3(rollup@4.24.0)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.24.0 + '@rollup/rollup-android-arm-eabi@4.24.0': optional: true @@ -6118,12 +6134,7 @@ snapshots: dependencies: '@babel/types': 7.25.6 - '@types/chrome@0.0.268': - dependencies: - '@types/filesystem': 0.0.36 - '@types/har-format': 1.2.15 - - '@types/chrome@0.0.269': + '@types/chrome@0.0.280': dependencies: '@types/filesystem': 0.0.36 '@types/har-format': 1.2.15 @@ -6208,7 +6219,7 @@ snapshots: '@types/web-bluetooth@0.0.20': {} - '@types/webextension-polyfill@0.10.7': {} + '@types/webextension-polyfill@0.12.1': {} '@types/yauzl@2.10.3': dependencies: @@ -6217,25 +6228,25 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@unocss/astro@0.63.6(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))': + '@unocss/astro@0.64.0(rollup@4.24.0)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))(vue@3.5.12(typescript@5.6.3))': dependencies: - '@unocss/core': 0.63.6 - '@unocss/reset': 0.63.6 - '@unocss/vite': 0.63.6(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) + '@unocss/core': 0.64.0 + '@unocss/reset': 0.64.0 + '@unocss/vite': 0.64.0(rollup@4.24.0)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))(vue@3.5.12(typescript@5.6.3)) optionalDependencies: vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) transitivePeerDependencies: - rollup - supports-color - - typescript + - vue - '@unocss/cli@0.63.6(rollup@4.24.0)': + '@unocss/cli@0.64.0(rollup@4.24.0)': dependencies: '@ampproject/remapping': 2.3.0 - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - '@unocss/config': 0.63.6 - '@unocss/core': 0.63.6 - '@unocss/preset-uno': 0.63.6 + '@rollup/pluginutils': 5.1.3(rollup@4.24.0) + '@unocss/config': 0.64.0 + '@unocss/core': 0.64.0 + '@unocss/preset-uno': 0.64.0 cac: 6.7.14 chokidar: 3.6.0 colorette: 2.0.20 @@ -6243,130 +6254,130 @@ snapshots: magic-string: 0.30.12 pathe: 1.1.2 perfect-debounce: 1.0.0 - tinyglobby: 0.2.9 + tinyglobby: 0.2.10 transitivePeerDependencies: - rollup - supports-color - '@unocss/config@0.63.6': + '@unocss/config@0.64.0': dependencies: - '@unocss/core': 0.63.6 + '@unocss/core': 0.64.0 unconfig: 0.5.5 transitivePeerDependencies: - supports-color - '@unocss/core@0.63.6': {} + '@unocss/core@0.64.0': {} - '@unocss/extractor-arbitrary-variants@0.63.6': + '@unocss/extractor-arbitrary-variants@0.64.0': dependencies: - '@unocss/core': 0.63.6 + '@unocss/core': 0.64.0 - '@unocss/inspector@0.63.6(typescript@5.6.3)': + '@unocss/inspector@0.64.0(vue@3.5.12(typescript@5.6.3))': dependencies: - '@unocss/core': 0.63.6 - '@unocss/rule-utils': 0.63.6 + '@unocss/core': 0.64.0 + '@unocss/rule-utils': 0.64.0 gzip-size: 6.0.0 sirv: 2.0.4 - vue-flow-layout: 0.0.5(typescript@5.6.3) + vue-flow-layout: 0.1.1(vue@3.5.12(typescript@5.6.3)) transitivePeerDependencies: - - typescript + - vue - '@unocss/postcss@0.63.6(postcss@8.4.47)': + '@unocss/postcss@0.64.0(postcss@8.4.47)': dependencies: - '@unocss/config': 0.63.6 - '@unocss/core': 0.63.6 - '@unocss/rule-utils': 0.63.6 - css-tree: 3.0.0 + '@unocss/config': 0.64.0 + '@unocss/core': 0.64.0 + '@unocss/rule-utils': 0.64.0 + css-tree: 3.0.1 postcss: 8.4.47 - tinyglobby: 0.2.9 + tinyglobby: 0.2.10 transitivePeerDependencies: - supports-color - '@unocss/preset-attributify@0.63.6': + '@unocss/preset-attributify@0.64.0': dependencies: - '@unocss/core': 0.63.6 + '@unocss/core': 0.64.0 - '@unocss/preset-icons@0.63.6': + '@unocss/preset-icons@0.64.0': dependencies: '@iconify/utils': 2.1.33 - '@unocss/core': 0.63.6 - ofetch: 1.4.0 + '@unocss/core': 0.64.0 + ofetch: 1.4.1 transitivePeerDependencies: - supports-color - '@unocss/preset-mini@0.63.6': + '@unocss/preset-mini@0.64.0': dependencies: - '@unocss/core': 0.63.6 - '@unocss/extractor-arbitrary-variants': 0.63.6 - '@unocss/rule-utils': 0.63.6 + '@unocss/core': 0.64.0 + '@unocss/extractor-arbitrary-variants': 0.64.0 + '@unocss/rule-utils': 0.64.0 - '@unocss/preset-tagify@0.63.6': + '@unocss/preset-tagify@0.64.0': dependencies: - '@unocss/core': 0.63.6 + '@unocss/core': 0.64.0 - '@unocss/preset-typography@0.63.6': + '@unocss/preset-typography@0.64.0': dependencies: - '@unocss/core': 0.63.6 - '@unocss/preset-mini': 0.63.6 + '@unocss/core': 0.64.0 + '@unocss/preset-mini': 0.64.0 - '@unocss/preset-uno@0.63.6': + '@unocss/preset-uno@0.64.0': dependencies: - '@unocss/core': 0.63.6 - '@unocss/preset-mini': 0.63.6 - '@unocss/preset-wind': 0.63.6 - '@unocss/rule-utils': 0.63.6 + '@unocss/core': 0.64.0 + '@unocss/preset-mini': 0.64.0 + '@unocss/preset-wind': 0.64.0 + '@unocss/rule-utils': 0.64.0 - '@unocss/preset-web-fonts@0.63.6': + '@unocss/preset-web-fonts@0.64.0': dependencies: - '@unocss/core': 0.63.6 - ofetch: 1.4.0 + '@unocss/core': 0.64.0 + ofetch: 1.4.1 - '@unocss/preset-wind@0.63.6': + '@unocss/preset-wind@0.64.0': dependencies: - '@unocss/core': 0.63.6 - '@unocss/preset-mini': 0.63.6 - '@unocss/rule-utils': 0.63.6 + '@unocss/core': 0.64.0 + '@unocss/preset-mini': 0.64.0 + '@unocss/rule-utils': 0.64.0 - '@unocss/reset@0.63.6': {} + '@unocss/reset@0.64.0': {} - '@unocss/rule-utils@0.63.6': + '@unocss/rule-utils@0.64.0': dependencies: - '@unocss/core': 0.63.6 + '@unocss/core': 0.64.0 magic-string: 0.30.12 - '@unocss/transformer-attributify-jsx@0.63.6': + '@unocss/transformer-attributify-jsx@0.64.0': dependencies: - '@unocss/core': 0.63.6 + '@unocss/core': 0.64.0 - '@unocss/transformer-compile-class@0.63.6': + '@unocss/transformer-compile-class@0.64.0': dependencies: - '@unocss/core': 0.63.6 + '@unocss/core': 0.64.0 - '@unocss/transformer-directives@0.63.6': + '@unocss/transformer-directives@0.64.0': dependencies: - '@unocss/core': 0.63.6 - '@unocss/rule-utils': 0.63.6 - css-tree: 3.0.0 + '@unocss/core': 0.64.0 + '@unocss/rule-utils': 0.64.0 + css-tree: 3.0.1 - '@unocss/transformer-variant-group@0.63.6': + '@unocss/transformer-variant-group@0.64.0': dependencies: - '@unocss/core': 0.63.6 + '@unocss/core': 0.64.0 - '@unocss/vite@0.63.6(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))': + '@unocss/vite@0.64.0(rollup@4.24.0)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))(vue@3.5.12(typescript@5.6.3))': dependencies: '@ampproject/remapping': 2.3.0 - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - '@unocss/config': 0.63.6 - '@unocss/core': 0.63.6 - '@unocss/inspector': 0.63.6(typescript@5.6.3) + '@rollup/pluginutils': 5.1.3(rollup@4.24.0) + '@unocss/config': 0.64.0 + '@unocss/core': 0.64.0 + '@unocss/inspector': 0.64.0(vue@3.5.12(typescript@5.6.3)) chokidar: 3.6.0 magic-string: 0.30.12 - tinyglobby: 0.2.9 + tinyglobby: 0.2.10 vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) transitivePeerDependencies: - rollup - supports-color - - typescript + - vue '@vitejs/plugin-react@4.3.3(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))': dependencies: @@ -6384,7 +6395,7 @@ snapshots: vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) vue: 3.5.12(typescript@5.6.3) - '@vitest/coverage-v8@2.1.4(vitest@2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7))': + '@vitest/coverage-v8@2.1.4(vitest@2.1.4(@types/node@20.17.6)(happy-dom@15.11.4)(sass@1.80.7))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -6398,7 +6409,7 @@ snapshots: std-env: 3.7.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 - vitest: 2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7) + vitest: 2.1.4(@types/node@20.17.6)(happy-dom@15.11.4)(sass@1.80.7) transitivePeerDependencies: - supports-color @@ -7026,9 +7037,9 @@ snapshots: mdn-data: 2.0.30 source-map-js: 1.2.1 - css-tree@3.0.0: + css-tree@3.0.1: dependencies: - mdn-data: 2.10.0 + mdn-data: 2.12.1 source-map-js: 1.2.1 css-what@6.1.0: {} @@ -7561,7 +7572,7 @@ snapshots: dependencies: duplexer: 0.1.2 - happy-dom@14.12.3: + happy-dom@15.11.4: dependencies: entities: 4.5.0 webidl-conversions: 7.0.0 @@ -8022,7 +8033,7 @@ snapshots: mdn-data@2.0.30: {} - mdn-data@2.10.0: {} + mdn-data@2.12.1: {} merge-anything@5.1.7: dependencies: @@ -8179,7 +8190,7 @@ snapshots: nan@2.17.0: optional: true - nano-spawn@0.1.0: {} + nano-spawn@0.2.0: {} nanoid@3.3.7: {} @@ -8254,6 +8265,12 @@ snapshots: node-fetch-native: 1.6.4 ufo: 1.5.4 + ofetch@1.4.1: + dependencies: + destr: 2.0.3 + node-fetch-native: 1.6.4 + ufo: 1.5.4 + ohash@1.1.4: {} once@1.4.0: @@ -8322,16 +8339,16 @@ snapshots: os-shim@0.1.3: {} - oxlint@0.9.10: + oxlint@0.11.1: optionalDependencies: - '@oxlint/darwin-arm64': 0.9.10 - '@oxlint/darwin-x64': 0.9.10 - '@oxlint/linux-arm64-gnu': 0.9.10 - '@oxlint/linux-arm64-musl': 0.9.10 - '@oxlint/linux-x64-gnu': 0.9.10 - '@oxlint/linux-x64-musl': 0.9.10 - '@oxlint/win32-arm64': 0.9.10 - '@oxlint/win32-x64': 0.9.10 + '@oxlint/darwin-arm64': 0.11.1 + '@oxlint/darwin-x64': 0.11.1 + '@oxlint/linux-arm64-gnu': 0.11.1 + '@oxlint/linux-arm64-musl': 0.11.1 + '@oxlint/linux-x64-gnu': 0.11.1 + '@oxlint/linux-x64-musl': 0.11.1 + '@oxlint/win32-arm64': 0.11.1 + '@oxlint/win32-x64': 0.11.1 p-cancelable@3.0.0: {} @@ -9097,11 +9114,9 @@ snapshots: tinybench@2.9.0: {} - tinyexec@0.3.0: {} - tinyexec@0.3.1: {} - tinyglobby@0.2.9: + tinyglobby@0.2.10: dependencies: fdir: 6.4.2(picomatch@4.0.2) picomatch: 4.0.2 @@ -9277,32 +9292,32 @@ snapshots: universalify@2.0.0: {} - unocss@0.63.6(postcss@8.4.47)(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)): + unocss@0.64.0(postcss@8.4.47)(rollup@4.24.0)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))(vue@3.5.12(typescript@5.6.3)): dependencies: - '@unocss/astro': 0.63.6(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) - '@unocss/cli': 0.63.6(rollup@4.24.0) - '@unocss/core': 0.63.6 - '@unocss/postcss': 0.63.6(postcss@8.4.47) - '@unocss/preset-attributify': 0.63.6 - '@unocss/preset-icons': 0.63.6 - '@unocss/preset-mini': 0.63.6 - '@unocss/preset-tagify': 0.63.6 - '@unocss/preset-typography': 0.63.6 - '@unocss/preset-uno': 0.63.6 - '@unocss/preset-web-fonts': 0.63.6 - '@unocss/preset-wind': 0.63.6 - '@unocss/transformer-attributify-jsx': 0.63.6 - '@unocss/transformer-compile-class': 0.63.6 - '@unocss/transformer-directives': 0.63.6 - '@unocss/transformer-variant-group': 0.63.6 - '@unocss/vite': 0.63.6(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) + '@unocss/astro': 0.64.0(rollup@4.24.0)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))(vue@3.5.12(typescript@5.6.3)) + '@unocss/cli': 0.64.0(rollup@4.24.0) + '@unocss/core': 0.64.0 + '@unocss/postcss': 0.64.0(postcss@8.4.47) + '@unocss/preset-attributify': 0.64.0 + '@unocss/preset-icons': 0.64.0 + '@unocss/preset-mini': 0.64.0 + '@unocss/preset-tagify': 0.64.0 + '@unocss/preset-typography': 0.64.0 + '@unocss/preset-uno': 0.64.0 + '@unocss/preset-web-fonts': 0.64.0 + '@unocss/preset-wind': 0.64.0 + '@unocss/transformer-attributify-jsx': 0.64.0 + '@unocss/transformer-compile-class': 0.64.0 + '@unocss/transformer-directives': 0.64.0 + '@unocss/transformer-variant-group': 0.64.0 + '@unocss/vite': 0.64.0(rollup@4.24.0)(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7))(vue@3.5.12(typescript@5.6.3)) optionalDependencies: vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) transitivePeerDependencies: - postcss - rollup - supports-color - - typescript + - vue unplugin@1.14.1(webpack-sources@3.2.3): dependencies: @@ -9468,17 +9483,17 @@ snapshots: - typescript - universal-cookie - vitest-mock-extended@2.0.2(typescript@5.6.3)(vitest@2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7)): + vitest-mock-extended@2.0.2(typescript@5.6.3)(vitest@2.1.4(@types/node@20.17.6)(happy-dom@15.11.4)(sass@1.80.7)): dependencies: ts-essentials: 10.0.1(typescript@5.6.3) typescript: 5.6.3 - vitest: 2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7) + vitest: 2.1.4(@types/node@20.17.6)(happy-dom@15.11.4)(sass@1.80.7) vitest-plugin-random-seed@1.1.0(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)): dependencies: vite: 5.4.11(@types/node@20.17.6)(sass@1.80.7) - vitest@2.1.4(@types/node@20.17.6)(happy-dom@14.12.3)(sass@1.80.7): + vitest@2.1.4(@types/node@20.17.6)(happy-dom@15.11.4)(sass@1.80.7): dependencies: '@vitest/expect': 2.1.4 '@vitest/mocker': 2.1.4(vite@5.4.11(@types/node@20.17.6)(sass@1.80.7)) @@ -9502,7 +9517,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.17.6 - happy-dom: 14.12.3 + happy-dom: 15.11.4 transitivePeerDependencies: - less - lightningcss @@ -9522,11 +9537,9 @@ snapshots: dependencies: vue: 3.5.12(typescript@5.6.3) - vue-flow-layout@0.0.5(typescript@5.6.3): + vue-flow-layout@0.1.1(vue@3.5.12(typescript@5.6.3)): dependencies: vue: 3.5.12(typescript@5.6.3) - transitivePeerDependencies: - - typescript vue@3.5.12(typescript@5.6.3): dependencies: diff --git a/taze.config.ts b/taze.config.ts index 0bc140cf..6d929bd9 100644 --- a/taze.config.ts +++ b/taze.config.ts @@ -7,5 +7,7 @@ export default { 'typedoc-vitepress-theme', // Manually manage version so a single version is used: 'esbuild', + // Maintained manually to match min-node version + '@types/node', ], }; diff --git a/templates/react/package.json b/templates/react/package.json index d35d9e58..ca5fb8ed 100644 --- a/templates/react/package.json +++ b/templates/react/package.json @@ -19,7 +19,7 @@ "react-dom": "^18.3.1" }, "devDependencies": { - "@types/chrome": "^0.0.269", + "@types/chrome": "^0.0.280", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@wxt-dev/module-react": "^1.1.1", diff --git a/templates/solid/package.json b/templates/solid/package.json index 4939a500..b726193f 100644 --- a/templates/solid/package.json +++ b/templates/solid/package.json @@ -18,7 +18,7 @@ "solid-js": "^1.9.3" }, "devDependencies": { - "@types/chrome": "^0.0.269", + "@types/chrome": "^0.0.280", "@wxt-dev/module-solid": "^1.1.2", "typescript": "^5.6.3", "wxt": "^0.19.13" diff --git a/templates/svelte/package.json b/templates/svelte/package.json index 51d3e750..1d050013 100644 --- a/templates/svelte/package.json +++ b/templates/svelte/package.json @@ -16,10 +16,10 @@ }, "devDependencies": { "@tsconfig/svelte": "^5.0.4", - "@types/chrome": "^0.0.269", - "@wxt-dev/module-svelte": "^1.0.1", - "svelte": "^4.2.19", - "svelte-check": "^3.8.6", + "@types/chrome": "^0.0.280", + "@wxt-dev/module-svelte": "^2.0.0", + "svelte": "^5.1.16", + "svelte-check": "^4.0.7", "tslib": "^2.8.1", "typescript": "^5.6.3", "wxt": "^0.19.13" diff --git a/templates/vanilla/package.json b/templates/vanilla/package.json index 09a0d1b1..fbd82f1c 100644 --- a/templates/vanilla/package.json +++ b/templates/vanilla/package.json @@ -15,7 +15,7 @@ "postinstall": "wxt prepare" }, "devDependencies": { - "@types/chrome": "^0.0.269", + "@types/chrome": "^0.0.280", "typescript": "^5.6.3", "wxt": "^0.19.13" } diff --git a/templates/vue/package.json b/templates/vue/package.json index 551bf8fb..229ed2b1 100644 --- a/templates/vue/package.json +++ b/templates/vue/package.json @@ -18,7 +18,7 @@ "vue": "^3.5.12" }, "devDependencies": { - "@types/chrome": "^0.0.269", + "@types/chrome": "^0.0.280", "@wxt-dev/module-vue": "^1.0.1", "typescript": "^5.6.3", "vue-tsc": "^2.1.10", From e770f9b6b7664847ab351060c6b56d3999a9797d Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 13 Nov 2024 15:06:31 +0000 Subject: [PATCH 029/130] chore(release): wxt v0.19.14 --- packages/wxt/CHANGELOG.md | 33 ++++++++++++++++++++++++++++++++- packages/wxt/package.json | 2 +- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/wxt/CHANGELOG.md b/packages/wxt/CHANGELOG.md index b1be4b69..70c86996 100644 --- a/packages/wxt/CHANGELOG.md +++ b/packages/wxt/CHANGELOG.md @@ -1,5 +1,36 @@ # Changelog +## v0.19.14 + +[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.13...wxt-v0.19.14) + +### 🚀 Enhancements + +- **storage:** Support storage items in batch functions ([#990](https://github.com/wxt-dev/wxt/pull/990)) +- Automatically disable 'Show warning about Self-XSS when pasing code' in new chrome ([#1159](https://github.com/wxt-dev/wxt/pull/1159)) + +### 🩹 Fixes + +- Throw when config file does not exist ([#1156](https://github.com/wxt-dev/wxt/pull/1156)) + +### 📖 Documentation + +- Cleanup typos and broken links ([bb5ea34](https://github.com/wxt-dev/wxt/commit/bb5ea34)) +- Fix typo in `popup` and `options` EntrypointOptions ([#1121](https://github.com/wxt-dev/wxt/pull/1121)) + +### 🏡 Chore + +- **deps:** Upgrade all non-major dependencies ([#1164](https://github.com/wxt-dev/wxt/pull/1164)) +- **deps:** Bump dev and non-breaking major dependencies ([#1167](https://github.com/wxt-dev/wxt/pull/1167)) + +### ❤️ Contributors + +- Aaron ([@aklinker1](http://github.com/aklinker1)) +- Rxliuli ([@rxliuli](http://github.com/rxliuli)) +- Kongmoumou ([@kongmoumou](http://github.com/kongmoumou)) +- Florian Metz ([@Timeraa](http://github.com/Timeraa)) +- Bread Grocery + ## v0.19.13 [compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.12...wxt-v0.19.13) @@ -2578,4 +2609,4 @@ Initial release of WXT. Full support for production builds and initial toolkit f ### 🤖 CI - Create validation workflow ([#12](https://github.com/wxt-dev/wxt/pull/12)) -- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13)) +- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13)) \ No newline at end of file diff --git a/packages/wxt/package.json b/packages/wxt/package.json index 2e430b03..4df0fce4 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -1,7 +1,7 @@ { "name": "wxt", "type": "module", - "version": "0.19.13", + "version": "0.19.14", "description": "Next gen framework for developing web extensions", "repository": { "type": "git", From c3959e84274f78761bb175c0f5b89c910f9a3880 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 13 Nov 2024 16:25:03 -0600 Subject: [PATCH 030/130] fix: Respect `configOrPath` for dev server (#1169) --- packages/unocss/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/unocss/src/index.ts b/packages/unocss/src/index.ts index 18ebf51c..5abb37da 100644 --- a/packages/unocss/src/index.ts +++ b/packages/unocss/src/index.ts @@ -28,7 +28,7 @@ export default defineWxtModule({ } wxt.hooks.hook('vite:devServer:extendConfig', (config) => { - config.plugins?.push(UnoCSS()); + config.plugins?.push(UnoCSS(resolvedOptions.configOrPath)); }); wxt.hooks.hook('vite:build:extendConfig', async (entries, config) => { From c846a1febfe4ca4990a794f3d70be985d89bd5b3 Mon Sep 17 00:00:00 2001 From: Qiwei Yang Date: Thu, 14 Nov 2024 15:07:53 +0800 Subject: [PATCH 031/130] docs: Add "Text Search Pro" to homepage (#1172) --- docs/.vitepress/components/UsingWxtSection.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/.vitepress/components/UsingWxtSection.vue b/docs/.vitepress/components/UsingWxtSection.vue index 06549ebd..d0ac7d78 100644 --- a/docs/.vitepress/components/UsingWxtSection.vue +++ b/docs/.vitepress/components/UsingWxtSection.vue @@ -53,6 +53,7 @@ const chromeExtensionIds = [ 'nmldnjcblcihmegipecakhmnieiofmgl', // Keyword Rank Checker 'gppllamhaciichleihemgilcpledblpn', // YouTube Simple View - Hide distractions & more 'pccbghdfdnnkkbcdcibchpbffdgednkf', // Propbar - Property Data Enhancer + 'lfknakglefggmdkjdfhhofkjnnolffkh', // Text Search Pro - Search by case and whole-word match! ]; const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds); From 064704c9b7e3cec68051a60145d22b86f7327fa0 Mon Sep 17 00:00:00 2001 From: Nishu Date: Thu, 14 Nov 2024 13:32:00 +0530 Subject: [PATCH 032/130] fix: Add "/" to `PublicPath` and `browser.runtime.getURL` (#1171) Co-authored-by: Aaron --- packages/wxt-demo/src/entrypoints/background.ts | 3 ++- packages/wxt/e2e/tests/typescript-project.test.ts | 1 + packages/wxt/src/core/generate-wxt-dir.ts | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/wxt-demo/src/entrypoints/background.ts b/packages/wxt-demo/src/entrypoints/background.ts index a8996acc..09626101 100644 --- a/packages/wxt-demo/src/entrypoints/background.ts +++ b/packages/wxt-demo/src/entrypoints/background.ts @@ -14,12 +14,13 @@ export default defineBackground({ console.log(useAppConfig()); - // @ts-expect-error: should only accept entrypoints or public assets browser.runtime.getURL('/'); browser.runtime.getURL('/background.js'); browser.runtime.getURL('/icons/128.png'); browser.runtime.getURL('/example.html#hash'); browser.runtime.getURL('/example.html?query=param'); + // @ts-expect-error: should only accept entrypoints or public assets + browser.runtime.getURL('/unknown'); // @ts-expect-error: should only allow hashes/query params on HTML files browser.runtime.getURL('/icon-128.png?query=param'); diff --git a/packages/wxt/e2e/tests/typescript-project.test.ts b/packages/wxt/e2e/tests/typescript-project.test.ts index 34c372af..167e01f8 100644 --- a/packages/wxt/e2e/tests/typescript-project.test.ts +++ b/packages/wxt/e2e/tests/typescript-project.test.ts @@ -48,6 +48,7 @@ describe('TypeScript Project', () => { declare module "wxt/browser" { export type PublicPath = + | "/" | "/options.html" | "/popup.html" | "/sandbox.html" diff --git a/packages/wxt/src/core/generate-wxt-dir.ts b/packages/wxt/src/core/generate-wxt-dir.ts index cf9980c7..a96c6353 100644 --- a/packages/wxt/src/core/generate-wxt-dir.ts +++ b/packages/wxt/src/core/generate-wxt-dir.ts @@ -78,6 +78,7 @@ async function getPathsDeclarationEntry( isHtmlEntrypoint(entry) ? '.html' : '.js', ), ) + .concat(['']) .concat(await getPublicFiles()); await wxt.hooks.callHook('prepare:publicPaths', wxt, paths); From 62ea796045df290ab9558597b9d1b981fe802f4a Mon Sep 17 00:00:00 2001 From: dsrrlldr Date: Thu, 14 Nov 2024 15:01:11 +0100 Subject: [PATCH 033/130] docs: Add "Invoice Generator" and "Monthly Bill Tracker" to homepage (#1174) --- docs/.vitepress/components/UsingWxtSection.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/.vitepress/components/UsingWxtSection.vue b/docs/.vitepress/components/UsingWxtSection.vue index d0ac7d78..1f08b491 100644 --- a/docs/.vitepress/components/UsingWxtSection.vue +++ b/docs/.vitepress/components/UsingWxtSection.vue @@ -54,6 +54,8 @@ const chromeExtensionIds = [ 'gppllamhaciichleihemgilcpledblpn', // YouTube Simple View - Hide distractions & more 'pccbghdfdnnkkbcdcibchpbffdgednkf', // Propbar - Property Data Enhancer 'lfknakglefggmdkjdfhhofkjnnolffkh', // Text Search Pro - Search by case and whole-word match! + 'mbenhbocjckkbaojacmaepiameldglij', // Invoice Generator + 'phlfhkmdofajnbhgmbmjkbkdgppgoppb', // Monthly Bill Tracker ]; const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds); From d74f6b0735877619735c4518a5861e6dfa913320 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 14 Nov 2024 10:00:08 -0600 Subject: [PATCH 034/130] feat: Extract `wxt/storage` to its own package, `@wxt-dev/storage` (#1129) --- .github/workflows/release.yml | 1 + .github/workflows/sync-releases.yml | 11 +- docs/.vitepress/config.ts | 3 +- docs/storage.md | 54 +- packages/storage/README.md | 36 + packages/storage/package.json | 56 ++ .../src/__tests__/index.test.ts} | 9 +- packages/storage/src/index.ts | 871 ++++++++++++++++++ packages/storage/tsconfig.json | 8 + packages/storage/vitest.config.ts | 9 + packages/storage/vitest.setup.ts | 4 + packages/wxt/package.json | 2 +- packages/wxt/src/storage.ts | 869 +---------------- packages/wxt/typedoc.json | 4 +- pnpm-lock.yaml | 128 ++- 15 files changed, 1166 insertions(+), 899 deletions(-) create mode 100644 packages/storage/README.md create mode 100644 packages/storage/package.json rename packages/{wxt/src/__tests__/storage.test.ts => storage/src/__tests__/index.test.ts} (99%) create mode 100644 packages/storage/src/index.ts create mode 100644 packages/storage/tsconfig.json create mode 100644 packages/storage/vitest.config.ts create mode 100644 packages/storage/vitest.setup.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4ab2d2c5..76e21651 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,7 @@ on: - module-solid - module-svelte - module-vue + - storage - unocss - wxt diff --git a/.github/workflows/sync-releases.yml b/.github/workflows/sync-releases.yml index 4287f089..d8f8ed83 100644 --- a/.github/workflows/sync-releases.yml +++ b/.github/workflows/sync-releases.yml @@ -7,13 +7,14 @@ on: default: wxt type: choice options: - - wxt - - module-react - - module-vue - - module-svelte - - module-solid - auto-icons - i18n + - module-react + - module-solid + - module-svelte + - module-vue + - storage + - wxt jobs: sync: diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 1a575731..0eb1aef0 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -13,6 +13,7 @@ import { version as wxtVersion } from '../../packages/wxt/package.json'; import { version as i18nVersion } from '../../packages/i18n/package.json'; import { version as autoIconsVersion } from '../../packages/auto-icons/package.json'; import { version as unocssVersion } from '../../packages/unocss/package.json'; +import { version as storageVersion } from '../../packages/storage/package.json'; const title = 'Next-gen Web Extension Framework'; const titleSuffix = ' – WXT'; @@ -87,7 +88,7 @@ export default defineConfig({ ), ]), navItem('Other Packages', [ - navItem(`wxt/storage — ${wxtVersion}`, '/storage'), + navItem(`@wxt-dev/storage — ${storageVersion}`, '/storage'), navItem(`@wxt-dev/auto-icons — ${autoIconsVersion}`, '/auto-icons'), navItem(`@wxt-dev/i18n — ${i18nVersion}`, '/i18n'), navItem(`@wxt-dev/unocss — ${unocssVersion}`, '/unocss'), diff --git a/docs/storage.md b/docs/storage.md index cb215b1c..8e03045b 100644 --- a/docs/storage.md +++ b/docs/storage.md @@ -6,23 +6,47 @@ outline: deep [Changelog](https://github.com/wxt-dev/wxt/blob/main/packages/wxt/CHANGELOG.md) -WXT provides a simplified API to replace the `browser.storage.*` APIs. Use the `storage` auto-import from `wxt/storage` or import it manually to get started: +A simplified wrapper around the extension storage APIs. + +## Installation + +### With WXT + +This module is built-in to WXT, so you don't need to install anything. ```ts import { storage } from 'wxt/storage'; ``` -> [!IMPORTANT] -> To use the `wxt/storage` API, the `"storage"` permission must be added to the manifest: -> -> ```ts -> // wxt.config.ts -> export default defineConfig({ -> manifest: { -> permissions: ['storage'], -> }, -> }); -> ``` +If you use auto-imports, `storage` is auto-imported for you, so you don't even need to import it! + +### Without WXT + +Install the NPM package: + +```sh +npm i @wxt-dev/storage +pnpm add @wxt-dev/storage +yarn add @wxt-dev/storage +bun add @wxt-dev/storage +``` + +```ts +import { storage } from '@wxt-dev/storage'; +``` + +## Storage Permission + +To use the `wxt/storage` API, the `"storage"` permission must be added to the manifest: + +```ts +// wxt.config.ts +export default defineConfig({ + manifest: { + permissions: ['storage'], + }, +}); +``` ## Basic Usage @@ -51,7 +75,7 @@ await storage.watch( await storage.getMeta<{ v: number }>('local:installDate'); ``` -For a full list of methods available, see the [API reference](/api/reference/wxt/storage/interfaces/WxtStorage). +For a full list of methods available, see the [API reference](/api/reference/@wxt-dev/storage/interfaces/WxtStorage). ## Watchers @@ -134,7 +158,7 @@ const unwatch = showChangelogOnUpdate.watch((newValue) => { }); ``` -For a full list of properties and methods available, see the [API reference](/api/reference/wxt/storage/interfaces/WxtStorageItem). +For a full list of properties and methods available, see the [API reference](/api/reference/@wxt-dev/storage/interfaces/WxtStorageItem). ### Versioning @@ -330,4 +354,4 @@ await storage.setItems([ ]); ``` -Refer to the [API Reference](/api/reference/wxt/storage/interfaces/WxtStorage) for types and examples of how to use all the bulk APIs. +Refer to the [API Reference](/api/reference/@wxt-dev/storage/interfaces/WxtStorage) for types and examples of how to use all the bulk APIs. diff --git a/packages/storage/README.md b/packages/storage/README.md new file mode 100644 index 00000000..2770193c --- /dev/null +++ b/packages/storage/README.md @@ -0,0 +1,36 @@ +# WXT Storage + +[Changelog](https://github.com/wxt-dev/wxt/blob/main/packages/storage/CHANGELOG.md) • [Docs](https://wxt.dev/storage.html) + +A simplified wrapper around the extension storage APIs. + +## Installation + +### With WXT + +This module is built-in to WXT, so you don't need to install anything. + +```ts +import { storage } from 'wxt/storage'; +``` + +If you use auto-imports, `storage` is auto-imported for you, so you don't even need to import it! + +### Without WXT + +Install the NPM package: + +```sh +npm i @wxt-dev/storage +pnpm add @wxt-dev/storage +yarn add @wxt-dev/storage +bun add @wxt-dev/storage +``` + +```ts +import { storage } from '@wxt-dev/storage'; +``` + +## Usage + +Read full docs on the [documentation website](https://wxt.dev/storage.html). diff --git a/packages/storage/package.json b/packages/storage/package.json new file mode 100644 index 00000000..4412f662 --- /dev/null +++ b/packages/storage/package.json @@ -0,0 +1,56 @@ +{ + "name": "@wxt-dev/storage", + "description": "Web extension storage API provided by WXT, supports all browsers.", + "version": "1.0.0", + "type": "module", + "repository": { + "type": "git", + "url": "git+https://github.com/wxt-dev/wxt.git", + "directory": "packages/storage" + }, + "homepage": "https://wxt.dev/storage.html", + "keywords": [ + "wxt", + "storage", + "extension", + "addon", + "chrome", + "firefox", + "edge" + ], + "author": { + "name": "Aaron Klinker", + "email": "aaronklinker1+wxt@gmail.com" + }, + "license": "MIT", + "scripts": { + "build": "buildc -- unbuild", + "check": "buildc --deps-only -- check", + "test": "buildc --deps-only -- vitest" + }, + "dependencies": { + "async-mutex": "^0.5.0", + "dequal": "^2.0.3" + }, + "devDependencies": { + "@aklinker1/check": "^1.4.5", + "@types/chrome": "^0.0.268", + "@webext-core/fake-browser": "^1.3.1", + "oxlint": "^0.9.9", + "publint": "^0.2.11", + "typescript": "^5.6.2", + "unbuild": "^2.0.0", + "vitest": "^2.0.0" + }, + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.mts", + "import": "./dist/index.mjs" + } + }, + "files": [ + "dist" + ] +} diff --git a/packages/wxt/src/__tests__/storage.test.ts b/packages/storage/src/__tests__/index.test.ts similarity index 99% rename from packages/wxt/src/__tests__/storage.test.ts rename to packages/storage/src/__tests__/index.test.ts index b0fb0ed3..6a5f5b4f 100644 --- a/packages/wxt/src/__tests__/storage.test.ts +++ b/packages/storage/src/__tests__/index.test.ts @@ -1,7 +1,6 @@ import { fakeBrowser } from '@webext-core/fake-browser'; import { describe, it, expect, beforeEach, vi, expectTypeOf } from 'vitest'; -import { browser } from 'wxt/browser'; -import { MigrationError, WxtStorageItem, storage } from '../storage'; +import { MigrationError, type WxtStorageItem, storage } from '../index'; /** * This works because fakeBrowser is synchronous, and is will finish any number of chained @@ -223,7 +222,7 @@ describe('Storage Utils', () => { describe('setMeta', () => { it('should set metadata at key+$', async () => { const existing = { v: 1 }; - await browser.storage[storageArea].set({ count$: existing }); + await chrome.storage[storageArea].set({ count$: existing }); const newValues = { date: Date.now(), }; @@ -239,7 +238,7 @@ describe('Storage Utils', () => { 'should remove any properties set to %s', async (version) => { const existing = { v: 1 }; - await browser.storage[storageArea].set({ count$: existing }); + await chrome.storage[storageArea].set({ count$: existing }); const expected = {}; await storage.setMeta(`${storageArea}:count`, { v: version }); @@ -1163,7 +1162,7 @@ describe('Storage Utils', () => { await item.removeValue(); // Make sure it's actually blank before running the test - expect(await browser.storage.local.get()).toEqual({}); + expect(await chrome.storage.local.get()).toEqual({}); init.mockClear(); const [value1, value2] = await Promise.all([ diff --git a/packages/storage/src/index.ts b/packages/storage/src/index.ts new file mode 100644 index 00000000..e0cc3943 --- /dev/null +++ b/packages/storage/src/index.ts @@ -0,0 +1,871 @@ +/// +/** + * Simplified storage APIs with support for versioned fields, snapshots, metadata, and item definitions. + * + * See [the guide](https://wxt.dev/storage.html) for more information. + * @module @wxt-dev/storage + */ +import { dequal } from 'dequal/lite'; +import { Mutex } from 'async-mutex'; + +export const storage = createStorage(); + +function createStorage(): WxtStorage { + const drivers: Record = { + local: createDriver('local'), + session: createDriver('session'), + sync: createDriver('sync'), + managed: createDriver('managed'), + }; + const getDriver = (area: StorageArea) => { + const driver = drivers[area]; + if (driver == null) { + const areaNames = Object.keys(drivers).join(', '); + throw Error(`Invalid area "${area}". Options: ${areaNames}`); + } + return driver; + }; + const resolveKey = (key: StorageItemKey) => { + const deliminatorIndex = key.indexOf(':'); + const driverArea = key.substring(0, deliminatorIndex) as StorageArea; + const driverKey = key.substring(deliminatorIndex + 1); + if (driverKey == null) + throw Error( + `Storage key should be in the form of "area:key", but received "${key}"`, + ); + + return { + driverArea, + driverKey, + driver: getDriver(driverArea), + }; + }; + const getMetaKey = (key: string) => key + '$'; + const mergeMeta = (oldMeta: any, newMeta: any): any => { + const newFields = { ...oldMeta }; + Object.entries(newMeta).forEach(([key, value]) => { + if (value == null) delete newFields[key]; + else newFields[key] = value; + }); + return newFields; + }; + const getValueOrFallback = (value: any, fallback: any) => + value ?? fallback ?? null; + const getMetaValue = (properties: any) => + typeof properties === 'object' && !Array.isArray(properties) + ? properties + : {}; + + const getItem = async ( + driver: WxtStorageDriver, + driverKey: string, + opts: GetItemOptions | undefined, + ) => { + const res = await driver.getItem(driverKey); + return getValueOrFallback(res, opts?.fallback ?? opts?.defaultValue); + }; + const getMeta = async (driver: WxtStorageDriver, driverKey: string) => { + const metaKey = getMetaKey(driverKey); + const res = await driver.getItem(metaKey); + return getMetaValue(res); + }; + const setItem = async ( + driver: WxtStorageDriver, + driverKey: string, + value: any, + ) => { + await driver.setItem(driverKey, value ?? null); + }; + const setMeta = async ( + driver: WxtStorageDriver, + driverKey: string, + properties: any | undefined, + ) => { + const metaKey = getMetaKey(driverKey); + const existingFields = getMetaValue(await driver.getItem(metaKey)); + await driver.setItem(metaKey, mergeMeta(existingFields, properties)); + }; + const removeItem = async ( + driver: WxtStorageDriver, + driverKey: string, + opts: RemoveItemOptions | undefined, + ) => { + await driver.removeItem(driverKey); + if (opts?.removeMeta) { + const metaKey = getMetaKey(driverKey); + await driver.removeItem(metaKey); + } + }; + const removeMeta = async ( + driver: WxtStorageDriver, + driverKey: string, + properties: string | string[] | undefined, + ) => { + const metaKey = getMetaKey(driverKey); + if (properties == null) { + await driver.removeItem(metaKey); + } else { + const newFields = getMetaValue(await driver.getItem(metaKey)); + [properties].flat().forEach((field) => delete newFields[field]); + await driver.setItem(metaKey, newFields); + } + }; + const watch = ( + driver: WxtStorageDriver, + driverKey: string, + cb: WatchCallback, + ) => { + return driver.watch(driverKey, cb); + }; + + const storage: WxtStorage = { + getItem: async (key, opts) => { + const { driver, driverKey } = resolveKey(key); + return await getItem(driver, driverKey, opts); + }, + getItems: async (keys) => { + const areaToKeyMap = new Map(); + const keyToOptsMap = new Map | undefined>(); + const orderedKeys: StorageItemKey[] = []; + + keys.forEach((key) => { + let keyStr: StorageItemKey; + let opts: GetItemOptions | undefined; + if (typeof key === 'string') { + // key: string + keyStr = key; + } else if ('getValue' in key) { + // key: WxtStorageItem + keyStr = key.key; + opts = { fallback: key.fallback }; + } else { + // key: { key, options } + keyStr = key.key; + opts = key.options; + } + orderedKeys.push(keyStr); + const { driverArea, driverKey } = resolveKey(keyStr); + const areaKeys = areaToKeyMap.get(driverArea) ?? []; + areaToKeyMap.set(driverArea, areaKeys.concat(driverKey)); + keyToOptsMap.set(keyStr, opts); + }); + + const resultsMap = new Map(); + await Promise.all( + Array.from(areaToKeyMap.entries()).map(async ([driverArea, keys]) => { + const driverResults = await drivers[driverArea].getItems(keys); + driverResults.forEach((driverResult) => { + const key = `${driverArea}:${driverResult.key}` as StorageItemKey; + const opts = keyToOptsMap.get(key); + const value = getValueOrFallback( + driverResult.value, + opts?.fallback ?? opts?.defaultValue, + ); + resultsMap.set(key, value); + }); + }), + ); + + return orderedKeys.map((key) => ({ + key, + value: resultsMap.get(key), + })); + }, + getMeta: async (key) => { + const { driver, driverKey } = resolveKey(key); + return await getMeta(driver, driverKey); + }, + getMetas: async (args) => { + const keys = args.map((arg) => { + const key = typeof arg === 'string' ? arg : arg.key; + const { driverArea, driverKey } = resolveKey(key); + return { + key, + driverArea, + driverKey, + driverMetaKey: getMetaKey(driverKey), + }; + }); + const areaToDriverMetaKeysMap = keys.reduce< + Partial> + >((map, key) => { + map[key.driverArea] ??= []; + map[key.driverArea]!.push(key); + return map; + }, {}); + + const resultsMap: Record = {}; + await Promise.all( + Object.entries(areaToDriverMetaKeysMap).map(async ([area, keys]) => { + const areaRes = await chrome.storage[area as StorageArea].get( + keys.map((key) => key.driverMetaKey), + ); + keys.forEach((key) => { + resultsMap[key.key] = areaRes[key.driverMetaKey] ?? {}; + }); + }), + ); + + return keys.map((key) => ({ + key: key.key, + meta: resultsMap[key.key], + })); + }, + setItem: async (key, value) => { + const { driver, driverKey } = resolveKey(key); + await setItem(driver, driverKey, value); + }, + setItems: async (items) => { + const areaToKeyValueMap: Partial< + Record> + > = {}; + items.forEach((item) => { + const { driverArea, driverKey } = resolveKey( + 'key' in item ? item.key : item.item.key, + ); + areaToKeyValueMap[driverArea] ??= []; + areaToKeyValueMap[driverArea].push({ + key: driverKey, + value: item.value, + }); + }); + await Promise.all( + Object.entries(areaToKeyValueMap).map(async ([driverArea, values]) => { + const driver = getDriver(driverArea as StorageArea); + await driver.setItems(values); + }), + ); + }, + setMeta: async (key, properties) => { + const { driver, driverKey } = resolveKey(key); + await setMeta(driver, driverKey, properties); + }, + setMetas: async (items) => { + const areaToMetaUpdatesMap: Partial< + Record + > = {}; + items.forEach((item) => { + const { driverArea, driverKey } = resolveKey( + 'key' in item ? item.key : item.item.key, + ); + areaToMetaUpdatesMap[driverArea] ??= []; + areaToMetaUpdatesMap[driverArea].push({ + key: driverKey, + properties: item.meta, + }); + }); + + await Promise.all( + Object.entries(areaToMetaUpdatesMap).map( + async ([storageArea, updates]) => { + const driver = getDriver(storageArea as StorageArea); + const metaKeys = updates.map(({ key }) => getMetaKey(key)); + console.log(storageArea, metaKeys); + const existingMetas = await driver.getItems(metaKeys); + const existingMetaMap = Object.fromEntries( + existingMetas.map(({ key, value }) => [key, getMetaValue(value)]), + ); + + const metaUpdates = updates.map(({ key, properties }) => { + const metaKey = getMetaKey(key); + return { + key: metaKey, + value: mergeMeta(existingMetaMap[metaKey] ?? {}, properties), + }; + }); + + await driver.setItems(metaUpdates); + }, + ), + ); + }, + removeItem: async (key, opts) => { + const { driver, driverKey } = resolveKey(key); + await removeItem(driver, driverKey, opts); + }, + removeItems: async (keys) => { + const areaToKeysMap: Partial> = {}; + + keys.forEach((key) => { + let keyStr: StorageItemKey; + let opts: RemoveItemOptions | undefined; + if (typeof key === 'string') { + // key: string + keyStr = key; + } else if ('getValue' in key) { + // key: WxtStorageItem + keyStr = key.key; + } else if ('item' in key) { + // key: { item, options } + keyStr = key.item.key; + opts = key.options; + } else { + // key: { key, options } + keyStr = key.key; + opts = key.options; + } + const { driverArea, driverKey } = resolveKey(keyStr); + areaToKeysMap[driverArea] ??= []; + areaToKeysMap[driverArea].push(driverKey); + if (opts?.removeMeta) { + areaToKeysMap[driverArea].push(getMetaKey(driverKey)); + } + }); + + await Promise.all( + Object.entries(areaToKeysMap).map(async ([driverArea, keys]) => { + const driver = getDriver(driverArea as StorageArea); + await driver.removeItems(keys); + }), + ); + }, + removeMeta: async (key, properties) => { + const { driver, driverKey } = resolveKey(key); + await removeMeta(driver, driverKey, properties); + }, + snapshot: async (base, opts) => { + const driver = getDriver(base); + const data = await driver.snapshot(); + opts?.excludeKeys?.forEach((key) => { + delete data[key]; + delete data[getMetaKey(key)]; + }); + return data; + }, + restoreSnapshot: async (base, data) => { + const driver = getDriver(base); + await driver.restoreSnapshot(data); + }, + watch: (key, cb) => { + const { driver, driverKey } = resolveKey(key); + return watch(driver, driverKey, cb); + }, + unwatch() { + Object.values(drivers).forEach((driver) => { + driver.unwatch(); + }); + }, + defineItem: (key, opts?: WxtStorageItemOptions) => { + const { driver, driverKey } = resolveKey(key); + + const { version: targetVersion = 1, migrations = {} } = opts ?? {}; + if (targetVersion < 1) { + throw Error( + 'Storage item version cannot be less than 1. Initial versions should be set to 1, not 0.', + ); + } + const migrate = async () => { + const driverMetaKey = getMetaKey(driverKey); + const [{ value }, { value: meta }] = await driver.getItems([ + driverKey, + driverMetaKey, + ]); + if (value == null) return; + + const currentVersion = meta?.v ?? 1; + if (currentVersion > targetVersion) { + throw Error( + `Version downgrade detected (v${currentVersion} -> v${targetVersion}) for "${key}"`, + ); + } + + console.debug( + `[@wxt-dev/storage] Running storage migration for ${key}: v${currentVersion} -> v${targetVersion}`, + ); + const migrationsToRun = Array.from( + { length: targetVersion - currentVersion }, + (_, i) => currentVersion + i + 1, + ); + let migratedValue = value; + for (const migrateToVersion of migrationsToRun) { + try { + migratedValue = + (await migrations?.[migrateToVersion]?.(migratedValue)) ?? + migratedValue; + } catch (err) { + throw Error(`v${migrateToVersion} migration failed for "${key}"`, { + cause: err, + }); + } + } + await driver.setItems([ + { key: driverKey, value: migratedValue }, + { key: driverMetaKey, value: { ...meta, v: targetVersion } }, + ]); + console.debug( + `[@wxt-dev/storage] Storage migration completed for ${key} v${targetVersion}`, + { migratedValue }, + ); + }; + const migrationsDone = + opts?.migrations == null + ? Promise.resolve() + : migrate().catch((err) => { + console.error( + `[@wxt-dev/storage] Migration failed for ${key}`, + err, + ); + }); + + const initMutex = new Mutex(); + + const getFallback = () => opts?.fallback ?? opts?.defaultValue ?? null; + + const getOrInitValue = () => + initMutex.runExclusive(async () => { + const value = await driver.getItem(driverKey); + // Don't init value if it already exists or the init function isn't provided + if (value != null || opts?.init == null) return value; + + const newValue = await opts.init(); + await driver.setItem(driverKey, newValue); + return newValue; + }); + + // Initialize the value once migrations have finished + migrationsDone.then(getOrInitValue); + + return { + key, + get defaultValue() { + return getFallback(); + }, + get fallback() { + return getFallback(); + }, + getValue: async () => { + await migrationsDone; + if (opts?.init) { + return await getOrInitValue(); + } else { + return await getItem(driver, driverKey, opts); + } + }, + getMeta: async () => { + await migrationsDone; + return await getMeta(driver, driverKey); + }, + setValue: async (value) => { + await migrationsDone; + return await setItem(driver, driverKey, value); + }, + setMeta: async (properties) => { + await migrationsDone; + return await setMeta(driver, driverKey, properties); + }, + removeValue: async (opts) => { + await migrationsDone; + return await removeItem(driver, driverKey, opts); + }, + removeMeta: async (properties) => { + await migrationsDone; + return await removeMeta(driver, driverKey, properties); + }, + watch: (cb) => + watch(driver, driverKey, (newValue, oldValue) => + cb(newValue ?? getFallback(), oldValue ?? getFallback()), + ), + migrate, + }; + }, + }; + return storage; +} + +function createDriver(storageArea: StorageArea): WxtStorageDriver { + const getStorageArea = () => { + if (chrome.runtime == null) { + throw Error( + [ + "'wxt/storage' must be loaded in a web extension environment", + '\n - If thrown during a build, see https://github.com/wxt-dev/wxt/issues/371', + " - If thrown during tests, mock 'wxt/browser' correctly. See https://wxt.dev/guide/go-further/testing.html\n", + ].join('\n'), + ); + } + if (chrome.storage == null) { + throw Error( + "You must add the 'storage' permission to your manifest to use 'wxt/storage'", + ); + } + + const area = chrome.storage[storageArea]; + if (area == null) + throw Error(`"chrome.storage.${storageArea}" is undefined`); + return area; + }; + const watchListeners = new Set<(changes: StorageAreaChanges) => void>(); + return { + getItem: async (key) => { + const res = await getStorageArea().get(key); + return res[key]; + }, + getItems: async (keys) => { + const result = await getStorageArea().get(keys); + return keys.map((key) => ({ key, value: result[key] ?? null })); + }, + setItem: async (key, value) => { + if (value == null) { + await getStorageArea().remove(key); + } else { + await getStorageArea().set({ [key]: value }); + } + }, + setItems: async (values) => { + const map = values.reduce>( + (map, { key, value }) => { + map[key] = value; + return map; + }, + {}, + ); + await getStorageArea().set(map); + }, + removeItem: async (key) => { + await getStorageArea().remove(key); + }, + removeItems: async (keys) => { + await getStorageArea().remove(keys); + }, + snapshot: async () => { + return await getStorageArea().get(); + }, + restoreSnapshot: async (data) => { + await getStorageArea().set(data); + }, + watch(key, cb) { + const listener = (changes: StorageAreaChanges) => { + const change = changes[key]; + if (change == null) return; + if (dequal(change.newValue, change.oldValue)) return; + cb(change.newValue ?? null, change.oldValue ?? null); + }; + getStorageArea().onChanged.addListener(listener); + watchListeners.add(listener); + return () => { + getStorageArea().onChanged.removeListener(listener); + watchListeners.delete(listener); + }; + }, + unwatch() { + watchListeners.forEach((listener) => { + getStorageArea().onChanged.removeListener(listener); + }); + watchListeners.clear(); + }, + }; +} + +export interface WxtStorage { + /** + * Get an item from storage, or return `null` if it doesn't exist. + * + * @example + * await storage.getItem("local:installDate"); + */ + getItem( + key: StorageItemKey, + opts: GetItemOptions & { fallback: TValue }, + ): Promise; + + getItem( + key: StorageItemKey, + opts?: GetItemOptions, + ): Promise; + + /** + * Get multiple items from storage. The return order is guaranteed to be the same as the order + * requested. + * + * @example + * await storage.getItems(["local:installDate", "session:someCounter"]); + */ + getItems( + keys: Array< + | StorageItemKey + | WxtStorageItem + | { key: StorageItemKey; options?: GetItemOptions } + >, + ): Promise>; + /** + * Return an object containing metadata about the key. Object is stored at `key + "$"`. If value + * is not an object, it returns an empty object. + * + * @example + * await storage.getMeta("local:installDate"); + */ + getMeta>(key: StorageItemKey): Promise; + /** + * Get the metadata of multiple storage items. + * + * @param items List of keys or items to get the metadata of. + * @returns An array containing storage keys and their metadata. + */ + getMetas( + keys: Array>, + ): Promise>; + /** + * Set a value in storage. Setting a value to `null` or `undefined` is equivalent to calling + * `removeItem`. + * + * @example + * await storage.setItem("local:installDate", Date.now()); + */ + setItem(key: StorageItemKey, value: T | null): Promise; + /** + * Set multiple values in storage. If a value is set to `null` or `undefined`, the key is removed. + * + * @example + * await storage.setItem([ + * { key: "local:installDate", value: Date.now() }, + * { key: "session:someCounter, value: 5 }, + * ]); + */ + setItems( + values: Array< + | { key: StorageItemKey; value: any } + | { item: WxtStorageItem; value: any } + >, + ): Promise; + /** + * Sets metadata properties. If some properties are already set, but are not included in the + * `properties` parameter, they will not be removed. + * + * @example + * await storage.setMeta("local:installDate", { appVersion }); + */ + setMeta>( + key: StorageItemKey, + properties: T | null, + ): Promise; + /** + * Set the metadata of multiple storage items. + * + * @param items List of storage keys or items and metadata to set for each. + */ + setMetas( + metas: Array< + | { key: StorageItemKey; meta: Record } + | { item: WxtStorageItem; meta: Record } + >, + ): Promise; + /** + * Removes an item from storage. + * + * @example + * await storage.removeItem("local:installDate"); + */ + removeItem(key: StorageItemKey, opts?: RemoveItemOptions): Promise; + /** + * Remove a list of keys from storage. + */ + removeItems( + keys: Array< + | StorageItemKey + | WxtStorageItem + | { key: StorageItemKey; options?: RemoveItemOptions } + | { item: WxtStorageItem; options?: RemoveItemOptions } + >, + ): Promise; + /** + * Remove the entire metadata for a key, or specific properties by name. + * + * @example + * // Remove all metadata properties from the item + * await storage.removeMeta("local:installDate"); + * + * // Remove only specific the "v" field + * await storage.removeMeta("local:installDate", "v") + */ + removeMeta( + key: StorageItemKey, + properties?: string | string[], + ): Promise; + /** + * Return all the items in storage. + */ + snapshot( + base: StorageArea, + opts?: SnapshotOptions, + ): Promise>; + /** + * Restores the results of `snapshot`. If new properties have been saved since the snapshot, they are + * not overridden. Only values existing in the snapshot are overridden. + */ + restoreSnapshot(base: StorageArea, data: any): Promise; + /** + * Watch for changes to a specific key in storage. + */ + watch(key: StorageItemKey, cb: WatchCallback): Unwatch; + /** + * Remove all watch listeners. + */ + unwatch(): void; + + /** + * Define a storage item with a default value, type, or versioning. + * + * Read full docs: https://wxt.dev/guide/extension-apis/storage.html#defining-storage-items + */ + defineItem = {}>( + key: StorageItemKey, + ): WxtStorageItem; + defineItem = {}>( + key: StorageItemKey, + options: WxtStorageItemOptions, + ): WxtStorageItem; +} + +interface WxtStorageDriver { + getItem(key: string): Promise; + getItems(keys: string[]): Promise<{ key: string; value: any }[]>; + setItem(key: string, value: T | null): Promise; + setItems(values: Array<{ key: string; value: any }>): Promise; + removeItem(key: string): Promise; + removeItems(keys: string[]): Promise; + snapshot(): Promise>; + restoreSnapshot(data: Record): Promise; + watch(key: string, cb: WatchCallback): Unwatch; + unwatch(): void; +} + +export interface WxtStorageItem< + TValue, + TMetadata extends Record, +> { + /** + * The storage key passed when creating the storage item. + */ + key: StorageItemKey; + /** + * @deprecated Renamed to fallback, use it instead. + */ + defaultValue: TValue; + /** + * The value provided by the `fallback` option. + */ + fallback: TValue; + /** + * Get the latest value from storage. + */ + getValue(): Promise; + /** + * Get metadata. + */ + getMeta(): Promise>; + /** + * Set the value in storage. + */ + setValue(value: TValue): Promise; + /** + * Set metadata properties. + */ + setMeta(properties: NullablePartial): Promise; + /** + * Remove the value from storage. + */ + removeValue(opts?: RemoveItemOptions): Promise; + /** + * Remove all metadata or certain properties from metadata. + */ + removeMeta(properties?: string[]): Promise; + /** + * Listen for changes to the value in storage. + */ + watch(cb: WatchCallback): Unwatch; + /** + * If there are migrations defined on the storage item, migrate to the latest version. + * + * **This function is ran automatically whenever the extension updates**, so you don't have to call it + * manually. + */ + migrate(): Promise; +} + +export type StorageArea = 'local' | 'session' | 'sync' | 'managed'; +export type StorageItemKey = `${StorageArea}:${string}`; + +export interface GetItemOptions { + /** + * @deprecated Renamed to `fallback`, use it instead. + */ + defaultValue?: T; + /** + * Default value returned when `getItem` would otherwise return `null`. + */ + fallback?: T; +} + +export interface RemoveItemOptions { + /** + * Optionally remove metadata when deleting a key. + * + * @default false + */ + removeMeta?: boolean; +} + +export interface SnapshotOptions { + /** + * Exclude a list of keys. The storage area prefix should be removed since the snapshot is for a + * specific storage area already. + */ + excludeKeys?: string[]; +} + +export interface WxtStorageItemOptions { + /** + * @deprecated Renamed to `fallback`, use it instead. + */ + defaultValue?: T; + /** + * Default value returned when `getValue` would otherwise return `null`. + */ + fallback?: T; + /** + * If passed, a value in storage will be initialized immediately after + * defining the storage item. This function returns the value that will be + * saved to storage during the initialization process if a value doesn't + * already exist. + */ + init?: () => T | Promise; + /** + * Provide a version number for the storage item to enable migrations. When changing the version + * in the future, migration functions will be ran on application startup. + */ + version?: number; + /** + * A map of version numbers to the functions used to migrate the data to that version. + */ + migrations?: Record any>; +} + +export type StorageAreaChanges = { + [key: string]: chrome.storage.StorageChange; +}; + +/** + * Same as `Partial`, but includes `| null`. It makes all the properties of an object optional and + * nullable. + */ +type NullablePartial = { + [key in keyof T]+?: T[key] | undefined | null; +}; +/** + * Callback called when a value in storage is changed. + */ +export type WatchCallback = (newValue: T, oldValue: T) => void; +/** + * Call to remove a watch listener + */ +export type Unwatch = () => void; + +export class MigrationError extends Error { + constructor( + public key: string, + public version: number, + options?: ErrorOptions, + ) { + super(`v${version} migration failed for "${key}"`, options); + } +} diff --git a/packages/storage/tsconfig.json b/packages/storage/tsconfig.json new file mode 100644 index 00000000..1d1ddb7f --- /dev/null +++ b/packages/storage/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "verbatimModuleSyntax": true, + "types": ["chrome"] + }, + "exclude": ["node_modules/**", "dist/**"] +} diff --git a/packages/storage/vitest.config.ts b/packages/storage/vitest.config.ts new file mode 100644 index 00000000..3980191b --- /dev/null +++ b/packages/storage/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineProject } from 'vitest/config'; + +export default defineProject({ + test: { + mockReset: true, + restoreMocks: true, + setupFiles: ['vitest.setup.ts'], + }, +}); diff --git a/packages/storage/vitest.setup.ts b/packages/storage/vitest.setup.ts new file mode 100644 index 00000000..c2c31b3f --- /dev/null +++ b/packages/storage/vitest.setup.ts @@ -0,0 +1,4 @@ +import { fakeBrowser } from '@webext-core/fake-browser'; +import { vi } from 'vitest'; + +vi.stubGlobal('chrome', fakeBrowser); diff --git a/packages/wxt/package.json b/packages/wxt/package.json index 4df0fce4..5e2cd4d3 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -87,6 +87,7 @@ "@webext-core/fake-browser": "^1.3.1", "@webext-core/isolated-element": "^1.1.2", "@webext-core/match-patterns": "^1.0.3", + "@wxt-dev/storage": "workspace:^1.0.0", "async-mutex": "^0.5.0", "c12": "^1.11.2", "cac": "^6.7.14", @@ -94,7 +95,6 @@ "ci-info": "^4.1.0", "consola": "^3.2.3", "defu": "^6.1.4", - "dequal": "^2.0.3", "dotenv": "^16.4.5", "esbuild": "^0.21.5", "fast-glob": "^3.3.2", diff --git a/packages/wxt/src/storage.ts b/packages/wxt/src/storage.ts index bad01977..cefb3a95 100644 --- a/packages/wxt/src/storage.ts +++ b/packages/wxt/src/storage.ts @@ -1,869 +1,4 @@ /** - * Simplified storage APIs with support for versioned fields, snapshots, metadata, and item definitions. - * - * See [the guide](https://wxt.dev/guide/extension-apis/storage.html) for more information. - * - * @module wxt/storage + * @module @wxt-dev/storage */ -import { Storage, browser } from 'wxt/browser'; -import { dequal } from 'dequal/lite'; -import { logger } from './sandbox/utils/logger'; -import { toArray } from './core/utils/arrays'; -import { Mutex } from 'async-mutex'; - -export const storage = createStorage(); - -function createStorage(): WxtStorage { - const drivers: Record = { - local: createDriver('local'), - session: createDriver('session'), - sync: createDriver('sync'), - managed: createDriver('managed'), - }; - const getDriver = (area: StorageArea) => { - const driver = drivers[area]; - if (driver == null) { - const areaNames = Object.keys(drivers).join(', '); - throw Error(`Invalid area "${area}". Options: ${areaNames}`); - } - return driver; - }; - const resolveKey = (key: StorageItemKey) => { - const deliminatorIndex = key.indexOf(':'); - const driverArea = key.substring(0, deliminatorIndex) as StorageArea; - const driverKey = key.substring(deliminatorIndex + 1); - if (driverKey == null) - throw Error( - `Storage key should be in the form of "area:key", but received "${key}"`, - ); - - return { - driverArea, - driverKey, - driver: getDriver(driverArea), - }; - }; - const getMetaKey = (key: string) => key + '$'; - const mergeMeta = (oldMeta: any, newMeta: any): any => { - const newFields = { ...oldMeta }; - Object.entries(newMeta).forEach(([key, value]) => { - if (value == null) delete newFields[key]; - else newFields[key] = value; - }); - return newFields; - }; - const getValueOrFallback = (value: any, fallback: any) => - value ?? fallback ?? null; - const getMetaValue = (properties: any) => - typeof properties === 'object' && !Array.isArray(properties) - ? properties - : {}; - - const getItem = async ( - driver: WxtStorageDriver, - driverKey: string, - opts: GetItemOptions | undefined, - ) => { - const res = await driver.getItem(driverKey); - return getValueOrFallback(res, opts?.fallback ?? opts?.defaultValue); - }; - const getMeta = async (driver: WxtStorageDriver, driverKey: string) => { - const metaKey = getMetaKey(driverKey); - const res = await driver.getItem(metaKey); - return getMetaValue(res); - }; - const setItem = async ( - driver: WxtStorageDriver, - driverKey: string, - value: any, - ) => { - await driver.setItem(driverKey, value ?? null); - }; - const setMeta = async ( - driver: WxtStorageDriver, - driverKey: string, - properties: any | undefined, - ) => { - const metaKey = getMetaKey(driverKey); - const existingFields = getMetaValue(await driver.getItem(metaKey)); - await driver.setItem(metaKey, mergeMeta(existingFields, properties)); - }; - const removeItem = async ( - driver: WxtStorageDriver, - driverKey: string, - opts: RemoveItemOptions | undefined, - ) => { - await driver.removeItem(driverKey); - if (opts?.removeMeta) { - const metaKey = getMetaKey(driverKey); - await driver.removeItem(metaKey); - } - }; - const removeMeta = async ( - driver: WxtStorageDriver, - driverKey: string, - properties: string | string[] | undefined, - ) => { - const metaKey = getMetaKey(driverKey); - if (properties == null) { - await driver.removeItem(metaKey); - } else { - const newFields = getMetaValue(await driver.getItem(metaKey)); - toArray(properties).forEach((field) => delete newFields[field]); - await driver.setItem(metaKey, newFields); - } - }; - const watch = ( - driver: WxtStorageDriver, - driverKey: string, - cb: WatchCallback, - ) => { - return driver.watch(driverKey, cb); - }; - - const storage: WxtStorage = { - getItem: async (key, opts) => { - const { driver, driverKey } = resolveKey(key); - return await getItem(driver, driverKey, opts); - }, - getItems: async (keys) => { - const areaToKeyMap = new Map(); - const keyToOptsMap = new Map | undefined>(); - const orderedKeys: StorageItemKey[] = []; - - keys.forEach((key) => { - let keyStr: StorageItemKey; - let opts: GetItemOptions | undefined; - if (typeof key === 'string') { - // key: string - keyStr = key; - } else if ('getValue' in key) { - // key: WxtStorageItem - keyStr = key.key; - opts = { fallback: key.fallback }; - } else { - // key: { key, options } - keyStr = key.key; - opts = key.options; - } - orderedKeys.push(keyStr); - const { driverArea, driverKey } = resolveKey(keyStr); - const areaKeys = areaToKeyMap.get(driverArea) ?? []; - areaToKeyMap.set(driverArea, areaKeys.concat(driverKey)); - keyToOptsMap.set(keyStr, opts); - }); - - const resultsMap = new Map(); - await Promise.all( - Array.from(areaToKeyMap.entries()).map(async ([driverArea, keys]) => { - const driverResults = await drivers[driverArea].getItems(keys); - driverResults.forEach((driverResult) => { - const key = `${driverArea}:${driverResult.key}` as StorageItemKey; - const opts = keyToOptsMap.get(key); - const value = getValueOrFallback( - driverResult.value, - opts?.fallback ?? opts?.defaultValue, - ); - resultsMap.set(key, value); - }); - }), - ); - - return orderedKeys.map((key) => ({ - key, - value: resultsMap.get(key), - })); - }, - getMeta: async (key) => { - const { driver, driverKey } = resolveKey(key); - return await getMeta(driver, driverKey); - }, - getMetas: async (args) => { - const keys = args.map((arg) => { - const key = typeof arg === 'string' ? arg : arg.key; - const { driverArea, driverKey } = resolveKey(key); - return { - key, - driverArea, - driverKey, - driverMetaKey: getMetaKey(driverKey), - }; - }); - const areaToDriverMetaKeysMap = keys.reduce< - Partial> - >((map, key) => { - map[key.driverArea] ??= []; - map[key.driverArea]!.push(key); - return map; - }, {}); - - const resultsMap: Record = {}; - await Promise.all( - Object.entries(areaToDriverMetaKeysMap).map(async ([area, keys]) => { - const areaRes = await browser.storage[area as StorageArea].get( - keys.map((key) => key.driverMetaKey), - ); - keys.forEach((key) => { - resultsMap[key.key] = areaRes[key.driverMetaKey] ?? {}; - }); - }), - ); - - return keys.map((key) => ({ - key: key.key, - meta: resultsMap[key.key], - })); - }, - setItem: async (key, value) => { - const { driver, driverKey } = resolveKey(key); - await setItem(driver, driverKey, value); - }, - setItems: async (items) => { - const areaToKeyValueMap: Partial< - Record> - > = {}; - items.forEach((item) => { - const { driverArea, driverKey } = resolveKey( - 'key' in item ? item.key : item.item.key, - ); - areaToKeyValueMap[driverArea] ??= []; - areaToKeyValueMap[driverArea].push({ - key: driverKey, - value: item.value, - }); - }); - await Promise.all( - Object.entries(areaToKeyValueMap).map(async ([driverArea, values]) => { - const driver = getDriver(driverArea as StorageArea); - await driver.setItems(values); - }), - ); - }, - setMeta: async (key, properties) => { - const { driver, driverKey } = resolveKey(key); - await setMeta(driver, driverKey, properties); - }, - setMetas: async (items) => { - const areaToMetaUpdatesMap: Partial< - Record - > = {}; - items.forEach((item) => { - const { driverArea, driverKey } = resolveKey( - 'key' in item ? item.key : item.item.key, - ); - areaToMetaUpdatesMap[driverArea] ??= []; - areaToMetaUpdatesMap[driverArea].push({ - key: driverKey, - properties: item.meta, - }); - }); - - await Promise.all( - Object.entries(areaToMetaUpdatesMap).map( - async ([storageArea, updates]) => { - const driver = getDriver(storageArea as StorageArea); - const metaKeys = updates.map(({ key }) => getMetaKey(key)); - console.log(storageArea, metaKeys); - const existingMetas = await driver.getItems(metaKeys); - const existingMetaMap = Object.fromEntries( - existingMetas.map(({ key, value }) => [key, getMetaValue(value)]), - ); - - const metaUpdates = updates.map(({ key, properties }) => { - const metaKey = getMetaKey(key); - return { - key: metaKey, - value: mergeMeta(existingMetaMap[metaKey] ?? {}, properties), - }; - }); - - await driver.setItems(metaUpdates); - }, - ), - ); - }, - removeItem: async (key, opts) => { - const { driver, driverKey } = resolveKey(key); - await removeItem(driver, driverKey, opts); - }, - removeItems: async (keys) => { - const areaToKeysMap: Partial> = {}; - - keys.forEach((key) => { - let keyStr: StorageItemKey; - let opts: RemoveItemOptions | undefined; - if (typeof key === 'string') { - // key: string - keyStr = key; - } else if ('getValue' in key) { - // key: WxtStorageItem - keyStr = key.key; - } else if ('item' in key) { - // key: { item, options } - keyStr = key.item.key; - opts = key.options; - } else { - // key: { key, options } - keyStr = key.key; - opts = key.options; - } - const { driverArea, driverKey } = resolveKey(keyStr); - areaToKeysMap[driverArea] ??= []; - areaToKeysMap[driverArea].push(driverKey); - if (opts?.removeMeta) { - areaToKeysMap[driverArea].push(getMetaKey(driverKey)); - } - }); - - await Promise.all( - Object.entries(areaToKeysMap).map(async ([driverArea, keys]) => { - const driver = getDriver(driverArea as StorageArea); - await driver.removeItems(keys); - }), - ); - }, - removeMeta: async (key, properties) => { - const { driver, driverKey } = resolveKey(key); - await removeMeta(driver, driverKey, properties); - }, - snapshot: async (base, opts) => { - const driver = getDriver(base); - const data = await driver.snapshot(); - opts?.excludeKeys?.forEach((key) => { - delete data[key]; - delete data[getMetaKey(key)]; - }); - return data; - }, - restoreSnapshot: async (base, data) => { - const driver = getDriver(base); - await driver.restoreSnapshot(data); - }, - watch: (key, cb) => { - const { driver, driverKey } = resolveKey(key); - return watch(driver, driverKey, cb); - }, - unwatch() { - Object.values(drivers).forEach((driver) => { - driver.unwatch(); - }); - }, - defineItem: (key, opts?: WxtStorageItemOptions) => { - const { driver, driverKey } = resolveKey(key); - - const { version: targetVersion = 1, migrations = {} } = opts ?? {}; - if (targetVersion < 1) { - throw Error( - 'Storage item version cannot be less than 1. Initial versions should be set to 1, not 0.', - ); - } - const migrate = async () => { - const driverMetaKey = getMetaKey(driverKey); - const [{ value }, { value: meta }] = await driver.getItems([ - driverKey, - driverMetaKey, - ]); - if (value == null) return; - - const currentVersion = meta?.v ?? 1; - if (currentVersion > targetVersion) { - throw Error( - `Version downgrade detected (v${currentVersion} -> v${targetVersion}) for "${key}"`, - ); - } - - logger.debug( - `Running storage migration for ${key}: v${currentVersion} -> v${targetVersion}`, - ); - const migrationsToRun = Array.from( - { length: targetVersion - currentVersion }, - (_, i) => currentVersion + i + 1, - ); - let migratedValue = value; - for (const migrateToVersion of migrationsToRun) { - try { - migratedValue = - (await migrations?.[migrateToVersion]?.(migratedValue)) ?? - migratedValue; - } catch (err) { - throw Error(`v${migrateToVersion} migration failed for "${key}"`, { - cause: err, - }); - } - } - await driver.setItems([ - { key: driverKey, value: migratedValue }, - { key: driverMetaKey, value: { ...meta, v: targetVersion } }, - ]); - logger.debug( - `Storage migration completed for ${key} v${targetVersion}`, - { migratedValue }, - ); - }; - const migrationsDone = - opts?.migrations == null - ? Promise.resolve() - : migrate().catch((err) => { - logger.error(`Migration failed for ${key}`, err); - }); - - const initMutex = new Mutex(); - - const getFallback = () => opts?.fallback ?? opts?.defaultValue ?? null; - - const getOrInitValue = () => - initMutex.runExclusive(async () => { - const value = await driver.getItem(driverKey); - // Don't init value if it already exists or the init function isn't provided - if (value != null || opts?.init == null) return value; - - const newValue = await opts.init(); - await driver.setItem(driverKey, newValue); - return newValue; - }); - - // Initialize the value once migrations have finished - migrationsDone.then(getOrInitValue); - - return { - key, - get defaultValue() { - return getFallback(); - }, - get fallback() { - return getFallback(); - }, - getValue: async () => { - await migrationsDone; - if (opts?.init) { - return await getOrInitValue(); - } else { - return await getItem(driver, driverKey, opts); - } - }, - getMeta: async () => { - await migrationsDone; - return await getMeta(driver, driverKey); - }, - setValue: async (value) => { - await migrationsDone; - return await setItem(driver, driverKey, value); - }, - setMeta: async (properties) => { - await migrationsDone; - return await setMeta(driver, driverKey, properties); - }, - removeValue: async (opts) => { - await migrationsDone; - return await removeItem(driver, driverKey, opts); - }, - removeMeta: async (properties) => { - await migrationsDone; - return await removeMeta(driver, driverKey, properties); - }, - watch: (cb) => - watch(driver, driverKey, (newValue, oldValue) => - cb(newValue ?? getFallback(), oldValue ?? getFallback()), - ), - migrate, - }; - }, - }; - return storage; -} - -function createDriver(storageArea: StorageArea): WxtStorageDriver { - const getStorageArea = () => { - if (browser.runtime == null) { - throw Error( - [ - "'wxt/storage' must be loaded in a web extension environment", - '\n - If thrown during a build, see https://github.com/wxt-dev/wxt/issues/371', - " - If thrown during tests, mock 'wxt/browser' correctly. See https://wxt.dev/guide/go-further/testing.html\n", - ].join('\n'), - ); - } - if (browser.storage == null) { - throw Error( - "You must add the 'storage' permission to your manifest to use 'wxt/storage'", - ); - } - - const area = browser.storage[storageArea]; - if (area == null) - throw Error(`"browser.storage.${storageArea}" is undefined`); - return area; - }; - const watchListeners = new Set< - (changes: Storage.StorageAreaOnChangedChangesType) => void - >(); - return { - getItem: async (key) => { - const res = await getStorageArea().get(key); - return res[key] as any; - }, - getItems: async (keys) => { - const result = await getStorageArea().get(keys); - return keys.map((key) => ({ key, value: result[key] ?? null })); - }, - setItem: async (key, value) => { - if (value == null) { - await getStorageArea().remove(key); - } else { - await getStorageArea().set({ [key]: value }); - } - }, - setItems: async (values) => { - const map = values.reduce>( - (map, { key, value }) => { - map[key] = value; - return map; - }, - {}, - ); - await getStorageArea().set(map); - }, - removeItem: async (key) => { - await getStorageArea().remove(key); - }, - removeItems: async (keys) => { - await getStorageArea().remove(keys); - }, - snapshot: async () => { - return await getStorageArea().get(); - }, - restoreSnapshot: async (data) => { - await getStorageArea().set(data); - }, - watch(key, cb) { - const listener = (changes: Storage.StorageAreaOnChangedChangesType) => { - const change = changes[key] as { newValue: any; oldValue: any }; - if (change == null) return; - if (dequal(change.newValue, change.oldValue)) return; - cb(change.newValue ?? null, change.oldValue ?? null); - }; - getStorageArea().onChanged.addListener(listener); - watchListeners.add(listener); - return () => { - getStorageArea().onChanged.removeListener(listener); - watchListeners.delete(listener); - }; - }, - unwatch() { - watchListeners.forEach((listener) => { - getStorageArea().onChanged.removeListener(listener); - }); - watchListeners.clear(); - }, - }; -} - -export interface WxtStorage { - /** - * Get an item from storage, or return `null` if it doesn't exist. - * - * @example - * await storage.getItem("local:installDate"); - */ - getItem( - key: StorageItemKey, - opts: GetItemOptions & { fallback: TValue }, - ): Promise; - - getItem( - key: StorageItemKey, - opts?: GetItemOptions, - ): Promise; - - /** - * Get multiple items from storage. The return order is guaranteed to be the same as the order - * requested. - * - * @example - * await storage.getItems(["local:installDate", "session:someCounter"]); - */ - getItems( - keys: Array< - | StorageItemKey - | WxtStorageItem - | { key: StorageItemKey; options?: GetItemOptions } - >, - ): Promise>; - /** - * Return an object containing metadata about the key. Object is stored at `key + "$"`. If value - * is not an object, it returns an empty object. - * - * @example - * await storage.getMeta("local:installDate"); - */ - getMeta>(key: StorageItemKey): Promise; - /** - * Get the metadata of multiple storage items. - * - * @param items List of keys or items to get the metadata of. - * @returns An array containing storage keys and their metadata. - */ - getMetas( - keys: Array>, - ): Promise>; - /** - * Set a value in storage. Setting a value to `null` or `undefined` is equivalent to calling - * `removeItem`. - * - * @example - * await storage.setItem("local:installDate", Date.now()); - */ - setItem(key: StorageItemKey, value: T | null): Promise; - /** - * Set multiple values in storage. If a value is set to `null` or `undefined`, the key is removed. - * - * @example - * await storage.setItem([ - * { key: "local:installDate", value: Date.now() }, - * { key: "session:someCounter, value: 5 }, - * ]); - */ - setItems( - values: Array< - | { key: StorageItemKey; value: any } - | { item: WxtStorageItem; value: any } - >, - ): Promise; - /** - * Sets metadata properties. If some properties are already set, but are not included in the - * `properties` parameter, they will not be removed. - * - * @example - * await storage.setMeta("local:installDate", { appVersion }); - */ - setMeta>( - key: StorageItemKey, - properties: T | null, - ): Promise; - /** - * Set the metadata of multiple storage items. - * - * @param items List of storage keys or items and metadata to set for each. - */ - setMetas( - metas: Array< - | { key: StorageItemKey; meta: Record } - | { item: WxtStorageItem; meta: Record } - >, - ): Promise; - /** - * Removes an item from storage. - * - * @example - * await storage.removeItem("local:installDate"); - */ - removeItem(key: StorageItemKey, opts?: RemoveItemOptions): Promise; - /** - * Remove a list of keys from storage. - */ - removeItems( - keys: Array< - | StorageItemKey - | WxtStorageItem - | { key: StorageItemKey; options?: RemoveItemOptions } - | { item: WxtStorageItem; options?: RemoveItemOptions } - >, - ): Promise; - /** - * Remove the entire metadata for a key, or specific properties by name. - * - * @example - * // Remove all metadata properties from the item - * await storage.removeMeta("local:installDate"); - * - * // Remove only specific the "v" field - * await storage.removeMeta("local:installDate", "v") - */ - removeMeta( - key: StorageItemKey, - properties?: string | string[], - ): Promise; - /** - * Return all the items in storage. - */ - snapshot( - base: StorageArea, - opts?: SnapshotOptions, - ): Promise>; - /** - * Restores the results of `snapshot`. If new properties have been saved since the snapshot, they are - * not overridden. Only values existing in the snapshot are overridden. - */ - restoreSnapshot(base: StorageArea, data: any): Promise; - /** - * Watch for changes to a specific key in storage. - */ - watch(key: StorageItemKey, cb: WatchCallback): Unwatch; - /** - * Remove all watch listeners. - */ - unwatch(): void; - - /** - * Define a storage item with a default value, type, or versioning. - * - * Read full docs: https://wxt.dev/guide/extension-apis/storage.html#defining-storage-items - */ - defineItem = {}>( - key: StorageItemKey, - ): WxtStorageItem; - defineItem = {}>( - key: StorageItemKey, - options: WxtStorageItemOptions, - ): WxtStorageItem; -} - -interface WxtStorageDriver { - getItem(key: string): Promise; - getItems(keys: string[]): Promise<{ key: string; value: any }[]>; - setItem(key: string, value: T | null): Promise; - setItems(values: Array<{ key: string; value: any }>): Promise; - removeItem(key: string): Promise; - removeItems(keys: string[]): Promise; - snapshot(): Promise>; - restoreSnapshot(data: Record): Promise; - watch(key: string, cb: WatchCallback): Unwatch; - unwatch(): void; -} - -export interface WxtStorageItem< - TValue, - TMetadata extends Record, -> { - /** - * The storage key passed when creating the storage item. - */ - key: StorageItemKey; - /** - * @deprecated Renamed to fallback, use it instead. - */ - defaultValue: TValue; - /** - * The value provided by the `fallback` option. - */ - fallback: TValue; - /** - * Get the latest value from storage. - */ - getValue(): Promise; - /** - * Get metadata. - */ - getMeta(): Promise>; - /** - * Set the value in storage. - */ - setValue(value: TValue): Promise; - /** - * Set metadata properties. - */ - setMeta(properties: NullablePartial): Promise; - /** - * Remove the value from storage. - */ - removeValue(opts?: RemoveItemOptions): Promise; - /** - * Remove all metadata or certain properties from metadata. - */ - removeMeta(properties?: string[]): Promise; - /** - * Listen for changes to the value in storage. - */ - watch(cb: WatchCallback): Unwatch; - /** - * If there are migrations defined on the storage item, migrate to the latest version. - * - * **This function is ran automatically whenever the extension updates**, so you don't have to call it - * manually. - */ - migrate(): Promise; -} - -export type StorageArea = 'local' | 'session' | 'sync' | 'managed'; -export type StorageItemKey = `${StorageArea}:${string}`; - -export interface GetItemOptions { - /** - * @deprecated Renamed to `fallback`, use it instead. - */ - defaultValue?: T; - /** - * Default value returned when `getItem` would otherwise return `null`. - */ - fallback?: T; -} - -export interface RemoveItemOptions { - /** - * Optionally remove metadata when deleting a key. - * - * @default false - */ - removeMeta?: boolean; -} - -export interface SnapshotOptions { - /** - * Exclude a list of keys. The storage area prefix should be removed since the snapshot is for a - * specific storage area already. - */ - excludeKeys?: string[]; -} - -export interface WxtStorageItemOptions { - /** - * @deprecated Renamed to `fallback`, use it instead. - */ - defaultValue?: T; - /** - * Default value returned when `getValue` would otherwise return `null`. - */ - fallback?: T; - /** - * If passed, a value in storage will be initialized immediately after - * defining the storage item. This function returns the value that will be - * saved to storage during the initialization process if a value doesn't - * already exist. - */ - init?: () => T | Promise; - /** - * Provide a version number for the storage item to enable migrations. When changing the version - * in the future, migration functions will be ran on application startup. - */ - version?: number; - /** - * A map of version numbers to the functions used to migrate the data to that version. - */ - migrations?: Record any>; -} - -/** - * Same as `Partial`, but includes `| null`. It makes all the properties of an object optional and - * nullable. - */ -export type NullablePartial = { - [key in keyof T]+?: T[key] | undefined | null; -}; -/** - * Callback called when a value in storage is changed. - */ -export type WatchCallback = (newValue: T, oldValue: T) => void; -/** - * Call to remove a watch listener - */ -export type Unwatch = () => void; - -export class MigrationError extends Error { - constructor( - public key: string, - public version: number, - options?: ErrorOptions, - ) { - super(`v${version} migration failed for "${key}"`, options); - } -} +export * from '@wxt-dev/storage'; diff --git a/packages/wxt/typedoc.json b/packages/wxt/typedoc.json index 31247281..d9edaddf 100644 --- a/packages/wxt/typedoc.json +++ b/packages/wxt/typedoc.json @@ -6,7 +6,7 @@ "src/browser/index.ts", "src/browser/chrome.ts", "src/index.ts", - "src/storage.ts", - "src/modules.ts" + "src/modules.ts", + "src/storage.ts" ] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c25bff72..c5884d35 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -260,6 +260,40 @@ importers: specifier: workspace:* version: link:../wxt + packages/storage: + dependencies: + async-mutex: + specifier: ^0.5.0 + version: 0.5.0 + dequal: + specifier: ^2.0.3 + version: 2.0.3 + devDependencies: + '@aklinker1/check': + specifier: ^1.4.5 + version: 1.4.5(typescript@5.6.3) + '@types/chrome': + specifier: ^0.0.268 + version: 0.0.268 + '@webext-core/fake-browser': + specifier: ^1.3.1 + version: 1.3.1 + oxlint: + specifier: ^0.9.9 + version: 0.9.10 + publint: + specifier: ^0.2.11 + version: 0.2.12 + typescript: + specifier: ^5.6.2 + version: 5.6.3 + unbuild: + specifier: ^2.0.0 + version: 2.0.0(sass@1.80.7)(typescript@5.6.3) + vitest: + specifier: ^2.0.0 + version: 2.1.4(@types/node@20.17.6)(happy-dom@15.11.4)(sass@1.80.7) + packages/unocss: dependencies: defu: @@ -311,6 +345,9 @@ importers: '@webext-core/match-patterns': specifier: ^1.0.3 version: 1.0.3 + '@wxt-dev/storage': + specifier: workspace:^1.0.0 + version: link:../storage async-mutex: specifier: ^0.5.0 version: 0.5.0 @@ -332,9 +369,6 @@ importers: defu: specifier: ^6.1.4 version: 6.1.4 - dequal: - specifier: ^2.0.3 - version: 2.0.3 dotenv: specifier: ^16.4.5 version: 16.4.5 @@ -1455,41 +1489,81 @@ packages: cpu: [arm64] os: [darwin] + '@oxlint/darwin-arm64@0.9.10': + resolution: {integrity: sha512-eOXKZYq5bnCSgDefgM5bzAg+4Fc//Rc4yjgKN8iDWUARweCaChiQXb6TXX8MfEfs6qayEMy6yVj0pqoFz0B1aw==} + cpu: [arm64] + os: [darwin] + '@oxlint/darwin-x64@0.11.1': resolution: {integrity: sha512-LPuF0D8uu30KIVEeVuGwIPwHwJRQ1i1otwFFH7tRsNXPgMgZJ4VgriyH22i6RWwBtclJoCSBLtGK6gLZ0oZBvw==} cpu: [x64] os: [darwin] + '@oxlint/darwin-x64@0.9.10': + resolution: {integrity: sha512-UeYICDvLUaUOcY+0ugZUEmBMRLP+x8iTgL7TeY6BlpGw2ahbtUOTbyIIRWtr/0O++TnjZ+v8TzhJ9crw6Ij6dg==} + cpu: [x64] + os: [darwin] + '@oxlint/linux-arm64-gnu@0.11.1': resolution: {integrity: sha512-CYBE+GRIPs5e+raD2pdicuBn6Y6E1xAnyWQ/kHE4GEWDAQZY0Um2VYEUTGH2ObwJ3uXr6jeJ16HOKJvr0S8a8w==} cpu: [arm64] os: [linux] + '@oxlint/linux-arm64-gnu@0.9.10': + resolution: {integrity: sha512-0Zn+vqHhrZyufFBfq9WOgiIool0gCR14BLsdS+0Dwd9o+kNxPGA5q7erQFkiC4rpkxtfBHeD3iIKMMt7d29Kyw==} + cpu: [arm64] + os: [linux] + '@oxlint/linux-arm64-musl@0.11.1': resolution: {integrity: sha512-iYXF5N5Gv+lc2wt90kxXy/W0cn7IEWu3UPzewIjPGDH8ajDckvGzZx6pTGYJnTyMh7U6hUKwOBFPVLMWI7UwKQ==} cpu: [arm64] os: [linux] + '@oxlint/linux-arm64-musl@0.9.10': + resolution: {integrity: sha512-tkQcWpYwF42bA/uRaV2iMFePHkBjTTgomOgeEaiw6XOSJX4nBEqGIIboqqLBWT4JnKCf/L+IG3y/e1MflhKByw==} + cpu: [arm64] + os: [linux] + '@oxlint/linux-x64-gnu@0.11.1': resolution: {integrity: sha512-D0tT8X0CsK/bpdkGdLSmsGftG3VndjyAUJuNGt56JYn0UfuPDkhQcLgUlkANHzNRXJ84tLQKhpf/MUDUHPB5cg==} cpu: [x64] os: [linux] + '@oxlint/linux-x64-gnu@0.9.10': + resolution: {integrity: sha512-JHbkMUnibqaSMBvLHyqTL5cWxcGW+jw+Ppt2baLISpvo34a6fBR+PI7v/A92sEDWe0W1rPhypzCwA8mKpkQ3DA==} + cpu: [x64] + os: [linux] + '@oxlint/linux-x64-musl@0.11.1': resolution: {integrity: sha512-WekaLYk8WLT7Di8+nyPvtqs9OlMoO6KjFDMlqqLDWQTk9ffjn8e76PCRigF3w39jQ70qP3c8k8cNKNw5ROuFcg==} cpu: [x64] os: [linux] + '@oxlint/linux-x64-musl@0.9.10': + resolution: {integrity: sha512-aBBwN7bQzidwHwEXr7BAdVvMTLWstCy5gikerjLnGDeCSXX9r+o6+yUzTOqZvOo66E+XBgOJaVbY8rsL1MLE0g==} + cpu: [x64] + os: [linux] + '@oxlint/win32-arm64@0.11.1': resolution: {integrity: sha512-/CN/bFtI33vB8uemOkZxlNRf6Q7CftP2pSO7a6Q2niG4NC99YRPj7ctXcPF0jGR0NQUhGZk7ajM4G/0MKcRdag==} cpu: [arm64] os: [win32] + '@oxlint/win32-arm64@0.9.10': + resolution: {integrity: sha512-LXDnk7vKHT3IY6G1jq0O7+XMhtcHOYuxLGIx4KP+4xS6vKgBY+Bsq4xV3AtmtKlvnXkP5FxHpfLmcEtm5AWysA==} + cpu: [arm64] + os: [win32] + '@oxlint/win32-x64@0.11.1': resolution: {integrity: sha512-0hLl0z6adYTvLIOPC5uyo+EAwNITkzi4AY4xImykQW8H89GhiV9Xl8MPJeZQMWSz7ajI1I2+hRsvA0QAzeBsxA==} cpu: [x64] os: [win32] + '@oxlint/win32-x64@0.9.10': + resolution: {integrity: sha512-w5XRAV4bhgwenjjpGYZGglqzG9Wv/sI+cjQWJBQsvfDXsr2w4vOBXzt1j3/Z3EcSqf4KtkCa/IIuAhQyeShUbA==} + cpu: [x64] + os: [win32] + '@parcel/watcher-android-arm64@2.5.0': resolution: {integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==} engines: {node: '>= 10.0.0'} @@ -1800,6 +1874,9 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/chrome@0.0.268': + resolution: {integrity: sha512-7N1QH9buudSJ7sI8Pe4mBHJr5oZ48s0hcanI9w3wgijAlv1OZNUZve9JR4x42dn5lJ5Sm87V1JNfnoh10EnQlA==} + '@types/chrome@0.0.280': resolution: {integrity: sha512-AotSmZrL9bcZDDmSI1D9dE7PGbhOur5L0cKxXd7IqbVizQWCY4gcvupPUVsQ4FfDj3V2tt/iOpomT9EY0s+w1g==} @@ -3790,6 +3867,11 @@ packages: engines: {node: '>=14.*'} hasBin: true + oxlint@0.9.10: + resolution: {integrity: sha512-bKiiFN7Hnoaist/rditTRBXz+GXKYuLd53/NB7Q6zHB/bifELJarSoRLkAUGElIJKl4PSr3lTh1g6zehh+rX0g==} + engines: {node: '>=14.*'} + hasBin: true + p-cancelable@3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} engines: {node: '>=12.20'} @@ -5840,27 +5922,51 @@ snapshots: '@oxlint/darwin-arm64@0.11.1': optional: true + '@oxlint/darwin-arm64@0.9.10': + optional: true + '@oxlint/darwin-x64@0.11.1': optional: true + '@oxlint/darwin-x64@0.9.10': + optional: true + '@oxlint/linux-arm64-gnu@0.11.1': optional: true + '@oxlint/linux-arm64-gnu@0.9.10': + optional: true + '@oxlint/linux-arm64-musl@0.11.1': optional: true + '@oxlint/linux-arm64-musl@0.9.10': + optional: true + '@oxlint/linux-x64-gnu@0.11.1': optional: true + '@oxlint/linux-x64-gnu@0.9.10': + optional: true + '@oxlint/linux-x64-musl@0.11.1': optional: true + '@oxlint/linux-x64-musl@0.9.10': + optional: true + '@oxlint/win32-arm64@0.11.1': optional: true + '@oxlint/win32-arm64@0.9.10': + optional: true + '@oxlint/win32-x64@0.11.1': optional: true + '@oxlint/win32-x64@0.9.10': + optional: true + '@parcel/watcher-android-arm64@2.5.0': optional: true @@ -6134,6 +6240,11 @@ snapshots: dependencies: '@babel/types': 7.25.6 + '@types/chrome@0.0.268': + dependencies: + '@types/filesystem': 0.0.36 + '@types/har-format': 1.2.15 + '@types/chrome@0.0.280': dependencies: '@types/filesystem': 0.0.36 @@ -8350,6 +8461,17 @@ snapshots: '@oxlint/win32-arm64': 0.11.1 '@oxlint/win32-x64': 0.11.1 + oxlint@0.9.10: + optionalDependencies: + '@oxlint/darwin-arm64': 0.9.10 + '@oxlint/darwin-x64': 0.9.10 + '@oxlint/linux-arm64-gnu': 0.9.10 + '@oxlint/linux-arm64-musl': 0.9.10 + '@oxlint/linux-x64-gnu': 0.9.10 + '@oxlint/linux-x64-musl': 0.9.10 + '@oxlint/win32-arm64': 0.9.10 + '@oxlint/win32-x64': 0.9.10 + p-cancelable@3.0.0: {} package-json-from-dist@1.0.0: {} From 6fc227bad62afe263982d3c6dc6d5628d70ba4ba Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 14 Nov 2024 10:03:55 -0600 Subject: [PATCH 035/130] chore: Init changelog for storage package --- packages/storage/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 packages/storage/CHANGELOG.md diff --git a/packages/storage/CHANGELOG.md b/packages/storage/CHANGELOG.md new file mode 100644 index 00000000..8f72d0ec --- /dev/null +++ b/packages/storage/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +## v1.0.0 + +Extracted `wxt/storage` into it's own package, `@wxt-dev/storage`! It's still shipped inside WXT and accessible via `wxt/storage`, but now other, non-WXT projects can use the storage wrapper and we can make breaking changes to the API separately. + +[Read the docs](https://wxt.dev/storage.html) for more details. From 592bdbe4d9f0863a714712289066c7bc38a93485 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 14 Nov 2024 10:09:40 -0600 Subject: [PATCH 036/130] chore: Speed up sync-releases workflow by not building packages --- .github/actions/setup/action.yml | 5 ++++- .github/workflows/sync-releases.yml | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 597ac8a3..a60583c9 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -4,6 +4,9 @@ inputs: install: default: 'true' description: Whether or not to run 'pnpm install' + installArgs: + default: '' + description: Additional args to append to "pnpm install" runs: using: composite steps: @@ -17,4 +20,4 @@ runs: - name: Install Dependencies if: ${{ inputs.install == 'true' }} shell: bash - run: pnpm install + run: pnpm install ${{ inputs.installArgs }} diff --git a/.github/workflows/sync-releases.yml b/.github/workflows/sync-releases.yml index d8f8ed83..35f18d26 100644 --- a/.github/workflows/sync-releases.yml +++ b/.github/workflows/sync-releases.yml @@ -22,6 +22,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup + with: + installArgs: --ignore-scripts - run: pnpm tsx scripts/sync-releases.ts ${{ inputs.package }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From f5b7f7e0e5ccd7544f4e43300b0de37782951b8c Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 14 Nov 2024 10:11:39 -0600 Subject: [PATCH 037/130] docs: Cleanup changelog --- packages/storage/CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/storage/CHANGELOG.md b/packages/storage/CHANGELOG.md index 8f72d0ec..c9f360d1 100644 --- a/packages/storage/CHANGELOG.md +++ b/packages/storage/CHANGELOG.md @@ -2,6 +2,13 @@ ## v1.0.0 -Extracted `wxt/storage` into it's own package, `@wxt-dev/storage`! It's still shipped inside WXT and accessible via `wxt/storage`, but now other, non-WXT projects can use the storage wrapper and we can make breaking changes to the API separately. +Extracted `wxt/storage` into it's own package, `@wxt-dev/storage`! + +It's still shipped inside WXT and accessible via `wxt/storage`, but now: + +- Non-WXT projects can use the storage wrapper. +- We can make breaking changes to the API separately. [Read the docs](https://wxt.dev/storage.html) for more details. + +> This is apart of the v1.0 initiative for WXT. From 52e5387d4775d1612741283bf5d119eac2a2238a Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 14 Nov 2024 10:30:51 -0600 Subject: [PATCH 038/130] fix: Add extension ID to event used to invalidate `ContentScriptContext` (#1175) --- .../wxt/src/client/content-scripts/content-script-context.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/wxt/src/client/content-scripts/content-script-context.ts b/packages/wxt/src/client/content-scripts/content-script-context.ts index 4474ca46..69f36c6c 100644 --- a/packages/wxt/src/client/content-scripts/content-script-context.ts +++ b/packages/wxt/src/client/content-scripts/content-script-context.ts @@ -35,7 +35,9 @@ import { createLocationWatcher } from './location-watcher'; * ``` */ export class ContentScriptContext implements AbortController { - private static SCRIPT_STARTED_MESSAGE_TYPE = 'wxt:content-script-started'; + private static SCRIPT_STARTED_MESSAGE_TYPE = getUniqueEventName( + 'wxt:content-script-started', + ); private isTopFrame = window.self === window.top; private abortController: AbortController; From ef53580ed1f02cbd8350aaec1dd09cd2f99119e2 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 14 Nov 2024 16:41:09 +0000 Subject: [PATCH 039/130] chore(release): wxt v0.19.15 --- packages/wxt/CHANGELOG.md | 18 ++++++++++++++++++ packages/wxt/package.json | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/wxt/CHANGELOG.md b/packages/wxt/CHANGELOG.md index 70c86996..1b7baf25 100644 --- a/packages/wxt/CHANGELOG.md +++ b/packages/wxt/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## v0.19.15 + +[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.14...wxt-v0.19.15) + +### 🚀 Enhancements + +- Extract `wxt/storage` to its own package, `@wxt-dev/storage` ([#1129](https://github.com/wxt-dev/wxt/pull/1129)) + +### 🩹 Fixes + +- Add "/" to `PublicPath` and `browser.runtime.getURL` ([#1171](https://github.com/wxt-dev/wxt/pull/1171)) +- Add extension ID to event used to invalidate `ContentScriptContext` ([#1175](https://github.com/wxt-dev/wxt/pull/1175)) + +### ❤️ Contributors + +- Aaron ([@aklinker1](http://github.com/aklinker1)) +- Nishu ([@nishu-murmu](http://github.com/nishu-murmu)) + ## v0.19.14 [compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.13...wxt-v0.19.14) diff --git a/packages/wxt/package.json b/packages/wxt/package.json index 5e2cd4d3..29f1ac72 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -1,7 +1,7 @@ { "name": "wxt", "type": "module", - "version": "0.19.14", + "version": "0.19.15", "description": "Next gen framework for developing web extensions", "repository": { "type": "git", From 4d4453b830531adfad3c5a5669877396ccf258ac Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 15 Nov 2024 08:52:22 -0600 Subject: [PATCH 040/130] feat(hooks): Add new `config:resolved` hook (#1177) --- docs/guide/essentials/wxt-modules.md | 2 +- packages/wxt/e2e/tests/hooks.test.ts | 6 ++++++ packages/wxt/src/__tests__/modules.test.ts | 10 +++++----- packages/wxt/src/builtin-modules/unimport.ts | 8 ++++---- packages/wxt/src/core/generate-wxt-dir.ts | 2 +- packages/wxt/src/core/wxt.ts | 2 ++ packages/wxt/src/modules.ts | 15 ++++++++------- packages/wxt/src/types.ts | 8 ++++++-- 8 files changed, 33 insertions(+), 20 deletions(-) diff --git a/docs/guide/essentials/wxt-modules.md b/docs/guide/essentials/wxt-modules.md index 9ec0c43f..9033df6a 100644 --- a/docs/guide/essentials/wxt-modules.md +++ b/docs/guide/essentials/wxt-modules.md @@ -75,7 +75,7 @@ import { defineWxtModule } from 'wxt/modules'; export default defineWxtModule({ setup(wxt) { - wxt.hook('ready', () => { + wxt.hook('config:resolved', () => { wxt.config.outDir = 'dist'; }); }, diff --git a/packages/wxt/e2e/tests/hooks.test.ts b/packages/wxt/e2e/tests/hooks.test.ts index e91a2209..0f9babc8 100644 --- a/packages/wxt/e2e/tests/hooks.test.ts +++ b/packages/wxt/e2e/tests/hooks.test.ts @@ -4,6 +4,7 @@ import { WxtHooks } from '../../src/types'; const hooks: WxtHooks = { ready: vi.fn(), + 'config:resolved': vi.fn(), 'prepare:types': vi.fn(), 'prepare:publicPaths': vi.fn(), 'build:before': vi.fn(), @@ -49,6 +50,7 @@ describe('Hooks', () => { expectHooksToBeCalled({ ready: true, + 'config:resolved': true, 'prepare:types': true, 'prepare:publicPaths': true, 'build:before': false, @@ -76,6 +78,7 @@ describe('Hooks', () => { expectHooksToBeCalled({ ready: true, + 'config:resolved': true, 'prepare:types': true, 'prepare:publicPaths': true, 'build:before': true, @@ -103,6 +106,7 @@ describe('Hooks', () => { expectHooksToBeCalled({ ready: true, + 'config:resolved': true, 'prepare:types': true, 'prepare:publicPaths': true, 'build:before': true, @@ -130,6 +134,7 @@ describe('Hooks', () => { expectHooksToBeCalled({ ready: true, + 'config:resolved': true, 'prepare:types': true, 'prepare:publicPaths': true, 'build:before': true, @@ -163,6 +168,7 @@ describe('Hooks', () => { expectHooksToBeCalled({ ready: true, + 'config:resolved': true, 'prepare:types': true, 'prepare:publicPaths': true, 'build:before': true, diff --git a/packages/wxt/src/__tests__/modules.test.ts b/packages/wxt/src/__tests__/modules.test.ts index 6e0982d1..a0455d0a 100644 --- a/packages/wxt/src/__tests__/modules.test.ts +++ b/packages/wxt/src/__tests__/modules.test.ts @@ -15,7 +15,7 @@ describe('Module Utilities', () => { wxt.config.vite = () => Promise.resolve(userConfig); addViteConfig(wxt, () => moduleConfig); - await wxt.hooks.callHook('ready', wxt); + await wxt.hooks.callHook('config:resolved', wxt); const actual = await wxt.config.vite(wxt.config.env); expect(actual).toEqual(expected); @@ -31,7 +31,7 @@ describe('Module Utilities', () => { wxt.config.vite = () => userConfig; addViteConfig(wxt, () => moduleConfig); - await wxt.hooks.callHook('ready', wxt); + await wxt.hooks.callHook('config:resolved', wxt); const actual = await wxt.config.vite(wxt.config.env); expect(actual).toEqual(expected); @@ -44,7 +44,7 @@ describe('Module Utilities', () => { const wxt = fakeWxt({ hooks: createHooks() }); addImportPreset(wxt, preset); - await wxt.hooks.callHook('ready', wxt); + await wxt.hooks.callHook('config:resolved', wxt); expect(wxt.config.imports && wxt.config.imports.presets).toContain( preset, @@ -63,7 +63,7 @@ describe('Module Utilities', () => { }); addImportPreset(wxt, preset); - await wxt.hooks.callHook('ready', wxt); + await wxt.hooks.callHook('config:resolved', wxt); expect(wxt.config.imports && wxt.config.imports.presets).toHaveLength(2); }); @@ -78,7 +78,7 @@ describe('Module Utilities', () => { }); addImportPreset(wxt, preset); - await wxt.hooks.callHook('ready', wxt); + await wxt.hooks.callHook('config:resolved', wxt); expect(wxt.config.imports).toBe(false); }); diff --git a/packages/wxt/src/builtin-modules/unimport.ts b/packages/wxt/src/builtin-modules/unimport.ts index a915e665..df6bf351 100644 --- a/packages/wxt/src/builtin-modules/unimport.ts +++ b/packages/wxt/src/builtin-modules/unimport.ts @@ -18,7 +18,7 @@ export default defineWxtModule({ let unimport: Unimport; // Add user module imports to config - wxt.hooks.hook('ready', () => { + wxt.hooks.hook('config:resolved', () => { const addModuleImports = (module: WxtModule) => { if (!module.imports) return; @@ -30,10 +30,10 @@ export default defineWxtModule({ wxt.config.userModules.forEach(addModuleImports); }); - // Create unimport instance AFTER "ready" so any modifications to the - // config inside "ready" are applied. + // Create unimport instance AFTER "config:resolved" so any modifications to the + // config inside "config:resolved" are applied. wxt.hooks.afterEach((event) => { - if (event.name === 'ready') { + if (event.name === 'config:resolved') { unimport = createUnimport(options); } }); diff --git a/packages/wxt/src/core/generate-wxt-dir.ts b/packages/wxt/src/core/generate-wxt-dir.ts index a96c6353..4fe3c535 100644 --- a/packages/wxt/src/core/generate-wxt-dir.ts +++ b/packages/wxt/src/core/generate-wxt-dir.ts @@ -177,7 +177,7 @@ declare module "wxt/browser" { message.message, ), ), - // Include a final union-based override so TS accepts valid string templates or concatinations + // Include a final union-based override so TS accepts valid string templates or concatenations // ie: browser.i18n.getMessage(`some_enum_${enumValue}`) renderGetMessageOverload( messages.map((message) => `"${message.name}"`).join(' | '), diff --git a/packages/wxt/src/core/wxt.ts b/packages/wxt/src/core/wxt.ts index a95c27ae..5c6c5194 100644 --- a/packages/wxt/src/core/wxt.ts +++ b/packages/wxt/src/core/wxt.ts @@ -47,6 +47,7 @@ export async function registerWxt( }, async reloadConfig() { wxt.config = await resolveConfig(inlineConfig, command); + await wxt.hooks.callHook('config:resolved', wxt); }, pm, builder, @@ -82,6 +83,7 @@ export async function registerWxt( } await wxt.hooks.callHook('ready', wxt); + await wxt.hooks.callHook('config:resolved', wxt); } /** diff --git a/packages/wxt/src/modules.ts b/packages/wxt/src/modules.ts index f96db619..d04c9b6f 100644 --- a/packages/wxt/src/modules.ts +++ b/packages/wxt/src/modules.ts @@ -28,7 +28,7 @@ export function defineWxtModule( /** * Adds a TS/JS file as an entrypoint to the project. This file will be bundled * along with the other entrypoints. - + * * If you're publishing the module to NPM, you should probably pre-build the * entrypoint and use `addPublicAssets` instead to copy pre-bundled assets into * the output directory. This will speed up project builds since it just has to @@ -53,7 +53,7 @@ export function defineWxtModule( * }); */ export function addEntrypoint(wxt: Wxt, entrypoint: Entrypoint): void { - wxt.hooks.hook('entrypoints:resolved', (wxt, entrypoints) => { + wxt.hooks.hook('entrypoints:resolved', (_, entrypoints) => { entrypoints.push(entrypoint); }); } @@ -106,7 +106,7 @@ export function addViteConfig( wxt: Wxt, viteConfig: (env: vite.ConfigEnv) => vite.UserConfig | undefined, ): void { - wxt.hooks.hook('ready', (wxt) => { + wxt.hooks.hook('config:resolved', (wxt) => { const userVite = wxt.config.vite; wxt.config.vite = async (env) => { const fromUser = await userVite(env); @@ -130,7 +130,7 @@ export function addViteConfig( * }); */ export function addWxtPlugin(wxt: Wxt, plugin: string): void { - wxt.hooks.hook('ready', (wxt) => { + wxt.hooks.hook('config:resolved', (wxt) => { wxt.config.plugins.push(plugin); }); } @@ -166,7 +166,7 @@ export function addImportPreset( wxt: Wxt, preset: UnimportOptions['presets'][0], ): void { - wxt.hooks.hook('ready', (wxt) => { + wxt.hooks.hook('config:resolved', (wxt) => { if (!wxt.config.imports) return; wxt.config.imports.presets ??= []; @@ -205,12 +205,13 @@ export function addImportPreset( * }); */ export function addAlias(wxt: Wxt, alias: string, path: string) { - wxt.hooks.hook('ready', (wxt) => { + wxt.hooks.hook('config:resolved', (wxt) => { const target = resolve(wxt.config.root, path); - if (wxt.config.alias[alias] != null) { + if (wxt.config.alias[alias] != null && wxt.config.alias[alias] !== target) { wxt.logger.warn( `Skipped adding alias (${alias} => ${target}) because an alias with the same name already exists: ${alias} => ${wxt.config.alias[alias]}`, ); + return; } wxt.config.alias[alias] = target; }); diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index 3ad48acf..90da349e 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -1100,11 +1100,15 @@ export type HookResult = Promise | void; export interface WxtHooks { /** - * Called after WXT initialization, when the WXT instance is ready to work. + * Called only one time after WXT initialization, when the WXT instance is ready to work. * @param wxt The configured WXT object - * @returns Promise */ ready: (wxt: Wxt) => HookResult; + /** + * Called whenever config is loaded or reloaded. Use this hook to modify config by modifying `wxt.config`. + * @param wxt The configured WXT object + */ + 'config:resolved': (wxt: Wxt) => HookResult; /** * Called before WXT writes .wxt/tsconfig.json and .wxt/wxt.d.ts, allowing * addition of custom references and declarations in wxt.d.ts, or directly From 497c902283844d0c3699d285fcef32632c604a41 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 15 Nov 2024 14:57:29 +0000 Subject: [PATCH 041/130] chore(release): wxt v0.19.16 --- packages/wxt/CHANGELOG.md | 12 ++++++++++++ packages/wxt/package.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/wxt/CHANGELOG.md b/packages/wxt/CHANGELOG.md index 1b7baf25..04381a7b 100644 --- a/packages/wxt/CHANGELOG.md +++ b/packages/wxt/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## v0.19.16 + +[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.15...wxt-v0.19.16) + +### 🚀 Enhancements + +- **hooks:** Add new `config:resolved` hook ([#1177](https://github.com/wxt-dev/wxt/pull/1177)) + +### ❤️ Contributors + +- Aaron ([@aklinker1](http://github.com/aklinker1)) + ## v0.19.15 [compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.14...wxt-v0.19.15) diff --git a/packages/wxt/package.json b/packages/wxt/package.json index 29f1ac72..c5fab74c 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -1,7 +1,7 @@ { "name": "wxt", "type": "module", - "version": "0.19.15", + "version": "0.19.16", "description": "Next gen framework for developing web extensions", "repository": { "type": "git", From eb1ace6d206a91443f356e21eaa5c50b840dabee Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 15 Nov 2024 09:08:14 -0600 Subject: [PATCH 042/130] fix: Use `config:resolved` hook to update config instead of `ready` (#1178) --- packages/module-react/modules/react.ts | 2 +- packages/module-react/package.json | 2 +- packages/module-solid/modules/solid.ts | 2 +- packages/module-solid/package.json | 2 +- packages/module-vue/package.json | 2 +- packages/module-vue/src/index.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/module-react/modules/react.ts b/packages/module-react/modules/react.ts index 111db775..09ceaa94 100644 --- a/packages/module-react/modules/react.ts +++ b/packages/module-react/modules/react.ts @@ -15,7 +15,7 @@ export default defineWxtModule({ addImportPreset(wxt, 'react'); // Enable auto-imports for JSX files - wxt.hooks.hook('ready', (wxt) => { + wxt.hook('config:resolved', (wxt) => { if (wxt.config.imports === false) return; wxt.config.imports.dirsScanOptions ??= {}; diff --git a/packages/module-react/package.json b/packages/module-react/package.json index 93be2acf..27dbf580 100644 --- a/packages/module-react/package.json +++ b/packages/module-react/package.json @@ -44,7 +44,7 @@ "prepare": "buildc --deps-only -- wxt prepare" }, "peerDependencies": { - "wxt": ">=0.18.6" + "wxt": ">=0.19.16" }, "dependencies": { "@vitejs/plugin-react": "^4.3.3" diff --git a/packages/module-solid/modules/solid.ts b/packages/module-solid/modules/solid.ts index 0309054c..8b9d53bd 100644 --- a/packages/module-solid/modules/solid.ts +++ b/packages/module-solid/modules/solid.ts @@ -18,7 +18,7 @@ export default defineWxtModule({ addImportPreset(wxt, 'solid-js'); // Enable auto-imports for JSX files - wxt.hooks.hook('ready', (wxt) => { + wxt.hook('config:resolved', (wxt) => { if (wxt.config.imports === false) return; wxt.config.imports.dirsScanOptions ??= {}; diff --git a/packages/module-solid/package.json b/packages/module-solid/package.json index 2a49a182..87161d2a 100644 --- a/packages/module-solid/package.json +++ b/packages/module-solid/package.json @@ -44,7 +44,7 @@ "prepare": "buildc --deps-only -- wxt prepare" }, "peerDependencies": { - "wxt": ">=0.18.6" + "wxt": ">=0.19.16" }, "dependencies": { "vite-plugin-solid": "^2.10.2" diff --git a/packages/module-vue/package.json b/packages/module-vue/package.json index 7eaf6107..d943f365 100644 --- a/packages/module-vue/package.json +++ b/packages/module-vue/package.json @@ -42,7 +42,7 @@ "check": "pnpm build && check" }, "peerDependencies": { - "wxt": ">=0.18.6" + "wxt": ">=0.19.16" }, "dependencies": { "@vitejs/plugin-vue": "^5.2.0" diff --git a/packages/module-vue/src/index.ts b/packages/module-vue/src/index.ts index bbc074a9..0595beae 100644 --- a/packages/module-vue/src/index.ts +++ b/packages/module-vue/src/index.ts @@ -19,7 +19,7 @@ export default defineWxtModule({ })); // Enable auto-imports in template files - wxt.hooks.hook('ready', (wxt) => { + wxt.hook('config:resolved', (wxt) => { if (!wxt.config.imports) return; wxt.config.imports.addons ??= {}; From a360965d92e7518ff8b40e1638e6653406310589 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 15 Nov 2024 15:28:53 +0000 Subject: [PATCH 043/130] chore(release): @wxt-dev/module-vue v1.0.2 --- packages/module-vue/CHANGELOG.md | 19 +++++++++++++++++++ packages/module-vue/package.json | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/module-vue/CHANGELOG.md b/packages/module-vue/CHANGELOG.md index 2c5330f7..d9c20d7c 100644 --- a/packages/module-vue/CHANGELOG.md +++ b/packages/module-vue/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## v1.0.2 + +[compare changes](https://github.com/wxt-dev/wxt/compare/module-vue-v1.0.1...module-vue-v1.0.2) + +### 🩹 Fixes + +- Use `config:resolved` hook to update config instead of `ready` ([#1178](https://github.com/wxt-dev/wxt/pull/1178)) + +### 🏡 Chore + +- Add `oxlint` for linting ([#947](https://github.com/wxt-dev/wxt/pull/947)) +- **deps:** Bump @vitejs/plugin-vue from 5.1.1 to 5.1.4 ([#1020](https://github.com/wxt-dev/wxt/pull/1020)) +- Upgrade all non-major dependencies ([#1040](https://github.com/wxt-dev/wxt/pull/1040)) +- **deps:** Upgrade all non-major dependencies ([#1164](https://github.com/wxt-dev/wxt/pull/1164)) + +### ❤️ Contributors + +- Aaron ([@aklinker1](http://github.com/aklinker1)) + ## v1.0.1 [compare changes](https://github.com/wxt-dev/wxt/compare/module-vue-v1.0.0...module-vue-v1.0.1) diff --git a/packages/module-vue/package.json b/packages/module-vue/package.json index d943f365..f5866685 100644 --- a/packages/module-vue/package.json +++ b/packages/module-vue/package.json @@ -17,7 +17,7 @@ "email": "aaronklinker1+wxt@gmail.com" }, "license": "MIT", - "version": "1.0.1", + "version": "1.0.2", "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.mjs", From 8f3a90eeb242e351c175a113a6fc5fea10e144f7 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 15 Nov 2024 15:38:36 +0000 Subject: [PATCH 044/130] chore(release): @wxt-dev/module-react v1.1.2 --- packages/module-react/CHANGELOG.md | 18 ++++++++++++++++++ packages/module-react/package.json | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/module-react/CHANGELOG.md b/packages/module-react/CHANGELOG.md index 3ffa0e4d..4c24a077 100644 --- a/packages/module-react/CHANGELOG.md +++ b/packages/module-react/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## v1.1.2 + +[compare changes](https://github.com/wxt-dev/wxt/compare/module-react-v1.1.1...module-react-v1.1.2) + +### 🩹 Fixes + +- Use `config:resolved` hook to update config instead of `ready` ([#1178](https://github.com/wxt-dev/wxt/pull/1178)) + +### 🏡 Chore + +- Add `oxlint` for linting ([#947](https://github.com/wxt-dev/wxt/pull/947)) +- Upgrade all non-major dependencies ([#1040](https://github.com/wxt-dev/wxt/pull/1040)) +- **deps:** Upgrade all non-major dependencies ([#1164](https://github.com/wxt-dev/wxt/pull/1164)) + +### ❤️ Contributors + +- Aaron ([@aklinker1](http://github.com/aklinker1)) + ## v1.1.1 [compare changes](https://github.com/wxt-dev/wxt/compare/module-react-v1.1.0...module-react-v1.1.1) diff --git a/packages/module-react/package.json b/packages/module-react/package.json index 27dbf580..30475ec5 100644 --- a/packages/module-react/package.json +++ b/packages/module-react/package.json @@ -17,7 +17,7 @@ "email": "aaronklinker1+wxt@gmail.com" }, "license": "MIT", - "version": "1.1.1", + "version": "1.1.2", "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.mjs", From 57229c69c171c15601a3278f02d246baa9d5886d Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 15 Nov 2024 15:46:51 +0000 Subject: [PATCH 045/130] chore(release): @wxt-dev/module-solid v1.1.3 --- packages/module-solid/CHANGELOG.md | 18 ++++++++++++++++++ packages/module-solid/package.json | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/module-solid/CHANGELOG.md b/packages/module-solid/CHANGELOG.md index 3db1c3d3..aa0f1708 100644 --- a/packages/module-solid/CHANGELOG.md +++ b/packages/module-solid/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## v1.1.3 + +[compare changes](https://github.com/wxt-dev/wxt/compare/module-solid-v1.1.2...module-solid-v1.1.3) + +### 🩹 Fixes + +- Use `config:resolved` hook to update config instead of `ready` ([#1178](https://github.com/wxt-dev/wxt/pull/1178)) + +### 🏡 Chore + +- Add `oxlint` for linting ([#947](https://github.com/wxt-dev/wxt/pull/947)) +- Upgrade all non-major dependencies ([#1040](https://github.com/wxt-dev/wxt/pull/1040)) +- **deps:** Upgrade all non-major dependencies ([#1164](https://github.com/wxt-dev/wxt/pull/1164)) + +### ❤️ Contributors + +- Aaron ([@aklinker1](http://github.com/aklinker1)) + ## v1.1.2 [compare changes](https://github.com/wxt-dev/wxt/compare/module-solid-v1.1.1...module-solid-v1.1.2) diff --git a/packages/module-solid/package.json b/packages/module-solid/package.json index 87161d2a..ecddc211 100644 --- a/packages/module-solid/package.json +++ b/packages/module-solid/package.json @@ -17,7 +17,7 @@ "email": "aaronklinker1+wxt@gmail.com" }, "license": "MIT", - "version": "1.1.2", + "version": "1.1.3", "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.mjs", From db025cd3a291e26249701b7f4d2b7b64f14765fa Mon Sep 17 00:00:00 2001 From: Aaron Date: Sat, 16 Nov 2024 08:56:08 -0600 Subject: [PATCH 046/130] fix: Re-initialize WXT modules correctly during development (#1176) --- packages/wxt-demo/wxt.config.ts | 2 +- packages/wxt/package.json | 2 +- packages/wxt/src/core/builders/vite/index.ts | 6 +- packages/wxt/src/core/create-server.ts | 158 ++++++++++-------- packages/wxt/src/core/resolve-config.ts | 12 +- .../__tests__/detect-dev-changes.test.ts | 27 +++ .../core/utils/building/detect-dev-changes.ts | 11 +- packages/wxt/src/core/wxt.ts | 56 ++++--- packages/wxt/src/types.ts | 2 +- pnpm-lock.yaml | 17 +- 10 files changed, 177 insertions(+), 116 deletions(-) diff --git a/packages/wxt-demo/wxt.config.ts b/packages/wxt-demo/wxt.config.ts index f732c1c3..6961eea2 100644 --- a/packages/wxt-demo/wxt.config.ts +++ b/packages/wxt-demo/wxt.config.ts @@ -25,7 +25,7 @@ export default defineConfig({ }, example: { a: 'a', - // @ts-expect-error: c is not defined, this should error out + // @ts-expect-error: c is not defined, this should be a type error, but it should show up in the module c: 'c', }, unocss: { diff --git a/packages/wxt/package.json b/packages/wxt/package.json index c5fab74c..fba6bb81 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -100,7 +100,7 @@ "fast-glob": "^3.3.2", "filesize": "^10.1.6", "fs-extra": "^11.2.0", - "get-port": "^7.1.0", + "get-port-please": "^3.1.2", "giget": "^1.2.3", "hookable": "^5.5.3", "is-wsl": "^3.1.0", diff --git a/packages/wxt/src/core/builders/vite/index.ts b/packages/wxt/src/core/builders/vite/index.ts index 6ab15ab4..d5a612d9 100644 --- a/packages/wxt/src/core/builders/vite/index.ts +++ b/packages/wxt/src/core/builders/vite/index.ts @@ -28,7 +28,7 @@ import { installSourcemapsSupport } from 'vite-node/source-map'; export async function createViteBuilder( wxtConfig: ResolvedConfig, hooks: Hookable, - server?: WxtDevServer, + getWxtDevServer?: () => WxtDevServer | undefined, ): Promise { const vite = await import('vite'); @@ -65,6 +65,8 @@ export async function createViteBuilder( ignored: [`${wxtConfig.outBaseDir}/**`, `${wxtConfig.wxtDir}/**`], }; + const server = getWxtDevServer?.(); + config.plugins ??= []; config.plugins.push( wxtPlugins.download(wxtConfig), @@ -193,7 +195,7 @@ export async function createViteBuilder( }; /** - * Return the basic config for building a sinlge CSS entrypoint in [multi-page mode](https://vitejs.dev/guide/build.html#multi-page-app). + * Return the basic config for building a single CSS entrypoint in [multi-page mode](https://vitejs.dev/guide/build.html#multi-page-app). */ const getCssConfig = (entrypoint: Entrypoint): vite.InlineConfig => { return { diff --git a/packages/wxt/src/core/create-server.ts b/packages/wxt/src/core/create-server.ts index aa834ae0..0a04eb39 100644 --- a/packages/wxt/src/core/create-server.ts +++ b/packages/wxt/src/core/create-server.ts @@ -21,7 +21,7 @@ import { createExtensionRunner } from './runners'; import { Mutex } from 'async-mutex'; import pc from 'picocolors'; import { relative } from 'node:path'; -import { registerWxt, wxt } from './wxt'; +import { deinitWxtModules, initWxtModules, registerWxt, wxt } from './wxt'; import { unnormalizePath } from './utils/paths'; import { getContentScriptJs, @@ -40,64 +40,102 @@ import { export async function createServer( inlineConfig?: InlineConfig, ): Promise { - await registerWxt('serve', inlineConfig, async (config) => { - const { port, hostname } = config.dev.server!; - const serverInfo: ServerInfo = { + await registerWxt('serve', inlineConfig); + + return (wxt.server = await createServerInternal()); +} + +async function createServerInternal(): Promise { + const getServerInfo = (): ServerInfo => { + const { port, hostname } = wxt.config.dev.server!; + return { port, hostname, origin: `http://${hostname}:${port}`, }; + }; - // Server instance must be created first so its reference can be added to the internal config used - // to pre-render entrypoints - const server: WxtDevServer = { - ...serverInfo, - get watcher() { - return builderServer.watcher; - }, - get ws() { - return builderServer.ws; - }, - currentOutput: undefined, - async start() { - await builderServer.listen(); - wxt.logger.success(`Started dev server @ ${serverInfo.origin}`); - await buildAndOpenBrowser(); - }, - async stop() { - await runner.closeBrowser(); - await builderServer.close(); - }, - async restart() { - await closeAndRecreateRunner(); - await buildAndOpenBrowser(); - }, - transformHtml(url, html, originalUrl) { - return builderServer.transformHtml(url, html, originalUrl); - }, - reloadContentScript(payload) { - server.ws.send('wxt:reload-content-script', payload); - }, - reloadPage(path) { - server.ws.send('wxt:reload-page', path); - }, - reloadExtension() { - server.ws.send('wxt:reload-extension'); - }, - async restartBrowser() { - await closeAndRecreateRunner(); - await runner.openBrowser(); - }, - }; - return server; - }); - - const server = wxt.server!; let [runner, builderServer] = await Promise.all([ createExtensionRunner(), - wxt.builder.createServer(server), + wxt.builder.createServer(getServerInfo()), ]); + // Used to track if modules need to be re-initialized + let wasStopped = false; + + // Server instance must be created first so its reference can be added to the internal config used + // to pre-render entrypoints + const server: WxtDevServer = { + get hostname() { + return getServerInfo().hostname; + }, + get port() { + return getServerInfo().port; + }, + get origin() { + return getServerInfo().origin; + }, + get watcher() { + return builderServer.watcher; + }, + get ws() { + return builderServer.ws; + }, + currentOutput: undefined, + async start() { + if (wasStopped) { + await wxt.reloadConfig(); + runner = await createExtensionRunner(); + builderServer = await wxt.builder.createServer(getServerInfo()); + await initWxtModules(); + } + + await builderServer.listen(); + wxt.logger.success(`Started dev server @ ${server.origin}`); + await buildAndOpenBrowser(); + + // Register content scripts for the first time after the background starts up since they're not + // listed in the manifest + server.ws.on('wxt:background-initialized', () => { + if (server.currentOutput == null) return; + reloadContentScripts(server.currentOutput.steps, server); + }); + + // Listen for file changes and reload different parts of the extension accordingly + const reloadOnChange = createFileReloader(server); + server.watcher.on('all', reloadOnChange); + }, + async stop() { + wasStopped = true; + await runner.closeBrowser(); + await builderServer.close(); + deinitWxtModules(); + server.currentOutput = undefined; + }, + async restart() { + await server.stop(); + await server.start(); + }, + transformHtml(url, html, originalUrl) { + return builderServer.transformHtml(url, html, originalUrl); + }, + reloadContentScript(payload) { + server.ws.send('wxt:reload-content-script', payload); + }, + reloadPage(path) { + server.ws.send('wxt:reload-page', path); + }, + reloadExtension() { + server.ws.send('wxt:reload-extension'); + }, + async restartBrowser() { + await runner.closeBrowser(); + await wxt.reloadConfig(); + runner = await createExtensionRunner(); + await runner.openBrowser(); + }, + }; + const buildAndOpenBrowser = async () => { // Build after starting the dev server so it can be used to transform HTML files server.currentOutput = await internalBuild(); @@ -114,26 +152,6 @@ export async function createServer( await runner.openBrowser(); }; - /** - * Stops the previous runner, grabs the latest config, and recreates the runner. - */ - const closeAndRecreateRunner = async () => { - await runner.closeBrowser(); - await wxt.reloadConfig(); - runner = await createExtensionRunner(); - }; - - // Register content scripts for the first time after the background starts up since they're not - // listed in the manifest - server.ws.on('wxt:background-initialized', () => { - if (server.currentOutput == null) return; - reloadContentScripts(server.currentOutput.steps, server); - }); - - // Listen for file changes and reload different parts of the extension accordingly - const reloadOnChange = createFileReloader(server); - server.watcher.on('all', reloadOnChange); - return server; } diff --git a/packages/wxt/src/core/resolve-config.ts b/packages/wxt/src/core/resolve-config.ts index cb6f40cb..0f91ba51 100644 --- a/packages/wxt/src/core/resolve-config.ts +++ b/packages/wxt/src/core/resolve-config.ts @@ -27,6 +27,7 @@ import { builtinModules } from '../builtin-modules'; import { getEslintVersion } from './utils/eslint'; import { safeStringToNumber } from './utils/number'; import { loadEnv } from './utils/env'; +import { getPort } from 'get-port-please'; /** * Given an inline config, discover the config file if necessary, merge the results, resolve any @@ -137,14 +138,19 @@ export async function resolveConfig( let devServerConfig: ResolvedConfig['dev']['server']; if (command === 'serve') { + const hostname = mergedConfig.dev?.server?.hostname ?? 'localhost'; let port = mergedConfig.dev?.server?.port; if (port == null || !isFinite(port)) { - const { default: getPort, portNumbers } = await import('get-port'); - port = await getPort({ port: portNumbers(3000, 3010) }); + port = await getPort({ + port: 3000, + portRange: [3001, 3010], + // Passing host required for Mac, unsure of Windows/Linux + host: hostname, + }); } devServerConfig = { port, - hostname: mergedConfig.dev?.server?.hostname ?? 'localhost', + hostname, watchDebounce: safeStringToNumber(process.env.WXT_WATCH_DEBOUNCE) ?? 800, }; } diff --git a/packages/wxt/src/core/utils/building/__tests__/detect-dev-changes.test.ts b/packages/wxt/src/core/utils/building/__tests__/detect-dev-changes.test.ts index a94e6dfc..838e2061 100644 --- a/packages/wxt/src/core/utils/building/__tests__/detect-dev-changes.test.ts +++ b/packages/wxt/src/core/utils/building/__tests__/detect-dev-changes.test.ts @@ -71,6 +71,33 @@ describe('Detect Dev Changes', () => { }); }); + describe('modules/*', () => { + it("should return 'full-restart' when one of the changed files is in the WXT modules folder", () => { + const modulesDir = '/root/modules'; + setFakeWxt({ + config: { + modulesDir, + }, + }); + const changes = [ + '/root/src/public/image.svg', + `${modulesDir}/example.ts`, + ]; + const currentOutput: BuildOutput = { + manifest: fakeManifest(), + publicAssets: [], + steps: [], + }; + const expected: DevModeChange = { + type: 'full-restart', + }; + + const actual = detectDevChanges(changes, currentOutput); + + expect(actual).toEqual(expected); + }); + }); + describe('web-ext.config.ts', () => { it("should return 'browser-restart' when one of the changed files is the config file", () => { const runnerFile = '/root/web-ext.config.ts'; diff --git a/packages/wxt/src/core/utils/building/detect-dev-changes.ts b/packages/wxt/src/core/utils/building/detect-dev-changes.ts index 9f7ffbd8..b5884288 100644 --- a/packages/wxt/src/core/utils/building/detect-dev-changes.ts +++ b/packages/wxt/src/core/utils/building/detect-dev-changes.ts @@ -26,7 +26,11 @@ import { wxt } from '../../wxt'; * - Background script is changed * - Manifest is different * - Restart browser - * - Config file changed (wxt.config.ts, .env, web-ext.config.ts, etc) + * - web-ext.config.ts (runner config changes) + * - Full dev server restart + * - wxt.config.ts (main config file) + * - modules/* (any file related to WXT modules) + * - .env (environment variable changed could effect build) */ export function detectDevChanges( changedFiles: string[], @@ -38,6 +42,11 @@ export function detectDevChanges( ); if (isConfigChange) return { type: 'full-restart' }; + const isWxtModuleChange = some(changedFiles, (file) => + file.startsWith(wxt.config.modulesDir), + ); + if (isWxtModuleChange) return { type: 'full-restart' }; + const isRunnerChange = some( changedFiles, (file) => file === wxt.config.runnerConfig.configFile, diff --git a/packages/wxt/src/core/wxt.ts b/packages/wxt/src/core/wxt.ts index 5c6c5194..eaad5233 100644 --- a/packages/wxt/src/core/wxt.ts +++ b/packages/wxt/src/core/wxt.ts @@ -1,12 +1,4 @@ -import { - InlineConfig, - ResolvedConfig, - Wxt, - WxtCommand, - WxtDevServer, - WxtHooks, - WxtModule, -} from '../types'; +import { InlineConfig, Wxt, WxtCommand, WxtHooks, WxtModule } from '../types'; import { resolveConfig } from './resolve-config'; import { createHooks } from 'hookable'; import { createWxtPackageManager } from './package-managers'; @@ -26,7 +18,6 @@ export let wxt: Wxt; export async function registerWxt( command: WxtCommand, inlineConfig: InlineConfig = {}, - getServer?: (config: ResolvedConfig) => Promise, ): Promise { // Default NODE_ENV environment variable before other packages, like vite, do it // See https://github.com/wxt-dev/wxt/issues/873#issuecomment-2254555523 @@ -34,8 +25,7 @@ export async function registerWxt( const hooks = createHooks(); const config = await resolveConfig(inlineConfig, command); - const server = await getServer?.(config); - const builder = await createViteBuilder(config, hooks, server); + const builder = await createViteBuilder(config, hooks, () => wxt.server); const pm = await createWxtPackageManager(config.root); wxt = { @@ -51,27 +41,25 @@ export async function registerWxt( }, pm, builder, - server, + server: undefined, }; - // Initialize modules - const initModule = async (module: WxtModule) => { - if (module.hooks) wxt.hooks.addHooks(module.hooks); - await module.setup?.( - wxt, - // @ts-expect-error: Untyped configKey field - module.configKey ? config[module.configKey] : undefined, - ); - }; - for (const builtinModule of builtinModules) await initModule(builtinModule); - for (const userModule of config.userModules) await initModule(userModule); + await initWxtModules(); +} + +export async function initWxtModules() { + // Call setup function and add hooks + for (const mod of builtinModules) await initWxtModule(mod); + for (const mod of wxt.config.userModules) await initWxtModule(mod); // Initialize hooks - wxt.hooks.addHooks(config.hooks); + wxt.hooks.addHooks(wxt.config.hooks); + + // Print order for debugging if (wxt.config.debug) { const order = [ ...builtinModules.map((module) => module.name), - ...config.userModules.map((module) => + ...wxt.config.userModules.map((module) => relative(wxt.config.root, module.id), ), 'wxt.config.ts > hooks', @@ -86,6 +74,22 @@ export async function registerWxt( await wxt.hooks.callHook('config:resolved', wxt); } +async function initWxtModule(module: WxtModule): Promise { + if (module.hooks) wxt.hooks.addHooks(module.hooks); + await module.setup?.( + wxt, + // @ts-expect-error: Untyped configKey field + module.configKey ? wxt.config[module.configKey] : undefined, + ); +} + +/** + * Unloads WXT modules. + */ +export function deinitWxtModules(): void { + wxt.hooks.removeAllHooks(); +} + /** * @internal ONLY USE FOR TESTING. * diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index 90da349e..f60d18ee 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -1243,7 +1243,7 @@ export interface Wxt { */ logger: Logger; /** - * Reload config file and update the `config` field with the result. + * Reload config file and update `wxt.config` with the result. */ reloadConfig: () => Promise; /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5884d35..80e4e061 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -384,9 +384,9 @@ importers: fs-extra: specifier: ^11.2.0 version: 11.2.0 - get-port: - specifier: ^7.1.0 - version: 7.1.0 + get-port-please: + specifier: ^3.1.2 + version: 3.1.2 giget: specifier: ^1.2.3 version: 1.2.3 @@ -3006,9 +3006,8 @@ packages: get-func-name@2.0.2: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - get-port@7.1.0: - resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} - engines: {node: '>=16'} + get-port-please@3.1.2: + resolution: {integrity: sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==} get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} @@ -3042,12 +3041,10 @@ packages: glob@6.0.4: resolution: {integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==} - deprecated: Glob versions prior to v9 are no longer supported glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported global-dirs@3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} @@ -3177,7 +3174,6 @@ packages: inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -4287,7 +4283,6 @@ packages: rimraf@2.4.5: resolution: {integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==} - deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rollup-plugin-dts@6.1.1: @@ -7586,7 +7581,7 @@ snapshots: get-func-name@2.0.2: {} - get-port@7.1.0: {} + get-port-please@3.1.2: {} get-stream@5.2.0: dependencies: From db41f270729fb5514a265e04fc3a927df86947c5 Mon Sep 17 00:00:00 2001 From: Aaron Date: Sat, 16 Nov 2024 09:29:32 -0600 Subject: [PATCH 047/130] feat: New `server:created`, `server:started`, and `server:closed` hooks (#1179) --- packages/wxt/e2e/tests/hooks.test.ts | 19 +++++++++++++++++++ packages/wxt/e2e/tests/modules.test.ts | 2 +- packages/wxt/src/core/create-server.ts | 8 +++++++- packages/wxt/src/types.ts | 26 ++++++++++++++++++++------ 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/packages/wxt/e2e/tests/hooks.test.ts b/packages/wxt/e2e/tests/hooks.test.ts index 0f9babc8..42665c23 100644 --- a/packages/wxt/e2e/tests/hooks.test.ts +++ b/packages/wxt/e2e/tests/hooks.test.ts @@ -21,6 +21,9 @@ const hooks: WxtHooks = { 'zip:sources:start': vi.fn(), 'zip:sources:done': vi.fn(), 'zip:done': vi.fn(), + 'server:created': vi.fn(), + 'server:started': vi.fn(), + 'server:closed': vi.fn(), }; function expectHooksToBeCalled( @@ -67,6 +70,9 @@ describe('Hooks', () => { 'zip:sources:start': false, 'zip:sources:done': false, 'zip:done': false, + 'server:created': false, + 'server:started': false, + 'server:closed': false, }); }); @@ -95,6 +101,9 @@ describe('Hooks', () => { 'zip:sources:start': false, 'zip:sources:done': false, 'zip:done': false, + 'server:created': false, + 'server:started': false, + 'server:closed': false, }); }); @@ -123,6 +132,9 @@ describe('Hooks', () => { 'zip:sources:start': false, 'zip:sources:done': false, 'zip:done': true, + 'server:created': false, + 'server:started': false, + 'server:closed': false, }); }); @@ -151,6 +163,9 @@ describe('Hooks', () => { 'zip:sources:start': true, 'zip:sources:done': true, 'zip:done': true, + 'server:created': false, + 'server:started': false, + 'server:closed': false, }); }); @@ -164,6 +179,7 @@ describe('Hooks', () => { disabled: true, }, }); + expect(hooks['server:closed']).not.toBeCalled(); await server.stop(); expectHooksToBeCalled({ @@ -185,6 +201,9 @@ describe('Hooks', () => { 'zip:sources:start': false, 'zip:sources:done': false, 'zip:done': false, + 'server:created': 1, + 'server:started': 1, + 'server:closed': 1, }); }); }); diff --git a/packages/wxt/e2e/tests/modules.test.ts b/packages/wxt/e2e/tests/modules.test.ts index 367099b5..11d86688 100644 --- a/packages/wxt/e2e/tests/modules.test.ts +++ b/packages/wxt/e2e/tests/modules.test.ts @@ -6,7 +6,7 @@ import { normalizePath } from '../../src/core/utils/paths'; describe('Module Helpers', () => { describe('options', () => { - it('should recieve the options defined in wxt.config.ts based on the configKey field', async () => { + it('should receive the options defined in wxt.config.ts based on the configKey field', async () => { const options = { key: '123' }; const reportOptions = vi.fn(); vi.stubGlobal('reportOptions', reportOptions); diff --git a/packages/wxt/src/core/create-server.ts b/packages/wxt/src/core/create-server.ts index 0a04eb39..c0b64339 100644 --- a/packages/wxt/src/core/create-server.ts +++ b/packages/wxt/src/core/create-server.ts @@ -42,7 +42,9 @@ export async function createServer( ): Promise { await registerWxt('serve', inlineConfig); - return (wxt.server = await createServerInternal()); + wxt.server = await createServerInternal(); + await wxt.hooks.callHook('server:created', wxt, wxt.server); + return wxt.server; } async function createServerInternal(): Promise { @@ -92,6 +94,8 @@ async function createServerInternal(): Promise { await builderServer.listen(); wxt.logger.success(`Started dev server @ ${server.origin}`); + await wxt.hooks.callHook('server:started', wxt, server); + await buildAndOpenBrowser(); // Register content scripts for the first time after the background starts up since they're not @@ -109,6 +113,8 @@ async function createServerInternal(): Promise { wasStopped = true; await runner.closeBrowser(); await builderServer.close(); + await wxt.hooks.callHook('server:closed', wxt, server); + deinitWxtModules(); server.currentOutput = undefined; }, diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index f60d18ee..da818b57 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -1100,7 +1100,8 @@ export type HookResult = Promise | void; export interface WxtHooks { /** - * Called only one time after WXT initialization, when the WXT instance is ready to work. + * Called after WXT modules are initialized, when the WXT instance is ready to + * be used. `wxt.server` isn't available yet, use `server:created` to get it. * @param wxt The configured WXT object */ ready: (wxt: Wxt) => HookResult; @@ -1196,39 +1197,52 @@ export interface WxtHooks { * @param wxt The configured WXT object */ 'zip:start': (wxt: Wxt) => HookResult; - /** * Called before zipping the extension files. * @param wxt The configured WXT object */ 'zip:extension:start': (wxt: Wxt) => HookResult; - /** * Called after zipping the extension files. * @param wxt The configured WXT object * @param zipPath The path to the created extension zip file */ 'zip:extension:done': (wxt: Wxt, zipPath: string) => HookResult; - /** * Called before zipping the source files (for Firefox). * @param wxt The configured WXT object */ 'zip:sources:start': (wxt: Wxt) => HookResult; - /** * Called after zipping the source files (for Firefox). * @param wxt The configured WXT object * @param zipPath The path to the created sources zip file */ 'zip:sources:done': (wxt: Wxt, zipPath: string) => HookResult; - /** * Called after the entire zip process is complete. * @param wxt The configured WXT object * @param zipFiles An array of paths to all created zip files */ 'zip:done': (wxt: Wxt, zipFiles: string[]) => HookResult; + /** + * Called when the dev server is created (and `wxt.server` is assigned). Server has not been started yet. + * @param wxt The configured WXT object + * @param server Same as `wxt.server`, the object WXT uses to control the dev server. + */ + 'server:created': (wxt: Wxt, server: WxtDevServer) => HookResult; + /** + * Called when the dev server is started. + * @param wxt The configured WXT object + * @param server Same as `wxt.server`, the object WXT uses to control the dev server. + */ + 'server:started': (wxt: Wxt, server: WxtDevServer) => HookResult; + /** + * Called when the dev server is stopped. + * @param wxt The configured WXT object + * @param server Same as `wxt.server`, the object WXT uses to control the dev server. + */ + 'server:closed': (wxt: Wxt, server: WxtDevServer) => HookResult; } export interface Wxt { From 226e6c9fbe5365abd7d5cd64ceda601da9b7d9cd Mon Sep 17 00:00:00 2001 From: Aaron Date: Sat, 16 Nov 2024 11:33:58 -0600 Subject: [PATCH 048/130] docs: Add FAQ around configuring browser startup --- docs/guide/resources/faq.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/guide/resources/faq.md b/docs/guide/resources/faq.md index 593a2508..7945e3ca 100644 --- a/docs/guide/resources/faq.md +++ b/docs/guide/resources/faq.md @@ -18,6 +18,14 @@ To list the content scripts registered during development, open the service work await chrome.scripting.getRegisteredContentScripts(); ``` +## How do I disable opening the browser automatically during development? + +See https://wxt.dev/guide/essentials/config/browser-startup.html#disable-opening-browser + +## How do I stay logged into a website during development? + +See https://wxt.dev/guide/essentials/config/browser-startup.html#persist-data + ## My component library doesn't work in content scripts! Component libraries place their CSS in the document's `` by default. When using `createShadowRoot`, your UI is isolated from the document's styles because it's inside a ShadowRoot. From cfdc6b3e6be62773ffeb732ef3aef87463085f9e Mon Sep 17 00:00:00 2001 From: Patato <22197@satitpatumwan.ac.th> Date: Mon, 18 Nov 2024 03:58:09 +0700 Subject: [PATCH 049/130] chore: Update Svelte template to Svelte 5 (#1184) Co-authored-by: Aaron --- templates/svelte/src/entrypoints/popup/main.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/svelte/src/entrypoints/popup/main.ts b/templates/svelte/src/entrypoints/popup/main.ts index 7ab96ab2..65a15e29 100644 --- a/templates/svelte/src/entrypoints/popup/main.ts +++ b/templates/svelte/src/entrypoints/popup/main.ts @@ -1,7 +1,8 @@ -import './app.css'; +import { mount } from 'svelte'; import App from './App.svelte'; +import './app.css'; -const app = new App({ +const app = mount(App, { target: document.getElementById('app')!, }); From 8c3d75618729b763472ffb272aa2cb58d26db402 Mon Sep 17 00:00:00 2001 From: Nithur <49608085+Nithur-M@users.noreply.github.com> Date: Wed, 20 Nov 2024 14:48:15 +0530 Subject: [PATCH 050/130] docs: Add "Wandpen" to homepage (#1191) Co-authored-by: Florian Metz --- docs/.vitepress/components/UsingWxtSection.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/.vitepress/components/UsingWxtSection.vue b/docs/.vitepress/components/UsingWxtSection.vue index 1f08b491..2722f2c4 100644 --- a/docs/.vitepress/components/UsingWxtSection.vue +++ b/docs/.vitepress/components/UsingWxtSection.vue @@ -56,6 +56,7 @@ const chromeExtensionIds = [ 'lfknakglefggmdkjdfhhofkjnnolffkh', // Text Search Pro - Search by case and whole-word match! 'mbenhbocjckkbaojacmaepiameldglij', // Invoice Generator 'phlfhkmdofajnbhgmbmjkbkdgppgoppb', // Monthly Bill Tracker + 'macmkmchfoclhpbncclinhjflmdkaoom', // Wandpen - Instantly improve your writing with AI ]; const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds); From b8ecbc5a54ffd6b78c098a4739b67af50a435d0d Mon Sep 17 00:00:00 2001 From: Florian Metz Date: Wed, 20 Nov 2024 15:46:42 +0100 Subject: [PATCH 051/130] docs: fix analyze typo in type (#1187) --- packages/wxt/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index da818b57..0582f81e 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -271,7 +271,7 @@ export interface InlineConfig { analysis?: { /** * Explicitly include bundle analysis when running `wxt build`. This can be overridden by the - * command line `--analysis` option. + * command line `--analyze` option. * * @default false */ From 4c511d57285af29ccb6be481a409cdf456cdb106 Mon Sep 17 00:00:00 2001 From: Tran Ngoc Tan Date: Thu, 21 Nov 2024 09:07:45 +0700 Subject: [PATCH 052/130] docs: Add "YouTube Hider" to homepage (#1193) --- docs/.vitepress/components/UsingWxtSection.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/.vitepress/components/UsingWxtSection.vue b/docs/.vitepress/components/UsingWxtSection.vue index 2722f2c4..4509ec9f 100644 --- a/docs/.vitepress/components/UsingWxtSection.vue +++ b/docs/.vitepress/components/UsingWxtSection.vue @@ -57,6 +57,7 @@ const chromeExtensionIds = [ 'mbenhbocjckkbaojacmaepiameldglij', // Invoice Generator 'phlfhkmdofajnbhgmbmjkbkdgppgoppb', // Monthly Bill Tracker 'macmkmchfoclhpbncclinhjflmdkaoom', // Wandpen - Instantly improve your writing with AI + 'lhmgechokhmdekdpgkkemoeecelcaonm', // YouTube Hider - Remove Comments By Keywords, Usernames & Tools ]; const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds); From 190c87d120084ebc146429368fdc8aba19067d8b Mon Sep 17 00:00:00 2001 From: Mira Dobrovolskaya Date: Thu, 21 Nov 2024 20:15:10 +0300 Subject: [PATCH 053/130] docs: fix typo in entrypoints options example (#1194) --- docs/guide/essentials/entrypoints.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/guide/essentials/entrypoints.md b/docs/guide/essentials/entrypoints.md index eaac9f6b..4b89ad93 100644 --- a/docs/guide/essentials/entrypoints.md +++ b/docs/guide/essentials/entrypoints.md @@ -304,10 +304,7 @@ Follow the [Devtools Example](https://github.com/wxt-dev/examples/tree/main/exam - + Options Title From 90bb145d91f08faa13dae6ad17071ca36cb1441d Mon Sep 17 00:00:00 2001 From: Jaguar Zhou Date: Fri, 22 Nov 2024 22:21:06 +0800 Subject: [PATCH 054/130] chore(vue-template): remove references to Vetur and TypeScript Vue Plugin (Volar) (#1197) --- templates/vue/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/vue/README.md b/templates/vue/README.md index 8e0cb6a1..a6fa4835 100644 --- a/templates/vue/README.md +++ b/templates/vue/README.md @@ -4,4 +4,4 @@ This template should help get you started developing with Vue 3 in WXT. ## Recommended IDE Setup -- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). +- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar). From ffb99af719c2d929f11ba39e4a30ef745ed1a024 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 22 Nov 2024 08:36:29 -0600 Subject: [PATCH 055/130] fix: use `browser` for mv2 storage (#1200) --- packages/storage/src/index.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/storage/src/index.ts b/packages/storage/src/index.ts index e0cc3943..c72fcbe7 100644 --- a/packages/storage/src/index.ts +++ b/packages/storage/src/index.ts @@ -8,6 +8,13 @@ import { dequal } from 'dequal/lite'; import { Mutex } from 'async-mutex'; +const browser: typeof chrome = + // @ts-expect-error + globalThis.browser?.runtime?.id == null + ? globalThis.chrome + : // @ts-expect-error + globalThis.browser; + export const storage = createStorage(); function createStorage(): WxtStorage { @@ -197,7 +204,7 @@ function createStorage(): WxtStorage { const resultsMap: Record = {}; await Promise.all( Object.entries(areaToDriverMetaKeysMap).map(async ([area, keys]) => { - const areaRes = await chrome.storage[area as StorageArea].get( + const areaRes = await browser.storage[area as StorageArea].get( keys.map((key) => key.driverMetaKey), ); keys.forEach((key) => { @@ -474,7 +481,7 @@ function createStorage(): WxtStorage { function createDriver(storageArea: StorageArea): WxtStorageDriver { const getStorageArea = () => { - if (chrome.runtime == null) { + if (browser.runtime == null) { throw Error( [ "'wxt/storage' must be loaded in a web extension environment", @@ -483,15 +490,15 @@ function createDriver(storageArea: StorageArea): WxtStorageDriver { ].join('\n'), ); } - if (chrome.storage == null) { + if (browser.storage == null) { throw Error( "You must add the 'storage' permission to your manifest to use 'wxt/storage'", ); } - const area = chrome.storage[storageArea]; + const area = browser.storage[storageArea]; if (area == null) - throw Error(`"chrome.storage.${storageArea}" is undefined`); + throw Error(`"browser.storage.${storageArea}" is undefined`); return area; }; const watchListeners = new Set<(changes: StorageAreaChanges) => void>(); From 9b54bc47042ba3407b645bf75799dc84426c7c20 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 22 Nov 2024 14:41:52 +0000 Subject: [PATCH 056/130] chore(release): @wxt-dev/storage v1.0.1 --- packages/storage/CHANGELOG.md | 22 +++++++++++++++++++++- packages/storage/package.json | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/storage/CHANGELOG.md b/packages/storage/CHANGELOG.md index c9f360d1..31cc2e31 100644 --- a/packages/storage/CHANGELOG.md +++ b/packages/storage/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## v1.0.1 + +[compare changes](https://github.com/wxt-dev/wxt/compare/storage-v1.0.0...storage-v1.0.1) + +### 🩹 Fixes + +- Use `browser` for mv2 storage ([#1200](https://github.com/wxt-dev/wxt/pull/1200)) + +### 📖 Documentation + +- Cleanup changelog ([f5b7f7e](https://github.com/wxt-dev/wxt/commit/f5b7f7e)) + +### 🏡 Chore + +- Init changelog for storage package ([6fc227b](https://github.com/wxt-dev/wxt/commit/6fc227b)) + +### ❤️ Contributors + +- Aaron ([@aklinker1](http://github.com/aklinker1)) + ## v1.0.0 Extracted `wxt/storage` into it's own package, `@wxt-dev/storage`! @@ -11,4 +31,4 @@ It's still shipped inside WXT and accessible via `wxt/storage`, but now: [Read the docs](https://wxt.dev/storage.html) for more details. -> This is apart of the v1.0 initiative for WXT. +> This is apart of the v1.0 initiative for WXT. \ No newline at end of file diff --git a/packages/storage/package.json b/packages/storage/package.json index 4412f662..83b29f95 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,7 +1,7 @@ { "name": "@wxt-dev/storage", "description": "Web extension storage API provided by WXT, supports all browsers.", - "version": "1.0.0", + "version": "1.0.1", "type": "module", "repository": { "type": "git", From 236e099b819b69f2c15b4061c55a157f76670f1b Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 22 Nov 2024 08:53:16 -0600 Subject: [PATCH 057/130] docs: Update windows config for persisting browser profile --- docs/guide/essentials/config/browser-startup.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/guide/essentials/config/browser-startup.md b/docs/guide/essentials/config/browser-startup.md index e5daa60c..24200805 100644 --- a/docs/guide/essentials/config/browser-startup.md +++ b/docs/guide/essentials/config/browser-startup.md @@ -62,7 +62,8 @@ import { resolve } from 'node:path'; export default defineRunnerConfig({ // On Windows, the path must be absolute - chromiumArgs: [`--user-data-dir="${resolve('.wxt/chrome-data')}"`], + chromiumProfile: resolve('.wxt/chrome-data'), + keepProfileChanges: true, }); ``` From 5a3790a7d3595b7b44cf1824c569b379485536dd Mon Sep 17 00:00:00 2001 From: Nishu Date: Fri, 22 Nov 2024 20:24:05 +0530 Subject: [PATCH 058/130] fix: ESLint config being generated when ESLint is not installed. (#1198) Co-authored-by: Aaron --- packages/wxt/e2e/tests/auto-imports.test.ts | 34 +++++++++++++++++++++ packages/wxt/src/core/resolve-config.ts | 3 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/wxt/e2e/tests/auto-imports.test.ts b/packages/wxt/e2e/tests/auto-imports.test.ts index b9cd24bb..f8b58e4e 100644 --- a/packages/wxt/e2e/tests/auto-imports.test.ts +++ b/packages/wxt/e2e/tests/auto-imports.test.ts @@ -152,6 +152,40 @@ describe('Auto Imports', () => { ).toMatchSnapshot(); }); + it('"enabled: false" should NOT output an ESlint config file', async () => { + const project = new TestProject(); + project.addFile('entrypoints/popup.html', ``); + + await project.prepare({ + imports: { + eslintrc: { + enabled: false, + }, + }, + }); + + expect(await project.fileExists('.wxt/eslint-auto-imports.mjs')).toBe( + false, + ); + expect(await project.fileExists('.wxt/eslintrc-auto-import.json')).toBe( + false, + ); + }); + + it('should NOT output an ESlint config file by default', async () => { + const project = new TestProject(); + project.addFile('entrypoints/popup.html', ``); + + await project.prepare(); + + expect(await project.fileExists('.wxt/eslint-auto-imports.mjs')).toBe( + false, + ); + expect(await project.fileExists('.wxt/eslintrc-auto-import.json')).toBe( + false, + ); + }); + it('should allow customizing the output', async () => { const project = new TestProject(); project.addFile('entrypoints/popup.html', ``); diff --git a/packages/wxt/src/core/resolve-config.ts b/packages/wxt/src/core/resolve-config.ts index 0f91ba51..7d739088 100644 --- a/packages/wxt/src/core/resolve-config.ts +++ b/packages/wxt/src/core/resolve-config.ts @@ -392,10 +392,11 @@ async function getUnimportEslintOptions( case 'auto': const version = await getEslintVersion(); let major = parseInt(version[0]); + if (isNaN(major)) eslintEnabled = false; if (major <= 8) eslintEnabled = 8; else if (major >= 9) eslintEnabled = 9; // NaN - else eslintEnabled = 8; + else eslintEnabled = false; break; case true: eslintEnabled = 8; From 54baf21490c996cce441a5183e04a8c89725941c Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 25 Nov 2024 09:29:34 -0600 Subject: [PATCH 059/130] fix: Validate entrypoints after hooks have modified them --- .../wxt/src/core/utils/building/find-entrypoints.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/wxt/src/core/utils/building/find-entrypoints.ts b/packages/wxt/src/core/utils/building/find-entrypoints.ts index 72ac7436..12ceb099 100644 --- a/packages/wxt/src/core/utils/building/find-entrypoints.ts +++ b/packages/wxt/src/core/utils/building/find-entrypoints.ts @@ -77,10 +77,6 @@ export async function findEntrypoints(): Promise { return results; }, []); - // Validation - preventNoEntrypoints(entrypointInfos); - preventDuplicateEntrypointNames(entrypointInfos); - // Import entrypoints to get their config let hasBackground = false; const env = createExtensionEnvironment(); @@ -174,6 +170,10 @@ export async function findEntrypoints(): Promise { wxt.logger.debug(`${wxt.config.browser} entrypoints:`, targetEntrypoints); await wxt.hooks.callHook('entrypoints:resolved', wxt, targetEntrypoints); + // Validation + preventNoEntrypoints(targetEntrypoints); + preventDuplicateEntrypointNames(targetEntrypoints); + return targetEntrypoints; } @@ -187,7 +187,7 @@ interface EntrypointInfo { skipped: boolean; } -function preventDuplicateEntrypointNames(files: EntrypointInfo[]) { +function preventDuplicateEntrypointNames(files: Entrypoint[]) { const namesToPaths = files.reduce>( (map, { name, inputPath }) => { map[name] ??= []; @@ -216,7 +216,7 @@ function preventDuplicateEntrypointNames(files: EntrypointInfo[]) { } } -function preventNoEntrypoints(files: EntrypointInfo[]) { +function preventNoEntrypoints(files: Entrypoint[]) { if (files.length === 0) { throw Error(`No entrypoints found in ${wxt.config.entrypointsDir}`); } From a2924d872a6c6f21d1dfbe3c11280d7c43bb39f9 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 25 Nov 2024 15:08:55 -0600 Subject: [PATCH 060/130] fix: Revert "Validate entrypoints after hooks have modified them (54baf21)" This reverts commit 54baf21490c996cce441a5183e04a8c89725941c. --- .../wxt/src/core/utils/building/find-entrypoints.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/wxt/src/core/utils/building/find-entrypoints.ts b/packages/wxt/src/core/utils/building/find-entrypoints.ts index 12ceb099..72ac7436 100644 --- a/packages/wxt/src/core/utils/building/find-entrypoints.ts +++ b/packages/wxt/src/core/utils/building/find-entrypoints.ts @@ -77,6 +77,10 @@ export async function findEntrypoints(): Promise { return results; }, []); + // Validation + preventNoEntrypoints(entrypointInfos); + preventDuplicateEntrypointNames(entrypointInfos); + // Import entrypoints to get their config let hasBackground = false; const env = createExtensionEnvironment(); @@ -170,10 +174,6 @@ export async function findEntrypoints(): Promise { wxt.logger.debug(`${wxt.config.browser} entrypoints:`, targetEntrypoints); await wxt.hooks.callHook('entrypoints:resolved', wxt, targetEntrypoints); - // Validation - preventNoEntrypoints(targetEntrypoints); - preventDuplicateEntrypointNames(targetEntrypoints); - return targetEntrypoints; } @@ -187,7 +187,7 @@ interface EntrypointInfo { skipped: boolean; } -function preventDuplicateEntrypointNames(files: Entrypoint[]) { +function preventDuplicateEntrypointNames(files: EntrypointInfo[]) { const namesToPaths = files.reduce>( (map, { name, inputPath }) => { map[name] ??= []; @@ -216,7 +216,7 @@ function preventDuplicateEntrypointNames(files: Entrypoint[]) { } } -function preventNoEntrypoints(files: Entrypoint[]) { +function preventNoEntrypoints(files: EntrypointInfo[]) { if (files.length === 0) { throw Error(`No entrypoints found in ${wxt.config.entrypointsDir}`); } From 767a3a987c9b7b516ad8661a65534f1d53403e30 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 25 Nov 2024 15:25:42 -0600 Subject: [PATCH 061/130] chore(vue-template): Fix incompatible typescript and vue-tsc versions (#1208) --- templates/vue/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/vue/package.json b/templates/vue/package.json index 229ed2b1..2bd62693 100644 --- a/templates/vue/package.json +++ b/templates/vue/package.json @@ -20,7 +20,7 @@ "devDependencies": { "@types/chrome": "^0.0.280", "@wxt-dev/module-vue": "^1.0.1", - "typescript": "^5.6.3", + "typescript": "5.6.3", "vue-tsc": "^2.1.10", "wxt": "^0.19.13" } From aab4244d842702872a42c1da18005fc8fa88f4cd Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 26 Nov 2024 14:26:11 -0600 Subject: [PATCH 062/130] fix: Update `vite` dependency range to support v6 (#1215) --- packages/wxt/package.json | 2 +- pnpm-lock.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/wxt/package.json b/packages/wxt/package.json index fba6bb81..6da00a06 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -122,7 +122,7 @@ "publish-browser-extension": "^2.2.2", "scule": "^1.3.0", "unimport": "^3.13.1", - "vite": "^5.4.11", + "vite": "^5.0.0 || ^6.0.0", "vite-node": "^2.1.4", "web-ext-run": "^0.2.1", "webextension-polyfill": "^0.12.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 80e4e061..ddf65b69 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -451,7 +451,7 @@ importers: specifier: ^3.13.1 version: 3.13.1(rollup@4.24.0)(webpack-sources@3.2.3) vite: - specifier: ^5.4.11 + specifier: ^5.0.0 || ^6.0.0 version: 5.4.11(@types/node@20.17.6)(sass@1.80.7) vite-node: specifier: ^2.1.4 From 241c907c65c23dfb786a8faf35ba5a435a4d2dbe Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 27 Nov 2024 10:14:32 -0600 Subject: [PATCH 063/130] fix: Automatically convert MV3 `content_security_policy` to MV2 (#1168) Co-authored-by: windmillcode0 Co-authored-by: windmillcode0 --- .../src/core/utils/__tests__/manifest.test.ts | 35 +++++++++++ packages/wxt/src/core/utils/manifest.ts | 58 +++++++++++-------- pnpm-lock.yaml | 4 ++ 3 files changed, 74 insertions(+), 23 deletions(-) diff --git a/packages/wxt/src/core/utils/__tests__/manifest.test.ts b/packages/wxt/src/core/utils/__tests__/manifest.test.ts index 1af508dc..459025f7 100644 --- a/packages/wxt/src/core/utils/__tests__/manifest.test.ts +++ b/packages/wxt/src/core/utils/__tests__/manifest.test.ts @@ -1581,6 +1581,41 @@ describe('Manifest Utils', () => { permissions: ['tabs', 'scripting'], }); }); + + it('should convert MV3 CSP object to MV2 CSP string with localhost for MV2', async () => { + const entrypoints: Entrypoint[] = []; + const buildOutput = fakeBuildOutput(); + const inputCsp = + "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"; + const expectedCsp = + "script-src 'self' 'wasm-unsafe-eval' http://localhost:3000; object-src 'self';"; + + // Setup WXT for Firefox and serve command + setFakeWxt({ + config: { + browser: 'firefox', + command: 'serve', + manifestVersion: 2, + manifest: { + content_security_policy: { + extension_pages: inputCsp, + }, + }, + }, + server: fakeWxtDevServer({ + port: 3000, + hostname: 'localhost', + origin: 'http://localhost:3000', + }), + }); + + const { manifest: actual } = await generateManifest( + entrypoints, + buildOutput, + ); + + expect(actual.content_security_policy).toEqual(expectedCsp); + }); }); }); diff --git a/packages/wxt/src/core/utils/manifest.ts b/packages/wxt/src/core/utils/manifest.ts index 17a9feb1..98ca5da5 100644 --- a/packages/wxt/src/core/utils/manifest.ts +++ b/packages/wxt/src/core/utils/manifest.ts @@ -117,11 +117,12 @@ export async function generateManifest( if (wxt.config.manifestVersion === 2) { convertWebAccessibleResourcesToMv2(manifest); convertActionToMv2(manifest); + convertCspToMv2(manifest); moveHostPermissionsToPermissions(manifest); } if (wxt.config.manifestVersion === 3) { - validateMv3WebAccessbileResources(manifest); + validateMv3WebAccessibleResources(manifest); } stripKeys(manifest); @@ -143,7 +144,7 @@ export async function generateManifest( } /** - * Removes suffixes from the version, like X.Y.Z-alpha1 (which brosers don't allow), so it's a + * Removes suffixes from the version, like X.Y.Z-alpha1 (which browsers don't allow), so it's a * simple version number, like X or X.Y or X.Y.Z, which browsers allow. */ function simplifyVersion(versionName: string): string { @@ -467,34 +468,28 @@ function addDevModeCsp(manifest: Manifest.WebExtensionManifest): void { } const extensionPagesCsp = new ContentSecurityPolicy( - manifest.manifest_version === 3 - ? // @ts-expect-error: extension_pages is not typed - (manifest.content_security_policy?.extension_pages ?? - "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';") // default extension_pages CSP for MV3 - : (manifest.content_security_policy ?? - "script-src 'self'; object-src 'self';"), // default CSP for MV2 + // @ts-expect-error: extension_pages exists, we convert MV2 CSPs to this earlier in the process + manifest.content_security_policy?.extension_pages ?? + (manifest.manifest_version === 3 + ? DEFAULT_MV3_EXTENSION_PAGES_CSP + : DEFAULT_MV2_CSP), ); const sandboxCsp = new ContentSecurityPolicy( // @ts-expect-error: sandbox is not typed - manifest.content_security_policy?.sandbox ?? - "sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self';", // default sandbox CSP for MV3 + manifest.content_security_policy?.sandbox ?? DEFAULT_MV3_SANDBOX_CSP, ); - if (wxt.server) { + if (wxt.config.command === 'serve') { extensionPagesCsp.add('script-src', allowedCsp); sandboxCsp.add('script-src', allowedCsp); } - if (manifest.manifest_version === 3) { - manifest.content_security_policy ??= {}; - // @ts-expect-error: extension_pages is not typed - manifest.content_security_policy.extension_pages = - extensionPagesCsp.toString(); - // @ts-expect-error: sandbox is not typed - manifest.content_security_policy.sandbox = sandboxCsp.toString(); - } else { - manifest.content_security_policy = extensionPagesCsp.toString(); - } + manifest.content_security_policy ??= {}; + // @ts-expect-error: extension_pages is not typed + manifest.content_security_policy.extension_pages = + extensionPagesCsp.toString(); + // @ts-expect-error: sandbox is not typed + manifest.content_security_policy.sandbox = sandboxCsp.toString(); } function addDevModePermissions(manifest: Manifest.WebExtensionManifest) { @@ -613,7 +608,7 @@ export function stripPathFromMatchPattern(pattern: string) { /** * Converts all MV3 web accessible resources to their MV2 forms. MV3 web accessible resources are * generated in this file, and may be defined by the user in their manifest. In both cases, when - * targetting MV2, automatically convert their definitions down to the basic MV2 array. + * targeting MV2, automatically convert their definitions down to the basic MV2 array. */ export function convertWebAccessibleResourcesToMv2( manifest: Manifest.WebExtensionManifest, @@ -652,10 +647,21 @@ function convertActionToMv2(manifest: Manifest.WebExtensionManifest): void { manifest.browser_action = manifest.action; } +function convertCspToMv2(manifest: Manifest.WebExtensionManifest): void { + if ( + typeof manifest.content_security_policy === 'string' || + manifest.content_security_policy?.extension_pages == null + ) + return; + + manifest.content_security_policy = + manifest.content_security_policy.extension_pages; +} + /** * Make sure all resources are in MV3 format. If not, add a wanring */ -export function validateMv3WebAccessbileResources( +export function validateMv3WebAccessibleResources( manifest: Manifest.WebExtensionManifest, ): void { if (manifest.web_accessible_resources == null) return; @@ -718,3 +724,9 @@ const mv3OnlyKeys = [ 'side_panel', ]; const firefoxMv3OnlyKeys = ['host_permissions']; + +const DEFAULT_MV3_EXTENSION_PAGES_CSP = + "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"; +const DEFAULT_MV3_SANDBOX_CSP = + "sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self';"; +const DEFAULT_MV2_CSP = "script-src 'self'; object-src 'self';"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ddf65b69..cd620fdd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3041,10 +3041,12 @@ packages: glob@6.0.4: resolution: {integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==} + deprecated: Glob versions prior to v9 are no longer supported glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported global-dirs@3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} @@ -3174,6 +3176,7 @@ packages: inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -4283,6 +4286,7 @@ packages: rimraf@2.4.5: resolution: {integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rollup-plugin-dts@6.1.1: From 586fe3950cd13dddd9eda6da0f9a3ba51f077bfd Mon Sep 17 00:00:00 2001 From: 1natsu <1natsu172@users.noreply.github.com> Date: Thu, 28 Nov 2024 13:39:25 +0900 Subject: [PATCH 064/130] fix: Correctly remove child elements with integrated UI remove (#1219) --- packages/wxt/src/client/content-scripts/ui/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/wxt/src/client/content-scripts/ui/index.ts b/packages/wxt/src/client/content-scripts/ui/index.ts index ee1097b6..5582a2db 100644 --- a/packages/wxt/src/client/content-scripts/ui/index.ts +++ b/packages/wxt/src/client/content-scripts/ui/index.ts @@ -34,6 +34,7 @@ export function createIntegratedUi( }; const remove = () => { options.onRemove?.(mounted); + wrapper.replaceChildren(); wrapper.remove(); mounted = undefined; }; From aae5f0789d60a5406eeb5c8f119e136ac519ea20 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 28 Nov 2024 00:00:18 -0600 Subject: [PATCH 065/130] fix: Make content script `matches` optional (#1220) --- docs/guide/essentials/scripting.md | 13 ++++++++----- packages/wxt/src/core/utils/content-scripts.ts | 2 +- packages/wxt/src/core/utils/manifest.ts | 6 +++--- packages/wxt/src/types.ts | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/guide/essentials/scripting.md b/docs/guide/essentials/scripting.md index 53b8a304..468e2c77 100644 --- a/docs/guide/essentials/scripting.md +++ b/docs/guide/essentials/scripting.md @@ -12,15 +12,18 @@ When using `browser.scripting.executeScript`, you can execute content scripts or // entrypoints/background.ts const res = await browser.scripting.executeScript({ target: { tabId }, - files: ['injected.js'], + files: ['content-scripts/example.js'], }); console.log(res); // "Hello John!" ``` ```ts -// entrypoints/injected.js -export default defineUnlistedScript(() => { - console.log('Script was injected!'); - return 'Hello John!'; +// entrypoints/example.content.ts +export default defineContentScript({ + registration: 'runtime', + main(ctx) { + console.log('Script was executed!'); + return 'Hello John!'; + }, }); ``` diff --git a/packages/wxt/src/core/utils/content-scripts.ts b/packages/wxt/src/core/utils/content-scripts.ts index f1c7dabf..3ba1eb18 100644 --- a/packages/wxt/src/core/utils/content-scripts.ts +++ b/packages/wxt/src/core/utils/content-scripts.ts @@ -51,7 +51,7 @@ export function mapWxtOptionsToContentScript( css: string[] | undefined, ): Manifest.ContentScript { return { - matches: options.matches, + matches: options.matches ?? [], all_frames: options.allFrames, match_about_blank: options.matchAboutBlank, exclude_globs: options.excludeGlobs, diff --git a/packages/wxt/src/core/utils/manifest.ts b/packages/wxt/src/core/utils/manifest.ts index 98ca5da5..9f785cd8 100644 --- a/packages/wxt/src/core/utils/manifest.ts +++ b/packages/wxt/src/core/utils/manifest.ts @@ -363,7 +363,7 @@ function addEntrypoints( // at runtime if (wxt.config.command === 'serve' && wxt.config.manifestVersion === 3) { contentScripts.forEach((script) => { - script.options.matches.forEach((matchPattern) => { + script.options.matches?.forEach((matchPattern) => { addHostPermission(manifest, matchPattern); }); }); @@ -406,7 +406,7 @@ function addEntrypoints( ); } runtimeContentScripts.forEach((script) => { - script.options.matches.forEach((matchPattern) => { + script.options.matches?.forEach((matchPattern) => { addHostPermission(manifest, matchPattern); }); }); @@ -547,7 +547,7 @@ export function getContentScriptCssWebAccessibleResources( resources.push({ resources: [cssFile], - matches: script.options.matches.map((matchPattern) => + matches: script.options.matches?.map((matchPattern) => stripPathFromMatchPattern(matchPattern), ), }); diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index 0582f81e..3205559c 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -588,7 +588,7 @@ export interface BackgroundEntrypointOptions extends BaseEntrypointOptions { export interface BaseContentScriptEntrypointOptions extends BaseEntrypointOptions { - matches: PerBrowserOption; + matches?: PerBrowserOption; /** * See https://developer.chrome.com/docs/extensions/mv3/content_scripts/ * @default "documentIdle" From f51f5dd19ffc4f56ba80d05b7661a1c828f4ef37 Mon Sep 17 00:00:00 2001 From: Jaguar Zhou Date: Thu, 28 Nov 2024 22:33:11 +0800 Subject: [PATCH 066/130] doc: remove dead links generated by typedoc (#1222) --- docs/typedoc.json | 11 +++++++++-- package.json | 1 + pnpm-lock.yaml | 13 +++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/typedoc.json b/docs/typedoc.json index 5ac20200..3d8541ef 100644 --- a/docs/typedoc.json +++ b/docs/typedoc.json @@ -2,11 +2,18 @@ "$schema": "https://typedoc.org/schema.json", "entryPointStrategy": "packages", "entryPoints": ["../packages/wxt"], - "plugin": ["typedoc-plugin-markdown", "typedoc-vitepress-theme"], + "plugin": [ + "typedoc-plugin-markdown", + "typedoc-vitepress-theme", + "typedoc-plugin-frontmatter" + ], "out": "./api/reference", "githubPages": false, "excludePrivate": true, "excludeProtected": true, "excludeInternal": true, - "readme": "none" + "readme": "none", + "frontmatterGlobals": { + "editLink": false + } } diff --git a/package.json b/package.json index 53264f64..0214dcba 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "simple-git-hooks": "^2.11.1", "tsx": "4.15.7", "typedoc": "^0.25.4", + "typedoc-plugin-frontmatter": "^1.1.0", "typedoc-plugin-markdown": "4.0.0-next.23", "typedoc-vitepress-theme": "1.0.0-next.3", "typescript": "^5.6.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd620fdd..2b3828b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,6 +58,9 @@ importers: typedoc: specifier: ^0.25.4 version: 0.25.4(typescript@5.6.3) + typedoc-plugin-frontmatter: + specifier: ^1.1.0 + version: 1.1.0(typedoc-plugin-markdown@4.0.0-next.23(typedoc@0.25.4(typescript@5.6.3))) typedoc-plugin-markdown: specifier: 4.0.0-next.23 version: 4.0.0-next.23(typedoc@0.25.4(typescript@5.6.3)) @@ -4682,6 +4685,11 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + typedoc-plugin-frontmatter@1.1.0: + resolution: {integrity: sha512-4PW4V2xDY2hw+fEWmg7g6FBCIWZdiEE+tzjJ5K4JhurvJ0t0Vp0IE/0nuHGGIZVtV5WxPIed+GpiH1uZrpDquQ==} + peerDependencies: + typedoc-plugin-markdown: '>=4.3.0' + typedoc-plugin-markdown@4.0.0-next.23: resolution: {integrity: sha512-bKD0LnrQxUTbuDRiJfCWYLlzyERuskWldJ5eWKKuNY38xBOyBhSN8P+vD5KxlJoAifTOm76MK58sNrl4wgOoWg==} peerDependencies: @@ -9294,6 +9302,11 @@ snapshots: typedarray@0.0.6: {} + typedoc-plugin-frontmatter@1.1.0(typedoc-plugin-markdown@4.0.0-next.23(typedoc@0.25.4(typescript@5.6.3))): + dependencies: + typedoc-plugin-markdown: 4.0.0-next.23(typedoc@0.25.4(typescript@5.6.3)) + yaml: 2.5.1 + typedoc-plugin-markdown@4.0.0-next.23(typedoc@0.25.4(typescript@5.6.3)): dependencies: typedoc: 0.25.4(typescript@5.6.3) From e5723aab030ca12b46bc9c313805f72f70459f4a Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 28 Nov 2024 14:42:49 +0000 Subject: [PATCH 067/130] chore(release): wxt v0.19.17 --- packages/wxt/CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ packages/wxt/package.json | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/wxt/CHANGELOG.md b/packages/wxt/CHANGELOG.md index 04381a7b..280550d2 100644 --- a/packages/wxt/CHANGELOG.md +++ b/packages/wxt/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## v0.19.17 + +[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.16...wxt-v0.19.17) + +### 🚀 Enhancements + +- New `server:created`, `server:started`, and `server:closed` hooks ([#1179](https://github.com/wxt-dev/wxt/pull/1179)) + +### 🩹 Fixes + +- Re-initialize WXT modules correctly during development ([#1176](https://github.com/wxt-dev/wxt/pull/1176)) +- ESLint config being generated when ESLint is not installed. ([#1198](https://github.com/wxt-dev/wxt/pull/1198)) +- Validate entrypoints after hooks have modified them ([54baf21](https://github.com/wxt-dev/wxt/commit/54baf21)) +- Revert "Validate entrypoints after hooks have modified them (54baf21)" ([a2924d8](https://github.com/wxt-dev/wxt/commit/a2924d8)) +- Update `vite` dependency range to support v6 ([#1215](https://github.com/wxt-dev/wxt/pull/1215)) +- Automatically convert MV3 `content_security_policy` to MV2 ([#1168](https://github.com/wxt-dev/wxt/pull/1168)) +- Correctly remove child elements with integrated UI remove ([#1219](https://github.com/wxt-dev/wxt/pull/1219)) +- Make content script `matches` optional ([#1220](https://github.com/wxt-dev/wxt/pull/1220)) + +### 📖 Documentation + +- Fix analyze typo in type ([#1187](https://github.com/wxt-dev/wxt/pull/1187)) + +### ❤️ Contributors + +- Aaron ([@aklinker1](http://github.com/aklinker1)) +- 1natsu ([@1natsu172](http://github.com/1natsu172)) +- Nishu +- Florian Metz ([@Timeraa](http://github.com/Timeraa)) + ## v0.19.16 [compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.15...wxt-v0.19.16) diff --git a/packages/wxt/package.json b/packages/wxt/package.json index 6da00a06..4595a101 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -1,7 +1,7 @@ { "name": "wxt", "type": "module", - "version": "0.19.16", + "version": "0.19.17", "description": "Next gen framework for developing web extensions", "repository": { "type": "git", From 866eb0694ce6df2d7b1e3eedb77c400226b81ef5 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 28 Nov 2024 08:48:40 -0600 Subject: [PATCH 068/130] Cleanup changelog --- packages/wxt/CHANGELOG.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/wxt/CHANGELOG.md b/packages/wxt/CHANGELOG.md index 280550d2..9a852e8a 100644 --- a/packages/wxt/CHANGELOG.md +++ b/packages/wxt/CHANGELOG.md @@ -12,8 +12,6 @@ - Re-initialize WXT modules correctly during development ([#1176](https://github.com/wxt-dev/wxt/pull/1176)) - ESLint config being generated when ESLint is not installed. ([#1198](https://github.com/wxt-dev/wxt/pull/1198)) -- Validate entrypoints after hooks have modified them ([54baf21](https://github.com/wxt-dev/wxt/commit/54baf21)) -- Revert "Validate entrypoints after hooks have modified them (54baf21)" ([a2924d8](https://github.com/wxt-dev/wxt/commit/a2924d8)) - Update `vite` dependency range to support v6 ([#1215](https://github.com/wxt-dev/wxt/pull/1215)) - Automatically convert MV3 `content_security_policy` to MV2 ([#1168](https://github.com/wxt-dev/wxt/pull/1168)) - Correctly remove child elements with integrated UI remove ([#1219](https://github.com/wxt-dev/wxt/pull/1219)) @@ -27,7 +25,7 @@ - Aaron ([@aklinker1](http://github.com/aklinker1)) - 1natsu ([@1natsu172](http://github.com/1natsu172)) -- Nishu +- Nishu ([@nishu-murmu](https://github.com/nishu-murmu)) - Florian Metz ([@Timeraa](http://github.com/Timeraa)) ## v0.19.16 @@ -2669,4 +2667,4 @@ Initial release of WXT. Full support for production builds and initial toolkit f ### 🤖 CI - Create validation workflow ([#12](https://github.com/wxt-dev/wxt/pull/12)) -- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13)) \ No newline at end of file +- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13)) From 799d6870612598a1258d10f26f46bfc8a2b17afb Mon Sep 17 00:00:00 2001 From: roechsli Date: Thu, 28 Nov 2024 17:34:20 +0100 Subject: [PATCH 069/130] docs: Add "QA Compass" to homepage (#1223) --- docs/.vitepress/components/UsingWxtSection.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/.vitepress/components/UsingWxtSection.vue b/docs/.vitepress/components/UsingWxtSection.vue index 4509ec9f..bf67438e 100644 --- a/docs/.vitepress/components/UsingWxtSection.vue +++ b/docs/.vitepress/components/UsingWxtSection.vue @@ -58,6 +58,7 @@ const chromeExtensionIds = [ 'phlfhkmdofajnbhgmbmjkbkdgppgoppb', // Monthly Bill Tracker 'macmkmchfoclhpbncclinhjflmdkaoom', // Wandpen - Instantly improve your writing with AI 'lhmgechokhmdekdpgkkemoeecelcaonm', // YouTube Hider - Remove Comments By Keywords, Usernames & Tools + 'imgheieooppmahcgniieddodaliodeeg', // QA Compass - Record standardized bug reports easily ]; const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds); From 0e4953fbfc782d3c15c54c14fde43c9d9a12b481 Mon Sep 17 00:00:00 2001 From: Jaguar Zhou Date: Sat, 30 Nov 2024 02:07:09 +0800 Subject: [PATCH 070/130] fix: correct `dev.reloadCommand` restriction to consider only commands with suggested keys (#1226) --- packages/wxt/src/core/utils/manifest.ts | 11 ++++++++-- .../src/core/utils/testing/fake-objects.ts | 21 ++++++++++++------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/wxt/src/core/utils/manifest.ts b/packages/wxt/src/core/utils/manifest.ts index 9f785cd8..3409aaff 100644 --- a/packages/wxt/src/core/utils/manifest.ts +++ b/packages/wxt/src/core/utils/manifest.ts @@ -82,9 +82,16 @@ export async function generateManifest( // Add reload command in dev mode if (wxt.config.command === 'serve' && wxt.config.dev.reloadCommand) { - if (manifest.commands && Object.keys(manifest.commands).length >= 4) { + if ( + manifest.commands && + // If the following limit is exceeded, Chrome will fail to load the extension. + // Error: "Too many commands specified for 'commands': The maximum is 4." + Object.values(manifest.commands).filter( + (command) => command.suggested_key, + ).length >= 4 + ) { warnings.push([ - "Extension already has 4 registered commands, WXT's reload command is disabled", + "Extension already has 4 registered commands with suggested keys, WXT's reload command is disabled", ]); } else { manifest.commands ??= {}; diff --git a/packages/wxt/src/core/utils/testing/fake-objects.ts b/packages/wxt/src/core/utils/testing/fake-objects.ts index c097aeab..9fd1402b 100644 --- a/packages/wxt/src/core/utils/testing/fake-objects.ts +++ b/packages/wxt/src/core/utils/testing/fake-objects.ts @@ -4,7 +4,7 @@ import { resolve } from 'path'; import { faker } from '@faker-js/faker'; import merge from 'lodash.merge'; -import { Commands, type Manifest } from 'wxt/browser'; +import type { Manifest } from 'wxt/browser'; import { FsCache, ResolvedConfig, @@ -355,13 +355,18 @@ export const fakeBuildStepOutput = fakeObjectCreator(() => ({ entrypoints: fakeArray(fakeEntrypoint), })); -export const fakeManifestCommand = fakeObjectCreator(() => ({ - description: faker.string.sample(), - shortcut: `${faker.helpers.arrayElement(['ctrl', 'alt'])}+${faker.number.int({ - min: 0, - max: 9, - })}`, -})); +export const fakeManifestCommand = + fakeObjectCreator(() => ({ + description: faker.string.sample(), + suggested_key: { + default: `${faker.helpers.arrayElement(['ctrl', 'alt'])}+${faker.number.int( + { + min: 0, + max: 9, + }, + )}`, + }, + })); export const fakeDevServer = fakeObjectCreator(() => ({ hostname: 'localhost', From c92ed13d6682db92baea87571ffa20876001f17b Mon Sep 17 00:00:00 2001 From: Aaron Date: Sun, 1 Dec 2024 09:28:30 -0600 Subject: [PATCH 071/130] fix: Modify dev `vite.resolve.conditions` to support Vite 6 + Svelte 5 (#1230) --- packages/module-svelte/src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/module-svelte/src/index.ts b/packages/module-svelte/src/index.ts index 9c30db22..9cf51d6b 100644 --- a/packages/module-svelte/src/index.ts +++ b/packages/module-svelte/src/index.ts @@ -12,7 +12,7 @@ export default defineWxtModule({ setup(wxt, options) { const { vite } = options ?? {}; - addViteConfig(wxt, () => ({ + addViteConfig(wxt, ({ mode }) => ({ plugins: [ svelte({ // Using a svelte.config.js file causes a segmentation fault when importing the file @@ -21,6 +21,9 @@ export default defineWxtModule({ ...vite, }), ], + resolve: { + conditions: mode === 'development' ? ['browser'] : [], + }, })); addImportPreset(wxt, 'svelte'); From 90aaed202c992ff1b7d87319d08050050da27860 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 1 Dec 2024 15:33:52 +0000 Subject: [PATCH 072/130] chore(release): @wxt-dev/module-svelte v2.0.1 --- packages/module-svelte/CHANGELOG.md | 18 +++++++++++++++++- packages/module-svelte/package.json | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/module-svelte/CHANGELOG.md b/packages/module-svelte/CHANGELOG.md index c724f471..9fb937b6 100644 --- a/packages/module-svelte/CHANGELOG.md +++ b/packages/module-svelte/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## v2.0.1 + +[compare changes](https://github.com/wxt-dev/wxt/compare/module-svelte-v2.0.0...module-svelte-v2.0.1) + +### 🩹 Fixes + +- Modify dev `vite.resolve.conditions` to support Vite 6 + Svelte 5 ([#1230](https://github.com/wxt-dev/wxt/pull/1230)) + +### 🏡 Chore + +- **deps:** Upgrade all non-major dependencies ([#1164](https://github.com/wxt-dev/wxt/pull/1164)) + +### ❤️ Contributors + +- Aaron ([@aklinker1](http://github.com/aklinker1)) + ## v2.0.0 [⚠️ breaking changes](https://wxt.dev/guide/upgrade-guide/wxt) • [compare changes](https://github.com/wxt-dev/wxt/compare/module-svelte-v1.0.1...module-svelte-v2.0.0) @@ -51,4 +67,4 @@ To upgrade to svelte 5, just install `svelte@5`. ## v1.0.0 -Initial release 🎉 +Initial release 🎉 \ No newline at end of file diff --git a/packages/module-svelte/package.json b/packages/module-svelte/package.json index d12b55d6..62297a03 100644 --- a/packages/module-svelte/package.json +++ b/packages/module-svelte/package.json @@ -17,7 +17,7 @@ "email": "aaronklinker1+wxt@gmail.com" }, "license": "MIT", - "version": "2.0.0", + "version": "2.0.1", "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.mjs", From 7aa30afa7f4bd3b6f4821ad3768e1c9d93c8f0c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Dec 2024 16:13:47 -0600 Subject: [PATCH 073/130] chore(deps): bump codecov/codecov-action from 4 to 5 (#1237) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 5ca40ed3..0a9e9db5 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -36,7 +36,7 @@ jobs: - uses: oven-sh/setup-bun@v2 - name: pnpm test:coverage run: pnpm test:coverage -- --reporter=default --reporter=hanging-process - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} windows-tests: From 4ce178c592ec106955697a4c4bc32cc0f24da65e Mon Sep 17 00:00:00 2001 From: Takuya HARA Date: Wed, 4 Dec 2024 02:07:46 +0900 Subject: [PATCH 074/130] docs: Changed "Chrome Extension" to "web extension" (#1229) Co-authored-by: Aaron --- docs/.vitepress/config.ts | 2 +- docs/guide/introduction.md | 2 +- docs/index.md | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 0eb1aef0..45d9f0c4 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -18,7 +18,7 @@ import { version as storageVersion } from '../../packages/storage/package.json'; const title = 'Next-gen Web Extension Framework'; const titleSuffix = ' – WXT'; const description = - "WXT provides the best developer experience, making it quick, easy, and fun to develop chrome extensions for all browsers. With built-in utilities for building, zipping, and publishing your extension, it's easy to get started."; + "WXT provides the best developer experience, making it quick, easy, and fun to develop web extensions. With built-in utilities for building, zipping, and publishing your extension, it's easy to get started."; const ogTitle = `${title}${titleSuffix}`; const ogUrl = 'https://wxt.dev'; const ogImage = 'https://wxt.dev/social-preview.png'; diff --git a/docs/guide/introduction.md b/docs/guide/introduction.md index f5a1c179..6b490a98 100644 --- a/docs/guide/introduction.md +++ b/docs/guide/introduction.md @@ -9,7 +9,7 @@ Check out the [comparison](/guide/resources/compare) to see how WXT compares to ## Prerequisites -These docs assume you have a basic knowledge of how Chrome extensions are structured and how you access the extension APIs. +These docs assume you have a basic knowledge of how web extensions are structured and how you access the extension APIs. :::warning New to extension development? If you have never written an extension before, follow Chrome's [Hello World tutorial](https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world) to first **_create an extension without WXT_**, then come back here. diff --git a/docs/index.md b/docs/index.md index b880af3e..4ad36693 100644 --- a/docs/index.md +++ b/docs/index.md @@ -6,7 +6,7 @@ title: Next-gen Web Extension Framework hero: name: WXT text: Next-gen Web Extension Framework - tagline: An open source tool that makes Chrome Extension development faster than ever before. + tagline: An open source tool that makes web extension development faster than ever before. image: src: /hero-logo.svg alt: WXT @@ -75,7 +75,7 @@ features: ## Put Developer Experience First -WXT simplifies the chrome extension development process by providing tools for zipping and publishing, the best-in-class dev mode, an opinionated project structure, and more. Iterate faster, develop features not build scripts, and use everything the JS ecosystem has to offer. +WXT simplifies the web extension development process by providing tools for zipping and publishing, the best-in-class dev mode, an opinionated project structure, and more. Iterate faster, develop features not build scripts, and use everything the JS ecosystem has to offer.
@@ -87,7 +87,7 @@ WXT simplifies the chrome extension development process by providing tools for z ## Who's Using WXT? -Battle tested and ready for production. Explore chrome extensions made with WXT. +Battle tested and ready for production. Explore web extensions made with WXT. From c2f5efbde4166cb594c69c6734850d9dda5244e7 Mon Sep 17 00:00:00 2001 From: spookyuser <16196262+spookyuser@users.noreply.github.com> Date: Tue, 3 Dec 2024 20:23:33 +0200 Subject: [PATCH 075/130] docs: Update `injectScript` docs to match example from examples repo (#1240) Co-authored-by: Aaron --- docs/guide/essentials/content-scripts.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/guide/essentials/content-scripts.md b/docs/guide/essentials/content-scripts.md index e8b350d9..584c90eb 100644 --- a/docs/guide/essentials/content-scripts.md +++ b/docs/guide/essentials/content-scripts.md @@ -548,16 +548,21 @@ To use `injectScript`, we need two entrypoints, one content script and one unlis ```ts // entrypoints/example-main-world.ts export default defineUnlistedScript(() => { - console.log('Hello from the main world!'); + console.log('Hello from the main world'); }); ``` ```ts // entrypoints/example.content.ts -export default defineContentScript(async () => { - await injectScript('/example-main-world.js', { - keepInDom: true, - }); +export default defineContentScript({ + matches: ['*://*/*'], + async main() { + console.log('Injecting script...'); + await injectScript('/example-main-world.js', { + keepInDom: true, + }); + console.log('Done!'); + }, }); ``` From 23ef101954478e7cf0dfd12a48aee37a6768239c Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 3 Dec 2024 12:36:11 -0600 Subject: [PATCH 076/130] chore: Refactor `findEntrypoints` to return all entrypoints with `skipped` set properly (#1244) --- .../essentials/target-different-browsers.md | 2 + .../__tests__/find-entrypoints.test.ts | 114 ++++++++++++++---- .../__tests__/group-entrypoints.test.ts | 22 +++- .../core/utils/building/find-entrypoints.ts | 93 +++++++------- .../core/utils/building/group-entrypoints.ts | 6 +- packages/wxt/src/types.ts | 10 +- 6 files changed, 167 insertions(+), 80 deletions(-) diff --git a/docs/guide/essentials/target-different-browsers.md b/docs/guide/essentials/target-different-browsers.md index 5db2fda5..c854a976 100644 --- a/docs/guide/essentials/target-different-browsers.md +++ b/docs/guide/essentials/target-different-browsers.md @@ -75,3 +75,5 @@ Here are some examples: ``` + +Alternatively, you can use the [`filterEntrypoints` config](/api/reference/wxt/interfaces/InlineConfig#filterentrypoints) to list all the entrypoints you want to build. diff --git a/packages/wxt/src/core/utils/building/__tests__/find-entrypoints.test.ts b/packages/wxt/src/core/utils/building/__tests__/find-entrypoints.test.ts index 6fd492a1..566a9c61 100644 --- a/packages/wxt/src/core/utils/building/__tests__/find-entrypoints.test.ts +++ b/packages/wxt/src/core/utils/building/__tests__/find-entrypoints.test.ts @@ -701,7 +701,7 @@ describe('findEntrypoints', () => { }); describe('include option', () => { - it("should filter out the background when include doesn't contain the target browser", async () => { + it("should mark the background as skipped when include doesn't contain the target browser", async () => { globMock.mockResolvedValueOnce(['background.ts']); importEntrypointMock.mockResolvedValue({ include: ['not' + config.browser], @@ -709,10 +709,15 @@ describe('findEntrypoints', () => { const entrypoints = await findEntrypoints(); - expect(entrypoints).toEqual([]); + expect(entrypoints).toEqual([ + expect.objectContaining({ + name: 'background', + skipped: true, + }), + ]); }); - it("should filter out content scripts when include doesn't contain the target browser", async () => { + it("should mark content scripts as skipped when include doesn't contain the target browser", async () => { globMock.mockResolvedValueOnce(['example.content.ts']); importEntrypointMock.mockResolvedValue({ include: ['not' + config.browser], @@ -720,10 +725,15 @@ describe('findEntrypoints', () => { const entrypoints = await findEntrypoints(); - expect(entrypoints).toEqual([]); + expect(entrypoints).toEqual([ + expect.objectContaining({ + name: 'example', + skipped: true, + }), + ]); }); - it("should filter out the popup when include doesn't contain the target browser", async () => { + it("should mark the popup as skipped when include doesn't contain the target browser", async () => { globMock.mockResolvedValueOnce(['popup.html']); readFileMock.mockResolvedValueOnce( ` @@ -737,10 +747,15 @@ describe('findEntrypoints', () => { const entrypoints = await findEntrypoints(); - expect(entrypoints).toEqual([]); + expect(entrypoints).toEqual([ + expect.objectContaining({ + name: 'popup', + skipped: true, + }), + ]); }); - it("should filter out the options page when include doesn't contain the target browser", async () => { + it("should mark the options page as skipped when include doesn't contain the target browser", async () => { globMock.mockResolvedValueOnce(['options.html']); readFileMock.mockResolvedValueOnce( ` @@ -754,10 +769,15 @@ describe('findEntrypoints', () => { const entrypoints = await findEntrypoints(); - expect(entrypoints).toEqual([]); + expect(entrypoints).toEqual([ + expect.objectContaining({ + name: 'options', + skipped: true, + }), + ]); }); - it("should filter out an unlisted page when include doesn't contain the target browser", async () => { + it("should mark unlisted pages as skipped when include doesn't contain the target browser", async () => { globMock.mockResolvedValueOnce(['unlisted.html']); readFileMock.mockResolvedValueOnce( ` @@ -771,12 +791,17 @@ describe('findEntrypoints', () => { const entrypoints = await findEntrypoints(); - expect(entrypoints).toEqual([]); + expect(entrypoints).toEqual([ + expect.objectContaining({ + name: 'unlisted', + skipped: true, + }), + ]); }); }); describe('exclude option', () => { - it('should filter out the background when exclude contains the target browser', async () => { + it('should mark the background as skipped when exclude contains the target browser', async () => { globMock.mockResolvedValueOnce(['background.ts']); importEntrypointMock.mockResolvedValue({ exclude: [config.browser], @@ -784,10 +809,15 @@ describe('findEntrypoints', () => { const entrypoints = await findEntrypoints(); - expect(entrypoints).toEqual([]); + expect(entrypoints).toEqual([ + expect.objectContaining({ + name: 'background', + skipped: true, + }), + ]); }); - it('should filter out content scripts when exclude contains the target browser', async () => { + it('should mark content scripts as skipped when exclude contains the target browser', async () => { globMock.mockResolvedValueOnce(['example.content.ts']); importEntrypointMock.mockResolvedValue({ exclude: [config.browser], @@ -795,10 +825,15 @@ describe('findEntrypoints', () => { const entrypoints = await findEntrypoints(); - expect(entrypoints).toEqual([]); + expect(entrypoints).toEqual([ + expect.objectContaining({ + name: 'example', + skipped: true, + }), + ]); }); - it('should filter out the popup when exclude contains the target browser', async () => { + it('should mark the popup as skipped when exclude contains the target browser', async () => { globMock.mockResolvedValueOnce(['popup.html']); readFileMock.mockResolvedValueOnce( ` @@ -810,10 +845,15 @@ describe('findEntrypoints', () => { const entrypoints = await findEntrypoints(); - expect(entrypoints).toEqual([]); + expect(entrypoints).toEqual([ + expect.objectContaining({ + name: 'popup', + skipped: true, + }), + ]); }); - it('should filter out the options page when exclude contains the target browser', async () => { + it('should mark the options page as skipped when exclude contains the target browser', async () => { globMock.mockResolvedValueOnce(['options.html']); readFileMock.mockResolvedValueOnce( ` @@ -825,10 +865,15 @@ describe('findEntrypoints', () => { const entrypoints = await findEntrypoints(); - expect(entrypoints).toEqual([]); + expect(entrypoints).toEqual([ + expect.objectContaining({ + name: 'options', + skipped: true, + }), + ]); }); - it('should filter out an unlisted page when exclude contains the target browser', async () => { + it('should mark unlisted pages as skipped when exclude contains the target browser', async () => { globMock.mockResolvedValueOnce(['unlisted.html']); readFileMock.mockResolvedValueOnce( ` @@ -840,12 +885,17 @@ describe('findEntrypoints', () => { const entrypoints = await findEntrypoints(); - expect(entrypoints).toEqual([]); + expect(entrypoints).toEqual([ + expect.objectContaining({ + name: 'unlisted', + skipped: true, + }), + ]); }); }); describe('filterEntrypoints option', () => { - it('should control entrypoints accessible', async () => { + it('should override include/exclude of individual entrypoint options', async () => { globMock.mockResolvedValue([ 'options/index.html', 'popup/index.html', @@ -867,9 +917,25 @@ describe('findEntrypoints', () => { importEntrypointMock.mockResolvedValue({}); const entrypoints = await findEntrypoints(); - const names = entrypoints.map((item) => item.name); - expect(names).toHaveLength(2); - expect(names).toEqual(filterEntrypoints); + + expect(entrypoints).toEqual([ + expect.objectContaining({ + name: 'injected', + skipped: true, + }), + expect.objectContaining({ + name: 'options', + skipped: true, + }), + expect.objectContaining({ + name: 'popup', + skipped: false, + }), + expect.objectContaining({ + name: 'ui', + skipped: false, + }), + ]); }); }); }); diff --git a/packages/wxt/src/core/utils/building/__tests__/group-entrypoints.test.ts b/packages/wxt/src/core/utils/building/__tests__/group-entrypoints.test.ts index 413c4d9b..bb21a6fd 100644 --- a/packages/wxt/src/core/utils/building/__tests__/group-entrypoints.test.ts +++ b/packages/wxt/src/core/utils/building/__tests__/group-entrypoints.test.ts @@ -152,12 +152,16 @@ describe('groupEntrypoints', () => { options: { type: 'module', }, + skipped: false, + }); + const popup = fakePopupEntrypoint({ + skipped: false, }); - const popup = fakePopupEntrypoint(); const sandbox = fakeGenericEntrypoint({ inputPath: '/entrypoints/sandbox.html', name: 'sandbox', type: 'sandbox', + skipped: false, }); const actual = groupEntrypoints([background, popup, sandbox]); @@ -165,6 +169,22 @@ describe('groupEntrypoints', () => { expect(actual).toEqual([[background, popup], [sandbox]]); }); + it('should exclude skipped entrypoints from the groups to build', () => { + const background = fakeBackgroundEntrypoint({ + options: { + type: 'module', + }, + skipped: false, + }); + const popup = fakePopupEntrypoint({ + skipped: true, + }); + + const actual = groupEntrypoints([background, popup]); + + expect(actual).toEqual([[background]]); + }); + it.todo( 'should group ESM compatible sandbox scripts with sandbox pages', () => { diff --git a/packages/wxt/src/core/utils/building/find-entrypoints.ts b/packages/wxt/src/core/utils/building/find-entrypoints.ts index 72ac7436..0f71d07f 100644 --- a/packages/wxt/src/core/utils/building/find-entrypoints.ts +++ b/packages/wxt/src/core/utils/building/find-entrypoints.ts @@ -65,14 +65,7 @@ export async function findEntrypoints(): Promise { ); if (matchingGlob) { const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob]; - results.push({ - name, - inputPath, - type, - skipped: - wxt.config.filterEntrypoints != null && - !wxt.config.filterEntrypoints.has(name), - }); + results.push({ name, inputPath, type }); } return results; }, []); @@ -84,7 +77,7 @@ export async function findEntrypoints(): Promise { // Import entrypoints to get their config let hasBackground = false; const env = createExtensionEnvironment(); - const entrypoints: Entrypoint[] = await env.run(() => + const entrypointsWithoutSkipped: Entrypoint[] = await env.run(() => Promise.all( entrypointInfos.map(async (info): Promise => { const { type } = info; @@ -130,61 +123,44 @@ export async function findEntrypoints(): Promise { ); if (wxt.config.command === 'serve' && !hasBackground) { - entrypoints.push( + entrypointsWithoutSkipped.push( await getBackgroundEntrypoint({ inputPath: VIRTUAL_NOOP_BACKGROUND_MODULE_ID, name: 'background', type: 'background', - skipped: false, }), ); } + // Mark entrypoints as skipped or not + const entrypoints = entrypointsWithoutSkipped.map((entry) => ({ + ...entry, + skipped: isEntrypointSkipped(entry), + })); + wxt.logger.debug('All entrypoints:', entrypoints); - const skippedEntrypointNames = entrypointInfos + const skippedEntrypointNames = entrypoints .filter((item) => item.skipped) .map((item) => item.name); if (skippedEntrypointNames.length) { wxt.logger.warn( - `Filter excluded the following entrypoints:\n${skippedEntrypointNames - .map((item) => `${pc.dim('-')} ${pc.cyan(item)}`) - .join('\n')}`, + [ + 'The following entrypoints have been skipped:', + ...skippedEntrypointNames.map( + (item) => `${pc.dim('-')} ${pc.cyan(item)}`, + ), + ].join('\n'), ); } - const targetEntrypoints = entrypoints.filter((entry) => { - const { include, exclude } = entry.options; - if (include?.length && exclude?.length) { - wxt.logger.warn( - `The ${entry.name} entrypoint lists both include and exclude, but only one can be used per entrypoint. Entrypoint ignored.`, - ); - return false; - } - if (exclude?.length && !include?.length) { - return !exclude.includes(wxt.config.browser); - } - if (include?.length && !exclude?.length) { - return include.includes(wxt.config.browser); - } - if (skippedEntrypointNames.includes(entry.name)) { - return false; - } + await wxt.hooks.callHook('entrypoints:resolved', wxt, entrypoints); - return true; - }); - wxt.logger.debug(`${wxt.config.browser} entrypoints:`, targetEntrypoints); - await wxt.hooks.callHook('entrypoints:resolved', wxt, targetEntrypoints); - - return targetEntrypoints; + return entrypoints; } interface EntrypointInfo { name: string; inputPath: string; type: Entrypoint['type']; - /** - * @default false - */ - skipped: boolean; } function preventDuplicateEntrypointNames(files: EntrypointInfo[]) { @@ -252,7 +228,6 @@ async function getPopupEntrypoint( options: resolvePerBrowserOptions(options, wxt.config.browser), inputPath: info.inputPath, outputDir: wxt.config.outDir, - skipped: info.skipped, }; } @@ -275,7 +250,6 @@ async function getOptionsEntrypoint( options: resolvePerBrowserOptions(options, wxt.config.browser), inputPath: info.inputPath, outputDir: wxt.config.outDir, - skipped: info.skipped, }; } @@ -293,14 +267,12 @@ async function getUnlistedPageEntrypoint( inputPath: info.inputPath, outputDir: wxt.config.outDir, options, - skipped: info.skipped, }; } async function getUnlistedScriptEntrypoint({ inputPath, name, - skipped, }: EntrypointInfo): Promise { const defaultExport = await wxt.builder.importEntrypoint(inputPath); @@ -316,14 +288,12 @@ async function getUnlistedScriptEntrypoint({ inputPath, outputDir: wxt.config.outDir, options: resolvePerBrowserOptions(options, wxt.config.browser), - skipped, }; } async function getBackgroundEntrypoint({ inputPath, name, - skipped, }: EntrypointInfo): Promise { let options: Omit = {}; if (inputPath !== VIRTUAL_NOOP_BACKGROUND_MODULE_ID) { @@ -348,14 +318,12 @@ async function getBackgroundEntrypoint({ inputPath, outputDir: wxt.config.outDir, options: resolvePerBrowserOptions(options, wxt.config.browser), - skipped, }; } async function getContentScriptEntrypoint({ inputPath, name, - skipped, }: EntrypointInfo): Promise { const defaultExport = await wxt.builder.importEntrypoint(inputPath); @@ -377,7 +345,6 @@ async function getContentScriptEntrypoint({ inputPath, outputDir: resolve(wxt.config.outDir, CONTENT_SCRIPT_OUT_DIR), options: resolvePerBrowserOptions(options, wxt.config.browser), - skipped, }; } @@ -409,7 +376,6 @@ async function getSidepanelEntrypoint( options: resolvePerBrowserOptions(options, wxt.config.browser), inputPath: info.inputPath, outputDir: wxt.config.outDir, - skipped: info.skipped, }; } @@ -457,6 +423,29 @@ async function getHtmlEntrypointOptions( return options; } +function isEntrypointSkipped(entry: Omit): boolean { + if (wxt.config.filterEntrypoints != null) { + return !wxt.config.filterEntrypoints.has(entry.name); + } + + const { include, exclude } = entry.options; + if (include?.length && exclude?.length) { + wxt.logger.warn( + `The ${entry.name} entrypoint lists both include and exclude, but only one can be used per entrypoint. Entrypoint skipped.`, + ); + return true; + } + + if (exclude?.length && !include?.length) { + return exclude.includes(wxt.config.browser); + } + if (include?.length && !exclude?.length) { + return !include.includes(wxt.config.browser); + } + + return false; +} + const PATH_GLOB_TO_TYPE_MAP: Record = { 'sandbox.html': 'sandbox', 'sandbox/index.html': 'sandbox', diff --git a/packages/wxt/src/core/utils/building/group-entrypoints.ts b/packages/wxt/src/core/utils/building/group-entrypoints.ts index eb519fa9..6381bbd7 100644 --- a/packages/wxt/src/core/utils/building/group-entrypoints.ts +++ b/packages/wxt/src/core/utils/building/group-entrypoints.ts @@ -1,8 +1,8 @@ import { Entrypoint, EntrypointGroup } from '../../../types'; /** - * Entrypoints can be build in groups. HTML pages can all be built together in a single step, while - * content scripts must be build individually. + * Entrypoints are built in groups. HTML pages can all be built together in a single step, + * content scripts must be build individually, etc. * * This function returns the entrypoints put into these types of groups. */ @@ -11,6 +11,8 @@ export function groupEntrypoints(entrypoints: Entrypoint[]): EntrypointGroup[] { const groups: EntrypointGroup[] = []; for (const entry of entrypoints) { + if (entry.skipped) continue; + let group = ENTRY_TYPE_TO_GROUP_MAP[entry.type]; if (entry.type === 'background' && entry.options.type === 'module') { group = 'esm'; diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index 3205559c..80b428b0 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -45,6 +45,7 @@ export interface InlineConfig { * A list of entrypoint names (`"popup"`, `"options"`, etc.) to build. Will speed up the build if * your extension has lots of entrypoints, and you don't need to build all of them to develop a * feature. + * If specified, this completely overrides the `include`/`exclude` option provided per-entrypoint. */ filterEntrypoints?: string[]; /** @@ -728,7 +729,14 @@ export interface BaseEntrypoint { * subdirectory of it. */ outputDir: string; - skipped: boolean; + /** + * When true, the entrypoint will not be built by WXT. Normally this is set + * based on the `filterEntrypoints` config or the entrypoint's + * `include`/`exclude` options defined inside the file. + * + * See https://wxt.dev/guide/essentials/target-different-browsers.html#filtering-entrypoints + */ + skipped?: boolean; } export interface GenericEntrypoint extends BaseEntrypoint { From f3e77585214bf046353b7ba5b3c12df1202cf05e Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 3 Dec 2024 16:55:33 -0600 Subject: [PATCH 077/130] types: Add overloads to `ContentScriptContext#addEventListener` for different targets (#1245) --- .../__tests__/content-script-context.test.ts | 40 ++++++++++++++++--- .../content-scripts/content-script-context.ts | 32 ++++++++++----- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/packages/wxt/src/client/content-scripts/__tests__/content-script-context.test.ts b/packages/wxt/src/client/content-scripts/__tests__/content-script-context.test.ts index 6f7d14c1..23ff12e8 100644 --- a/packages/wxt/src/client/content-scripts/__tests__/content-script-context.test.ts +++ b/packages/wxt/src/client/content-scripts/__tests__/content-script-context.test.ts @@ -3,7 +3,15 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { ContentScriptContext } from '..'; import { fakeBrowser } from '@webext-core/fake-browser'; -import { sleep } from '../../../core/utils/time'; + +/** + * When dispatching events on document/window/etc, they are fired on the next + * tick of the event loop. So waiting a timeout of 0 will ensure they've been + * fired. + */ +function waitForEventsToFire() { + return new Promise((res) => setTimeout(res)); +} describe('Content Script Context', () => { beforeEach(() => { @@ -31,11 +39,11 @@ describe('Content Script Context', () => { ctx.onInvalidated(onInvalidated); // Wait for events to run before next tick next tick - await sleep(0); + await waitForEventsToFire(); // Create a new context after first is initialized, and wait for it to initialize new ContentScriptContext(name); - await sleep(0); + await waitForEventsToFire(); expect(onInvalidated).toBeCalled(); expect(ctx.isValid).toBe(false); @@ -47,13 +55,35 @@ describe('Content Script Context', () => { ctx.onInvalidated(onInvalidated); // Wait for events to run before next tick next tick - await sleep(0); + await waitForEventsToFire(); // Create a new context after first is initialized, and wait for it to initialize new ContentScriptContext('test2'); - await sleep(0); + await waitForEventsToFire(); expect(onInvalidated).not.toBeCalled(); expect(ctx.isValid).toBe(true); }); + + describe('addEventListener', () => { + const context = new ContentScriptContext('test'); + it('should infer types correctly for the window target', () => { + context.addEventListener(window, 'DOMContentLoaded', (_) => {}); + context.addEventListener(window, 'orientationchange', (_) => {}); + context.addEventListener(window, 'wxt:locationchange', (_) => {}); + // @ts-expect-error + context.addEventListener(window, 'visibilitychange', (_) => {}); + }); + + it('should infer types correctly for the document target', () => { + context.addEventListener(document, 'visibilitychange', (_) => {}); + context.addEventListener(document, 'readystatechange', (_) => {}); + }); + + it('should infer types correctly for HTML element targets', () => { + const button = document.createElement('button'); + context.addEventListener(button, 'click', (_) => {}); + context.addEventListener(button, 'mouseover', (_) => {}); + }); + }); }); diff --git a/packages/wxt/src/client/content-scripts/content-script-context.ts b/packages/wxt/src/client/content-scripts/content-script-context.ts index 69f36c6c..62f5c9a8 100644 --- a/packages/wxt/src/client/content-scripts/content-script-context.ts +++ b/packages/wxt/src/client/content-scripts/content-script-context.ts @@ -175,19 +175,32 @@ export class ContentScriptContext implements AbortController { * ctx.addEventListener(document, "visibilitychange", () => { * // ... * }); - * ctx.addEventListener(document, "wxt:locationchange", () => { + * ctx.addEventListener(window, "wxt:locationchange", () => { * // ... * }); */ - addEventListener< - TTarget extends EventTarget, - TType extends keyof WxtContentScriptEventMap, - >( - target: TTarget, + addEventListener( + target: Window, type: TType, - handler: (event: WxtContentScriptEventMap[TType]) => void, + handler: (event: WxtWindowEventMap[TType]) => void, options?: AddEventListenerOptions, - ) { + ): void; + addEventListener( + target: Document, + type: keyof DocumentEventMap, + handler: (event: DocumentEventMap[TType]) => void, + options?: AddEventListenerOptions, + ): void; + addEventListener( + target: TTarget, + ...params: Parameters + ): void; + addEventListener( + target: EventTarget, + type: string, + handler: (event: Event) => void, + options?: AddEventListenerOptions, + ): void { if (type === 'wxt:locationchange') { // Start the location watcher when adding the event for the first time if (this.isValid) this.locationWatcher.run(); @@ -195,7 +208,6 @@ export class ContentScriptContext implements AbortController { target.addEventListener?.( type.startsWith('wxt:') ? getUniqueEventName(type) : type, - // @ts-expect-error: Event don't match, but that's OK, EventTarget doesn't allow custom types in the callback handler, { ...options, @@ -247,6 +259,6 @@ export class ContentScriptContext implements AbortController { } } -interface WxtContentScriptEventMap extends WindowEventMap { +export interface WxtWindowEventMap extends WindowEventMap { 'wxt:locationchange': WxtLocationChangeEvent; } From cb143d2bbc6f791c20b56d21c909eac18528a242 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 3 Dec 2024 23:00:59 +0000 Subject: [PATCH 078/130] chore(release): wxt v0.19.18 --- packages/wxt/CHANGELOG.md | 23 ++++++++++++++++++++++- packages/wxt/package.json | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/wxt/CHANGELOG.md b/packages/wxt/CHANGELOG.md index 9a852e8a..bc90b912 100644 --- a/packages/wxt/CHANGELOG.md +++ b/packages/wxt/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## v0.19.18 + +[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.17...wxt-v0.19.18) + +### 🩹 Fixes + +- Correct `dev.reloadCommand` restriction to consider only commands with suggested keys ([#1226](https://github.com/wxt-dev/wxt/pull/1226)) + +### 🌊 Types + +- Add overloads to `ContentScriptContext#addEventListener` for different targets ([#1245](https://github.com/wxt-dev/wxt/pull/1245)) + +### 🏡 Chore + +- Refactor `findEntrypoints` to return all entrypoints with `skipped` set properly ([#1244](https://github.com/wxt-dev/wxt/pull/1244)) + +### ❤️ Contributors + +- Aaron ([@aklinker1](http://github.com/aklinker1)) +- Jaguar Zhou ([@aiktb](http://github.com/aiktb)) + ## v0.19.17 [compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.16...wxt-v0.19.17) @@ -2667,4 +2688,4 @@ Initial release of WXT. Full support for production builds and initial toolkit f ### 🤖 CI - Create validation workflow ([#12](https://github.com/wxt-dev/wxt/pull/12)) -- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13)) +- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13)) \ No newline at end of file diff --git a/packages/wxt/package.json b/packages/wxt/package.json index 4595a101..dc6991e5 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -1,7 +1,7 @@ { "name": "wxt", "type": "module", - "version": "0.19.17", + "version": "0.19.18", "description": "Next gen framework for developing web extensions", "repository": { "type": "git", From 0d796b63c22e41f347ba8bfe0c88f73b40107522 Mon Sep 17 00:00:00 2001 From: Nishu Date: Wed, 4 Dec 2024 09:12:44 +0530 Subject: [PATCH 079/130] feat: Keyboard shortcut to reopen the browser without restarting the dev command (#1211) Co-authored-by: Aaron Co-authored-by: Florian Metz --- packages/wxt/src/core/create-server.ts | 9 ++++ packages/wxt/src/core/keyboard-shortcuts.ts | 60 +++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 packages/wxt/src/core/keyboard-shortcuts.ts diff --git a/packages/wxt/src/core/create-server.ts b/packages/wxt/src/core/create-server.ts index c0b64339..6b183198 100644 --- a/packages/wxt/src/core/create-server.ts +++ b/packages/wxt/src/core/create-server.ts @@ -27,6 +27,7 @@ import { getContentScriptJs, mapWxtOptionsToRegisteredContentScript, } from './utils/content-scripts'; +import { createKeyboardShortcuts } from './keyboard-shortcuts'; /** * Creates a dev server and pre-builds all the files that need to exist before loading the extension. @@ -108,9 +109,13 @@ async function createServerInternal(): Promise { // Listen for file changes and reload different parts of the extension accordingly const reloadOnChange = createFileReloader(server); server.watcher.on('all', reloadOnChange); + keyboardShortcuts.start(); + keyboardShortcuts.printHelp(); }, + async stop() { wasStopped = true; + keyboardShortcuts.stop(); await runner.closeBrowser(); await builderServer.close(); await wxt.hooks.callHook('server:closed', wxt, server); @@ -136,11 +141,14 @@ async function createServerInternal(): Promise { }, async restartBrowser() { await runner.closeBrowser(); + keyboardShortcuts.stop(); await wxt.reloadConfig(); runner = await createExtensionRunner(); await runner.openBrowser(); + keyboardShortcuts.start(); }, }; + const keyboardShortcuts = createKeyboardShortcuts(server); const buildAndOpenBrowser = async () => { // Build after starting the dev server so it can be used to transform HTML files @@ -230,6 +238,7 @@ function createFileReloader(server: WxtDevServer) { break; case 'content-script-reload': reloadContentScripts(changes.changedSteps, server); + const rebuiltNames = changes.rebuildGroups .flat() .map((entry) => entry.name); diff --git a/packages/wxt/src/core/keyboard-shortcuts.ts b/packages/wxt/src/core/keyboard-shortcuts.ts new file mode 100644 index 00000000..5d0fe9ad --- /dev/null +++ b/packages/wxt/src/core/keyboard-shortcuts.ts @@ -0,0 +1,60 @@ +import readline from 'node:readline'; +import { WxtDevServer } from '../types'; +import { wxt } from './wxt'; +import pc from 'picocolors'; + +export interface KeyboardShortcutWatcher { + start(): void; + stop(): void; + printHelp(): void; +} + +/** + * Function that creates a keyboard shortcut handler for the extension. + */ +export function createKeyboardShortcuts( + server: WxtDevServer, +): KeyboardShortcutWatcher { + let isWatching = false; + let rl: readline.Interface | undefined; + + const handleInput = (line: string) => { + // Only handle our specific command + if (line.trim() === 'o') { + server.restartBrowser(); + } + }; + + return { + start() { + if (isWatching) return; + + rl = readline.createInterface({ + input: process.stdin, + terminal: false, // Don't intercept ctrl+C, ctrl+Z, etc + }); + + rl.on('line', handleInput); + isWatching = true; + }, + + stop() { + if (!isWatching) return; + + if (rl) { + rl.close(); + rl = undefined; + } + + isWatching = false; + }, + + printHelp() { + if (!wxt.config.runnerConfig.config.disabled) { + wxt.logger.info( + `${pc.dim('Press')} ${pc.bold('o + enter')} ${pc.dim('to reopen the browser')}`, + ); + } + }, + }; +} From fd436a0d633ddbb2ad9fd47e8eb9beb4e3a11d7c Mon Sep 17 00:00:00 2001 From: Jeremie Corpinot <5097282+rootasjey@users.noreply.github.com> Date: Wed, 4 Dec 2024 23:00:23 +0100 Subject: [PATCH 080/130] docs: Add "aesthetic Notion, styled" to homepage (#1247) --- docs/.vitepress/components/UsingWxtSection.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/.vitepress/components/UsingWxtSection.vue b/docs/.vitepress/components/UsingWxtSection.vue index bf67438e..baa67c2f 100644 --- a/docs/.vitepress/components/UsingWxtSection.vue +++ b/docs/.vitepress/components/UsingWxtSection.vue @@ -59,6 +59,7 @@ const chromeExtensionIds = [ 'macmkmchfoclhpbncclinhjflmdkaoom', // Wandpen - Instantly improve your writing with AI 'lhmgechokhmdekdpgkkemoeecelcaonm', // YouTube Hider - Remove Comments By Keywords, Usernames & Tools 'imgheieooppmahcgniieddodaliodeeg', // QA Compass - Record standardized bug reports easily + 'npgghjedpchajflknnbngajkjkdhncdo', // aesthetic Notion, styled ]; const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds); From 17af05acc82dc0ba0fef0eafaeba5f81d5d11f46 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 5 Dec 2024 11:48:49 -0600 Subject: [PATCH 081/130] fix: Prevent changing dev server port when reloading config (#1241) --- packages/wxt/e2e/tests/dev.test.ts | 24 ++++++++++++++++++++++++ packages/wxt/src/core/wxt.ts | 8 ++++++++ 2 files changed, 32 insertions(+) create mode 100644 packages/wxt/e2e/tests/dev.test.ts diff --git a/packages/wxt/e2e/tests/dev.test.ts b/packages/wxt/e2e/tests/dev.test.ts new file mode 100644 index 00000000..c782bb94 --- /dev/null +++ b/packages/wxt/e2e/tests/dev.test.ts @@ -0,0 +1,24 @@ +import { describe, it, expect } from 'vitest'; +import { TestProject } from '../utils'; + +describe('Dev Mode', () => { + it('should not change ports when restarting the server', async () => { + const project = new TestProject(); + project.addFile( + 'entrypoints/background.ts', + 'export default defineBackground(() => {})', + ); + + const server = await project.startServer({ + runner: { + disabled: true, + }, + }); + const initialPort = server.port; + await server.restart(); + const finalPort = server.port; + await server.stop(); + + expect(finalPort).toBe(initialPort); + }); +}); diff --git a/packages/wxt/src/core/wxt.ts b/packages/wxt/src/core/wxt.ts index eaad5233..2c616b75 100644 --- a/packages/wxt/src/core/wxt.ts +++ b/packages/wxt/src/core/wxt.ts @@ -36,6 +36,14 @@ export async function registerWxt( return config.logger; }, async reloadConfig() { + // Prevent changing the server port when resolving config multiple times + // get-port-please doesn't always return the same port if it was recently closed. + if (wxt.config.dev.server?.port) { + inlineConfig.dev ??= {}; + inlineConfig.dev.server ??= {}; + inlineConfig.dev.server.port = wxt.config.dev.server.port; + } + wxt.config = await resolveConfig(inlineConfig, command); await wxt.hooks.callHook('config:resolved', wxt); }, From 88f126e658e52c96a83e724277cbcb11e9cc4c00 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 5 Dec 2024 18:16:38 -0600 Subject: [PATCH 082/130] fix: Ensure content scripts are registered immediately in dev mode (#1253) --- packages/wxt/src/core/create-server.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/wxt/src/core/create-server.ts b/packages/wxt/src/core/create-server.ts index 6b183198..b83e6bd4 100644 --- a/packages/wxt/src/core/create-server.ts +++ b/packages/wxt/src/core/create-server.ts @@ -97,15 +97,17 @@ async function createServerInternal(): Promise { wxt.logger.success(`Started dev server @ ${server.origin}`); await wxt.hooks.callHook('server:started', wxt, server); - await buildAndOpenBrowser(); - - // Register content scripts for the first time after the background starts up since they're not - // listed in the manifest + // Register content scripts for the first time after the background starts + // up since they're not listed in the manifest. + // Add listener before opening the browser to guarentee it is present when + // the extension sends back the initialization message. server.ws.on('wxt:background-initialized', () => { if (server.currentOutput == null) return; reloadContentScripts(server.currentOutput.steps, server); }); + await buildAndOpenBrowser(); + // Listen for file changes and reload different parts of the extension accordingly const reloadOnChange = createFileReloader(server); server.watcher.on('all', reloadOnChange); From e221252325835672a544d5a39341599e29f39edd Mon Sep 17 00:00:00 2001 From: Nishu Date: Fri, 6 Dec 2024 05:53:00 +0530 Subject: [PATCH 083/130] fix: Exclude skipped entrypoints from Firefox sources zip (#1238) Co-authored-by: Aaron Klinker --- docs/guide/essentials/publishing.md | 2 +- packages/wxt/e2e/tests/hooks.test.ts | 2 +- packages/wxt/e2e/tests/zip.test.ts | 36 ++++++++++++++++++++++++++++ packages/wxt/src/core/zip.ts | 12 ++++++++-- 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/docs/guide/essentials/publishing.md b/docs/guide/essentials/publishing.md index 7edf7d1d..a7ae7b20 100644 --- a/docs/guide/essentials/publishing.md +++ b/docs/guide/essentials/publishing.md @@ -129,7 +129,7 @@ wxt zip Firefox requires you to upload a ZIP of your source code. This allows them to rebuild your extension and review the code in a readable way. More details can be found in [Firefox's docs](https://extensionworkshop.com/documentation/publish/source-code-submission/). -When running `wxt zip -b firefox`, WXT will zip both your extension and sources. Certain files (such as config files, hidden files, and tests) are automatically excluded from your sources. However, it's important to manually check the ZIP to ensure it only contains the files necessary to rebuild your extension. +When running `wxt zip -b firefox`, WXT will zip both your extension and sources. Certain files (such as config files, hidden files, tests, and excluded entrypoints) are automatically excluded from your sources. However, it's important to manually check the ZIP to ensure it only contains the files necessary to rebuild your extension. To customize which files are zipped, add the `zip` option to your config file. diff --git a/packages/wxt/e2e/tests/hooks.test.ts b/packages/wxt/e2e/tests/hooks.test.ts index 42665c23..c7f4a86a 100644 --- a/packages/wxt/e2e/tests/hooks.test.ts +++ b/packages/wxt/e2e/tests/hooks.test.ts @@ -154,7 +154,7 @@ describe('Hooks', () => { 'build:publicAssets': true, 'build:manifestGenerated': true, 'entrypoints:grouped': true, - 'entrypoints:resolved': true, + 'entrypoints:resolved': 2, 'vite:build:extendConfig': 1, 'vite:devServer:extendConfig': false, 'zip:start': true, diff --git a/packages/wxt/e2e/tests/zip.test.ts b/packages/wxt/e2e/tests/zip.test.ts index e20fccc1..3ff265aa 100644 --- a/packages/wxt/e2e/tests/zip.test.ts +++ b/packages/wxt/e2e/tests/zip.test.ts @@ -181,6 +181,42 @@ describe('Zipping', () => { ); }); + it('should exclude skipped entrypoints from respective browser sources zip', async () => { + const project = new TestProject({ + name: 'test', + version: '1.0.0', + }); + project.addFile( + 'entrypoints/not-firefox.content.ts', + `export default defineContentScript({ + matches: ['*://*/*'], + exclude: ['firefox'], + main() {}, + });`, + ); + project.addFile( + 'entrypoints/all.content.ts', + `export default defineContentScript({ + matches: ['*://*/*'], + main(ctx) {}, + }); +`, + ); + const unzipDir = project.resolvePath('.output/test-1.0.0-sources'); + const sourcesZip = project.resolvePath('.output/test-1.0.0-sources.zip'); + + await project.zip({ + browser: 'firefox', + }); + await extract(sourcesZip, { dir: unzipDir }); + expect( + await project.fileExists(unzipDir, 'entrypoints/not-firefox.content.ts'), + ).toBe(false); + expect( + await project.fileExists(unzipDir, 'entrypoints/all.content.ts'), + ).toBe(true); + }); + it.each(['firefox', 'opera'])( 'should create sources zip for "%s" browser when sourcesZip is undefined', async (browser) => { diff --git a/packages/wxt/src/core/zip.ts b/packages/wxt/src/core/zip.ts index 72e6a8a2..092d5c27 100644 --- a/packages/wxt/src/core/zip.ts +++ b/packages/wxt/src/core/zip.ts @@ -6,7 +6,7 @@ import { getPackageJson } from './utils/package'; import { minimatch } from 'minimatch'; import { formatDuration } from './utils/time'; import { printFileList } from './utils/log/printFileList'; -import { internalBuild } from './utils/building'; +import { findEntrypoints, internalBuild } from './utils/building'; import { registerWxt, wxt } from './wxt'; import JSZip from 'jszip'; import glob from 'fast-glob'; @@ -55,6 +55,14 @@ export async function zip(config?: InlineConfig): Promise { await wxt.hooks.callHook('zip:extension:done', wxt, outZipPath); if (wxt.config.zip.zipSources) { + const entrypoints = await findEntrypoints(); + const skippedEntrypoints = entrypoints.filter((entry) => entry.skipped); + const excludeSources = [ + ...wxt.config.zip.excludeSources, + ...skippedEntrypoints.map((entry) => + path.relative(wxt.config.zip.sourcesRoot, entry.inputPath), + ), + ].map((paths) => paths.replaceAll('\\', '/')); await wxt.hooks.callHook('zip:sources:start', wxt); const { overrides, files: downloadedPackages } = await downloadPrivatePackages(); @@ -65,7 +73,7 @@ export async function zip(config?: InlineConfig): Promise { ); await zipDir(wxt.config.zip.sourcesRoot, sourcesZipPath, { include: wxt.config.zip.includeSources, - exclude: wxt.config.zip.excludeSources, + exclude: excludeSources, transform(absolutePath, zipPath, content) { if (zipPath.endsWith('package.json')) { return addOverridesToPackageJson(absolutePath, content, overrides); From a643852387c988ef5aed8aeb1aba50f351516c91 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 6 Dec 2024 00:28:20 +0000 Subject: [PATCH 084/130] chore(release): wxt v0.19.19 --- packages/wxt/CHANGELOG.md | 19 +++++++++++++++++++ packages/wxt/package.json | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/wxt/CHANGELOG.md b/packages/wxt/CHANGELOG.md index bc90b912..0b0f915e 100644 --- a/packages/wxt/CHANGELOG.md +++ b/packages/wxt/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## v0.19.19 + +[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.18...wxt-v0.19.19) + +### 🚀 Enhancements + +- Keyboard shortcut to reopen the browser without restarting the dev command ([#1211](https://github.com/wxt-dev/wxt/pull/1211)) + +### 🩹 Fixes + +- Prevent changing dev server port when reloading config ([#1241](https://github.com/wxt-dev/wxt/pull/1241)) +- Ensure content scripts are registered immediately in dev mode ([#1253](https://github.com/wxt-dev/wxt/pull/1253)) +- Exclude skipped entrypoints from Firefox sources zip ([#1238](https://github.com/wxt-dev/wxt/pull/1238)) + +### ❤️ Contributors + +- Nishu ([@nishu-murmu](http://github.com/nishu-murmu)) +- Aaron ([@aklinker1](http://github.com/aklinker1)) + ## v0.19.18 [compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.17...wxt-v0.19.18) diff --git a/packages/wxt/package.json b/packages/wxt/package.json index dc6991e5..fed57d31 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -1,7 +1,7 @@ { "name": "wxt", "type": "module", - "version": "0.19.18", + "version": "0.19.19", "description": "Next gen framework for developing web extensions", "repository": { "type": "git", From 78e896e5fb7cfb854aa406390dcce8355bbf17ac Mon Sep 17 00:00:00 2001 From: Aaron Klinker Date: Sat, 7 Dec 2024 01:12:23 -0600 Subject: [PATCH 085/130] docs: Remove duplicate section from upgrade docs --- docs/guide/resources/upgrading.md | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/docs/guide/resources/upgrading.md b/docs/guide/resources/upgrading.md index 4bf8f1c1..55e37a92 100644 --- a/docs/guide/resources/upgrading.md +++ b/docs/guide/resources/upgrading.md @@ -109,27 +109,6 @@ You have two options: ``` 2. Rename your `modules` directory to something else. -## v0.18.0 → v0.18.5 - -> When this version was released, it was not considered a breaking change... but it should have been. - -### New `modules/` Directory - -WXT now recognizes the `modules/` directory as a folder containing [WXT modules](/guide/essentials/wxt-modules). - -If you already have `/modules` or `/Modules` directory, `wxt prepare` and other commands will fail. - -You have two options: - -1. [Recommended] Keep your files where they are and tell WXT to look in a different folder: - ```ts - // wxt.config.ts - export default defineConfig({ - modulesDir: 'wxt-modules', // defaults to "modules" - }); - ``` -2. Rename your `modules` directory to something else. - ## v0.17.0 → v0.18.0 ### Automatic MV3 `host_permissions` to MV2 `permissions` From 8f71d80f94a5927b4a5c6e7ca9f1ae75bf0c385d Mon Sep 17 00:00:00 2001 From: 1natsu <1natsu172@users.noreply.github.com> Date: Sat, 7 Dec 2024 23:21:52 +0900 Subject: [PATCH 086/130] feat: `autoMount` content script UIs (#1210) Co-authored-by: Aaron --- docs/guide/essentials/content-scripts.md | 33 +++ .../entrypoints/automount.content/index.ts | 76 ++++++ .../entrypoints/automount.content/style.css | 3 + packages/wxt/package.json | 1 + .../ui/__tests__/index.test.ts | 254 +++++++++++++++++- .../src/client/content-scripts/ui/index.ts | 146 +++++++++- .../src/client/content-scripts/ui/types.ts | 50 +++- pnpm-lock.yaml | 16 ++ 8 files changed, 558 insertions(+), 21 deletions(-) create mode 100644 packages/wxt-demo/src/entrypoints/automount.content/index.ts create mode 100644 packages/wxt-demo/src/entrypoints/automount.content/style.css diff --git a/docs/guide/essentials/content-scripts.md b/docs/guide/essentials/content-scripts.md index 584c90eb..eda5288c 100644 --- a/docs/guide/essentials/content-scripts.md +++ b/docs/guide/essentials/content-scripts.md @@ -576,6 +576,39 @@ For MV3, `injectScript` is synchronous and the injected script will be evaluated However for MV2, `injectScript` has to `fetch` the script's text content and create an inline `