From 924c35b8432d96f7da485ec4b19ef3ef1b090ea6 Mon Sep 17 00:00:00 2001
From: Shane Osbourne <shane.osbourne8@gmail.com>
Date: Fri, 1 Nov 2024 13:16:42 +0000
Subject: [PATCH 01/10] ntp: update notification + context menu fixes (#1178)

* ntp: update notification

* linting

* linting

* adding support for context menu

* docs structure

* add visibilityMenuItems to contextMenu

* make 'updateNotification' nullable on `initialSetup`

* styling

* lint

* text update

* Allow the component to be pulled up

---------

Co-authored-by: Shane Osbourne <sosbourne@duckduckgo.com>
---
 .stylelintrc.json                             |   3 +-
 .../messages/new-tab/contextMenu.notify.json  |  24 ++++
 .../messages/new-tab/examples/widgets.js      |  13 +-
 .../new-tab/initialSetup.response.json        |  12 +-
 .../new-tab/types/update-notification.json    |  30 +++++
 .../updateNotification_dismiss.notify.json    |   3 +
 ...teNotification_onDataUpdate.subscribe.json |   8 ++
 .../new-tab/app/components/App.module.css     |   7 +-
 .../pages/new-tab/app/components/Icons.js     |  10 ++
 .../new-tab/app/customizer/Customizer.js      |  33 ++++-
 special-pages/pages/new-tab/app/docs.js       |  12 +-
 .../pages/new-tab/app/favorites/Favorites.js  |   4 +-
 special-pages/pages/new-tab/app/index.js      |  18 +--
 special-pages/pages/new-tab/app/new-tab.md    |  40 ++++++
 .../new-tab/app/privacy-stats/PrivacyStats.js |   4 +-
 .../app/privacy-stats/PrivacyStats.module.css |   1 +
 .../app/privacy-stats/privacy-stats.md        |  35 +++++
 .../privacy-stats/privacy-stats.service.js    |  33 -----
 .../RemoteMessagingFramework.js               |   8 +-
 .../RemoteMessagingFramework.module.css       |   4 +-
 .../{rmf.service.md => rmf.md}                |   2 +-
 .../remote-messaging-framework/rmf.service.js |   4 -
 special-pages/pages/new-tab/app/types.js      |   7 +
 .../update-notification/UpdateNotification.js |  99 ++++++++++++++
 .../UpdateNotification.module.css             | 126 ++++++++++++++++++
 .../UpdateNotificationProvider.js             |  98 ++++++++++++++
 .../update-notification.spec.js               |  34 +++++
 .../mocks/update-notification.data.js         |  17 +++
 .../update-notification.md                    |  56 ++++++++
 .../update-notification.service.js            |  49 +++++++
 .../new-tab/app/widget-list/WidgetList.js     |   9 +-
 .../new-tab/app/widget-list/widget-config.md  |  26 ++++
 .../app/widget-list/widget-config.service.js  |  24 ----
 .../new-tab/integration-tests/new-tab.page.js |  11 +-
 .../new-tab/integration-tests/new-tab.spec.js |  29 ++++
 special-pages/pages/new-tab/src/js/index.js   |   8 ++
 .../pages/new-tab/src/js/mock-transport.js    |  29 +++-
 .../pages/new-tab/src/locales/en/newtab.json  |  12 ++
 special-pages/playwright.config.js            |   3 +-
 special-pages/types/new-tab.ts                |  41 ++++++
 40 files changed, 883 insertions(+), 103 deletions(-)
 create mode 100644 special-pages/messages/new-tab/contextMenu.notify.json
 create mode 100644 special-pages/messages/new-tab/types/update-notification.json
 create mode 100644 special-pages/messages/new-tab/updateNotification_dismiss.notify.json
 create mode 100644 special-pages/messages/new-tab/updateNotification_onDataUpdate.subscribe.json
 create mode 100644 special-pages/pages/new-tab/app/new-tab.md
 create mode 100644 special-pages/pages/new-tab/app/privacy-stats/privacy-stats.md
 rename special-pages/pages/new-tab/app/remote-messaging-framework/{rmf.service.md => rmf.md} (98%)
 create mode 100644 special-pages/pages/new-tab/app/update-notification/UpdateNotification.js
 create mode 100644 special-pages/pages/new-tab/app/update-notification/UpdateNotification.module.css
 create mode 100644 special-pages/pages/new-tab/app/update-notification/UpdateNotificationProvider.js
 create mode 100644 special-pages/pages/new-tab/app/update-notification/integration-tests/update-notification.spec.js
 create mode 100644 special-pages/pages/new-tab/app/update-notification/mocks/update-notification.data.js
 create mode 100644 special-pages/pages/new-tab/app/update-notification/update-notification.md
 create mode 100644 special-pages/pages/new-tab/app/update-notification/update-notification.service.js
 create mode 100644 special-pages/pages/new-tab/app/widget-list/widget-config.md

diff --git a/.stylelintrc.json b/.stylelintrc.json
index 71443bfa3..3d344459f 100644
--- a/.stylelintrc.json
+++ b/.stylelintrc.json
@@ -32,9 +32,10 @@
         "keyframes-name-pattern": null,
         "block-no-empty": null,
         "selector-id-pattern": null,
+        "selector-pseudo-class-no-unknown": null,
         "declaration-block-no-shorthand-property-overrides": null,
         "font-family-no-missing-generic-family-keyword": null,
         "font-family-name-quotes": null,
         "value-no-vendor-prefix": null
     }
-}
\ No newline at end of file
+}
diff --git a/special-pages/messages/new-tab/contextMenu.notify.json b/special-pages/messages/new-tab/contextMenu.notify.json
new file mode 100644
index 000000000..43e576967
--- /dev/null
+++ b/special-pages/messages/new-tab/contextMenu.notify.json
@@ -0,0 +1,24 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "type": "object",
+  "required": ["visibilityMenuItems"],
+  "properties": {
+    "visibilityMenuItems": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "title": "Visibility Menu Item",
+        "required": ["id", "title"],
+        "properties": {
+          "id": {
+            "type": "string"
+          },
+          "title": {
+            "description": "Translated name of the section",
+            "type": "string"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/special-pages/messages/new-tab/examples/widgets.js b/special-pages/messages/new-tab/examples/widgets.js
index 569583b01..2ec7a1daa 100644
--- a/special-pages/messages/new-tab/examples/widgets.js
+++ b/special-pages/messages/new-tab/examples/widgets.js
@@ -34,16 +34,19 @@ const widgetConfig = {
  */
 const initialSetupResponse = {
     widgets: [
-        { "id": "weatherWidget" },
-        { "id": "newsWidget" }
+        { "id": "updateNotification" },
+        { "id": "rmf" },
+        { "id": "favorites" },
+        { "id": "privacyStats" }
     ],
     widgetConfigs: [
-        { "id": "weatherWidget", "visibility": "visible" },
-        { "id": "newsWidget", "visibility": "visible" }
+        { "id": "favorites", "visibility": "visible" },
+        { "id": "privacyStats", "visibility": "visible" }
     ],
     env: 'production',
     locale: 'en',
-    platform: {name: 'windows'}
+    platform: { name: 'windows' },
+    updateNotification: { content: null }
 }
 
 export {}
diff --git a/special-pages/messages/new-tab/initialSetup.response.json b/special-pages/messages/new-tab/initialSetup.response.json
index c465c9e43..2bb750405 100644
--- a/special-pages/messages/new-tab/initialSetup.response.json
+++ b/special-pages/messages/new-tab/initialSetup.response.json
@@ -1,7 +1,7 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
   "type": "object",
-  "required": ["widgets", "widgetConfigs", "locale", "env", "platform"],
+  "required": ["widgets", "widgetConfigs", "locale", "env", "platform", "updateNotification"],
   "properties": {
     "widgets": {
       "$ref": "./types/widget-list.json"
@@ -25,6 +25,16 @@
           "enum": ["macos", "windows", "android", "ios", "integration"]
         }
       }
+    },
+    "updateNotification": {
+      "oneOf": [
+        {
+          "type": "null"
+        },
+        {
+          "$ref": "types/update-notification.json"
+        }
+      ]
     }
   }
 }
diff --git a/special-pages/messages/new-tab/types/update-notification.json b/special-pages/messages/new-tab/types/update-notification.json
new file mode 100644
index 000000000..6ebfae833
--- /dev/null
+++ b/special-pages/messages/new-tab/types/update-notification.json
@@ -0,0 +1,30 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "type": "object",
+    "title": "Update Notification Data",
+    "required": ["content"],
+    "properties": {
+        "content": {
+            "oneOf": [
+                {
+                    "type": "null"
+                },
+                {
+                    "title": "Update Notification",
+                    "required": ["version", "notes"],
+                    "properties": {
+                        "version": {
+                            "type": "string"
+                        },
+                        "notes": {
+                            "type": "array",
+                            "items": {
+                                "type": "string"
+                            }
+                        }
+                    }
+                }
+            ]
+        }
+    }
+}
diff --git a/special-pages/messages/new-tab/updateNotification_dismiss.notify.json b/special-pages/messages/new-tab/updateNotification_dismiss.notify.json
new file mode 100644
index 000000000..0af74a319
--- /dev/null
+++ b/special-pages/messages/new-tab/updateNotification_dismiss.notify.json
@@ -0,0 +1,3 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#"
+}
diff --git a/special-pages/messages/new-tab/updateNotification_onDataUpdate.subscribe.json b/special-pages/messages/new-tab/updateNotification_onDataUpdate.subscribe.json
new file mode 100644
index 000000000..79940a08c
--- /dev/null
+++ b/special-pages/messages/new-tab/updateNotification_onDataUpdate.subscribe.json
@@ -0,0 +1,8 @@
+{
+    "$schema": "http://json-schema.org/draft-07/schema#",
+    "allOf": [
+        {
+            "$ref": "types/update-notification.json"
+        }
+    ]
+}
diff --git a/special-pages/pages/new-tab/app/components/App.module.css b/special-pages/pages/new-tab/app/components/App.module.css
index 76fb547cf..82acbcd45 100644
--- a/special-pages/pages/new-tab/app/components/App.module.css
+++ b/special-pages/pages/new-tab/app/components/App.module.css
@@ -8,15 +8,16 @@ body {
     font-size: var(--body-font-size);
     font-weight: var(--body-font-weight);
     line-height: var(--body-line-height);
-    padding-left: var(--sp-6);
-    padding-right: var(--sp-6);
 }
 
 .layout {
     padding-top: var(--sp-16);
     padding-bottom: var(--sp-16);
-    max-width: calc(504 * var(--px-in-rem));
     margin-left: auto;
     margin-right: auto;
 }
 
+:global(.layout-centered) {
+    margin-inline: auto;
+    max-width: calc(504 * var(--px-in-rem));
+}
diff --git a/special-pages/pages/new-tab/app/components/Icons.js b/special-pages/pages/new-tab/app/components/Icons.js
index 62b60e003..f6ff3266b 100644
--- a/special-pages/pages/new-tab/app/components/Icons.js
+++ b/special-pages/pages/new-tab/app/components/Icons.js
@@ -49,3 +49,13 @@ export function Shield () {
         </svg>
     )
 }
+
+export function Cross () {
+    return (
+        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
+            <path
+                d="M11.4419 5.44194C11.686 5.19786 11.686 4.80214 11.4419 4.55806C11.1979 4.31398 10.8021 4.31398 10.5581 4.55806L8 7.11612L5.44194 4.55806C5.19786 4.31398 4.80214 4.31398 4.55806 4.55806C4.31398 4.80214 4.31398 5.19786 4.55806 5.44194L7.11612 8L4.55806 10.5581C4.31398 10.8021 4.31398 11.1979 4.55806 11.4419C4.80214 11.686 5.19786 11.686 5.44194 11.4419L8 8.88388L10.5581 11.4419C10.8021 11.686 11.1979 11.686 11.4419 11.4419C11.686 11.1979 11.686 10.8021 11.4419 10.5581L8.88388 8L11.4419 5.44194Z"
+                fill="currentColor"/>
+        </svg>
+    )
+}
diff --git a/special-pages/pages/new-tab/app/customizer/Customizer.js b/special-pages/pages/new-tab/app/customizer/Customizer.js
index 74e5d8aa9..2314fa117 100644
--- a/special-pages/pages/new-tab/app/customizer/Customizer.js
+++ b/special-pages/pages/new-tab/app/customizer/Customizer.js
@@ -4,9 +4,10 @@ import styles from './Customizer.module.css'
 import { VisibilityMenu } from './VisibilityMenu.js'
 import { CustomizeIcon } from '../components/Icons.js'
 import cn from 'classnames'
+import { useMessaging } from '../types.js'
 
 /**
- * @import { Widgets, WidgetConfigItem, WidgetVisibility } from '../../../../types/new-tab.js'
+ * @import { Widgets, WidgetConfigItem, WidgetVisibility, VisibilityMenuItem } from '../../../../types/new-tab.js'
  */
 
 /**
@@ -16,6 +17,8 @@ export function Customizer () {
     const { setIsOpen, buttonRef, dropdownRef, isOpen } = useDropdown()
     const [rowData, setRowData] = useState(/** @type {VisibilityRowData[]} */([]))
 
+    useContextMenu()
+
     /**
      * Dispatch an event every time the customizer is opened - this
      * allows widgets to register themselves and provide titles/icons etc.
@@ -63,7 +66,7 @@ export function Customizer () {
 Customizer.OPEN_EVENT = 'ntp-customizer-open'
 Customizer.UPDATE_EVENT = 'ntp-customizer-update'
 
-function getItems () {
+export function getItems () {
     /** @type {VisibilityRowData[]} */
     const next = []
     const detail = {
@@ -77,6 +80,32 @@ function getItems () {
     return next
 }
 
+/**
+ * Forward the contextmenu event
+ */
+export function useContextMenu () {
+    const messaging = useMessaging()
+    useEffect(() => {
+        function handler (e) {
+            e.preventDefault()
+            e.stopImmediatePropagation()
+            const items = getItems()
+            /** @type {VisibilityMenuItem[]} */
+            const simplified = items.map(item => {
+                return {
+                    id: item.id,
+                    title: item.title
+                }
+            })
+            messaging.contextMenu({ visibilityMenuItems: simplified })
+        }
+        document.body.addEventListener('contextmenu', handler)
+        return () => {
+            document.body.removeEventListener('contextmenu', handler)
+        }
+    }, [messaging])
+}
+
 /**
  * @param {object} props
  * @param {string} [props.menuId]
diff --git a/special-pages/pages/new-tab/app/docs.js b/special-pages/pages/new-tab/app/docs.js
index bbb566d2b..3b278c671 100644
--- a/special-pages/pages/new-tab/app/docs.js
+++ b/special-pages/pages/new-tab/app/docs.js
@@ -1,11 +1,9 @@
 /**
- *
- * - {@link WidgetConfigService}
- * - {@link PrivacyStatsService}
- * - {@link RMFService}
+ * @document ./new-tab.md
+ * @document ./widget-list/widget-config.md
+ * @document ./remote-messaging-framework/rmf.md
+ * @document ./update-notification/update-notification.md
+ * @document ./privacy-stats/privacy-stats.md
  *
  * @module NewTab Services
  */
-export * from './privacy-stats/privacy-stats.service.js'
-export * from './widget-list/widget-config.service.js'
-export * from './remote-messaging-framework/rmf.service.js'
diff --git a/special-pages/pages/new-tab/app/favorites/Favorites.js b/special-pages/pages/new-tab/app/favorites/Favorites.js
index 33062d5fa..1a4dc55cf 100644
--- a/special-pages/pages/new-tab/app/favorites/Favorites.js
+++ b/special-pages/pages/new-tab/app/favorites/Favorites.js
@@ -15,6 +15,8 @@ export function FavoritesCustomized () {
         return null
     }
     return (
-        <p>Favourites here... (id: <code>{id}</code>)</p>
+        <div class="layout-centered">
+            <p>Favourites here... (id: <code>{id}</code>)</p>
+        </div>
     )
 }
diff --git a/special-pages/pages/new-tab/app/index.js b/special-pages/pages/new-tab/app/index.js
index 3f04e3a7b..93267c80f 100644
--- a/special-pages/pages/new-tab/app/index.js
+++ b/special-pages/pages/new-tab/app/index.js
@@ -4,7 +4,7 @@ import { EnvironmentProvider, UpdateEnvironment } from '../../../shared/componen
 import { Fallback } from '../../../shared/components/Fallback/Fallback.jsx'
 import { ErrorBoundary } from '../../../shared/components/ErrorBoundary.js'
 import { SettingsProvider } from './settings.provider.js'
-import { MessagingContext } from './types'
+import { InitialSetupContext, MessagingContext } from './types'
 import { TranslationProvider } from '../../../shared/components/TranslationsProvider.js'
 import { WidgetConfigService } from './widget-list/widget-config.service.js'
 import enStrings from '../src/locales/en/newtab.json'
@@ -91,13 +91,15 @@ export async function init (messaging, baseEnvironment) {
             <ErrorBoundary didCatch={didCatch} fallback={<Fallback showDetails={environment.env === 'development'}/>}>
                 <UpdateEnvironment search={window.location.search}/>
                 <MessagingContext.Provider value={messaging}>
-                    <SettingsProvider settings={settings}>
-                        <TranslationProvider translationObject={strings} fallback={strings} textLength={environment.textLength}>
-                            <WidgetConfigProvider api={widgetConfigAPI} widgetConfigs={init.widgetConfigs} widgets={init.widgets}>
-                                <App />
-                            </WidgetConfigProvider>
-                        </TranslationProvider>
-                    </SettingsProvider>
+                    <InitialSetupContext.Provider value={init}>
+                        <SettingsProvider settings={settings}>
+                            <TranslationProvider translationObject={strings} fallback={strings} textLength={environment.textLength}>
+                                <WidgetConfigProvider api={widgetConfigAPI} widgetConfigs={init.widgetConfigs} widgets={init.widgets}>
+                                    <App />
+                                </WidgetConfigProvider>
+                            </TranslationProvider>
+                        </SettingsProvider>
+                    </InitialSetupContext.Provider>
                 </MessagingContext.Provider>
             </ErrorBoundary>
         </EnvironmentProvider>
diff --git a/special-pages/pages/new-tab/app/new-tab.md b/special-pages/pages/new-tab/app/new-tab.md
new file mode 100644
index 000000000..e49a8a9b7
--- /dev/null
+++ b/special-pages/pages/new-tab/app/new-tab.md
@@ -0,0 +1,40 @@
+---
+title: New Tab Page
+---
+
+## Requests
+
+- {@link "NewTab Messages".InitialSetupRequest `initialSetup`}
+  - Returns {@link "NewTab Messages".InitialSetupResponse}
+  - See the `initialSetupResponse` section of [example of initial data](../../../messages/new-tab/examples/widgets.js)
+
+## Notifications
+
+- {@link "NewTab Messages".ContextMenuNotification `contextMenu`}
+  - Sent when the user right-clicks in the page
+  - Note: Other widgets might prevent this (and send their own, eg: favorites)
+  - Sends: {@link "NewTab Messages".ContextMenuNotify}
+  - Example:
+
+```json
+{
+  "visibilityMenuItems": [
+    {
+      "id": "favorites",
+      "title": "Favorites"
+    },
+    {
+      "id": "privacyStats",
+      "title": "Privacy Stats"
+    }
+  ]
+}
+```
+
+- {@link "NewTab Messages".ReportInitExceptionNotification `reportInitException`}
+  - Sent when the application fails to initialize (for example, a JavaScript exception prevented it)
+  - Sends: `{ message: string }` - see {@link "NewTab Messages".ReportInitExceptionNotify}
+
+- {@link "NewTab Messages".ReportPageExceptionNotification `reportPageException`}
+  - Sent when the application failed after initialization (for example, a JavaScript exception prevented it)
+  - Sends: `{ message: string }` - see {@link "NewTab Messages".ReportPageExceptionNotify}
diff --git a/special-pages/pages/new-tab/app/privacy-stats/PrivacyStats.js b/special-pages/pages/new-tab/app/privacy-stats/PrivacyStats.js
index 3e0a899a6..90a929d92 100644
--- a/special-pages/pages/new-tab/app/privacy-stats/PrivacyStats.js
+++ b/special-pages/pages/new-tab/app/privacy-stats/PrivacyStats.js
@@ -1,4 +1,5 @@
 import { h } from 'preact'
+import cn from 'classnames'
 import styles from './PrivacyStats.module.css'
 import { useTypedTranslation } from '../types.js'
 import { useContext, useState, useId, useCallback } from 'preact/hooks'
@@ -60,8 +61,9 @@ function PrivacyStatsConfigured ({ parentRef, expansion, data, toggle }) {
     // see: https://www.w3.org/WAI/ARIA/apg/patterns/accordion/examples/accordion/
     const WIDGET_ID = useId()
     const TOGGLE_ID = useId()
+
     return (
-        <div class={styles.root} ref={parentRef}>
+        <div class={cn('layout-centered', styles.root)} ref={parentRef}>
             <Heading
                 totalCount={data.totalCount}
                 trackerCompanies={data.trackerCompanies}
diff --git a/special-pages/pages/new-tab/app/privacy-stats/PrivacyStats.module.css b/special-pages/pages/new-tab/app/privacy-stats/PrivacyStats.module.css
index 677f417f3..a54db52a4 100644
--- a/special-pages/pages/new-tab/app/privacy-stats/PrivacyStats.module.css
+++ b/special-pages/pages/new-tab/app/privacy-stats/PrivacyStats.module.css
@@ -7,6 +7,7 @@
     align-items: start;
     grid-template-columns: auto;
     grid-row-gap: 18px;
+    width: 100%;
 
     @media screen and (prefers-color-scheme: dark) {
         border-color: var(--color-white-at-9);
diff --git a/special-pages/pages/new-tab/app/privacy-stats/privacy-stats.md b/special-pages/pages/new-tab/app/privacy-stats/privacy-stats.md
new file mode 100644
index 000000000..fc1093436
--- /dev/null
+++ b/special-pages/pages/new-tab/app/privacy-stats/privacy-stats.md
@@ -0,0 +1,35 @@
+---
+title: Privacy Stats
+---
+
+## Requests:
+- {@link "NewTab Messages".StatsGetDataRequest `stats_getData`}
+    - Used to fetch the initial data (during the first render)
+    - returns {@link "NewTab Messages".PrivacyStatsData}
+
+- {@link "NewTab Messages".StatsGetDataRequest `stats_getConfig`}
+    - Used to fetch the initial config data (eg: expanded vs collapsed)
+    - returns {@link "NewTab Messages".StatsConfig}
+
+## Subscriptions:
+- {@link "NewTab Messages".StatsOnDataUpdateSubscription `stats_onDataUpdate`}.
+    - The tracker/company data used in the feed.
+    - returns {@link "NewTab Messages".PrivacyStatsData}
+- {@link "NewTab Messages".StatsOnDataUpdateSubscription `stats_onConfigUpdate`}.
+    - The widget config
+    - returns {@link "NewTab Messages".StatsConfig}
+
+## Notifications:
+- {@link "NewTab Messages".StatsSetConfigNotification `stats_setConfig`}
+    - Sent when the user toggles the expansion of the stats
+    - sends {@link "NewTab Messages".StatsConfig}
+    - example payload:
+      ```json
+      {
+        "expansion": "collapsed"
+      }
+      ```
+
+## Examples:
+The following examples show the data types in JSON format:
+[messages/new-tab/examples/stats.js](../../../../messages/new-tab/examples/stats.js)
diff --git a/special-pages/pages/new-tab/app/privacy-stats/privacy-stats.service.js b/special-pages/pages/new-tab/app/privacy-stats/privacy-stats.service.js
index e9ab6f20e..cce8ced8c 100644
--- a/special-pages/pages/new-tab/app/privacy-stats/privacy-stats.service.js
+++ b/special-pages/pages/new-tab/app/privacy-stats/privacy-stats.service.js
@@ -4,39 +4,6 @@
  */
 import { Service } from '../service.js'
 
-/**
- * ## Requests:
- * - {@link "NewTab Messages".StatsGetDataRequest `stats_getData`}
- *   - Used to fetch the initial data (during the first render)
- *   - returns {@link "NewTab Messages".PrivacyStatsData}
- *
- * - {@link "NewTab Messages".StatsGetDataRequest `stats_getConfig`}
- *   - Used to fetch the initial config data (eg: expanded vs collapsed)
- *   - returns {@link "NewTab Messages".StatsConfig}
- *
- * ## Subscriptions:
- * - {@link "NewTab Messages".StatsOnDataUpdateSubscription `stats_onDataUpdate`}.
- *   - The tracker/company data used in the feed.
- *   - returns {@link "NewTab Messages".PrivacyStatsData}
- * - {@link "NewTab Messages".StatsOnDataUpdateSubscription `stats_onConfigUpdate`}.
- *   - The widget config
- *   - returns {@link "NewTab Messages".StatsConfig}
- *
- * ## Notifications:
- * - {@link "NewTab Messages".StatsSetConfigNotification `stats_setConfig`}
- *   - Sent when the user toggles the expansion of the stats
- *   - sends {@link "NewTab Messages".StatsConfig}
- *   - example payload:
- *     ```json
- *     {
- *       "expansion": "collapsed"
- *     }
- *     ```
- *
- * ## Examples:
- * The following examples show the data types in JSON format:
- * [messages/new-tab/examples/stats.js](../../../../messages/new-tab/examples/stats.js)
- */
 export class PrivacyStatsService {
     /**
      * @param {import("../../src/js/index.js").NewTabPage} ntp - The internal data feed, expected to have a `subscribe` method.
diff --git a/special-pages/pages/new-tab/app/remote-messaging-framework/RemoteMessagingFramework.js b/special-pages/pages/new-tab/app/remote-messaging-framework/RemoteMessagingFramework.js
index e86226950..a785dcd15 100644
--- a/special-pages/pages/new-tab/app/remote-messaging-framework/RemoteMessagingFramework.js
+++ b/special-pages/pages/new-tab/app/remote-messaging-framework/RemoteMessagingFramework.js
@@ -3,6 +3,7 @@ import cn from 'classnames'
 import styles from './RemoteMessagingFramework.module.css'
 import { useContext } from 'preact/hooks'
 import { RMFContext } from './RMFProvider.js'
+import { Cross } from '../components/Icons.js'
 
 /**
   * @import { RMFMessage } from "../../../../types/new-tab"
@@ -16,7 +17,7 @@ import { RMFContext } from './RMFProvider.js'
 export function RemoteMessagingFramework ({ message, primaryAction, secondaryAction, dismiss }) {
     const { id, messageType, titleText, descriptionText } = message
     return (
-        <div id={id} class={cn(styles.root, (messageType !== 'small' && message.icon) && styles.icon)}>
+        <div id={id} class={cn('layout-centered', styles.root, (messageType !== 'small' && message.icon) && styles.icon)}>
             {messageType !== 'small' && message.icon && (
                 <span class={styles.iconBlock}>
                     <img src={`./icons/${message.icon}.svg`} alt=""/>
@@ -42,9 +43,8 @@ export function RemoteMessagingFramework ({ message, primaryAction, secondaryAct
                 </div>
             )}
             <button className={cn(styles.btn, styles.dismissBtn)} onClick={() => dismiss(id)} aria-label="Close">
-                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none" >
-                    <path d="M11.4419 5.44194C11.686 5.19786 11.686 4.80214 11.4419 4.55806C11.1979 4.31398 10.8021 4.31398 10.5581 4.55806L8 7.11612L5.44194 4.55806C5.19786 4.31398 4.80214 4.31398 4.55806 4.55806C4.31398 4.80214 4.31398 5.19786 4.55806 5.44194L7.11612 8L4.55806 10.5581C4.31398 10.8021 4.31398 11.1979 4.55806 11.4419C4.80214 11.686 5.19786 11.686 5.44194 11.4419L8 8.88388L10.5581 11.4419C10.8021 11.686 11.1979 11.686 11.4419 11.4419C11.686 11.1979 11.686 10.8021 11.4419 10.5581L8.88388 8L11.4419 5.44194Z" fill="currentColor" />
-                </svg></button>
+                <Cross />
+            </button>
         </div>
 
     )
diff --git a/special-pages/pages/new-tab/app/remote-messaging-framework/RemoteMessagingFramework.module.css b/special-pages/pages/new-tab/app/remote-messaging-framework/RemoteMessagingFramework.module.css
index 087b9b0a9..850fbb6dd 100644
--- a/special-pages/pages/new-tab/app/remote-messaging-framework/RemoteMessagingFramework.module.css
+++ b/special-pages/pages/new-tab/app/remote-messaging-framework/RemoteMessagingFramework.module.css
@@ -9,7 +9,7 @@
     align-items: flex-start;
     font-family: system-ui, BlinkMacSystemFont, 'Segoe UI', Roboto;
     color: var(--color-black);
-
+    width: 100%;
 
     &.icon {
         padding-left: var(--sp-2);
@@ -192,4 +192,4 @@
             color: var(--color-white-at-84);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/special-pages/pages/new-tab/app/remote-messaging-framework/rmf.service.md b/special-pages/pages/new-tab/app/remote-messaging-framework/rmf.md
similarity index 98%
rename from special-pages/pages/new-tab/app/remote-messaging-framework/rmf.service.md
rename to special-pages/pages/new-tab/app/remote-messaging-framework/rmf.md
index eac5f2951..7b3176a71 100644
--- a/special-pages/pages/new-tab/app/remote-messaging-framework/rmf.service.md
+++ b/special-pages/pages/new-tab/app/remote-messaging-framework/rmf.md
@@ -44,4 +44,4 @@ title: Remote Messaging Framework
 ## Examples:
 
 The following examples show the data types in JSON format:
-[messages/new-tab/examples/stats.js](../../../../messages/new-tab/examples/rmf.js)
\ No newline at end of file
+[messages/new-tab/examples/stats.js](../../../../messages/new-tab/examples/rmf.js)
diff --git a/special-pages/pages/new-tab/app/remote-messaging-framework/rmf.service.js b/special-pages/pages/new-tab/app/remote-messaging-framework/rmf.service.js
index fd1af55eb..6fb1f9a34 100644
--- a/special-pages/pages/new-tab/app/remote-messaging-framework/rmf.service.js
+++ b/special-pages/pages/new-tab/app/remote-messaging-framework/rmf.service.js
@@ -3,10 +3,6 @@
  */
 import { Service } from '../service.js'
 
-/**
- * @document ./rmf.service.md
- */
-
 export class RMFService {
     /**
      * @param {import("../../src/js/index.js").NewTabPage} ntp - The internal data feed, expected to have a `subscribe` method.
diff --git a/special-pages/pages/new-tab/app/types.js b/special-pages/pages/new-tab/app/types.js
index 32d7e6b03..8c83518ea 100644
--- a/special-pages/pages/new-tab/app/types.js
+++ b/special-pages/pages/new-tab/app/types.js
@@ -4,6 +4,10 @@ import { TranslationContext } from '../../../shared/components/TranslationsProvi
 import json from '../src/locales/en/newtab.json'
 import { createContext } from 'preact'
 
+/**
+ * @import { InitialSetupResponse } from "../../../types/new-tab.js";
+ */
+
 /**
  * This is a wrapper to only allow keys from the default translation file
  * @type {() => { t: (key: keyof json, replacements?: Record<string, string>) => string }}
@@ -16,3 +20,6 @@ export function useTypedTranslation () {
 
 export const MessagingContext = createContext(/** @type {import("../src/js/index.js").NewTabPage} */({}))
 export const useMessaging = () => useContext(MessagingContext)
+
+export const InitialSetupContext = createContext(/** @type {InitialSetupResponse} */({}))
+export const useInitialSetupData = () => useContext(InitialSetupContext)
diff --git a/special-pages/pages/new-tab/app/update-notification/UpdateNotification.js b/special-pages/pages/new-tab/app/update-notification/UpdateNotification.js
new file mode 100644
index 000000000..61899b418
--- /dev/null
+++ b/special-pages/pages/new-tab/app/update-notification/UpdateNotification.js
@@ -0,0 +1,99 @@
+import { h } from 'preact'
+import cn from 'classnames'
+import styles from './UpdateNotification.module.css'
+import { useContext, useId, useRef } from 'preact/hooks'
+import { UpdateNotificationContext } from './UpdateNotificationProvider.js'
+import { useTypedTranslation } from '../types.js'
+import { Trans } from '../../../../shared/components/TranslationsProvider.js'
+import { Cross } from '../components/Icons.js'
+
+/**
+  * @param {object} props
+  * @param {string[]} props.notes
+  * @param {string} props.version
+  * @param {() => void} [props.dismiss]
+  */
+
+export function UpdateNotification ({ notes, dismiss, version }) {
+    const { t } = useTypedTranslation()
+
+    return (
+        <div class={styles.root}>
+            <div class={cn('layout-centered', styles.body)}>
+                {notes.length > 0 && <WithNotes notes={notes} version={version} />}
+                {notes.length === 0 && <WithoutNotes version={version} />}
+            </div>
+            <div class={styles.dismiss}>
+                <button onClick={dismiss} class={styles.dismissBtn}>
+                    <span class="sr-only">{t('updateNotification_dismiss_btn')}</span>
+                    <Cross />
+                </button>
+            </div>
+        </div>
+    )
+}
+
+export function PulledUp ({ children }) {
+    return (
+        <div class={styles.pulled}>
+            {children}
+        </div>
+    )
+}
+
+export function WithNotes ({ notes, version }) {
+    const id = useId()
+    const ref = useRef(/** @type {HTMLDetailsElement|null} */(null))
+    const { t } = useTypedTranslation()
+    const inlineLink = <Trans
+        str={t('updateNotification_whats_new')}
+        values={{
+            a: {
+                href: `#${id}`,
+                class: styles.inlineLink,
+                click: (e) => {
+                    e.preventDefault()
+                    if (!ref.current) return
+                    ref.current.open = !ref.current.open
+                }
+            }
+        }}
+    />
+    return (
+        <details ref={ref}>
+            <summary tabIndex={-1} className={styles.summary}>{t('updateNotification_updated_version', { version })} {inlineLink}</summary>
+            <div id={id} class={styles.detailsContent}>
+                <ul class={styles.list}>
+                    {notes.map((note, index) => {
+                        return <li key={note + index}>{note}</li>
+                    })}
+                </ul>
+            </div>
+        </details>
+    )
+}
+
+export function WithoutNotes ({ version }) {
+    const { t } = useTypedTranslation()
+    return (
+        <p>{t('updateNotification_updated_version', { version })}</p>
+    )
+}
+
+export function UpdateNotificationConsumer () {
+    const { state, dismiss } = useContext(UpdateNotificationContext)
+
+    // `state.data.content` can be empty - meaning there's no message to display!
+    if (state.status === 'ready' && state.data.content) {
+        return (
+            <PulledUp>
+                <UpdateNotification
+                    notes={state.data.content.notes}
+                    version={state.data.content.version}
+                    dismiss={dismiss}
+                />
+            </PulledUp>
+        )
+    }
+    return null
+}
diff --git a/special-pages/pages/new-tab/app/update-notification/UpdateNotification.module.css b/special-pages/pages/new-tab/app/update-notification/UpdateNotification.module.css
new file mode 100644
index 000000000..8c54785df
--- /dev/null
+++ b/special-pages/pages/new-tab/app/update-notification/UpdateNotification.module.css
@@ -0,0 +1,126 @@
+.pulled {
+    margin-top: -40px;
+}
+.root {
+    border-bottom: 1px solid var(--ntp-surface-border-color);
+    position: relative;
+    min-height: var(--sp-10);
+
+    &:hover {
+        .dismiss {
+            opacity: 1;
+        }
+    }
+    &:focus-within {
+        .dismiss {
+            opacity: 1;
+        }
+    }
+}
+.body {
+    position: relative;
+    text-align: center;
+}
+
+.inlineLink {
+    text-decoration: none;
+    &:focus-visible {
+        text-decoration: underline;
+    }
+    &:focus {
+        outline: none
+    }
+    &:hover {
+        text-decoration: underline;
+    }
+    @media screen and (prefers-color-scheme: dark) {
+        color: white;
+        text-decoration: underline;
+
+        &:hover {
+            text-decoration: none;
+        }
+        &:focus-visible {
+            outline: 1px dotted var(--ntp-focus-outline-color);
+            border-radius: 4px;
+            text-decoration: none;
+        }
+    }
+}
+
+.summary {
+    list-style: none;
+}
+
+.summary::-webkit-details-marker {
+    display: none;
+}
+
+.dismiss {
+    opacity: 0;
+    transition: all .3s;
+    position: absolute;
+    top: -2px;
+    right: var(--sp-2);
+}
+
+.detailsContent {
+    padding-inline: var(--sp-2);
+    padding-block: var(--sp-4);
+    text-align: left;
+}
+
+.list {
+    margin-left: var(--sp-20);
+    li {
+        list-style: disc
+    }
+}
+
+.dismissBtn {
+    height: 24px;
+    width: 24px;
+    padding: 0;
+    background-color: var(--ntp-background-color);
+    color: var(--color-black-at-60);
+    border: none;
+    border-radius: 50%;
+    position: relative;
+    cursor: pointer;
+    transition: background-color .3s;
+
+    svg {
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        transform: translateY(-50%) translateX(-50%);
+    }
+
+    &:active {
+        background-color: var(--color-black-at-18);
+        color: var(--color-black-at-84);
+    }
+
+    &:hover {
+        background-color: var(--color-black-at-9);
+    }
+
+    &:focus-visible {
+        outline: 1px dotted var(--ntp-focus-outline-color);
+        outline-offset: 4px;
+    }
+
+    @media screen and (prefers-color-scheme: dark) {
+        color: var(--color-white-at-60);
+
+        &:hover {
+            background-color: var(--color-white-at-9);
+        }
+
+        &:active {
+            background-color: var(--color-white-at-18);
+            color: var(--color-white-at-84);
+        }
+    }
+}
+
diff --git a/special-pages/pages/new-tab/app/update-notification/UpdateNotificationProvider.js b/special-pages/pages/new-tab/app/update-notification/UpdateNotificationProvider.js
new file mode 100644
index 000000000..5b1a38ae5
--- /dev/null
+++ b/special-pages/pages/new-tab/app/update-notification/UpdateNotificationProvider.js
@@ -0,0 +1,98 @@
+import { createContext, h } from 'preact'
+import { useCallback, useEffect, useReducer, useRef } from 'preact/hooks'
+import { useInitialSetupData, useMessaging } from '../types.js'
+import { UpdateNotificationService } from './update-notification.service.js'
+import {
+    reducer,
+    useDataSubscription
+} from '../service.hooks.js'
+
+/**
+ * @typedef {import('../../../../types/new-tab.js').UpdateNotificationData} UpdateNotificationData
+ * @typedef {import('../service.hooks.js').State<UpdateNotificationData, undefined>} State
+ * @typedef {import('../service.hooks.js').Events<UpdateNotificationData, undefined>} Events
+ */
+
+/**
+ * These are the values exposed to consumers.
+ */
+export const UpdateNotificationContext = createContext({
+    /** @type {State} */
+    state: { status: 'idle', data: null, config: null },
+    /** @type {() => void} */
+    dismiss: () => {
+        throw new Error('must implement dismiss')
+    }
+})
+
+export const UpdateNotificationDispatchContext = createContext(/** @type {import("preact/hooks").Dispatch<Events>} */({}))
+
+/**
+ * A data provider that will use `RMFService` to fetch data, subscribe
+ * to updates and modify state.
+ *
+ * @param {Object} props
+ * @param {import("preact").ComponentChild} props.children
+ */
+export function UpdateNotificationProvider (props) {
+    const { updateNotification } = useInitialSetupData()
+    if (updateNotification === null) {
+        return null
+    }
+    return (
+        <UpdateNotificationWithInitial updateNotification={updateNotification}>
+            {props.children}
+        </UpdateNotificationWithInitial>
+    )
+}
+
+/**
+ * @param {Object} props
+ * @param {import("preact").ComponentChild} props.children
+ * @param {UpdateNotificationData} props.updateNotification
+ */
+function UpdateNotificationWithInitial ({ updateNotification, children }) {
+    const initial = /** @type {State} */({
+        status: 'ready',
+        data: updateNotification,
+        config: undefined
+    })
+
+    // const [state, dispatch] = useReducer(withLog('RMFProvider', reducer), initial)
+    const [state, dispatch] = useReducer(reducer, initial)
+
+    // create an instance of `RMFService` for the lifespan of this component.
+    const service = useService(updateNotification)
+
+    // subscribe to data updates
+    useDataSubscription({ dispatch, service })
+
+    const dismiss = useCallback(() => {
+        service.current?.dismiss()
+    }, [service])
+
+    return (
+        <UpdateNotificationContext.Provider value={{ state, dismiss }}>
+            <UpdateNotificationDispatchContext.Provider value={dispatch}>
+                {children}
+            </UpdateNotificationDispatchContext.Provider>
+        </UpdateNotificationContext.Provider>
+    )
+}
+
+/**
+ * @param {UpdateNotificationData} initial
+ * @return {import("preact").RefObject<UpdateNotificationService>}
+ */
+export function useService (initial) {
+    const service = useRef(/** @type {UpdateNotificationService|null} */(null))
+    const ntp = useMessaging()
+    useEffect(() => {
+        const stats = new UpdateNotificationService(ntp, initial)
+        service.current = stats
+        return () => {
+            stats.destroy()
+        }
+    }, [ntp, initial])
+    return service
+}
diff --git a/special-pages/pages/new-tab/app/update-notification/integration-tests/update-notification.spec.js b/special-pages/pages/new-tab/app/update-notification/integration-tests/update-notification.spec.js
new file mode 100644
index 000000000..7bba5f290
--- /dev/null
+++ b/special-pages/pages/new-tab/app/update-notification/integration-tests/update-notification.spec.js
@@ -0,0 +1,34 @@
+import { test, expect } from '@playwright/test'
+import { NewtabPage } from '../../../integration-tests/new-tab.page.js'
+
+test.describe('newtab update notifications', () => {
+    test('handles empty notes', async ({ page }, workerInfo) => {
+        const ntp = NewtabPage.create(page, workerInfo)
+        await ntp.reducedMotion()
+        await ntp.openPage({ updateNotification: 'empty' })
+
+        const calls1 = await ntp.mocks.waitForCallCount({ method: 'initialSetup', count: 1 })
+
+        expect(calls1.length).toBe(1)
+        const text = page.getByText('Browser Updated to version 1.65.0.')
+        await text.waitFor()
+        await page.getByRole('button', { name: 'Dismiss' }).click()
+        await ntp.mocks.waitForCallCount({ method: 'updateNotification_dismiss', count: 1 })
+        await expect(text).not.toBeVisible()
+    })
+    test('handles populated notes', async ({ page }, workerInfo) => {
+        const ntp = NewtabPage.create(page, workerInfo)
+        await ntp.reducedMotion()
+        await ntp.openPage({ updateNotification: 'populated' })
+
+        const calls1 = await ntp.mocks.waitForCallCount({ method: 'initialSetup', count: 1 })
+
+        expect(calls1.length).toBe(1)
+
+        await page.getByText('Browser Updated to version 1.91. See what\'s new in this release.').waitFor()
+        await page.getByRole('link', { name: 'what\'s new' }).click()
+        await page.getByText('Bug fixes and improvements').waitFor()
+        await page.getByRole('button', { name: 'Dismiss' }).click()
+        await ntp.mocks.waitForCallCount({ method: 'updateNotification_dismiss', count: 1 })
+    })
+})
diff --git a/special-pages/pages/new-tab/app/update-notification/mocks/update-notification.data.js b/special-pages/pages/new-tab/app/update-notification/mocks/update-notification.data.js
new file mode 100644
index 000000000..710676eac
--- /dev/null
+++ b/special-pages/pages/new-tab/app/update-notification/mocks/update-notification.data.js
@@ -0,0 +1,17 @@
+/**
+ * @type {Record<string, {content: NonNullable<import("../../../../../types/new-tab").UpdateNotificationData['content']>}>}
+ */
+export const updateNotificationExamples = {
+    empty: {
+        content: {
+            version: '1.65.0',
+            notes: []
+        }
+    },
+    populated: {
+        content: {
+            notes: ['Bug fixes and improvements'],
+            version: '1.91'
+        }
+    }
+}
diff --git a/special-pages/pages/new-tab/app/update-notification/update-notification.md b/special-pages/pages/new-tab/app/update-notification/update-notification.md
new file mode 100644
index 000000000..63e710551
--- /dev/null
+++ b/special-pages/pages/new-tab/app/update-notification/update-notification.md
@@ -0,0 +1,56 @@
+---
+title: Update Notification
+---
+
+## Initial Data 
+
+- Add the key `updateNotification` to `initialSetup`
+- The data takes the following form: {@link "NewTab Messages".UpdateNotificationData}
+- Example from `initialSetup`
+
+*Signal there are no notes to show* 
+```json
+{
+  "...": "...",
+  "updateNotification": {
+    "content": null
+  }
+}
+```
+
+Signal there are notes to show.
+
+```json
+{
+  "...": "...",
+  "updateNotification": {
+    "content": {
+      "version": "1.98.0",
+      "notes": ["Bug fixes and improvements"]
+    }
+  }
+}
+```
+
+If your platform does not support Update Notifications, you can set this initial property to `null`
+
+```json
+{
+  "...": "...",
+  "updateNotification": null
+}
+```
+
+
+**NOTE**: `updateNotification` and it's field `content` are **required**. Set `content` to `null` to indicate 
+nothing to show. This mirrors how RMF is designed
+
+## Subscriptions:
+- {@link "NewTab Messages".UpdateNotificationOnDataUpdateSubscription `updateNotification_onDataUpdate`}.
+    - Sends fresh {@link "NewTab Messages".UpdateNotificationData} whenever needed.
+    - For example, it might send `{ content: null }` to remove existing notes
+    - Or, it might send fresh at any point
+
+## Notifications:
+- {@link "NewTab Messages".UpdateNotificationDismissNotification `updateNotification_dismiss`}
+    - Sent when the user chooses to dismiss the release notes
diff --git a/special-pages/pages/new-tab/app/update-notification/update-notification.service.js b/special-pages/pages/new-tab/app/update-notification/update-notification.service.js
new file mode 100644
index 000000000..a8730205b
--- /dev/null
+++ b/special-pages/pages/new-tab/app/update-notification/update-notification.service.js
@@ -0,0 +1,49 @@
+/**
+ * @typedef {import("../../../../types/new-tab.js").UpdateNotificationData} UpdateNotificationData
+ */
+import { Service } from '../service.js'
+
+/**
+ * @document ./update-notification.service.md
+ */
+
+export class UpdateNotificationService {
+    /**
+     * @param {import("../../src/js/index.js").NewTabPage} ntp - The internal data feed, expected to have a `subscribe` method.
+     * @param {UpdateNotificationData} initial
+     * @internal
+     */
+    constructor (ntp, initial) {
+        this.ntp = ntp
+        /** @type {Service<UpdateNotificationData>} */
+        this.dataService = new Service({
+            subscribe: (cb) => ntp.messaging.subscribe('updateNotification_onDataUpdate', cb)
+        }, initial)
+    }
+
+    /**
+     * @internal
+     */
+    destroy () {
+        this.dataService.destroy()
+    }
+
+    /**
+     * @param {(evt: {data: UpdateNotificationData, source: 'manual' | 'subscription'}) => void} cb
+     * @internal
+     */
+    onData (cb) {
+        return this.dataService.onData(cb)
+    }
+
+    /**
+     * @internal
+     */
+    dismiss () {
+        this.ntp.messaging.notify('updateNotification_dismiss')
+        // eslint-disable-next-line @typescript-eslint/no-unused-vars
+        this.dataService.update(_old => {
+            return { content: null }
+        })
+    }
+}
diff --git a/special-pages/pages/new-tab/app/widget-list/WidgetList.js b/special-pages/pages/new-tab/app/widget-list/WidgetList.js
index 238011abf..58fca0a0e 100644
--- a/special-pages/pages/new-tab/app/widget-list/WidgetList.js
+++ b/special-pages/pages/new-tab/app/widget-list/WidgetList.js
@@ -10,6 +10,8 @@ import {
 } from '../customizer/Customizer.js'
 import { RMFProvider } from '../remote-messaging-framework/RMFProvider.js'
 import { RMFConsumer } from '../remote-messaging-framework/RemoteMessagingFramework.js'
+import { UpdateNotificationProvider } from '../update-notification/UpdateNotificationProvider.js'
+import { UpdateNotificationConsumer } from '../update-notification/UpdateNotification.js'
 
 const widgetMap = {
     privacyStats: () => (
@@ -22,6 +24,11 @@ const widgetMap = {
         <RMFProvider>
             <RMFConsumer />
         </RMFProvider>
+    ),
+    updateNotification: () => (
+        <UpdateNotificationProvider>
+            <UpdateNotificationConsumer />
+        </UpdateNotificationProvider>
     )
 }
 
@@ -41,7 +48,7 @@ export function WidgetList () {
                             </Fragment>
                         )
                     }
-                    console.warn('missing config for widget: ', widget)
+                    console.warn('missing component for widget id:', widget)
                     return null
                 }
                 return (
diff --git a/special-pages/pages/new-tab/app/widget-list/widget-config.md b/special-pages/pages/new-tab/app/widget-list/widget-config.md
new file mode 100644
index 000000000..ed0d19a22
--- /dev/null
+++ b/special-pages/pages/new-tab/app/widget-list/widget-config.md
@@ -0,0 +1,26 @@
+---
+title: Widget Config
+---
+
+## InitialSetup:
+
+- Data for widgets should be provided as part of the {@link "NewTab Messages".InitialSetupResponse `initialSetup`} response.
+- The following keys should be added (also see the example below)
+    - `widgets`: {@link "NewTab Messages".Widgets}
+    - `widgetConfigs`: {@link "NewTab Messages".WidgetConfigs}
+
+## Subscriptions:
+- {@link "NewTab Messages".WidgetsOnConfigUpdatedSubscription `widgets_onConfigUpdated`}
+- returns {@link "NewTab Messages".WidgetConfigs}.
+
+
+## Notifications:
+- {@link "NewTab Messages".WidgetsOnConfigUpdatedSubscription `widgets_setConfig`}
+- sends {@link "NewTab Messages".WidgetConfigs}
+
+  If the user toggles the visibility of a section in the frontend, then the entire structure is sent to the
+  native side.
+
+## Examples:
+The following examples show the data types in JSON format:
+[messages/new-tab/examples/stats.js](../../../../messages/new-tab/examples/widgets.js)
diff --git a/special-pages/pages/new-tab/app/widget-list/widget-config.service.js b/special-pages/pages/new-tab/app/widget-list/widget-config.service.js
index c52e81b7b..29f97586d 100644
--- a/special-pages/pages/new-tab/app/widget-list/widget-config.service.js
+++ b/special-pages/pages/new-tab/app/widget-list/widget-config.service.js
@@ -3,30 +3,6 @@
  */
 import { Service } from '../service.js'
 
-/**
- * ## InitialSetup:
- *
- * - Data for widgets should be provided as part of the {@link "NewTab Messages".InitialSetupResponse `initialSetup`} response.
- * - The following keys should be added (also see the example below)
- *   - `widgets`: {@link "NewTab Messages".Widgets}
- *   - `widgetConfigs`: {@link "NewTab Messages".WidgetConfigs}
- *
- * ## Subscriptions:
- * - {@link "NewTab Messages".WidgetsOnConfigUpdatedSubscription `widgets_onConfigUpdated`}
- * - returns {@link "NewTab Messages".WidgetConfigs}.
- *
- *
- * ## Notifications:
- * - {@link "NewTab Messages".WidgetsOnConfigUpdatedSubscription `widgets_setConfig`}
- * - sends {@link "NewTab Messages".WidgetConfigs}
- *
- *   If the user toggles the visibility of a section in the frontend, then the entire structure is sent to the
- *   native side.
- *
- * ## Examples:
- * The following examples show the data types in JSON format:
- * [messages/new-tab/examples/stats.js](../../../../messages/new-tab/examples/widgets.js)
- */
 export class WidgetConfigService {
     /**
      * @param {import("../../src/js/index.js").NewTabPage} ntp - The internal data feed
diff --git a/special-pages/pages/new-tab/integration-tests/new-tab.page.js b/special-pages/pages/new-tab/integration-tests/new-tab.page.js
index b9fc0a374..e6859d6e8 100644
--- a/special-pages/pages/new-tab/integration-tests/new-tab.page.js
+++ b/special-pages/pages/new-tab/integration-tests/new-tab.page.js
@@ -34,7 +34,6 @@ export class NewtabPage {
                     { id: 'privacyStats' }
                 ],
                 widgetConfigs: [
-                    { id: 'rmf', visibility: 'visible' },
                     { id: 'favorites', visibility: 'visible' },
                     { id: 'privacyStats', visibility: 'visible' }
                 ],
@@ -42,7 +41,8 @@ export class NewtabPage {
                 locale: 'en',
                 platform: {
                     name: this.platform.name || 'windows'
-                }
+                },
+                updateNotification: { content: null }
             },
             stats_getConfig: {},
             stats_getData: {},
@@ -61,9 +61,10 @@ export class NewtabPage {
      * @param {boolean} [params.willThrow] - Optional flag to simulate an exception
      * @param {number} [params.favoritesCount] - Optional flag to preload a list of favorites
      * @param {string} [params.rmf] - Optional flag to point to display=components view with certain rmf example visible
+     * @param {string} [params.updateNotification] - Optional flag to point to display=components view with certain rmf example visible
      * @param {string} [params.platformName] - Optional parameters for opening the page.
      */
-    async openPage ({ mode = 'debug', platformName, willThrow = false, favoritesCount, rmf } = { }) {
+    async openPage ({ mode = 'debug', platformName, willThrow = false, favoritesCount, rmf, updateNotification } = { }) {
         await this.mocks.install()
         const searchParams = new URLSearchParams({ mode, willThrow: String(willThrow) })
 
@@ -79,6 +80,10 @@ export class NewtabPage {
             searchParams.set('platform', platformName)
         }
 
+        if (updateNotification !== undefined) {
+            searchParams.set('update-notification', updateNotification)
+        }
+
         await this.page.goto('/new-tab' + '?' + searchParams.toString())
     }
 
diff --git a/special-pages/pages/new-tab/integration-tests/new-tab.spec.js b/special-pages/pages/new-tab/integration-tests/new-tab.spec.js
index 8ac94ddd3..3b13607f6 100644
--- a/special-pages/pages/new-tab/integration-tests/new-tab.spec.js
+++ b/special-pages/pages/new-tab/integration-tests/new-tab.spec.js
@@ -62,4 +62,33 @@ test.describe('newtab widgets', () => {
             }
         }])
     })
+    test('context menu', async ({ page }, workerInfo) => {
+        const ntp = NewtabPage.create(page, workerInfo)
+        await ntp.reducedMotion()
+        await ntp.openPage()
+
+        // wait for the menu, as a signal that the JS is ready
+        await page.getByRole('button', { name: 'Customize' }).waitFor()
+
+        await page.locator('body').click({ button: 'right' })
+
+        const calls = await ntp.mocks.waitForCallCount({ method: 'contextMenu', count: 1 })
+        expect(calls[0].payload).toStrictEqual({
+            context: 'specialPages',
+            featureName: 'newTabPage',
+            method: 'contextMenu',
+            params: {
+                visibilityMenuItems: [
+                    {
+                        id: 'favorites',
+                        title: 'Favorites'
+                    },
+                    {
+                        id: 'privacyStats',
+                        title: 'Privacy Stats'
+                    }
+                ]
+            }
+        })
+    })
 })
diff --git a/special-pages/pages/new-tab/src/js/index.js b/special-pages/pages/new-tab/src/js/index.js
index 14cd3632d..92c75c229 100644
--- a/special-pages/pages/new-tab/src/js/index.js
+++ b/special-pages/pages/new-tab/src/js/index.js
@@ -47,6 +47,14 @@ export class NewTabPage {
     reportPageException (params) {
         this.messaging.notify('reportPageException', params)
     }
+
+    /**
+     * Sent when a right-click occurs, and wasn't intercepted by another widget
+     * @param {import('../../../../types/new-tab.js').ContextMenuNotify} params
+     */
+    contextMenu (params) {
+        this.messaging.notify('contextMenu', params)
+    }
 }
 
 const baseEnvironment = new Environment()
diff --git a/special-pages/pages/new-tab/src/js/mock-transport.js b/special-pages/pages/new-tab/src/js/mock-transport.js
index 369c54000..97fb087de 100644
--- a/special-pages/pages/new-tab/src/js/mock-transport.js
+++ b/special-pages/pages/new-tab/src/js/mock-transport.js
@@ -2,9 +2,11 @@ import { TestTransportConfig } from '@duckduckgo/messaging'
 
 import { stats } from '../../app/privacy-stats/mocks/stats.js'
 import { rmfDataExamples } from '../../app/remote-messaging-framework/mocks/rmf.data.js'
+import { updateNotificationExamples } from '../../app/update-notification/mocks/update-notification.data.js'
 
 /**
  * @typedef {import('../../../../types/new-tab').StatsConfig} StatsConfig
+ * @typedef {import('../../../../types/new-tab').UpdateNotificationData} UpdateNotificationData
  * @typedef {import('../../../../types/new-tab.js').NewTabMessages['subscriptions']['subscriptionEvent']} SubscriptionNames
  */
 
@@ -159,6 +161,18 @@ export function mockTransport () {
                 }
                 return () => {}
             }
+            case 'updateNotification_onDataUpdate': {
+                const update = url.searchParams.get('update-notification')
+                const delay = url.searchParams.get('update-notification-delay')
+                if (update && delay && update in updateNotificationExamples) {
+                    const ms = parseInt(delay, 10)
+                    const timeout = setTimeout(() => {
+                        const message = updateNotificationExamples[update]
+                        cb(message)
+                    }, ms)
+                    return () => clearTimeout(timeout)
+                }
+            }
             }
             return () => { }
         },
@@ -200,6 +214,7 @@ export function mockTransport () {
             }
             case 'initialSetup': {
                 const widgetsFromStorage = read('widgets') || [
+                    { id: 'updateNotification' },
                     { id: 'rmf' },
                     { id: 'favorites' },
                     { id: 'privacyStats' }
@@ -210,13 +225,25 @@ export function mockTransport () {
                     { id: 'privacyStats', visibility: 'visible' }
                 ]
 
+                /** @type {UpdateNotificationData} */
+                let updateNotification = { content: null }
+                const isDelayed = url.searchParams.has('update-notification-delay')
+
+                if (!isDelayed && url.searchParams.get('update-notification') === 'empty') {
+                    updateNotification = updateNotificationExamples.empty
+                }
+                if (!isDelayed && url.searchParams.get('update-notification') === 'populated') {
+                    updateNotification = updateNotificationExamples.populated
+                }
+
                 /** @type {import('../../../../types/new-tab.js').InitialSetupResponse} */
                 const initial = {
                     widgets: widgetsFromStorage,
                     widgetConfigs: widgetConfigFromStorage,
                     platform: { name: 'integration' },
                     env: 'development',
-                    locale: 'en'
+                    locale: 'en',
+                    updateNotification
                 }
 
                 return Promise.resolve(initial)
diff --git a/special-pages/pages/new-tab/src/locales/en/newtab.json b/special-pages/pages/new-tab/src/locales/en/newtab.json
index 4bf7e7910..d0693ddf5 100644
--- a/special-pages/pages/new-tab/src/locales/en/newtab.json
+++ b/special-pages/pages/new-tab/src/locales/en/newtab.json
@@ -60,5 +60,17 @@
   "favorites_menu_title": {
     "title": "Favorites",
     "note": "Used as a toggle label in a page customization menu"
+  },
+  "updateNotification_updated_version": {
+    "title": "Browser Updated to version {version}.",
+    "note": "Text to indicate which new version was updated. `version` will be formatted like `1.22.0`"
+  },
+  "updateNotification_whats_new": {
+    "title": "See <a>what's new</a> in this release.",
+    "note": "The `<a>` tag represents a toggle"
+  },
+  "updateNotification_dismiss_btn": {
+    "title": "Dismiss",
+    "note": "Button label text for an action that removes the widget from the screen."
   }
 }
diff --git a/special-pages/playwright.config.js b/special-pages/playwright.config.js
index e14e74195..a3650acfc 100644
--- a/special-pages/playwright.config.js
+++ b/special-pages/playwright.config.js
@@ -20,7 +20,8 @@ export default defineConfig({
             testMatch: [
                 'privacy-stats.spec.js',
                 'rmf.spec.js',
-                'new-tab.spec.js'
+                'new-tab.spec.js',
+                'update-notification.spec.js'
             ],
             use: {
                 ...devices['Desktop Chrome'],
diff --git a/special-pages/types/new-tab.ts b/special-pages/types/new-tab.ts
index 675e9f2b7..3d87d9ebf 100644
--- a/special-pages/types/new-tab.ts
+++ b/special-pages/types/new-tab.ts
@@ -34,20 +34,40 @@ export type RMFIcon = "Announce" | "DDGAnnounce" | "CriticalUpdate" | "AppUpdate
  */
 export interface NewTabMessages {
   notifications:
+    | ContextMenuNotification
     | ReportInitExceptionNotification
     | ReportPageExceptionNotification
     | RmfDismissNotification
     | RmfPrimaryActionNotification
     | RmfSecondaryActionNotification
     | StatsSetConfigNotification
+    | UpdateNotificationDismissNotification
     | WidgetsSetConfigNotification;
   requests: InitialSetupRequest | RmfGetDataRequest | StatsGetConfigRequest | StatsGetDataRequest;
   subscriptions:
     | RmfOnDataUpdateSubscription
     | StatsOnConfigUpdateSubscription
     | StatsOnDataUpdateSubscription
+    | UpdateNotificationOnDataUpdateSubscription
     | WidgetsOnConfigUpdatedSubscription;
 }
+/**
+ * Generated from @see "../messages/new-tab/contextMenu.notify.json"
+ */
+export interface ContextMenuNotification {
+  method: "contextMenu";
+  params: ContextMenuNotify;
+}
+export interface ContextMenuNotify {
+  visibilityMenuItems: VisibilityMenuItem[];
+}
+export interface VisibilityMenuItem {
+  id: string;
+  /**
+   * Translated name of the section
+   */
+  title: string;
+}
 /**
  * Generated from @see "../messages/new-tab/reportInitException.notify.json"
  */
@@ -124,6 +144,12 @@ export interface ViewTransitions {
 export interface Auto {
   kind: "auto-animate";
 }
+/**
+ * Generated from @see "../messages/new-tab/updateNotification_dismiss.notify.json"
+ */
+export interface UpdateNotificationDismissNotification {
+  method: "updateNotification_dismiss";
+}
 /**
  * Generated from @see "../messages/new-tab/widgets_setConfig.notify.json"
  */
@@ -153,6 +179,7 @@ export interface InitialSetupResponse {
   platform: {
     name: "macos" | "windows" | "android" | "ios" | "integration";
   };
+  updateNotification: null | UpdateNotificationData;
 }
 export interface WidgetListItem {
   /**
@@ -160,6 +187,13 @@ export interface WidgetListItem {
    */
   id: string;
 }
+export interface UpdateNotificationData {
+  content: null | UpdateNotification;
+}
+export interface UpdateNotification {
+  version: string;
+  notes: string[];
+}
 /**
  * Generated from @see "../messages/new-tab/rmf_getData.request.json"
  */
@@ -249,6 +283,13 @@ export interface StatsOnDataUpdateSubscription {
   subscriptionEvent: "stats_onDataUpdate";
   params: PrivacyStatsData;
 }
+/**
+ * Generated from @see "../messages/new-tab/updateNotification_onDataUpdate.subscribe.json"
+ */
+export interface UpdateNotificationOnDataUpdateSubscription {
+  subscriptionEvent: "updateNotification_onDataUpdate";
+  params: UpdateNotificationData;
+}
 /**
  * Generated from @see "../messages/new-tab/widgets_onConfigUpdated.subscribe.json"
  */

From 3cfada438c6105c5baec91d549963f6c0aeb23be Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 1 Nov 2024 15:45:15 +0000
Subject: [PATCH 02/10] Bump the typescript group across 1 directory with 2
 updates (#1187)

Bumps the typescript group with 2 updates in the / directory: [@types/chrome](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/chrome) and [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/chrome` from 0.0.278 to 0.0.279
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/chrome)

Updates `@types/node` from 22.7.7 to 22.8.6
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/chrome"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: typescript
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 injected/package.json |  4 ++--
 package-lock.json     | 36 ++++++++++++++++++------------------
 2 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/injected/package.json b/injected/package.json
index faa440868..430eb8fe6 100644
--- a/injected/package.json
+++ b/injected/package.json
@@ -44,9 +44,9 @@
     "@rollup/plugin-commonjs": "^26.0.1",
     "@rollup/plugin-node-resolve": "^15.2.3",
     "@rollup/plugin-replace": "^5.0.7",
-    "@types/chrome": "^0.0.278",
+    "@types/chrome": "^0.0.279",
     "@types/jasmine": "^5.1.4",
-    "@types/node": "^22.7.7",
+    "@types/node": "^22.8.6",
     "@typescript-eslint/eslint-plugin": "^6.9.1",
     "config-builder": "github:duckduckgo/privacy-configuration#1729260354597",
     "fast-check": "^3.22.0",
diff --git a/package-lock.json b/package-lock.json
index aee01ef03..e1bd0605c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -42,9 +42,9 @@
         "@rollup/plugin-commonjs": "^26.0.1",
         "@rollup/plugin-node-resolve": "^15.2.3",
         "@rollup/plugin-replace": "^5.0.7",
-        "@types/chrome": "^0.0.278",
+        "@types/chrome": "^0.0.279",
         "@types/jasmine": "^5.1.4",
-        "@types/node": "^22.7.7",
+        "@types/node": "^22.8.6",
         "@typescript-eslint/eslint-plugin": "^6.9.1",
         "config-builder": "git+ssh://git@github.com/duckduckgo/privacy-configuration.git#207bcafcd8d67d0530569f7efcf84463194b999b",
         "fast-check": "^3.22.0",
@@ -1134,9 +1134,9 @@
       "license": "MIT"
     },
     "node_modules/@types/chrome": {
-      "version": "0.0.278",
-      "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.278.tgz",
-      "integrity": "sha512-PDIJodOu7o54PpSOYLybPW/MDZBCjM1TKgf31I3Q/qaEbNpIH09rOM3tSEH3N7Q+FAqb1933LhF8ksUPYeQLNg==",
+      "version": "0.0.279",
+      "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.279.tgz",
+      "integrity": "sha512-wl0IxQ2OQiMazPZM5LimHQ7Jwd72/O8UvvzyptplXT2S4eUqXH5C0n8S+v8PtKhyX89p0igCPpNy3Bwksyk57g==",
       "dev": true,
       "dependencies": {
         "@types/filesystem": "*",
@@ -1222,12 +1222,12 @@
       "license": "MIT"
     },
     "node_modules/@types/node": {
-      "version": "22.7.7",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.7.tgz",
-      "integrity": "sha512-SRxCrrg9CL/y54aiMCG3edPKdprgMVGDXjA3gB8UmmBW5TcXzRUYAh8EWzTnSJFAd1rgImPELza+A3bJ+qxz8Q==",
+      "version": "22.8.6",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz",
+      "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==",
       "dev": true,
       "dependencies": {
-        "undici-types": "~6.19.2"
+        "undici-types": "~6.19.8"
       }
     },
     "node_modules/@types/normalize-package-data": {
@@ -7952,9 +7952,9 @@
       "dev": true
     },
     "@types/chrome": {
-      "version": "0.0.278",
-      "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.278.tgz",
-      "integrity": "sha512-PDIJodOu7o54PpSOYLybPW/MDZBCjM1TKgf31I3Q/qaEbNpIH09rOM3tSEH3N7Q+FAqb1933LhF8ksUPYeQLNg==",
+      "version": "0.0.279",
+      "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.279.tgz",
+      "integrity": "sha512-wl0IxQ2OQiMazPZM5LimHQ7Jwd72/O8UvvzyptplXT2S4eUqXH5C0n8S+v8PtKhyX89p0igCPpNy3Bwksyk57g==",
       "dev": true,
       "requires": {
         "@types/filesystem": "*",
@@ -8037,12 +8037,12 @@
       "dev": true
     },
     "@types/node": {
-      "version": "22.7.7",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.7.tgz",
-      "integrity": "sha512-SRxCrrg9CL/y54aiMCG3edPKdprgMVGDXjA3gB8UmmBW5TcXzRUYAh8EWzTnSJFAd1rgImPELza+A3bJ+qxz8Q==",
+      "version": "22.8.6",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz",
+      "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==",
       "dev": true,
       "requires": {
-        "undici-types": "~6.19.2"
+        "undici-types": "~6.19.8"
       }
     },
     "@types/normalize-package-data": {
@@ -9991,9 +9991,9 @@
         "@rollup/plugin-commonjs": "^26.0.1",
         "@rollup/plugin-node-resolve": "^15.2.3",
         "@rollup/plugin-replace": "^5.0.7",
-        "@types/chrome": "^0.0.278",
+        "@types/chrome": "^0.0.279",
         "@types/jasmine": "^5.1.4",
-        "@types/node": "^22.7.7",
+        "@types/node": "^22.8.6",
         "@typescript-eslint/eslint-plugin": "^6.9.1",
         "config-builder": "git+ssh://git@github.com/duckduckgo/privacy-configuration.git#207bcafcd8d67d0530569f7efcf84463194b999b",
         "fast-check": "^3.22.0",

From 1c74900e68325f88ae021bb5fbd137c9d1d6df3c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Nov 2024 11:04:24 +0000
Subject: [PATCH 03/10] Bump esbuild from 0.19.5 to 0.24.0 (#1134)

Bumps [esbuild](https://github.com/evanw/esbuild) from 0.19.5 to 0.24.0.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2023.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.19.5...v0.24.0)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 package-lock.json          | 468 ++++++++++++++++++++-----------------
 special-pages/package.json |   2 +-
 2 files changed, 260 insertions(+), 210 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index e1bd0605c..efdd19a2a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -304,10 +304,26 @@
       "resolved": "messaging",
       "link": true
     },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz",
+      "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/@esbuild/android-arm": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz",
-      "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz",
+      "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==",
       "cpu": [
         "arm"
       ],
@@ -317,13 +333,13 @@
         "android"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/android-arm64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz",
-      "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz",
+      "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==",
       "cpu": [
         "arm64"
       ],
@@ -333,13 +349,13 @@
         "android"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/android-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz",
-      "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz",
+      "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==",
       "cpu": [
         "x64"
       ],
@@ -349,13 +365,13 @@
         "android"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/darwin-arm64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz",
-      "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz",
+      "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==",
       "cpu": [
         "arm64"
       ],
@@ -365,13 +381,13 @@
         "darwin"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/darwin-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz",
-      "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz",
+      "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==",
       "cpu": [
         "x64"
       ],
@@ -381,13 +397,13 @@
         "darwin"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/freebsd-arm64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz",
-      "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz",
+      "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==",
       "cpu": [
         "arm64"
       ],
@@ -397,13 +413,13 @@
         "freebsd"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/freebsd-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz",
-      "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz",
+      "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==",
       "cpu": [
         "x64"
       ],
@@ -413,13 +429,13 @@
         "freebsd"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/linux-arm": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz",
-      "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz",
+      "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==",
       "cpu": [
         "arm"
       ],
@@ -429,13 +445,13 @@
         "linux"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/linux-arm64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz",
-      "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz",
+      "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==",
       "cpu": [
         "arm64"
       ],
@@ -445,13 +461,13 @@
         "linux"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/linux-ia32": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz",
-      "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz",
+      "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==",
       "cpu": [
         "ia32"
       ],
@@ -461,13 +477,13 @@
         "linux"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/linux-loong64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz",
-      "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz",
+      "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==",
       "cpu": [
         "loong64"
       ],
@@ -477,13 +493,13 @@
         "linux"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/linux-mips64el": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz",
-      "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz",
+      "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==",
       "cpu": [
         "mips64el"
       ],
@@ -493,13 +509,13 @@
         "linux"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/linux-ppc64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz",
-      "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz",
+      "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==",
       "cpu": [
         "ppc64"
       ],
@@ -509,13 +525,13 @@
         "linux"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/linux-riscv64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz",
-      "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz",
+      "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==",
       "cpu": [
         "riscv64"
       ],
@@ -525,13 +541,13 @@
         "linux"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/linux-s390x": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz",
-      "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz",
+      "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==",
       "cpu": [
         "s390x"
       ],
@@ -541,13 +557,13 @@
         "linux"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/linux-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz",
-      "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz",
+      "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==",
       "cpu": [
         "x64"
       ],
@@ -557,13 +573,13 @@
         "linux"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/netbsd-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz",
-      "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz",
+      "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==",
       "cpu": [
         "x64"
       ],
@@ -573,13 +589,29 @@
         "netbsd"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz",
+      "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/openbsd-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz",
-      "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz",
+      "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==",
       "cpu": [
         "x64"
       ],
@@ -589,13 +621,13 @@
         "openbsd"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/sunos-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz",
-      "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz",
+      "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==",
       "cpu": [
         "x64"
       ],
@@ -605,13 +637,13 @@
         "sunos"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/win32-arm64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz",
-      "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz",
+      "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==",
       "cpu": [
         "arm64"
       ],
@@ -621,13 +653,13 @@
         "win32"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/win32-ia32": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz",
-      "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz",
+      "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==",
       "cpu": [
         "ia32"
       ],
@@ -637,13 +669,13 @@
         "win32"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@esbuild/win32-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz",
-      "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz",
+      "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==",
       "cpu": [
         "x64"
       ],
@@ -653,7 +685,7 @@
         "win32"
       ],
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       }
     },
     "node_modules/@eslint-community/eslint-utils": {
@@ -2709,40 +2741,42 @@
       }
     },
     "node_modules/esbuild": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz",
-      "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz",
+      "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==",
       "dev": true,
       "hasInstallScript": true,
       "bin": {
         "esbuild": "bin/esbuild"
       },
       "engines": {
-        "node": ">=12"
+        "node": ">=18"
       },
       "optionalDependencies": {
-        "@esbuild/android-arm": "0.19.5",
-        "@esbuild/android-arm64": "0.19.5",
-        "@esbuild/android-x64": "0.19.5",
-        "@esbuild/darwin-arm64": "0.19.5",
-        "@esbuild/darwin-x64": "0.19.5",
-        "@esbuild/freebsd-arm64": "0.19.5",
-        "@esbuild/freebsd-x64": "0.19.5",
-        "@esbuild/linux-arm": "0.19.5",
-        "@esbuild/linux-arm64": "0.19.5",
-        "@esbuild/linux-ia32": "0.19.5",
-        "@esbuild/linux-loong64": "0.19.5",
-        "@esbuild/linux-mips64el": "0.19.5",
-        "@esbuild/linux-ppc64": "0.19.5",
-        "@esbuild/linux-riscv64": "0.19.5",
-        "@esbuild/linux-s390x": "0.19.5",
-        "@esbuild/linux-x64": "0.19.5",
-        "@esbuild/netbsd-x64": "0.19.5",
-        "@esbuild/openbsd-x64": "0.19.5",
-        "@esbuild/sunos-x64": "0.19.5",
-        "@esbuild/win32-arm64": "0.19.5",
-        "@esbuild/win32-ia32": "0.19.5",
-        "@esbuild/win32-x64": "0.19.5"
+        "@esbuild/aix-ppc64": "0.24.0",
+        "@esbuild/android-arm": "0.24.0",
+        "@esbuild/android-arm64": "0.24.0",
+        "@esbuild/android-x64": "0.24.0",
+        "@esbuild/darwin-arm64": "0.24.0",
+        "@esbuild/darwin-x64": "0.24.0",
+        "@esbuild/freebsd-arm64": "0.24.0",
+        "@esbuild/freebsd-x64": "0.24.0",
+        "@esbuild/linux-arm": "0.24.0",
+        "@esbuild/linux-arm64": "0.24.0",
+        "@esbuild/linux-ia32": "0.24.0",
+        "@esbuild/linux-loong64": "0.24.0",
+        "@esbuild/linux-mips64el": "0.24.0",
+        "@esbuild/linux-ppc64": "0.24.0",
+        "@esbuild/linux-riscv64": "0.24.0",
+        "@esbuild/linux-s390x": "0.24.0",
+        "@esbuild/linux-x64": "0.24.0",
+        "@esbuild/netbsd-x64": "0.24.0",
+        "@esbuild/openbsd-arm64": "0.24.0",
+        "@esbuild/openbsd-x64": "0.24.0",
+        "@esbuild/sunos-x64": "0.24.0",
+        "@esbuild/win32-arm64": "0.24.0",
+        "@esbuild/win32-ia32": "0.24.0",
+        "@esbuild/win32-x64": "0.24.0"
       }
     },
     "node_modules/escape-goat": {
@@ -7304,7 +7338,7 @@
       "devDependencies": {
         "@duckduckgo/messaging": "*",
         "@playwright/test": "^1.40.1",
-        "esbuild": "^0.19.5",
+        "esbuild": "^0.24.0",
         "fast-check": "^3.22.0",
         "http-server": "^14.1.1",
         "web-resource-inliner": "^6.0.1"
@@ -7458,157 +7492,171 @@
     "@duckduckgo/messaging": {
       "version": "file:messaging"
     },
+    "@esbuild/aix-ppc64": {
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz",
+      "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==",
+      "dev": true,
+      "optional": true
+    },
     "@esbuild/android-arm": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz",
-      "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz",
+      "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==",
       "dev": true,
       "optional": true
     },
     "@esbuild/android-arm64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz",
-      "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz",
+      "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==",
       "dev": true,
       "optional": true
     },
     "@esbuild/android-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz",
-      "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz",
+      "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==",
       "dev": true,
       "optional": true
     },
     "@esbuild/darwin-arm64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz",
-      "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz",
+      "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==",
       "dev": true,
       "optional": true
     },
     "@esbuild/darwin-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz",
-      "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz",
+      "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==",
       "dev": true,
       "optional": true
     },
     "@esbuild/freebsd-arm64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz",
-      "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz",
+      "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==",
       "dev": true,
       "optional": true
     },
     "@esbuild/freebsd-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz",
-      "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz",
+      "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-arm": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz",
-      "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz",
+      "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-arm64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz",
-      "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz",
+      "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-ia32": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz",
-      "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz",
+      "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-loong64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz",
-      "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz",
+      "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-mips64el": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz",
-      "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz",
+      "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-ppc64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz",
-      "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz",
+      "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-riscv64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz",
-      "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz",
+      "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-s390x": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz",
-      "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz",
+      "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz",
-      "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz",
+      "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==",
       "dev": true,
       "optional": true
     },
     "@esbuild/netbsd-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz",
-      "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz",
+      "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/openbsd-arm64": {
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz",
+      "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==",
       "dev": true,
       "optional": true
     },
     "@esbuild/openbsd-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz",
-      "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz",
+      "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==",
       "dev": true,
       "optional": true
     },
     "@esbuild/sunos-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz",
-      "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz",
+      "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==",
       "dev": true,
       "optional": true
     },
     "@esbuild/win32-arm64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz",
-      "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz",
+      "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==",
       "dev": true,
       "optional": true
     },
     "@esbuild/win32-ia32": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz",
-      "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz",
+      "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==",
       "dev": true,
       "optional": true
     },
     "@esbuild/win32-x64": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz",
-      "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==",
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz",
+      "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==",
       "dev": true,
       "optional": true
     },
@@ -9039,33 +9087,35 @@
       }
     },
     "esbuild": {
-      "version": "0.19.5",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz",
-      "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==",
-      "dev": true,
-      "requires": {
-        "@esbuild/android-arm": "0.19.5",
-        "@esbuild/android-arm64": "0.19.5",
-        "@esbuild/android-x64": "0.19.5",
-        "@esbuild/darwin-arm64": "0.19.5",
-        "@esbuild/darwin-x64": "0.19.5",
-        "@esbuild/freebsd-arm64": "0.19.5",
-        "@esbuild/freebsd-x64": "0.19.5",
-        "@esbuild/linux-arm": "0.19.5",
-        "@esbuild/linux-arm64": "0.19.5",
-        "@esbuild/linux-ia32": "0.19.5",
-        "@esbuild/linux-loong64": "0.19.5",
-        "@esbuild/linux-mips64el": "0.19.5",
-        "@esbuild/linux-ppc64": "0.19.5",
-        "@esbuild/linux-riscv64": "0.19.5",
-        "@esbuild/linux-s390x": "0.19.5",
-        "@esbuild/linux-x64": "0.19.5",
-        "@esbuild/netbsd-x64": "0.19.5",
-        "@esbuild/openbsd-x64": "0.19.5",
-        "@esbuild/sunos-x64": "0.19.5",
-        "@esbuild/win32-arm64": "0.19.5",
-        "@esbuild/win32-ia32": "0.19.5",
-        "@esbuild/win32-x64": "0.19.5"
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz",
+      "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==",
+      "dev": true,
+      "requires": {
+        "@esbuild/aix-ppc64": "0.24.0",
+        "@esbuild/android-arm": "0.24.0",
+        "@esbuild/android-arm64": "0.24.0",
+        "@esbuild/android-x64": "0.24.0",
+        "@esbuild/darwin-arm64": "0.24.0",
+        "@esbuild/darwin-x64": "0.24.0",
+        "@esbuild/freebsd-arm64": "0.24.0",
+        "@esbuild/freebsd-x64": "0.24.0",
+        "@esbuild/linux-arm": "0.24.0",
+        "@esbuild/linux-arm64": "0.24.0",
+        "@esbuild/linux-ia32": "0.24.0",
+        "@esbuild/linux-loong64": "0.24.0",
+        "@esbuild/linux-mips64el": "0.24.0",
+        "@esbuild/linux-ppc64": "0.24.0",
+        "@esbuild/linux-riscv64": "0.24.0",
+        "@esbuild/linux-s390x": "0.24.0",
+        "@esbuild/linux-x64": "0.24.0",
+        "@esbuild/netbsd-x64": "0.24.0",
+        "@esbuild/openbsd-arm64": "0.24.0",
+        "@esbuild/openbsd-x64": "0.24.0",
+        "@esbuild/sunos-x64": "0.24.0",
+        "@esbuild/win32-arm64": "0.24.0",
+        "@esbuild/win32-ia32": "0.24.0",
+        "@esbuild/win32-x64": "0.24.0"
       }
     },
     "escape-goat": {
@@ -11447,7 +11497,7 @@
         "@playwright/test": "^1.40.1",
         "@rive-app/canvas-single": "^2.21.7",
         "classnames": "^2.3.2",
-        "esbuild": "^0.19.5",
+        "esbuild": "^0.24.0",
         "fast-check": "^3.22.0",
         "http-server": "^14.1.1",
         "preact": "^10.24.3",
diff --git a/special-pages/package.json b/special-pages/package.json
index be52188c1..5aea52fe3 100644
--- a/special-pages/package.json
+++ b/special-pages/package.json
@@ -25,7 +25,7 @@
   "license": "ISC",
   "devDependencies": {
     "@duckduckgo/messaging": "*",
-    "esbuild": "^0.19.5",
+    "esbuild": "^0.24.0",
     "@playwright/test": "^1.40.1",
     "http-server": "^14.1.1",
     "web-resource-inliner": "^6.0.1",

From 4ffffa5c783ed986e0ff67aff515a7f5a4aee046 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Nov 2024 11:27:04 +0000
Subject: [PATCH 04/10] Bump the rollup group across 1 directory with 6 updates
 (#1175)

Bumps the rollup group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@rollup/plugin-commonjs](https://github.com/rollup/plugins/tree/HEAD/packages/commonjs) | `26.0.1` | `28.0.1` |
| [@rollup/plugin-node-resolve](https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve) | `15.2.3` | `15.3.0` |
| [@rollup/plugin-replace](https://github.com/rollup/plugins/tree/HEAD/packages/replace) | `5.0.7` | `6.0.1` |
| [rollup](https://github.com/rollup/rollup) | `3.29.5` | `4.24.3` |
| [rollup-plugin-import-css](https://github.com/jleeson/rollup-plugin-import-css) | `3.5.2` | `3.5.6` |
| [rollup-plugin-svg-import](https://github.com/korywka/rollup-plugin-svg-import) | `2.1.0` | `3.0.0` |



Updates `@rollup/plugin-commonjs` from 26.0.1 to 28.0.1
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/commonjs/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/commonjs-v28.0.1/packages/commonjs)

Updates `@rollup/plugin-node-resolve` from 15.2.3 to 15.3.0
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/node-resolve/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/node-resolve-v15.3.0/packages/node-resolve)

Updates `@rollup/plugin-replace` from 5.0.7 to 6.0.1
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/replace/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/json-v6.0.1/packages/replace)

Updates `rollup` from 3.29.5 to 4.24.3
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v3.29.5...v4.24.3)

Updates `rollup-plugin-import-css` from 3.5.2 to 3.5.6
- [Commits](https://github.com/jleeson/rollup-plugin-import-css/commits)

Updates `rollup-plugin-svg-import` from 2.1.0 to 3.0.0
- [Release notes](https://github.com/korywka/rollup-plugin-svg-import/releases)
- [Commits](https://github.com/korywka/rollup-plugin-svg-import/compare/2.1.0...3.0.0)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-commonjs"
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: rollup
- dependency-name: "@rollup/plugin-node-resolve"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: rollup
- dependency-name: "@rollup/plugin-replace"
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: rollup
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: rollup
- dependency-name: rollup-plugin-import-css
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: rollup
- dependency-name: rollup-plugin-svg-import
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: rollup
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 injected/package.json |  12 +-
 package-lock.json     | 661 +++++++++++++++++++++++++++++++-----------
 2 files changed, 497 insertions(+), 176 deletions(-)

diff --git a/injected/package.json b/injected/package.json
index 430eb8fe6..128ed7b73 100644
--- a/injected/package.json
+++ b/injected/package.json
@@ -41,9 +41,9 @@
   "devDependencies": {
     "@canvas/image-data": "^1.0.0",
     "@fingerprintjs/fingerprintjs": "^4.5.1",
-    "@rollup/plugin-commonjs": "^26.0.1",
-    "@rollup/plugin-node-resolve": "^15.2.3",
-    "@rollup/plugin-replace": "^5.0.7",
+    "@rollup/plugin-commonjs": "^28.0.1",
+    "@rollup/plugin-node-resolve": "^15.3.0",
+    "@rollup/plugin-replace": "^6.0.1",
     "@types/chrome": "^0.0.279",
     "@types/jasmine": "^5.1.4",
     "@types/node": "^22.8.6",
@@ -52,8 +52,8 @@
     "fast-check": "^3.22.0",
     "jasmine": "^5.4.0",
     "minimist": "^1.2.8",
-    "rollup": "^3.29.5",
-    "rollup-plugin-import-css": "^3.5.2",
-    "rollup-plugin-svg-import": "^2.1.0"
+    "rollup": "^4.24.3",
+    "rollup-plugin-import-css": "^3.5.6",
+    "rollup-plugin-svg-import": "^3.0.0"
   }
 }
diff --git a/package-lock.json b/package-lock.json
index efdd19a2a..783d72536 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -39,9 +39,9 @@
       "devDependencies": {
         "@canvas/image-data": "^1.0.0",
         "@fingerprintjs/fingerprintjs": "^4.5.1",
-        "@rollup/plugin-commonjs": "^26.0.1",
-        "@rollup/plugin-node-resolve": "^15.2.3",
-        "@rollup/plugin-replace": "^5.0.7",
+        "@rollup/plugin-commonjs": "^28.0.1",
+        "@rollup/plugin-node-resolve": "^15.3.0",
+        "@rollup/plugin-replace": "^6.0.1",
         "@types/chrome": "^0.0.279",
         "@types/jasmine": "^5.1.4",
         "@types/node": "^22.8.6",
@@ -50,9 +50,9 @@
         "fast-check": "^3.22.0",
         "jasmine": "^5.4.0",
         "minimist": "^1.2.8",
-        "rollup": "^3.29.5",
-        "rollup-plugin-import-css": "^3.5.2",
-        "rollup-plugin-svg-import": "^2.1.0"
+        "rollup": "^4.24.3",
+        "rollup-plugin-import-css": "^3.5.6",
+        "rollup-plugin-svg-import": "^3.0.0"
       }
     },
     "messaging": {
@@ -967,17 +967,18 @@
       "integrity": "sha512-6qswkh/o0lRZFslrGYF4xUZbP9D1SBIVfEiRBFPaoUfHh4d6rHZrE+Oifik+kjT+JCMfyD/pWAorn1GtfvJq5A=="
     },
     "node_modules/@rollup/plugin-commonjs": {
-      "version": "26.0.1",
-      "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-26.0.1.tgz",
-      "integrity": "sha512-UnsKoZK6/aGIH6AdkptXhNvhaqftcjq3zZdT+LY5Ftms6JR06nADcDsYp5hTU9E2lbJUEOhdlY5J4DNTneM+jQ==",
+      "version": "28.0.1",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz",
+      "integrity": "sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==",
       "dev": true,
       "dependencies": {
         "@rollup/pluginutils": "^5.0.1",
         "commondir": "^1.0.1",
         "estree-walker": "^2.0.2",
-        "glob": "^10.4.1",
+        "fdir": "^6.2.0",
         "is-reference": "1.2.1",
-        "magic-string": "^0.30.3"
+        "magic-string": "^0.30.3",
+        "picomatch": "^4.0.2"
       },
       "engines": {
         "node": ">=16.0.0 || 14 >= 14.17"
@@ -991,60 +992,41 @@
         }
       }
     },
-    "node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
-      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+    "node_modules/@rollup/plugin-commonjs/node_modules/fdir": {
+      "version": "6.4.2",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz",
+      "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==",
       "dev": true,
-      "dependencies": {
-        "balanced-match": "^1.0.0"
-      }
-    },
-    "node_modules/@rollup/plugin-commonjs/node_modules/glob": {
-      "version": "10.4.5",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
-      "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
-      "dev": true,
-      "dependencies": {
-        "foreground-child": "^3.1.0",
-        "jackspeak": "^3.1.2",
-        "minimatch": "^9.0.4",
-        "minipass": "^7.1.2",
-        "package-json-from-dist": "^1.0.0",
-        "path-scurry": "^1.11.1"
-      },
-      "bin": {
-        "glob": "dist/esm/bin.mjs"
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
       },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
       }
     },
-    "node_modules/@rollup/plugin-commonjs/node_modules/minimatch": {
-      "version": "9.0.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
-      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+    "node_modules/@rollup/plugin-commonjs/node_modules/picomatch": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+      "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
       "dev": true,
-      "dependencies": {
-        "brace-expansion": "^2.0.1"
-      },
       "engines": {
-        "node": ">=16 || 14 >=14.17"
+        "node": ">=12"
       },
       "funding": {
-        "url": "https://github.com/sponsors/isaacs"
+        "url": "https://github.com/sponsors/jonschlinkert"
       }
     },
     "node_modules/@rollup/plugin-node-resolve": {
-      "version": "15.2.3",
-      "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz",
-      "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==",
+      "version": "15.3.0",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz",
+      "integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==",
       "dev": true,
       "dependencies": {
         "@rollup/pluginutils": "^5.0.1",
         "@types/resolve": "1.20.2",
         "deepmerge": "^4.2.2",
-        "is-builtin-module": "^3.2.1",
         "is-module": "^1.0.0",
         "resolve": "^1.22.1"
       },
@@ -1061,9 +1043,9 @@
       }
     },
     "node_modules/@rollup/plugin-replace": {
-      "version": "5.0.7",
-      "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.7.tgz",
-      "integrity": "sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ==",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.1.tgz",
+      "integrity": "sha512-2sPh9b73dj5IxuMmDAsQWVFT7mR+yoHweBaXG2W/R8vQ+IWZlnaI7BR7J6EguVQUp1hd8Z7XuozpDjEKQAAC2Q==",
       "dev": true,
       "dependencies": {
         "@rollup/pluginutils": "^5.0.1",
@@ -1103,6 +1085,240 @@
         }
       }
     },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.3.tgz",
+      "integrity": "sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.3.tgz",
+      "integrity": "sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.3.tgz",
+      "integrity": "sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.3.tgz",
+      "integrity": "sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-arm64": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.3.tgz",
+      "integrity": "sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-x64": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.3.tgz",
+      "integrity": "sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.3.tgz",
+      "integrity": "sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.3.tgz",
+      "integrity": "sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.3.tgz",
+      "integrity": "sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.3.tgz",
+      "integrity": "sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.3.tgz",
+      "integrity": "sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.3.tgz",
+      "integrity": "sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.3.tgz",
+      "integrity": "sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.3.tgz",
+      "integrity": "sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.3.tgz",
+      "integrity": "sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.3.tgz",
+      "integrity": "sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.3.tgz",
+      "integrity": "sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.3.tgz",
+      "integrity": "sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
     "node_modules/@rtsao/scc": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -1176,9 +1392,9 @@
       }
     },
     "node_modules/@types/estree": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
-      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+      "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
       "dev": true
     },
     "node_modules/@types/filesystem": {
@@ -1955,18 +2171,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/builtin-modules": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
-      "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/builtins": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz",
@@ -4166,21 +4370,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/is-builtin-module": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
-      "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
-      "dev": true,
-      "dependencies": {
-        "builtin-modules": "^3.3.0"
-      },
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/is-callable": {
       "version": "1.2.7",
       "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
@@ -5933,25 +6122,46 @@
       }
     },
     "node_modules/rollup": {
-      "version": "3.29.5",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz",
-      "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.3.tgz",
+      "integrity": "sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==",
       "dev": true,
+      "dependencies": {
+        "@types/estree": "1.0.6"
+      },
       "bin": {
         "rollup": "dist/bin/rollup"
       },
       "engines": {
-        "node": ">=14.18.0",
+        "node": ">=18.0.0",
         "npm": ">=8.0.0"
       },
       "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.24.3",
+        "@rollup/rollup-android-arm64": "4.24.3",
+        "@rollup/rollup-darwin-arm64": "4.24.3",
+        "@rollup/rollup-darwin-x64": "4.24.3",
+        "@rollup/rollup-freebsd-arm64": "4.24.3",
+        "@rollup/rollup-freebsd-x64": "4.24.3",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.24.3",
+        "@rollup/rollup-linux-arm-musleabihf": "4.24.3",
+        "@rollup/rollup-linux-arm64-gnu": "4.24.3",
+        "@rollup/rollup-linux-arm64-musl": "4.24.3",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.24.3",
+        "@rollup/rollup-linux-riscv64-gnu": "4.24.3",
+        "@rollup/rollup-linux-s390x-gnu": "4.24.3",
+        "@rollup/rollup-linux-x64-gnu": "4.24.3",
+        "@rollup/rollup-linux-x64-musl": "4.24.3",
+        "@rollup/rollup-win32-arm64-msvc": "4.24.3",
+        "@rollup/rollup-win32-ia32-msvc": "4.24.3",
+        "@rollup/rollup-win32-x64-msvc": "4.24.3",
         "fsevents": "~2.3.2"
       }
     },
     "node_modules/rollup-plugin-import-css": {
-      "version": "3.5.2",
-      "resolved": "https://registry.npmjs.org/rollup-plugin-import-css/-/rollup-plugin-import-css-3.5.2.tgz",
-      "integrity": "sha512-zAkG73dG0n03xtHkqnmcvo/bjlmO9wA/YFnMHWz8XPycYsOZNmnovbTnxRP6tj7vUskdgoCW4f+zNBXqHqc5EA==",
+      "version": "3.5.6",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-import-css/-/rollup-plugin-import-css-3.5.6.tgz",
+      "integrity": "sha512-a+EVWlL8hGRrrAAa5Ds791sIxBxson5DCYeXAwWqYm+ITziUpmiFTR5czS7EEkuw1k7IZj1lGnHnuCVo0tmxzg==",
       "dev": true,
       "dependencies": {
         "@rollup/pluginutils": "^5.0.4"
@@ -5964,18 +6174,18 @@
       }
     },
     "node_modules/rollup-plugin-svg-import": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/rollup-plugin-svg-import/-/rollup-plugin-svg-import-2.1.0.tgz",
-      "integrity": "sha512-uHTYG/shURfBvX2wieST5898uvKBnhE6XuhFDGlNzr0HSMNGEapo/oT9u7Xogr4bgpAvhL1hsdZGdWXA1X1N1g==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-svg-import/-/rollup-plugin-svg-import-3.0.0.tgz",
+      "integrity": "sha512-5fUESTM5hdqJojrwO53JQUO7NespLNx4iLeMsToQfuaGGqGT5sz85Ns5gCDNxLO6yBPbn7p0A/6YA+Rq3clg4Q==",
       "dev": true,
       "dependencies": {
         "@rollup/pluginutils": "^5.0.1"
       },
       "engines": {
-        "node": ">=14.0.0"
+        "node": ">=18.0.0"
       },
       "peerDependencies": {
-        "rollup": "^2.0.0||^3.0.0"
+        "rollup": "^3.0.0||^4.0.0"
       }
     },
     "node_modules/run-parallel": {
@@ -7860,71 +8070,52 @@
       "integrity": "sha512-6qswkh/o0lRZFslrGYF4xUZbP9D1SBIVfEiRBFPaoUfHh4d6rHZrE+Oifik+kjT+JCMfyD/pWAorn1GtfvJq5A=="
     },
     "@rollup/plugin-commonjs": {
-      "version": "26.0.1",
-      "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-26.0.1.tgz",
-      "integrity": "sha512-UnsKoZK6/aGIH6AdkptXhNvhaqftcjq3zZdT+LY5Ftms6JR06nADcDsYp5hTU9E2lbJUEOhdlY5J4DNTneM+jQ==",
+      "version": "28.0.1",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz",
+      "integrity": "sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==",
       "dev": true,
       "requires": {
         "@rollup/pluginutils": "^5.0.1",
         "commondir": "^1.0.1",
         "estree-walker": "^2.0.2",
-        "glob": "^10.4.1",
+        "fdir": "^6.2.0",
         "is-reference": "1.2.1",
-        "magic-string": "^0.30.3"
+        "magic-string": "^0.30.3",
+        "picomatch": "^4.0.2"
       },
       "dependencies": {
-        "brace-expansion": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
-          "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
-          "dev": true,
-          "requires": {
-            "balanced-match": "^1.0.0"
-          }
-        },
-        "glob": {
-          "version": "10.4.5",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
-          "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+        "fdir": {
+          "version": "6.4.2",
+          "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz",
+          "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==",
           "dev": true,
-          "requires": {
-            "foreground-child": "^3.1.0",
-            "jackspeak": "^3.1.2",
-            "minimatch": "^9.0.4",
-            "minipass": "^7.1.2",
-            "package-json-from-dist": "^1.0.0",
-            "path-scurry": "^1.11.1"
-          }
+          "requires": {}
         },
-        "minimatch": {
-          "version": "9.0.5",
-          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
-          "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
-          "dev": true,
-          "requires": {
-            "brace-expansion": "^2.0.1"
-          }
+        "picomatch": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+          "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+          "dev": true
         }
       }
     },
     "@rollup/plugin-node-resolve": {
-      "version": "15.2.3",
-      "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz",
-      "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==",
+      "version": "15.3.0",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz",
+      "integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==",
       "dev": true,
       "requires": {
         "@rollup/pluginutils": "^5.0.1",
         "@types/resolve": "1.20.2",
         "deepmerge": "^4.2.2",
-        "is-builtin-module": "^3.2.1",
         "is-module": "^1.0.0",
         "resolve": "^1.22.1"
       }
     },
     "@rollup/plugin-replace": {
-      "version": "5.0.7",
-      "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.7.tgz",
-      "integrity": "sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ==",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.1.tgz",
+      "integrity": "sha512-2sPh9b73dj5IxuMmDAsQWVFT7mR+yoHweBaXG2W/R8vQ+IWZlnaI7BR7J6EguVQUp1hd8Z7XuozpDjEKQAAC2Q==",
       "dev": true,
       "requires": {
         "@rollup/pluginutils": "^5.0.1",
@@ -7942,6 +8133,132 @@
         "picomatch": "^2.3.1"
       }
     },
+    "@rollup/rollup-android-arm-eabi": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.3.tgz",
+      "integrity": "sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-android-arm64": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.3.tgz",
+      "integrity": "sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-darwin-arm64": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.3.tgz",
+      "integrity": "sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-darwin-x64": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.3.tgz",
+      "integrity": "sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-freebsd-arm64": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.3.tgz",
+      "integrity": "sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-freebsd-x64": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.3.tgz",
+      "integrity": "sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.3.tgz",
+      "integrity": "sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.3.tgz",
+      "integrity": "sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.3.tgz",
+      "integrity": "sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-arm64-musl": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.3.tgz",
+      "integrity": "sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-powerpc64le-gnu": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.3.tgz",
+      "integrity": "sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.3.tgz",
+      "integrity": "sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.3.tgz",
+      "integrity": "sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-x64-gnu": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.3.tgz",
+      "integrity": "sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-linux-x64-musl": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.3.tgz",
+      "integrity": "sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.3.tgz",
+      "integrity": "sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.3.tgz",
+      "integrity": "sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@rollup/rollup-win32-x64-msvc": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.3.tgz",
+      "integrity": "sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==",
+      "dev": true,
+      "optional": true
+    },
     "@rtsao/scc": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -8010,9 +8327,9 @@
       }
     },
     "@types/estree": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
-      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+      "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
       "dev": true
     },
     "@types/filesystem": {
@@ -8540,12 +8857,6 @@
         "fill-range": "^7.0.1"
       }
     },
-    "builtin-modules": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
-      "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
-      "dev": true
-    },
     "builtins": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz",
@@ -10038,9 +10349,9 @@
       "requires": {
         "@canvas/image-data": "^1.0.0",
         "@fingerprintjs/fingerprintjs": "^4.5.1",
-        "@rollup/plugin-commonjs": "^26.0.1",
-        "@rollup/plugin-node-resolve": "^15.2.3",
-        "@rollup/plugin-replace": "^5.0.7",
+        "@rollup/plugin-commonjs": "^28.0.1",
+        "@rollup/plugin-node-resolve": "^15.3.0",
+        "@rollup/plugin-replace": "^6.0.1",
         "@types/chrome": "^0.0.279",
         "@types/jasmine": "^5.1.4",
         "@types/node": "^22.8.6",
@@ -10051,9 +10362,9 @@
         "jasmine": "^5.4.0",
         "minimist": "^1.2.8",
         "parse-address": "^1.1.2",
-        "rollup": "^3.29.5",
-        "rollup-plugin-import-css": "^3.5.2",
-        "rollup-plugin-svg-import": "^2.1.0",
+        "rollup": "^4.24.3",
+        "rollup-plugin-import-css": "^3.5.6",
+        "rollup-plugin-svg-import": "^3.0.0",
         "seedrandom": "^3.0.5",
         "sjcl": "^1.0.8"
       }
@@ -10104,15 +10415,6 @@
         "has-tostringtag": "^1.0.0"
       }
     },
-    "is-builtin-module": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
-      "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
-      "dev": true,
-      "requires": {
-        "builtin-modules": "^3.3.0"
-      }
-    },
     "is-callable": {
       "version": "1.2.7",
       "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
@@ -11267,27 +11569,46 @@
       }
     },
     "rollup": {
-      "version": "3.29.5",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz",
-      "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
-      "dev": true,
-      "requires": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.3.tgz",
+      "integrity": "sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==",
+      "dev": true,
+      "requires": {
+        "@rollup/rollup-android-arm-eabi": "4.24.3",
+        "@rollup/rollup-android-arm64": "4.24.3",
+        "@rollup/rollup-darwin-arm64": "4.24.3",
+        "@rollup/rollup-darwin-x64": "4.24.3",
+        "@rollup/rollup-freebsd-arm64": "4.24.3",
+        "@rollup/rollup-freebsd-x64": "4.24.3",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.24.3",
+        "@rollup/rollup-linux-arm-musleabihf": "4.24.3",
+        "@rollup/rollup-linux-arm64-gnu": "4.24.3",
+        "@rollup/rollup-linux-arm64-musl": "4.24.3",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.24.3",
+        "@rollup/rollup-linux-riscv64-gnu": "4.24.3",
+        "@rollup/rollup-linux-s390x-gnu": "4.24.3",
+        "@rollup/rollup-linux-x64-gnu": "4.24.3",
+        "@rollup/rollup-linux-x64-musl": "4.24.3",
+        "@rollup/rollup-win32-arm64-msvc": "4.24.3",
+        "@rollup/rollup-win32-ia32-msvc": "4.24.3",
+        "@rollup/rollup-win32-x64-msvc": "4.24.3",
+        "@types/estree": "1.0.6",
         "fsevents": "~2.3.2"
       }
     },
     "rollup-plugin-import-css": {
-      "version": "3.5.2",
-      "resolved": "https://registry.npmjs.org/rollup-plugin-import-css/-/rollup-plugin-import-css-3.5.2.tgz",
-      "integrity": "sha512-zAkG73dG0n03xtHkqnmcvo/bjlmO9wA/YFnMHWz8XPycYsOZNmnovbTnxRP6tj7vUskdgoCW4f+zNBXqHqc5EA==",
+      "version": "3.5.6",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-import-css/-/rollup-plugin-import-css-3.5.6.tgz",
+      "integrity": "sha512-a+EVWlL8hGRrrAAa5Ds791sIxBxson5DCYeXAwWqYm+ITziUpmiFTR5czS7EEkuw1k7IZj1lGnHnuCVo0tmxzg==",
       "dev": true,
       "requires": {
         "@rollup/pluginutils": "^5.0.4"
       }
     },
     "rollup-plugin-svg-import": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/rollup-plugin-svg-import/-/rollup-plugin-svg-import-2.1.0.tgz",
-      "integrity": "sha512-uHTYG/shURfBvX2wieST5898uvKBnhE6XuhFDGlNzr0HSMNGEapo/oT9u7Xogr4bgpAvhL1hsdZGdWXA1X1N1g==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-svg-import/-/rollup-plugin-svg-import-3.0.0.tgz",
+      "integrity": "sha512-5fUESTM5hdqJojrwO53JQUO7NespLNx4iLeMsToQfuaGGqGT5sz85Ns5gCDNxLO6yBPbn7p0A/6YA+Rq3clg4Q==",
       "dev": true,
       "requires": {
         "@rollup/pluginutils": "^5.0.1"

From b94d14bc213b8bffbfbab7073b0d17770aeb318a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Nov 2024 11:37:30 +0000
Subject: [PATCH 05/10] Bump @rive-app/canvas-single from 2.21.7 to 2.23.3
 (#1189)

Bumps [@rive-app/canvas-single](https://github.com/rive-app/rive-wasm) from 2.21.7 to 2.23.3.
- [Changelog](https://github.com/rive-app/rive-wasm/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rive-app/rive-wasm/compare/2.21.7...2.23.3)

---
updated-dependencies:
- dependency-name: "@rive-app/canvas-single"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 package-lock.json          | 16 ++++++++--------
 special-pages/package.json |  2 +-
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 783d72536..5cb6dd033 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -962,9 +962,9 @@
       }
     },
     "node_modules/@rive-app/canvas-single": {
-      "version": "2.21.7",
-      "resolved": "https://registry.npmjs.org/@rive-app/canvas-single/-/canvas-single-2.21.7.tgz",
-      "integrity": "sha512-6qswkh/o0lRZFslrGYF4xUZbP9D1SBIVfEiRBFPaoUfHh4d6rHZrE+Oifik+kjT+JCMfyD/pWAorn1GtfvJq5A=="
+      "version": "2.23.3",
+      "resolved": "https://registry.npmjs.org/@rive-app/canvas-single/-/canvas-single-2.23.3.tgz",
+      "integrity": "sha512-smy7iudTwslPCAEWC776FNtRhPErUnG8eoC00j8CSn47YiD3mBBNxRid2Ejj3smbfk2X4z/o1icUvXVjMXmSdA=="
     },
     "node_modules/@rollup/plugin-commonjs": {
       "version": "28.0.1",
@@ -7541,7 +7541,7 @@
       "license": "ISC",
       "dependencies": {
         "@formkit/auto-animate": "^0.8.2",
-        "@rive-app/canvas-single": "^2.21.7",
+        "@rive-app/canvas-single": "^2.23.3",
         "classnames": "^2.3.2",
         "preact": "^10.24.3"
       },
@@ -8065,9 +8065,9 @@
       }
     },
     "@rive-app/canvas-single": {
-      "version": "2.21.7",
-      "resolved": "https://registry.npmjs.org/@rive-app/canvas-single/-/canvas-single-2.21.7.tgz",
-      "integrity": "sha512-6qswkh/o0lRZFslrGYF4xUZbP9D1SBIVfEiRBFPaoUfHh4d6rHZrE+Oifik+kjT+JCMfyD/pWAorn1GtfvJq5A=="
+      "version": "2.23.3",
+      "resolved": "https://registry.npmjs.org/@rive-app/canvas-single/-/canvas-single-2.23.3.tgz",
+      "integrity": "sha512-smy7iudTwslPCAEWC776FNtRhPErUnG8eoC00j8CSn47YiD3mBBNxRid2Ejj3smbfk2X4z/o1icUvXVjMXmSdA=="
     },
     "@rollup/plugin-commonjs": {
       "version": "28.0.1",
@@ -11816,7 +11816,7 @@
         "@duckduckgo/messaging": "*",
         "@formkit/auto-animate": "^0.8.2",
         "@playwright/test": "^1.40.1",
-        "@rive-app/canvas-single": "^2.21.7",
+        "@rive-app/canvas-single": "^2.23.3",
         "classnames": "^2.3.2",
         "esbuild": "^0.24.0",
         "fast-check": "^3.22.0",
diff --git a/special-pages/package.json b/special-pages/package.json
index 5aea52fe3..986929aab 100644
--- a/special-pages/package.json
+++ b/special-pages/package.json
@@ -35,6 +35,6 @@
     "preact": "^10.24.3",
     "classnames": "^2.3.2",
     "@formkit/auto-animate": "^0.8.2",
-    "@rive-app/canvas-single": "^2.21.7"
+    "@rive-app/canvas-single": "^2.23.3"
   }
 }

From ba28604bf18187c00ccab0369bf33ea1d388464b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Nov 2024 11:48:57 +0000
Subject: [PATCH 06/10] Bump classnames from 2.3.2 to 2.5.1 (#1131)

Bumps [classnames](https://github.com/JedWatson/classnames) from 2.3.2 to 2.5.1.
- [Changelog](https://github.com/JedWatson/classnames/blob/main/HISTORY.md)
- [Commits](https://github.com/JedWatson/classnames/compare/v2.3.2...v2.5.1)

---
updated-dependencies:
- dependency-name: classnames
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 package-lock.json          | 16 ++++++++--------
 special-pages/package.json |  2 +-
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 5cb6dd033..21149e980 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2304,9 +2304,9 @@
       }
     },
     "node_modules/classnames": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
-      "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+      "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
     },
     "node_modules/color-convert": {
       "version": "2.0.1",
@@ -7542,7 +7542,7 @@
       "dependencies": {
         "@formkit/auto-animate": "^0.8.2",
         "@rive-app/canvas-single": "^2.23.3",
-        "classnames": "^2.3.2",
+        "classnames": "^2.5.1",
         "preact": "^10.24.3"
       },
       "devDependencies": {
@@ -8941,9 +8941,9 @@
       "dev": true
     },
     "classnames": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
-      "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+      "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
     },
     "color-convert": {
       "version": "2.0.1",
@@ -11817,7 +11817,7 @@
         "@formkit/auto-animate": "^0.8.2",
         "@playwright/test": "^1.40.1",
         "@rive-app/canvas-single": "^2.23.3",
-        "classnames": "^2.3.2",
+        "classnames": "^2.5.1",
         "esbuild": "^0.24.0",
         "fast-check": "^3.22.0",
         "http-server": "^14.1.1",
diff --git a/special-pages/package.json b/special-pages/package.json
index 986929aab..9eb28c492 100644
--- a/special-pages/package.json
+++ b/special-pages/package.json
@@ -33,7 +33,7 @@
   },
   "dependencies": {
     "preact": "^10.24.3",
-    "classnames": "^2.3.2",
+    "classnames": "^2.5.1",
     "@formkit/auto-animate": "^0.8.2",
     "@rive-app/canvas-single": "^2.23.3"
   }

From 4e1c4bc8c632fd02981f5afe4c5b46d396d8caa8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Nov 2024 12:06:26 +0000
Subject: [PATCH 07/10] rebase conflicts (#1055)

Co-authored-by: Shane Osbourne <sosbourne@duckduckgo.com>
---
 .github/workflows/build-pr.yml | 2 +-
 .github/workflows/build.yml    | 4 ++--
 .github/workflows/tests.yml    | 6 +++---
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml
index 6062eb755..5991d16f8 100644
--- a/.github/workflows/build-pr.yml
+++ b/.github/workflows/build-pr.yml
@@ -16,7 +16,7 @@ jobs:
         uses: actions/checkout@v4
 
       - name: Use Node.js 20
-        uses: actions/setup-node@v1
+        uses: actions/setup-node@v4
         with:
           node-version: 20.x
       - uses: actions/cache@v4
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index eed4eef6f..4d4a5934e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,7 +13,7 @@ jobs:
 
     steps:
       - uses: actions/checkout@v4
-      - uses: actions/setup-node@v3
+      - uses: actions/setup-node@v4
         with:
           node-version: 20
           cache: 'npm'
@@ -75,7 +75,7 @@ jobs:
         run: |
           ls -la ${{ github.workspace }}/CHANGELOG.txt
           cat ${{ github.workspace }}/CHANGELOG.txt
-    
+
       - name: Create Release
         uses: softprops/action-gh-release@v2
         env:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 0c055af33..c0fa120c8 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -21,7 +21,7 @@ jobs:
     steps:
       - uses: actions/checkout@v4
       - name: Use Node.js 20
-        uses: actions/setup-node@v1
+        uses: actions/setup-node@v4
         with:
           node-version: 20.x
       - uses: actions/cache@v4
@@ -43,7 +43,7 @@ jobs:
     steps:
     - uses: actions/checkout@v4
     - name: Use Node.js 20
-      uses: actions/setup-node@v1
+      uses: actions/setup-node@v4
       with:
         node-version: 20.x
     - uses: actions/cache@v4
@@ -86,7 +86,7 @@ jobs:
     steps:
       - uses: actions/checkout@v4
       - name: Use Node.js 20
-        uses: actions/setup-node@v1
+        uses: actions/setup-node@v4
         with:
           node-version: 20.x
       - name: Cache build outputs

From b4bc20e07687522957cea59e8380d162ba055aa9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Nov 2024 12:20:54 +0000
Subject: [PATCH 08/10] Bump immutable-json-patch from 5.1.3 to 6.0.1 (#1102)

Bumps [immutable-json-patch](https://github.com/josdejong/immutable-json-patch) from 5.1.3 to 6.0.1.
- [Changelog](https://github.com/josdejong/immutable-json-patch/blob/main/CHANGELOG.md)
- [Commits](https://github.com/josdejong/immutable-json-patch/compare/v5.1.3...v6.0.1)

---
updated-dependencies:
- dependency-name: immutable-json-patch
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 injected/package.json |  2 +-
 package-lock.json     | 16 ++++++++--------
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/injected/package.json b/injected/package.json
index 128ed7b73..c90525221 100644
--- a/injected/package.json
+++ b/injected/package.json
@@ -33,7 +33,7 @@
   },
   "type": "module",
   "dependencies": {
-    "immutable-json-patch": "^5.1.3",
+    "immutable-json-patch": "^6.0.1",
     "parse-address": "^1.1.2",
     "seedrandom": "^3.0.5",
     "sjcl": "^1.0.8"
diff --git a/package-lock.json b/package-lock.json
index 21149e980..605604913 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -31,7 +31,7 @@
     "injected": {
       "hasInstallScript": true,
       "dependencies": {
-        "immutable-json-patch": "^5.1.3",
+        "immutable-json-patch": "^6.0.1",
         "parse-address": "^1.1.2",
         "seedrandom": "^3.0.5",
         "sjcl": "^1.0.8"
@@ -4226,9 +4226,9 @@
       }
     },
     "node_modules/immutable-json-patch": {
-      "version": "5.1.3",
-      "resolved": "https://registry.npmjs.org/immutable-json-patch/-/immutable-json-patch-5.1.3.tgz",
-      "integrity": "sha512-95AsF9hJTPpwtBGAnHmw57PASL672tb+vGHR5xLhH2VPuHSsLho7grjlfgQ65DIhHP+UmLCjdmuuA6L1ndJbZg=="
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/immutable-json-patch/-/immutable-json-patch-6.0.1.tgz",
+      "integrity": "sha512-BHL/cXMjwFZlTOffiWNdY8ZTvNyYLrutCnWxrcKPHr5FqpAb6vsO6WWSPnVSys3+DruFN6lhHJJPHi8uELQL5g=="
     },
     "node_modules/import-fresh": {
       "version": "3.3.0",
@@ -10290,9 +10290,9 @@
       "dev": true
     },
     "immutable-json-patch": {
-      "version": "5.1.3",
-      "resolved": "https://registry.npmjs.org/immutable-json-patch/-/immutable-json-patch-5.1.3.tgz",
-      "integrity": "sha512-95AsF9hJTPpwtBGAnHmw57PASL672tb+vGHR5xLhH2VPuHSsLho7grjlfgQ65DIhHP+UmLCjdmuuA6L1ndJbZg=="
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/immutable-json-patch/-/immutable-json-patch-6.0.1.tgz",
+      "integrity": "sha512-BHL/cXMjwFZlTOffiWNdY8ZTvNyYLrutCnWxrcKPHr5FqpAb6vsO6WWSPnVSys3+DruFN6lhHJJPHi8uELQL5g=="
     },
     "import-fresh": {
       "version": "3.3.0",
@@ -10358,7 +10358,7 @@
         "@typescript-eslint/eslint-plugin": "^6.9.1",
         "config-builder": "git+ssh://git@github.com/duckduckgo/privacy-configuration.git#207bcafcd8d67d0530569f7efcf84463194b999b",
         "fast-check": "^3.22.0",
-        "immutable-json-patch": "^5.1.3",
+        "immutable-json-patch": "^6.0.1",
         "jasmine": "^5.4.0",
         "minimist": "^1.2.8",
         "parse-address": "^1.1.2",

From 72ffd84383b61fed8de6c27a1b7f6d9bdc2e110a Mon Sep 17 00:00:00 2001
From: Shane Osbourne <shane.osbourne8@gmail.com>
Date: Mon, 4 Nov 2024 10:25:12 +0000
Subject: [PATCH 09/10] updating playwright (#1176)

* updating playwright

* separating the tests

* support appending the script outside of the extension

* Add a comment in the test

---------

Co-authored-by: Shane Osbourne <sosbourne@duckduckgo.com>
Co-authored-by: Maxim Tsoy <maks.tsoy@gmail.com>
---
 injected/integration-test/helpers/harness.js  |   9 +-
 .../web-compat-android.spec.js                | 272 ++++++++++++++++++
 injected/integration-test/web-compat.spec.js  | 269 -----------------
 injected/playwright.config.js                 |   3 +-
 package-lock.json                             |  60 ++--
 package.json                                  |   3 +-
 special-pages/package.json                    |   1 -
 7 files changed, 315 insertions(+), 302 deletions(-)
 create mode 100644 injected/integration-test/web-compat-android.spec.js

diff --git a/injected/integration-test/helpers/harness.js b/injected/integration-test/helpers/harness.js
index fd149e7c1..668b119c8 100644
--- a/injected/integration-test/helpers/harness.js
+++ b/injected/integration-test/helpers/harness.js
@@ -92,9 +92,10 @@ export function testContextForExtension (test) {
  * @param {string} urlString
  * @param {Record<string, any>} [args]
  * @param {string|null} [evalBeforeInit]
+ * @param {"extension" | "script"} [kind] - if 'extension', the script will be loaded separately. if 'script' we'll append a script tag
  * @returns {Promise<void>}
  */
-export async function gotoAndWait (page, urlString, args = {}, evalBeforeInit = null) {
+export async function gotoAndWait (page, urlString, args = {}, evalBeforeInit = null, kind = 'extension') {
     // eslint-disable-next-line @typescript-eslint/no-unused-vars
     const [_, search] = urlString.split('?')
     const searchParams = new URLSearchParams(search)
@@ -104,6 +105,12 @@ export async function gotoAndWait (page, urlString, args = {}, evalBeforeInit =
 
     await page.goto(urlString + '?' + searchParams.toString())
 
+    if (kind === 'script') {
+        await page.addScriptTag({
+            url: './build/contentScope.js'
+        })
+    }
+
     // wait until contentScopeFeatures.load() has completed
     await page.waitForFunction(() => {
         // @ts-expect-error https://app.asana.com/0/1201614831475344/1203979574128023/f
diff --git a/injected/integration-test/web-compat-android.spec.js b/injected/integration-test/web-compat-android.spec.js
new file mode 100644
index 000000000..bb9fab14f
--- /dev/null
+++ b/injected/integration-test/web-compat-android.spec.js
@@ -0,0 +1,272 @@
+import { gotoAndWait } from './helpers/harness.js'
+import { test, expect } from '@playwright/test'
+
+test.describe('Web Share API', () => {
+    function checkForCanShare () {
+        return 'canShare' in navigator
+    }
+    function checkForShare () {
+        return 'share' in navigator
+    }
+
+    test.describe('disabled feature', () => {
+        test('should not expose navigator.canShare() and navigator.share()', async ({ page }) => {
+            await gotoAndWait(page, '/blank.html', { site: { enabledFeatures: [] } }, null, 'script')
+            const noCanShare = await page.evaluate(checkForCanShare)
+            const noShare = await page.evaluate(checkForShare)
+            // Base implementation of the test env should not have it (it's only available on mobile)
+            expect(noCanShare).toEqual(false)
+            expect(noShare).toEqual(false)
+        })
+    })
+
+    test.describe('disabled sub-feature', () => {
+        test('should not expose navigator.canShare() and navigator.share()', async ({ page }) => {
+            await gotoAndWait(page, '/blank.html', {
+                site: {
+                    enabledFeatures: ['webCompat']
+                },
+                featureSettings: {
+                    webCompat: {
+                        // no webShare
+                    }
+                }
+            }, null, 'script')
+            const noCanShare = await page.evaluate(checkForCanShare)
+            const noShare = await page.evaluate(checkForShare)
+            // Base implementation of the test env should not have it (it's only available on mobile)
+            expect(noCanShare).toEqual(false)
+            expect(noShare).toEqual(false)
+        })
+    })
+
+    test.describe('enabled feature', () => {
+        async function navigate (page) {
+            page.on('console', console.log)
+            await gotoAndWait(page, '/blank.html', {
+                site: {
+                    enabledFeatures: ['webCompat']
+                },
+                featureSettings: {
+                    webCompat: {
+                        webShare: 'enabled'
+                    }
+                }
+            }, null, 'script')
+        }
+
+        test('should expose navigator.canShare() and navigator.share() when enabled', async ({ page }) => {
+            await navigate(page)
+            const hasCanShare = await page.evaluate(checkForCanShare)
+            const hasShare = await page.evaluate(checkForShare)
+            expect(hasCanShare).toEqual(true)
+            expect(hasShare).toEqual(true)
+        })
+
+        test.describe('navigator.canShare()', () => {
+            test('should not let you share files', async ({ page }) => {
+                await navigate(page)
+                const refuseFileShare = await page.evaluate(() => {
+                    return navigator.canShare({ text: 'xxx', files: [] })
+                })
+                expect(refuseFileShare).toEqual(false)
+            })
+
+            test('should not let you share non-http urls', async ({ page }) => {
+                await navigate(page)
+                const refuseShare = await page.evaluate(() => {
+                    return navigator.canShare({ url: 'chrome://bla' })
+                })
+                expect(refuseShare).toEqual(false)
+            })
+
+            test('should allow relative links', async ({ page }) => {
+                await navigate(page)
+                const allowShare = await page.evaluate(() => {
+                    return navigator.canShare({ url: 'bla' })
+                })
+                expect(allowShare).toEqual(true)
+            })
+
+            test('should support only the specific fields', async ({ page }) => {
+                await navigate(page)
+                const refuseShare = await page.evaluate(() => {
+                    // eslint-disable-next-line
+                    // @ts-ignore intentionally malformed data
+                    return navigator.canShare({ foo: 'bar' })
+                })
+                expect(refuseShare).toEqual(false)
+            })
+
+            test('should let you share stuff', async ({ page }) => {
+                await navigate(page)
+                let canShare = await page.evaluate(() => {
+                    return navigator.canShare({ url: 'http://example.com' })
+                })
+                expect(canShare).toEqual(true)
+
+                canShare = await page.evaluate(() => {
+                    return navigator.canShare({ text: 'the grass was greener' })
+                })
+                expect(canShare).toEqual(true)
+
+                canShare = await page.evaluate(() => {
+                    return navigator.canShare({ title: 'the light was brighter' })
+                })
+                expect(canShare).toEqual(true)
+
+                canShare = await page.evaluate(() => {
+                    return navigator.canShare({ text: 'with friends surrounded', title: 'the nights of wonder' })
+                })
+                expect(canShare).toEqual(true)
+            })
+        })
+
+        test.describe('navigator.share()', () => {
+            async function beforeEach (page) {
+                await page.evaluate(() => {
+                    globalThis.shareReq = null
+                    globalThis.cssMessaging.impl.request = (req) => {
+                        globalThis.shareReq = req
+                        return Promise.resolve({})
+                    }
+                })
+            }
+            test.describe('(no errors from Android)', () => {
+                /**
+                 * @param {import("@playwright/test").Page} page
+                 * @param {any} data
+                 * @return {Promise<any>}
+                 */
+                async function checkShare (page, data) {
+                    const payload = `navigator.share(${JSON.stringify(data)})`
+                    const result = await page.evaluate(payload).catch((e) => {
+                        return { threw: e }
+                    })
+                    console.log('check share', result)
+                    const message = await page.evaluate(() => {
+                        console.log('did read?')
+                        return globalThis.shareReq
+                    })
+                    return { result, message }
+                }
+
+                test('should let you share text', async ({ page }) => {
+                    await navigate(page)
+                    await beforeEach(page)
+                    const { result, message } = await checkShare(page, { text: 'xxx' })
+                    expect(message).toMatchObject({ featureName: 'webCompat', method: 'webShare', params: { text: 'xxx' } })
+                    expect(result).toBeUndefined()
+                })
+
+                test('should let you share url', async ({ page }) => {
+                    await navigate(page)
+                    await beforeEach(page)
+                    const { result, message } = await checkShare(page, { url: 'http://example.com' })
+                    expect(message).toMatchObject({ featureName: 'webCompat', method: 'webShare', params: { url: 'http://example.com/' } })
+                    expect(result).toBeUndefined()
+                })
+
+                test('should let you share title alone', async ({ page }) => {
+                    await navigate(page)
+                    await beforeEach(page)
+                    const { result, message } = await checkShare(page, { title: 'xxx' })
+                    expect(message).toMatchObject({ featureName: 'webCompat', method: 'webShare', params: { title: 'xxx', text: '' } })
+                    expect(result).toBeUndefined()
+                })
+
+                test('should let you share title and text', async ({ page }) => {
+                    await navigate(page)
+                    await beforeEach(page)
+                    const { result, message } = await checkShare(page, { title: 'xxx', text: 'yyy' })
+                    expect(message).toMatchObject({ featureName: 'webCompat', method: 'webShare', params: { title: 'xxx', text: 'yyy' } })
+                    expect(result).toBeUndefined()
+                })
+
+                test('should let you share title and url', async ({ page }) => {
+                    await navigate(page)
+                    await beforeEach(page)
+                    const { result, message } = await checkShare(page, { title: 'xxx', url: 'http://example.com' })
+                    expect(message).toMatchObject({ featureName: 'webCompat', method: 'webShare', params: { title: 'xxx', url: 'http://example.com/' } })
+                    expect(result).toBeUndefined()
+                })
+
+                test('should combine text and url when both are present', async ({ page }) => {
+                    await navigate(page)
+                    await beforeEach(page)
+                    const { result, message } = await checkShare(page, { text: 'xxx', url: 'http://example.com' })
+                    expect(message).toMatchObject({ featureName: 'webCompat', method: 'webShare', params: { text: 'xxx http://example.com/' } })
+                    expect(result).toBeUndefined()
+                })
+
+                test('should throw when sharing files', async ({ page }) => {
+                    await navigate(page)
+                    await beforeEach(page)
+                    const { result, message } = await checkShare(page, { title: 'title', files: [] })
+                    expect(message).toBeNull()
+                    expect(result.threw.message).toContain('TypeError: Invalid share data')
+                })
+
+                test('should throw when sharing non-http urls', async ({ page }) => {
+                    await navigate(page)
+                    await beforeEach(page)
+                    const { result, message } = await checkShare(page, { url: 'chrome://bla' })
+                    expect(message).toBeNull()
+                    expect(result.threw.message).toContain('TypeError: Invalid share data')
+                })
+
+                test('should handle relative urls', async ({ page }) => {
+                    await navigate(page)
+                    await beforeEach(page)
+                    const { result, message } = await checkShare(page, { url: 'bla' })
+                    expect(message.params.url).toMatch(/^http:\/\/localhost:\d+\/bla$/)
+                    expect(result).toBeUndefined()
+                })
+
+                test('should treat empty url as relative', async ({ page }) => {
+                    await navigate(page)
+                    await beforeEach(page)
+                    const { result, message } = await checkShare(page, { url: '' })
+                    expect(message.params.url).toMatch(/^http:\/\/localhost:\d+\//)
+                    expect(result).toBeUndefined()
+                })
+            })
+
+            test.describe('(handling errors from Android)', () => {
+                test('should handle messaging error', async ({ page }) => {
+                    // page.on('console', (msg) => console.log(msg.type(), msg.text()))
+                    await navigate(page)
+                    await beforeEach(page)
+
+                    await page.evaluate(() => {
+                        globalThis.cssMessaging.impl.request = () => {
+                            return Promise.reject(new Error('something wrong'))
+                        }
+                    })
+                    const result = await page.evaluate('navigator.share({ text: "xxx" })').catch((e) => {
+                        return { threw: e }
+                    })
+
+                    // In page context, it should be a DOMException with name DataError, but page.evaluate() serializes everything in the message
+                    expect(result.threw.message).toContain('DataError: something wrong')
+                })
+
+                test('should handle soft failures', async ({ page }) => {
+                    await navigate(page)
+                    await beforeEach(page)
+                    await page.evaluate(() => {
+                        globalThis.cssMessaging.impl.request = () => {
+                            return Promise.resolve({ failure: { name: 'AbortError', message: 'some error message' } })
+                        }
+                    })
+                    const result = await page.evaluate('navigator.share({ text: "xxx" })').catch((e) => {
+                        return { threw: e }
+                    })
+
+                    // In page context, it should be a DOMException with name AbortError, but page.evaluate() serializes everything in the message
+                    expect(result.threw.message).toContain('AbortError: some error message')
+                })
+            })
+        })
+    })
+})
diff --git a/injected/integration-test/web-compat.spec.js b/injected/integration-test/web-compat.spec.js
index 19f5440d4..497633c7b 100644
--- a/injected/integration-test/web-compat.spec.js
+++ b/injected/integration-test/web-compat.spec.js
@@ -422,275 +422,6 @@ test.describe('ScreenOrientation API', () => {
     })
 })
 
-test.describe('Web Share API', () => {
-    function checkForCanShare () {
-        return 'canShare' in navigator
-    }
-    function checkForShare () {
-        return 'share' in navigator
-    }
-
-    test.describe('disabled feature', () => {
-        test('should not expose navigator.canShare() and navigator.share()', async ({ page }) => {
-            await gotoAndWait(page, '/blank.html', { site: { enabledFeatures: [] } })
-            const noCanShare = await page.evaluate(checkForCanShare)
-            const noShare = await page.evaluate(checkForShare)
-            // Base implementation of the test env should not have it (it's only available on mobile)
-            expect(noCanShare).toEqual(false)
-            expect(noShare).toEqual(false)
-        })
-    })
-
-    test.describe('disabled sub-feature', () => {
-        test('should not expose navigator.canShare() and navigator.share()', async ({ page }) => {
-            await gotoAndWait(page, '/blank.html', {
-                site: {
-                    enabledFeatures: ['webCompat']
-                },
-                featureSettings: {
-                    webCompat: {
-                        // no webShare
-                    }
-                }
-            })
-            const noCanShare = await page.evaluate(checkForCanShare)
-            const noShare = await page.evaluate(checkForShare)
-            // Base implementation of the test env should not have it (it's only available on mobile)
-            expect(noCanShare).toEqual(false)
-            expect(noShare).toEqual(false)
-        })
-    })
-
-    test.describe('enabled feature', () => {
-        async function navigate (page) {
-            await gotoAndWait(page, '/blank.html', {
-                site: {
-                    enabledFeatures: ['webCompat']
-                },
-                featureSettings: {
-                    webCompat: {
-                        webShare: 'enabled'
-                    }
-                }
-            })
-        }
-
-        test('should expose navigator.canShare() and navigator.share() when enabled', async ({ page }) => {
-            await navigate(page)
-            const hasCanShare = await page.evaluate(checkForCanShare)
-            const hasShare = await page.evaluate(checkForShare)
-            expect(hasCanShare).toEqual(true)
-            expect(hasShare).toEqual(true)
-        })
-
-        test.describe('navigator.canShare()', () => {
-            test('should not let you share files', async ({ page }) => {
-                await navigate(page)
-                const refuseFileShare = await page.evaluate(() => {
-                    return navigator.canShare({ text: 'xxx', files: [] })
-                })
-                expect(refuseFileShare).toEqual(false)
-            })
-
-            test('should not let you share non-http urls', async ({ page }) => {
-                await navigate(page)
-                const refuseShare = await page.evaluate(() => {
-                    return navigator.canShare({ url: 'chrome://bla' })
-                })
-                expect(refuseShare).toEqual(false)
-            })
-
-            test('should allow relative links', async ({ page }) => {
-                await navigate(page)
-                const allowShare = await page.evaluate(() => {
-                    return navigator.canShare({ url: 'bla' })
-                })
-                expect(allowShare).toEqual(true)
-            })
-
-            test('should support only the specific fields', async ({ page }) => {
-                await navigate(page)
-                const refuseShare = await page.evaluate(() => {
-                    // eslint-disable-next-line
-                    // @ts-ignore intentionally malformed data
-                    return navigator.canShare({ foo: 'bar' })
-                })
-                expect(refuseShare).toEqual(false)
-            })
-
-            test('should let you share stuff', async ({ page }) => {
-                await navigate(page)
-                let canShare = await page.evaluate(() => {
-                    return navigator.canShare({ url: 'http://example.com' })
-                })
-                expect(canShare).toEqual(true)
-
-                canShare = await page.evaluate(() => {
-                    return navigator.canShare({ text: 'the grass was greener' })
-                })
-                expect(canShare).toEqual(true)
-
-                canShare = await page.evaluate(() => {
-                    return navigator.canShare({ title: 'the light was brighter' })
-                })
-                expect(canShare).toEqual(true)
-
-                canShare = await page.evaluate(() => {
-                    return navigator.canShare({ text: 'with friends surrounded', title: 'the nights of wonder' })
-                })
-                expect(canShare).toEqual(true)
-            })
-        })
-
-        test.describe('navigator.share()', () => {
-            async function beforeEach (page) {
-                await page.evaluate(() => {
-                    globalThis.shareReq = null
-                    globalThis.cssMessaging.impl.request = (req) => {
-                        globalThis.shareReq = req
-                        return Promise.resolve({})
-                    }
-                })
-            }
-            test.describe('(no errors from Android)', () => {
-                /**
-                 * @param {import("@playwright/test").Page} page
-                 * @param {any} data
-                 * @return {Promise<any>}
-                 */
-                async function checkShare (page, data) {
-                    const payload = `navigator.share(${JSON.stringify(data)})`
-                    const result = await page.evaluate(payload).catch((e) => {
-                        return { threw: e }
-                    })
-                    console.log('check share')
-                    const message = await page.evaluate(() => {
-                        console.log('did read?')
-                        return globalThis.shareReq
-                    })
-                    return { result, message }
-                }
-
-                test('should let you share text', async ({ page }) => {
-                    await navigate(page)
-                    await beforeEach(page)
-                    const { result, message } = await checkShare(page, { text: 'xxx' })
-                    expect(message).toMatchObject({ featureName: 'webCompat', method: 'webShare', params: { text: 'xxx' } })
-                    expect(result).toBeUndefined()
-                })
-
-                test('should let you share url', async ({ page }) => {
-                    await navigate(page)
-                    await beforeEach(page)
-                    const { result, message } = await checkShare(page, { url: 'http://example.com' })
-                    expect(message).toMatchObject({ featureName: 'webCompat', method: 'webShare', params: { url: 'http://example.com/' } })
-                    expect(result).toBeUndefined()
-                })
-
-                test('should let you share title alone', async ({ page }) => {
-                    await navigate(page)
-                    await beforeEach(page)
-                    const { result, message } = await checkShare(page, { title: 'xxx' })
-                    expect(message).toMatchObject({ featureName: 'webCompat', method: 'webShare', params: { title: 'xxx', text: '' } })
-                    expect(result).toBeUndefined()
-                })
-
-                test('should let you share title and text', async ({ page }) => {
-                    await navigate(page)
-                    await beforeEach(page)
-                    const { result, message } = await checkShare(page, { title: 'xxx', text: 'yyy' })
-                    expect(message).toMatchObject({ featureName: 'webCompat', method: 'webShare', params: { title: 'xxx', text: 'yyy' } })
-                    expect(result).toBeUndefined()
-                })
-
-                test('should let you share title and url', async ({ page }) => {
-                    await navigate(page)
-                    await beforeEach(page)
-                    const { result, message } = await checkShare(page, { title: 'xxx', url: 'http://example.com' })
-                    expect(message).toMatchObject({ featureName: 'webCompat', method: 'webShare', params: { title: 'xxx', url: 'http://example.com/' } })
-                    expect(result).toBeUndefined()
-                })
-
-                test('should combine text and url when both are present', async ({ page }) => {
-                    await navigate(page)
-                    await beforeEach(page)
-                    const { result, message } = await checkShare(page, { text: 'xxx', url: 'http://example.com' })
-                    expect(message).toMatchObject({ featureName: 'webCompat', method: 'webShare', params: { text: 'xxx http://example.com/' } })
-                    expect(result).toBeUndefined()
-                })
-
-                test('should throw when sharing files', async ({ page }) => {
-                    await navigate(page)
-                    await beforeEach(page)
-                    const { result, message } = await checkShare(page, { title: 'title', files: [] })
-                    expect(message).toBeNull()
-                    expect(result.threw.message).toContain('TypeError: Invalid share data')
-                })
-
-                test('should throw when sharing non-http urls', async ({ page }) => {
-                    await navigate(page)
-                    await beforeEach(page)
-                    const { result, message } = await checkShare(page, { url: 'chrome://bla' })
-                    expect(message).toBeNull()
-                    expect(result.threw.message).toContain('TypeError: Invalid share data')
-                })
-
-                test('should handle relative urls', async ({ page }) => {
-                    await navigate(page)
-                    await beforeEach(page)
-                    const { result, message } = await checkShare(page, { url: 'bla' })
-                    expect(message.params.url).toMatch(/^http:\/\/localhost:\d+\/bla$/)
-                    expect(result).toBeUndefined()
-                })
-
-                test('should treat empty url as relative', async ({ page }) => {
-                    await navigate(page)
-                    await beforeEach(page)
-                    const { result, message } = await checkShare(page, { url: '' })
-                    expect(message.params.url).toMatch(/^http:\/\/localhost:\d+\//)
-                    expect(result).toBeUndefined()
-                })
-            })
-
-            test.describe('(handling errors from Android)', () => {
-                test('should handle messaging error', async ({ page }) => {
-                    // page.on('console', (msg) => console.log(msg.type(), msg.text()))
-                    await navigate(page)
-                    await beforeEach(page)
-
-                    await page.evaluate(() => {
-                        globalThis.cssMessaging.impl.request = () => {
-                            return Promise.reject(new Error('something wrong'))
-                        }
-                    })
-                    const result = await page.evaluate('navigator.share({ text: "xxx" })').catch((e) => {
-                        return { threw: e }
-                    })
-
-                    expect(result.threw.message).toContain('something wrong')
-                    expect(result.threw.message).toContain('DOMException')
-                })
-
-                test('should handle soft failures', async ({ page }) => {
-                    await navigate(page)
-                    await beforeEach(page)
-                    await page.evaluate(() => {
-                        globalThis.cssMessaging.impl.request = () => {
-                            return Promise.resolve({ failure: { name: 'AbortError', message: 'some error message' } })
-                        }
-                    })
-                    const result = await page.evaluate('navigator.share({ text: "xxx" })').catch((e) => {
-                        return { threw: e }
-                    })
-                    console.error(result.threw)
-                    expect(result.threw.message).toContain('some error message')
-                    expect(result.threw.message).toContain('DOMException')
-                })
-            })
-        })
-    })
-})
-
 test.describe('Viewport fixes', () => {
     function getViewportValue () {
         return document.querySelector('meta[name="viewport"]')?.getAttribute('content')
diff --git a/injected/playwright.config.js b/injected/playwright.config.js
index a1353ca5a..5cb230f02 100644
--- a/injected/playwright.config.js
+++ b/injected/playwright.config.js
@@ -40,7 +40,8 @@ export default defineConfig({
         {
             name: 'android',
             testMatch: [
-                'integration-test/duckplayer-mobile.spec.js'
+                'integration-test/duckplayer-mobile.spec.js',
+                'integration-test/web-compat-android.spec.js'
             ],
             use: { injectName: 'android', platform: 'android', ...devices['Galaxy S5'] }
         },
diff --git a/package-lock.json b/package-lock.json
index 605604913..0dc2ab5ca 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
         "types-generator"
       ],
       "devDependencies": {
+        "@playwright/test": "^1.48.2",
         "@typescript-eslint/eslint-plugin": "^6.9.1",
         "eslint": "^8.57.1",
         "eslint-config-standard": "^17.1.0",
@@ -46,7 +47,7 @@
         "@types/jasmine": "^5.1.4",
         "@types/node": "^22.8.6",
         "@typescript-eslint/eslint-plugin": "^6.9.1",
-        "config-builder": "git+ssh://git@github.com/duckduckgo/privacy-configuration.git#207bcafcd8d67d0530569f7efcf84463194b999b",
+        "config-builder": "github:duckduckgo/privacy-configuration#1729260354597",
         "fast-check": "^3.22.0",
         "jasmine": "^5.4.0",
         "minimist": "^1.2.8",
@@ -947,18 +948,19 @@
       }
     },
     "node_modules/@playwright/test": {
-      "version": "1.40.1",
-      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.1.tgz",
-      "integrity": "sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==",
+      "version": "1.48.2",
+      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz",
+      "integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==",
       "dev": true,
+      "license": "Apache-2.0",
       "dependencies": {
-        "playwright": "1.40.1"
+        "playwright": "1.48.2"
       },
       "bin": {
         "playwright": "cli.js"
       },
       "engines": {
-        "node": ">=16"
+        "node": ">=18"
       }
     },
     "node_modules/@rive-app/canvas-single": {
@@ -5675,33 +5677,35 @@
       }
     },
     "node_modules/playwright": {
-      "version": "1.40.1",
-      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.40.1.tgz",
-      "integrity": "sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==",
+      "version": "1.48.2",
+      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz",
+      "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==",
       "dev": true,
+      "license": "Apache-2.0",
       "dependencies": {
-        "playwright-core": "1.40.1"
+        "playwright-core": "1.48.2"
       },
       "bin": {
         "playwright": "cli.js"
       },
       "engines": {
-        "node": ">=16"
+        "node": ">=18"
       },
       "optionalDependencies": {
         "fsevents": "2.3.2"
       }
     },
     "node_modules/playwright-core": {
-      "version": "1.40.1",
-      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz",
-      "integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==",
+      "version": "1.48.2",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz",
+      "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==",
       "dev": true,
+      "license": "Apache-2.0",
       "bin": {
         "playwright-core": "cli.js"
       },
       "engines": {
-        "node": ">=16"
+        "node": ">=18"
       }
     },
     "node_modules/portfinder": {
@@ -7547,7 +7551,6 @@
       },
       "devDependencies": {
         "@duckduckgo/messaging": "*",
-        "@playwright/test": "^1.40.1",
         "esbuild": "^0.24.0",
         "fast-check": "^3.22.0",
         "http-server": "^14.1.1",
@@ -8056,12 +8059,12 @@
       "optional": true
     },
     "@playwright/test": {
-      "version": "1.40.1",
-      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.1.tgz",
-      "integrity": "sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==",
+      "version": "1.48.2",
+      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz",
+      "integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==",
       "dev": true,
       "requires": {
-        "playwright": "1.40.1"
+        "playwright": "1.48.2"
       }
     },
     "@rive-app/canvas-single": {
@@ -10356,7 +10359,7 @@
         "@types/jasmine": "^5.1.4",
         "@types/node": "^22.8.6",
         "@typescript-eslint/eslint-plugin": "^6.9.1",
-        "config-builder": "git+ssh://git@github.com/duckduckgo/privacy-configuration.git#207bcafcd8d67d0530569f7efcf84463194b999b",
+        "config-builder": "github:duckduckgo/privacy-configuration#1729260354597",
         "fast-check": "^3.22.0",
         "immutable-json-patch": "^6.0.1",
         "jasmine": "^5.4.0",
@@ -11306,19 +11309,19 @@
       "dev": true
     },
     "playwright": {
-      "version": "1.40.1",
-      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.40.1.tgz",
-      "integrity": "sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==",
+      "version": "1.48.2",
+      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz",
+      "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==",
       "dev": true,
       "requires": {
         "fsevents": "2.3.2",
-        "playwright-core": "1.40.1"
+        "playwright-core": "1.48.2"
       }
     },
     "playwright-core": {
-      "version": "1.40.1",
-      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz",
-      "integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==",
+      "version": "1.48.2",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz",
+      "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==",
       "dev": true
     },
     "portfinder": {
@@ -11815,7 +11818,6 @@
       "requires": {
         "@duckduckgo/messaging": "*",
         "@formkit/auto-animate": "^0.8.2",
-        "@playwright/test": "^1.40.1",
         "@rive-app/canvas-single": "^2.23.3",
         "classnames": "^2.5.1",
         "esbuild": "^0.24.0",
diff --git a/package.json b/package.json
index dbda1888f..e8af51315 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
     "stylelint-csstree-validator": "^3.0.0",
     "minimist": "^1.2.8",
     "typedoc": "^0.26.10",
-    "typescript": "^5.6.3"
+    "typescript": "^5.6.3",
+    "@playwright/test": "^1.48.2"
   }
 }
diff --git a/special-pages/package.json b/special-pages/package.json
index 9eb28c492..14ac502ea 100644
--- a/special-pages/package.json
+++ b/special-pages/package.json
@@ -26,7 +26,6 @@
   "devDependencies": {
     "@duckduckgo/messaging": "*",
     "esbuild": "^0.24.0",
-    "@playwright/test": "^1.40.1",
     "http-server": "^14.1.1",
     "web-resource-inliner": "^6.0.1",
     "fast-check": "^3.22.0"

From 76bab4d80982ddab63265c694ab96c27a0fa5138 Mon Sep 17 00:00:00 2001
From: Maxim Tsoy <mtsoy@duckduckgo.com>
Date: Mon, 4 Nov 2024 12:37:09 +0100
Subject: [PATCH 10/10] Shared eslint config (#1185)

* Upgrade eslint and use a shared ddg config

* Auto-fix some lint problems

* Tweak eslint config

* Manual lint fixes

* Pin the eslint config version

* Fix linting the contentScope.js output
---
 .eslintignore                                 |   14 -
 .eslintrc                                     |   38 -
 .github/scripts/diff-directories.js           |    8 +-
 build-output.eslint.config.js                 |   14 +
 eslint.config.js                              |  104 +
 injected/build-output.eslintrc                |   12 -
 injected/integration-test/.eslintrc           |    8 -
 .../autofill-password-import.spec.js          |    4 +-
 injected/integration-test/duckplayer.spec.js  |    2 +-
 injected/integration-test/fingerprint.spec.js |    1 +
 injected/integration-test/helpers/harness.js  |    1 +
 .../page-objects/broker-protection.js         |    2 +
 .../page-objects/duckplayer-overlays.js       |    2 +-
 .../test-pages/shared/utils.js                |    1 -
 injected/integration-test/type-helpers.mjs    |    2 +-
 injected/package.json                         |    2 -
 injected/playwright-e2e.config.js             |    1 +
 injected/playwright.config.js                 |    1 +
 injected/scripts/bundleTrackers.mjs           |    1 +
 injected/scripts/types.mjs                    |    2 +
 injected/src/captured-globals.js              |    1 +
 injected/src/content-feature.js               |    9 +-
 .../src/features/autofill-password-import.js  |    1 +
 .../broker-protection/actions/extract.js      |    2 +-
 .../broker-protection/extractors/age.js       |    2 +-
 .../broker-protection/extractors/name.js      |    2 +-
 .../src/features/broker-protection/types.js   |    4 +-
 injected/src/features/click-to-load.js        |    2 +-
 .../components/ctl-placeholder-blocked.js     |    1 +
 injected/src/features/cookie.js               |    1 +
 injected/src/features/duck-player.js          |    2 +-
 .../features/fingerprinting-screen-size.js    |    2 +-
 injected/src/features/web-compat.js           |    1 +
 injected/src/performance.js                   |    1 +
 injected/src/utils.js                         |    5 +-
 injected/unit-test/canvas.js                  |    1 +
 injected/unit-test/content-feature.js         |    2 +-
 injected/unit-test/messaging.js               |    8 +-
 messaging/index.js                            |    6 +-
 messaging/lib/examples/payloads.js            |    2 +-
 messaging/lib/test-utils.mjs                  |   18 +-
 messaging/lib/webkit.js                       |    2 +-
 messaging/lib/windows.js                      |    4 +-
 package-lock.json                             | 2308 ++++++++++++-----
 package.json                                  |   16 +-
 special-pages/index.mjs                       |   34 +-
 .../pages/duckplayer/app/features/iframe.js   |    2 +-
 .../duckplayer/app/features/title-capture.js  |    2 +-
 .../new-tab/app/privacy-stats/PrivacyStats.js |    1 +
 .../app/widget-list/widget-config.provider.js |    4 +-
 special-pages/pages/new-tab/src/js/index.js   |    2 +-
 .../pages/onboarding/app/components/App.js    |    2 +-
 .../pages/onboarding/app/components/App2.js   |    2 +-
 .../onboarding/app/components/Background.js   |    2 +-
 .../onboarding/app/components/ListItem.js     |    2 +-
 .../pages/onboarding/app/components/Typed.js  |    2 +-
 .../onboarding/app/components/v3/Animation.js |    1 +
 .../app/components/v3/Background.js           |    2 +-
 .../onboarding/app/components/v3/Heading.js   |    4 +
 .../app/components/v3/SettingsStep.js         |    2 +-
 .../onboarding/app/pages/CleanBrowsing.js     |    2 +-
 .../onboarding/app/pages/PrivacyDefault.js    |    2 +-
 .../pages/onboarding/app/pages/Summary.js     |    2 +-
 .../pages/release-notes/app/components/App.js |    2 +-
 .../app/components/ReleaseNotes.js            |    3 +-
 .../pages/release-notes/src/js/index.js       |    2 +-
 .../release-notes/src/js/mock-transport.js    |    2 +-
 special-pages/playwright.config.js            |    1 +
 special-pages/shared/components/Text/Text.js  |    1 +
 .../shared/create-special-page-messaging.js   |    2 +-
 .../tests/duckplayer-screenshots.spec.js      |    1 +
 special-pages/types.mjs                       |    1 +
 tsconfig.json                                 |    3 +-
 types-generator/build-types.mjs               |   12 +-
 types-generator/json-schema-fs.mjs            |    9 +-
 types-generator/json-schema.mjs               |   80 +-
 76 files changed, 1940 insertions(+), 872 deletions(-)
 delete mode 100644 .eslintignore
 delete mode 100644 .eslintrc
 create mode 100644 build-output.eslint.config.js
 create mode 100644 eslint.config.js
 delete mode 100644 injected/build-output.eslintrc
 delete mode 100644 injected/integration-test/.eslintrc

diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index 540337213..000000000
--- a/.eslintignore
+++ /dev/null
@@ -1,14 +0,0 @@
-build/
-docs/
-injected/lib
-Sources/ContentScopeScripts/dist/
-injected/integration-test/extension/contentScope.js
-injected/integration-test/test-pages/duckplayer/scripts/dist
-special-pages/pages/**/public
-special-pages/playwright-report/
-special-pages/test-results/
-special-pages/types/
-special-pages/messages/
-/playwright-report
-/test-results
-injected/src/types
diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 2992fb545..000000000
--- a/.eslintrc
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-    "extends": ["standard", "plugin:@typescript-eslint/recommended"],
-    "root": true,
-    "parserOptions": {
-      "ecmaVersion": "latest",
-      "project": "./tsconfig.json"
-    },
-    "parser": "@typescript-eslint/parser",
-    "plugins": ["promise","@typescript-eslint"],
-    "globals": {
-        "$USER_PREFERENCES$": "readonly",
-        "$USER_UNPROTECTED_DOMAINS$": "readonly",
-        "$CONTENT_SCOPE$": "readonly",
-        "$BUNDLED_CONFIG$": "readonly",
-        "windowsInteropPostMessage": "readonly",
-        "windowsInteropAddEventListener": "readonly",
-        "windowsInteropRemoveEventListener": "readonly"
-    },
-    "rules": {
-        "no-restricted-syntax": [
-            "error",
-            {
-                "selector": "MethodDefinition[key.type='PrivateIdentifier']",
-                "message": "Private methods are currently unsupported in older WebKit and ESR Firefox"
-            }
-        ],
-        "indent": ["error", 4],
-        "require-await": ["error"],
-        "promise/prefer-await-to-then": ["error"],
-        "@typescript-eslint/await-thenable": "error",
-        "@typescript-eslint/no-unused-vars": "error"
-    },
-    "env": {
-        "webextensions": true,
-        "browser": true,
-        "jasmine": true
-    }
-}
diff --git a/.github/scripts/diff-directories.js b/.github/scripts/diff-directories.js
index fd1650f3e..465a0afc4 100644
--- a/.github/scripts/diff-directories.js
+++ b/.github/scripts/diff-directories.js
@@ -46,7 +46,7 @@ function displayDiffs (dir1Files, dir2Files, isOpen) {
     }
     for (const [filePath, fileContent] of Object.entries(dir1Files)) {
         let diffOut = ''
-        let compareOut = undefined
+        let compareOut
         if (filePath in dir2Files) {
             const fileOut = fileContent
             const file2Out = dir2Files[filePath]
@@ -71,7 +71,7 @@ function displayDiffs (dir1Files, dir2Files, isOpen) {
         const rollup = rollupGrouping[key]
         let outString = `
         `
-        let title = key
+        const title = key
         if (rollup.files.length) {
             for (const file of rollup.files) {
                 outString += `- ${file}\n`
@@ -84,7 +84,7 @@ function displayDiffs (dir1Files, dir2Files, isOpen) {
 }
 
 function renderDetails (section, text, isOpen) {
-    if (section == 'dist') {
+    if (section === 'dist') {
         section = 'apple'
     }
     const open = section !== 'integration' ? 'open' : ''
@@ -119,6 +119,6 @@ sortFiles(readFilesRecursively(dir1 + sourcesOutput), 'dir1')
 sortFiles(readFilesRecursively(dir2 + sourcesOutput), 'dir2')
 
 
-//console.log(Object.keys(files))
+// console.log(Object.keys(files))
 const fileOut = displayDiffs(sections.dir1, sections.dir2, true)
 console.log(fileOut)
\ No newline at end of file
diff --git a/build-output.eslint.config.js b/build-output.eslint.config.js
new file mode 100644
index 000000000..833e9a0c1
--- /dev/null
+++ b/build-output.eslint.config.js
@@ -0,0 +1,14 @@
+// this belongs to the injected/ package, but eslint has issues with linting files outside of the base directory:
+// https://github.com/eslint/eslint/discussions/18806#discussioncomment-10848750
+
+export default [
+    {
+        languageOptions: {
+            ecmaVersion: "latest",
+            sourceType: "script",
+        },
+        rules: {
+            "no-implicit-globals": "error",
+        }
+    }
+];
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 000000000..c636ad174
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,104 @@
+import tseslint from 'typescript-eslint';
+import ddgConfig from "@duckduckgo/eslint-config";
+import globals from "globals";
+
+// @ts-check
+export default tseslint.config(
+    ...ddgConfig,
+    ...tseslint.configs.recommended,
+    {
+        ignores: [
+            "**/build/",
+            "**/docs/",
+            "injected/lib",
+            "Sources/ContentScopeScripts/dist/",
+            "injected/integration-test/extension/contentScope.js",
+            "injected/integration-test/test-pages/duckplayer/scripts/dist",
+            "special-pages/pages/**/public",
+            "special-pages/playwright-report/",
+            "special-pages/test-results/",
+            "special-pages/types/",
+            "special-pages/messages/",
+            "playwright-report",
+            "test-results",
+            "injected/src/types",
+        ],
+    },
+    {
+        languageOptions: {
+            globals: {
+                $USER_PREFERENCES$: "readonly",
+                $USER_UNPROTECTED_DOMAINS$: "readonly",
+                $CONTENT_SCOPE$: "readonly",
+                $BUNDLED_CONFIG$: "readonly",
+            },
+
+            ecmaVersion: "latest",
+            sourceType: "script",
+        },
+
+        rules: {
+            "no-restricted-syntax": ["error", {
+                selector: "MethodDefinition[key.type='PrivateIdentifier']",
+                message: "Private methods are currently unsupported in older WebKit and ESR Firefox",
+            }],
+
+            "require-await": ["error"],
+            "promise/prefer-await-to-then": ["error"],
+            "@typescript-eslint/no-unused-vars": ["error", {
+                args: "none",
+                caughtErrors: "none",
+                ignoreRestSiblings: true,
+                vars: "all"
+            }],
+        },
+    },
+    {
+        ignores: ["injected/integration-test/test-pages/**", "injected/integration-test/extension/**"],
+        languageOptions: {
+            parserOptions: {
+                projectService: {
+                    allowDefaultProject: ['eslint.config.js', 'build-output.eslint.config.js'],
+                },
+            }
+        },
+        rules: {
+            "@typescript-eslint/await-thenable": "error",
+        },
+    },
+    {
+        files: ["**/scripts/*.js", "**/*.mjs", "**/unit-test/**/*.js", "**/integration-test/**/*.spec.js"],
+        languageOptions: {
+            globals: {
+                ...globals.node,
+            }
+        }
+    },
+    {
+        files: ["injected/**/*.js"],
+        languageOptions: {
+            globals: {
+                windowsInteropPostMessage: "readonly",
+                windowsInteropAddEventListener: "readonly",
+                windowsInteropRemoveEventListener: "readonly",
+            }
+        }
+    },
+    {
+        files: ["**/unit-test/*.js"],
+        languageOptions: {
+            globals: {
+                ...globals.jasmine,
+            }
+        }
+    },
+    {
+        ignores: ["**/scripts/*.js"],
+        languageOptions: {
+            globals: {
+                ...globals.browser,
+                ...globals.webextensions,
+            }
+        }
+    }
+);
diff --git a/injected/build-output.eslintrc b/injected/build-output.eslintrc
deleted file mode 100644
index cf788cf14..000000000
--- a/injected/build-output.eslintrc
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-    "root": true,
-    "plugins": ["promise", "@typescript-eslint", "eslint-plugin-n"],
-    "parserOptions": {
-      "ecmaVersion": "latest",
-    },
-    "rules": {
-        "no-implicit-globals": "error",
-        "typescript-eslint/ban-ts-comment": "off",
-        "promise/prefer-await-to-then": "off",
-    }
-}
diff --git a/injected/integration-test/.eslintrc b/injected/integration-test/.eslintrc
deleted file mode 100644
index eed473ae6..000000000
--- a/injected/integration-test/.eslintrc
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-    "parserOptions": {
-        "project": null
-    },
-    "rules": {
-        "@typescript-eslint/await-thenable": "off"
-    }
-}
\ No newline at end of file
diff --git a/injected/integration-test/autofill-password-import.spec.js b/injected/integration-test/autofill-password-import.spec.js
index d9bca0403..3957d92cf 100644
--- a/injected/integration-test/autofill-password-import.spec.js
+++ b/injected/integration-test/autofill-password-import.spec.js
@@ -97,7 +97,7 @@ export class AutofillPasswordImportSpec {
      * @param {string} selector
      */
     async waitForAnimation (selector) {
-        const locator = await this.page.locator(selector)
+        const locator = this.page.locator(selector)
         return await locator.evaluate((el) => {
             if (el != null) {
                 return el.getAnimations().some((animation) => animation.playState === 'running')
@@ -112,7 +112,7 @@ export class AutofillPasswordImportSpec {
      * @param {string} text
      */
     async clickOnElement (text) {
-        const element = await this.page.getByText(text)
+        const element = this.page.getByText(text)
         await element.click()
     }
 }
diff --git a/injected/integration-test/duckplayer.spec.js b/injected/integration-test/duckplayer.spec.js
index af7aa14ac..4dd019eb6 100644
--- a/injected/integration-test/duckplayer.spec.js
+++ b/injected/integration-test/duckplayer.spec.js
@@ -39,7 +39,7 @@ test.describe('Thumbnail Overlays', () => {
         const navigation = overlays.requestWillFail()
         await overlays.clicksFirstShortsThumbnail()
         const url = await navigation
-        await overlays.opensShort(url)
+        overlays.opensShort(url)
     })
     test('Overlays don\'t show on thumbnails when disabled', async ({ page }, workerInfo) => {
         const overlays = DuckplayerOverlays.create(page, workerInfo)
diff --git a/injected/integration-test/fingerprint.spec.js b/injected/integration-test/fingerprint.spec.js
index 6243ff73a..f35a80f6d 100644
--- a/injected/integration-test/fingerprint.spec.js
+++ b/injected/integration-test/fingerprint.spec.js
@@ -5,6 +5,7 @@ import { test as base, expect } from '@playwright/test'
 import { testContextForExtension } from './helpers/harness.js'
 import { createRequire } from 'node:module'
 
+// eslint-disable-next-line no-redeclare
 const require = createRequire(import.meta.url)
 
 const test = testContextForExtension(base)
diff --git a/injected/integration-test/helpers/harness.js b/injected/integration-test/helpers/harness.js
index 668b119c8..58cc1e8b6 100644
--- a/injected/integration-test/helpers/harness.js
+++ b/injected/integration-test/helpers/harness.js
@@ -1,3 +1,4 @@
+/* global process */
 import { mkdtempSync, rmSync } from 'node:fs'
 import { tmpdir } from 'os'
 import { join } from 'path'
diff --git a/injected/integration-test/page-objects/broker-protection.js b/injected/integration-test/page-objects/broker-protection.js
index ac0f952d8..acfcd525f 100644
--- a/injected/integration-test/page-objects/broker-protection.js
+++ b/injected/integration-test/page-objects/broker-protection.js
@@ -265,10 +265,12 @@ export class BrokerProtectionPage {
      * @param {object} response
      */
     isErrorMessage (response) {
+        // eslint-disable-next-line no-unsafe-optional-chaining
         expect('error' in response[0].payload?.params?.result).toBe(true)
     }
 
     isSuccessMessage (response) {
+        // eslint-disable-next-line no-unsafe-optional-chaining
         expect('success' in response[0].payload?.params?.result).toBe(true)
     }
 
diff --git a/injected/integration-test/page-objects/duckplayer-overlays.js b/injected/integration-test/page-objects/duckplayer-overlays.js
index e83043d23..7d70afa6b 100644
--- a/injected/integration-test/page-objects/duckplayer-overlays.js
+++ b/injected/integration-test/page-objects/duckplayer-overlays.js
@@ -110,7 +110,7 @@ export class DuckplayerOverlays {
      * @return {Promise<string>}
      */
     async clicksFirstThumbnail () {
-        const elem = await this.page.locator('a[href^="/watch?v"]:has(img)').first()
+        const elem = this.page.locator('a[href^="/watch?v"]:has(img)').first()
         const link = await elem.getAttribute('href')
         if (!link) throw new Error('link must exist')
         await elem.click({ force: true })
diff --git a/injected/integration-test/test-pages/shared/utils.js b/injected/integration-test/test-pages/shared/utils.js
index 52e14984f..415ea176d 100644
--- a/injected/integration-test/test-pages/shared/utils.js
+++ b/injected/integration-test/test-pages/shared/utils.js
@@ -16,7 +16,6 @@ function buildTableCell (value, tagName = 'td') {
  * @param {Record<string, ResultRow>} results The results to build the table for.
  * @return {Element} The table element.
  */
-// eslint-disable-next-line no-unused-vars
 function buildResultTable (results) {
     const table = document.createElement('table')
     table.className = 'results'
diff --git a/injected/integration-test/type-helpers.mjs b/injected/integration-test/type-helpers.mjs
index 127412e87..87091e402 100644
--- a/injected/integration-test/type-helpers.mjs
+++ b/injected/integration-test/type-helpers.mjs
@@ -59,7 +59,7 @@ export class Build {
         const path = this.switch({
             windows: () => '../build/windows/contentScope.js',
             android: () => '../build/android/contentScope.js',
-            'apple': () => '../Sources/ContentScopeScripts/dist/contentScope.js',
+            apple: () => '../Sources/ContentScopeScripts/dist/contentScope.js',
             'apple-isolated': () => '../Sources/ContentScopeScripts/dist/contentScopeIsolated.js',
             'android-autofill-password-import': () => '../build/android/autofillPasswordImport.js'
         })
diff --git a/injected/package.json b/injected/package.json
index c90525221..bceef8bd6 100644
--- a/injected/package.json
+++ b/injected/package.json
@@ -15,8 +15,6 @@
     "build-integration": "node scripts/entry-points.js --platform integration",
     "build-types": "node scripts/types.mjs",
     "bundle-trackers": "node scripts/bundleTrackers.mjs --output ../build/tracker-lookup.json",
-    "lint": "npm run lint-no-output-globals",
-    "lint-no-output-globals": "eslint --no-eslintrc --config=build-output.eslintrc --no-ignore ../Sources/ContentScopeScripts/dist/contentScope.js",
     "test-unit": "jasmine --config=unit-test/config.json",
     "test-int": "npm run build-integration && npm run playwright",
     "test-int-x": "xvfb-run --server-args='-screen 0 1024x768x24' npm run test-int",
diff --git a/injected/playwright-e2e.config.js b/injected/playwright-e2e.config.js
index 981615675..a7d39bb80 100644
--- a/injected/playwright-e2e.config.js
+++ b/injected/playwright-e2e.config.js
@@ -1,3 +1,4 @@
+/* global process */
 import { defineConfig } from '@playwright/test'
 import { dirname, join } from 'node:path'
 const __dirname = dirname(new URL(import.meta.url).pathname)
diff --git a/injected/playwright.config.js b/injected/playwright.config.js
index 5cb230f02..89c112da3 100644
--- a/injected/playwright.config.js
+++ b/injected/playwright.config.js
@@ -1,3 +1,4 @@
+/* global process */
 import { defineConfig, devices } from '@playwright/test'
 
 export default defineConfig({
diff --git a/injected/scripts/bundleTrackers.mjs b/injected/scripts/bundleTrackers.mjs
index b204aba14..29a9a030c 100644
--- a/injected/scripts/bundleTrackers.mjs
+++ b/injected/scripts/bundleTrackers.mjs
@@ -1,3 +1,4 @@
+// eslint-disable-next-line no-redeclare
 import fetch from 'node-fetch'
 import { parseArgs, write } from '../../scripts/script-utils.js'
 
diff --git a/injected/scripts/types.mjs b/injected/scripts/types.mjs
index c04c1c86d..9cc240190 100644
--- a/injected/scripts/types.mjs
+++ b/injected/scripts/types.mjs
@@ -5,6 +5,7 @@ import { buildTypes } from "../../types-generator/build-types.mjs";
 
 const injectRoot = join(cwd(import.meta.url), '..')
 
+// eslint-disable-next-line no-redeclare
 const require = createRequire(import.meta.url);
 const configBuilderRoot = dirname(require.resolve("config-builder"));
 
@@ -42,6 +43,7 @@ const injectSchemaMapping = {
 
 if (isLaunchFile(import.meta.url)) {
     buildTypes(injectSchemaMapping)
+        // eslint-disable-next-line promise/prefer-await-to-then
         .catch((error) => {
             console.error(error)
             process.exit(1)
diff --git a/injected/src/captured-globals.js b/injected/src/captured-globals.js
index c4f682938..e64ec34a5 100644
--- a/injected/src/captured-globals.js
+++ b/injected/src/captured-globals.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-redeclare */
 export const Set = globalThis.Set
 export const Reflect = globalThis.Reflect
 export const customElementsGet = globalThis.customElements?.get.bind(globalThis.customElements)
diff --git a/injected/src/content-feature.js b/injected/src/content-feature.js
index 3460ff7c8..bd570ffbe 100644
--- a/injected/src/content-feature.js
+++ b/injected/src/content-feature.js
@@ -2,6 +2,7 @@ import { camelcase, matchHostname, processAttr, computeEnabledFeatures, parseFea
 import { immutableJSONPatch } from 'immutable-json-patch'
 import { PerformanceMonitor } from './performance.js'
 import { defineProperty, shimInterface, shimProperty, wrapMethod, wrapProperty, wrapToString } from './wrapper-utils.js'
+// eslint-disable-next-line no-redeclare
 import { Proxy, Reflect } from './captured-globals.js'
 import { Messaging, MessagingContext } from '../../messaging/index.js'
 import { extensionConstructMessagingConfig } from './sendmessage-transport.js'
@@ -28,8 +29,10 @@ export default class ContentFeature {
     /** @type {boolean | undefined} */
     #documentOriginIsTracker
     /** @type {Record<string, unknown> | undefined} */
+    // eslint-disable-next-line no-unused-private-class-members
     #bundledfeatureSettings
     /** @type {import('../../messaging').Messaging} */
+    // eslint-disable-next-line no-unused-private-class-members
     #messaging
     /** @type {boolean} */
     #isDebugFlagSet = false
@@ -214,7 +217,7 @@ export default class ContentFeature {
         })
     }
 
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
+     
     init (args) {
     }
 
@@ -227,7 +230,7 @@ export default class ContentFeature {
         this.measure()
     }
 
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
+     
     load (args) {
     }
 
@@ -296,7 +299,7 @@ export default class ContentFeature {
         }
     }
 
-    // eslint-disable-next-line @typescript-eslint/no-empty-function
+     
     update () {
     }
 
diff --git a/injected/src/features/autofill-password-import.js b/injected/src/features/autofill-password-import.js
index 05d8d2eec..2b846cc8f 100644
--- a/injected/src/features/autofill-password-import.js
+++ b/injected/src/features/autofill-password-import.js
@@ -166,6 +166,7 @@ export default class AutofillPasswordImport extends ContentFeature {
             try {
                 const { element, style, shouldTap } = await this.getElementAndStyleFromPath(path) ?? {}
                 if (element != null) {
+                    // eslint-disable-next-line @typescript-eslint/no-unused-expressions
                     shouldTap ? this.autotapElement(element) : this.animateElement(element, style)
                 }
             } catch {
diff --git a/injected/src/features/broker-protection/actions/extract.js b/injected/src/features/broker-protection/actions/extract.js
index eff9e9fef..6d84ae526 100644
--- a/injected/src/features/broker-protection/actions/extract.js
+++ b/injected/src/features/broker-protection/actions/extract.js
@@ -63,7 +63,7 @@ export async function extract (action, userData, root = document) {
     const filtered = await Promise.all(filteredPromises)
 
     // omit the DOM node from data transfer
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+     
     const debugResults = extractResult.results.map((result) => result.asData())
 
     return new SuccessResponse({
diff --git a/injected/src/features/broker-protection/extractors/age.js b/injected/src/features/broker-protection/extractors/age.js
index bf49ad6c3..4a098747c 100644
--- a/injected/src/features/broker-protection/extractors/age.js
+++ b/injected/src/features/broker-protection/extractors/age.js
@@ -9,7 +9,7 @@ export class AgeExtractor {
      * @param {string[]} strs
      * @param {import('../actions/extract.js').ExtractorParams} _extractorParams
      */
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+     
     extract (strs, _extractorParams) {
         if (!strs[0]) return null
         return strs[0].match(/\d+/)?.[0] ?? null
diff --git a/injected/src/features/broker-protection/extractors/name.js b/injected/src/features/broker-protection/extractors/name.js
index b85e74f04..36470e88d 100644
--- a/injected/src/features/broker-protection/extractors/name.js
+++ b/injected/src/features/broker-protection/extractors/name.js
@@ -10,7 +10,7 @@ export class NameExtractor {
      * @param {string[]} strs
      * @param {import('../actions/extract.js').ExtractorParams} _extractorParams
      */
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+     
     extract (strs, _extractorParams) {
         if (!strs[0]) return null
         return strs[0].replace(/\n/g, ' ').trim()
diff --git a/injected/src/features/broker-protection/types.js b/injected/src/features/broker-protection/types.js
index f131d6fbb..193de0864 100644
--- a/injected/src/features/broker-protection/types.js
+++ b/injected/src/features/broker-protection/types.js
@@ -79,7 +79,7 @@ export class Extractor {
      * @param {import("./actions/extract").ExtractorParams} extractorParams
      * @return {JsonValue}
      */
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+     
     extract (noneEmptyStringArray, extractorParams) {
         throw new Error('must implement extract')
     }
@@ -94,7 +94,7 @@ export class AsyncProfileTransform {
      * @param {Record<string, any>} profileParams - the original action params from `action.profile`
      * @return {Promise<Record<string, any>>}
      */
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+     
     transform (profile, profileParams) {
         throw new Error('must implement extract')
     }
diff --git a/injected/src/features/click-to-load.js b/injected/src/features/click-to-load.js
index 6562bc623..1b7763add 100644
--- a/injected/src/features/click-to-load.js
+++ b/injected/src/features/click-to-load.js
@@ -1887,7 +1887,7 @@ export default class ClickToLoad extends ContentFeature {
         })
         // Listen to message from Platform letting CTL know that we're ready to
         // replace elements in the page
-        // eslint-disable-next-line promise/prefer-await-to-then
+         
         this.messaging.subscribe(
             'displayClickToLoadPlaceholders',
             // TODO: Pass `message.options.ruleAction` through, that way only
diff --git a/injected/src/features/click-to-load/components/ctl-placeholder-blocked.js b/injected/src/features/click-to-load/components/ctl-placeholder-blocked.js
index 495319a24..f88106943 100644
--- a/injected/src/features/click-to-load/components/ctl-placeholder-blocked.js
+++ b/injected/src/features/click-to-load/components/ctl-placeholder-blocked.js
@@ -113,6 +113,7 @@ export class DDGCtlPlaceholderBlockedElement extends HTMLElement {
         /**
          * Append both to the shadow root
          */
+        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
         feedbackLink && this.placeholderBlocked.appendChild(feedbackLink)
         shadow.appendChild(this.placeholderBlocked)
         shadow.appendChild(style)
diff --git a/injected/src/features/cookie.js b/injected/src/features/cookie.js
index efcfe52ac..0d6aa9432 100644
--- a/injected/src/features/cookie.js
+++ b/injected/src/features/cookie.js
@@ -45,6 +45,7 @@ let loadedPolicyResolve
  * @param {any} ctx
  */
 function debugHelper (action, reason, ctx) {
+    // eslint-disable-next-line @typescript-eslint/no-unused-expressions
     cookiePolicy.debug && postDebugMessage('jscookie', {
         action,
         reason,
diff --git a/injected/src/features/duck-player.js b/injected/src/features/duck-player.js
index 0e1c6eb62..13c7019a4 100644
--- a/injected/src/features/duck-player.js
+++ b/injected/src/features/duck-player.js
@@ -59,7 +59,7 @@ import { Environment, initOverlays } from './duckplayer/overlays.js'
  * @internal
  */
 export default class DuckPlayerFeature extends ContentFeature {
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+     
     init (args) {
         /**
          * This feature never operates in a frame
diff --git a/injected/src/features/fingerprinting-screen-size.js b/injected/src/features/fingerprinting-screen-size.js
index 0a5aa7cfe..3e4aa99fb 100644
--- a/injected/src/features/fingerprinting-screen-size.js
+++ b/injected/src/features/fingerprinting-screen-size.js
@@ -65,7 +65,7 @@ export default class FingerprintingScreenSize extends ContentFeature {
         try {
             this.defineProperty(globalThis, property, {
                 get: () => value,
-                // eslint-disable-next-line @typescript-eslint/no-empty-function
+                 
                 set: () => {},
                 configurable: true,
                 enumerable: true
diff --git a/injected/src/features/web-compat.js b/injected/src/features/web-compat.js
index 8d5c924c7..79897357d 100644
--- a/injected/src/features/web-compat.js
+++ b/injected/src/features/web-compat.js
@@ -1,4 +1,5 @@
 import ContentFeature from '../content-feature.js'
+// eslint-disable-next-line no-redeclare
 import { URL } from '../captured-globals.js'
 
 /**
diff --git a/injected/src/performance.js b/injected/src/performance.js
index 85124dc70..ada6bc91c 100644
--- a/injected/src/performance.js
+++ b/injected/src/performance.js
@@ -30,6 +30,7 @@ export class PerformanceMonitor {
 /**
  * Tiny wrapper around performance.mark and performance.measure
  */
+// eslint-disable-next-line no-redeclare
 export class PerformanceMark {
     /**
      * @param {string} name
diff --git a/injected/src/utils.js b/injected/src/utils.js
index ca0899bb3..019a18099 100644
--- a/injected/src/utils.js
+++ b/injected/src/utils.js
@@ -1,8 +1,9 @@
+/* eslint-disable no-redeclare, no-global-assign */
 /* global cloneInto, exportFunction, mozProxies */
 import { Set } from './captured-globals.js'
 
 // Only use globalThis for testing this breaks window.wrappedJSObject code in Firefox
-// eslint-disable-next-line no-global-assign
+ 
 let globalObj = typeof window === 'undefined' ? globalThis : window
 let Error = globalObj.Error
 let messageSecret
@@ -284,7 +285,7 @@ const functionMap = {
         // eslint-disable-next-line no-debugger
         debugger
     },
-    // eslint-disable-next-line @typescript-eslint/no-empty-function
+     
     noop: () => { }
 }
 
diff --git a/injected/unit-test/canvas.js b/injected/unit-test/canvas.js
index a5be4ad77..18da139f3 100644
--- a/injected/unit-test/canvas.js
+++ b/injected/unit-test/canvas.js
@@ -1,3 +1,4 @@
+// eslint-disable-next-line no-redeclare
 import ImageData from '@canvas/image-data'
 import { modifyPixelData } from '../src/canvas.js'
 
diff --git a/injected/unit-test/content-feature.js b/injected/unit-test/content-feature.js
index 7258bf8e6..d471bf5de 100644
--- a/injected/unit-test/content-feature.js
+++ b/injected/unit-test/content-feature.js
@@ -55,7 +55,7 @@ describe('ContentFeature class', () => {
             // eslint-disable-next-line
             // @ts-ignore partial mock
             messaging = {
-                // eslint-disable-next-line
+                 
                 notify (name, data) {}
             }
         }
diff --git a/injected/unit-test/messaging.js b/injected/unit-test/messaging.js
index 1ca9b266a..14059045e 100644
--- a/injected/unit-test/messaging.js
+++ b/injected/unit-test/messaging.js
@@ -15,7 +15,7 @@ describe('Messaging Transports', () => {
         messaging.request('helloWorld', { foo: 'bar' })
 
         // grab the auto-generated `id` field
-        const [requestMessage] = spy.calls.first()?.args
+        const [requestMessage] = spy.calls.first()?.args ?? []
         expect(typeof requestMessage.id).toBe('string')
         expect(requestMessage.id.length).toBeGreaterThan(0)
 
@@ -206,16 +206,16 @@ describe('Android', () => {
 function createMessaging () {
     /** @type {import("@duckduckgo/messaging").MessagingTransport} */
     const transport = {
-        // eslint-disable-next-line @typescript-eslint/no-unused-vars
+         
         notify (msg) {
             // test
         },
-        // eslint-disable-next-line @typescript-eslint/no-unused-vars
+         
         request: (_msg) => {
             // test
             return Promise.resolve(null)
         },
-        // eslint-disable-next-line @typescript-eslint/no-unused-vars
+         
         subscribe (_msg) {
             // test
             return () => {
diff --git a/messaging/index.js b/messaging/index.js
index 356085f03..de1133965 100644
--- a/messaging/index.js
+++ b/messaging/index.js
@@ -133,7 +133,7 @@ export class MessagingTransport {
      * @param {NotificationMessage} msg
      * @returns {void}
      */
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+     
     notify (msg) {
         throw new Error("must implement 'notify'")
     }
@@ -143,7 +143,7 @@ export class MessagingTransport {
      * @param {{signal?: AbortSignal}} [options]
      * @return {Promise<any>}
      */
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+     
     request (msg, options = {}) {
         throw new Error('must implement')
     }
@@ -153,7 +153,7 @@ export class MessagingTransport {
      * @param {(value: unknown) => void} callback
      * @return {() => void}
      */
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+     
     subscribe (msg, callback) {
         throw new Error('must implement')
     }
diff --git a/messaging/lib/examples/payloads.js b/messaging/lib/examples/payloads.js
index ed486340d..9273f61bd 100644
--- a/messaging/lib/examples/payloads.js
+++ b/messaging/lib/examples/payloads.js
@@ -52,7 +52,7 @@ const messageResponseError = {
  * This is the payload response for a subscriptionEvent
  * @type {SubscriptionEvent}
  */
-const event = {
+const myEvent = {
     context: 'contentScopeScripts',
     featureName: 'duckPlayerOverlays',
     subscriptionName: 'setUserValues',
diff --git a/messaging/lib/test-utils.mjs b/messaging/lib/test-utils.mjs
index 2bc17b3a5..4093bd620 100644
--- a/messaging/lib/test-utils.mjs
+++ b/messaging/lib/test-utils.mjs
@@ -24,9 +24,9 @@ export function mockWindowsMessaging(params) {
         }
     }
     const listeners = []
-    // @ts-ignore
+    // @ts-expect-error mocking is intentional
     window.chrome = {};
-    // @ts-ignore
+    // @ts-expect-error mocking is intentional
     window.chrome.webview = {
         /**
          * @param {AnyWindowsMessage} input
@@ -87,7 +87,6 @@ export function mockWindowsMessaging(params) {
                             result: response,
                             context: msg.context,
                             featureName: msg.featureName,
-                            // @ts-ignore - shane: fix this
                             id: msg.id,
                         },
                     })
@@ -134,7 +133,7 @@ export function mockWebkitMessaging(params) {
                     })))
 
                     // force a 'tick' to allow tests to reset mocks before reading responses
-                    await new Promise(res => setTimeout(res, 0));
+                    await new Promise(resolve => setTimeout(resolve, 0));
 
                     // if it's a notification, simulate the empty response and don't check for a response
                     if (!('id' in msg)) {
@@ -152,7 +151,6 @@ export function mockWebkitMessaging(params) {
                         result: response,
                         context: msg.context,
                         featureName: msg.featureName,
-                        // @ts-ignore - shane: fix this
                         id: msg.id,
                     }
 
@@ -185,6 +183,7 @@ export function mockAndroidMessaging(params) {
          * @param {string} secret
          * @return {Promise<void>}
          */
+        // eslint-disable-next-line require-await
         process: async (jsonString, secret) => {
 
             /** @type {RequestMessage | NotificationMessage} */
@@ -210,11 +209,10 @@ export function mockAndroidMessaging(params) {
                 result: response,
                 context: msg.context,
                 featureName: msg.featureName,
-                // @ts-ignore - shane: fix this
                 id: msg.id,
             }
 
-            globalThis['messageCallback']?.(secret, r);
+            globalThis.messageCallback?.(secret, r);
         }
     }
 }
@@ -260,7 +258,7 @@ export function readOutgoingMessages() {
  * @param {Record<string, any>} replacements
  */
 export function wrapWindowsScripts(js, replacements) {
-    for (let [find, replace] of Object.entries(replacements)) {
+    for (const [find, replace] of Object.entries(replacements)) {
         js = js.replace(find, JSON.stringify(replace));
     }
     return `
@@ -286,7 +284,7 @@ export function wrapWindowsScripts(js, replacements) {
  * @param {Record<string, any>} replacements
  */
 export function wrapWebkitScripts(js, replacements) {
-    for (let [find, replace] of Object.entries(replacements)) {
+    for (const [find, replace] of Object.entries(replacements)) {
         js = js.replace(find, JSON.stringify(replace));
     }
     return js;
@@ -308,7 +306,7 @@ export function simulateSubscriptionMessage(params) {
     }
     switch (params.injectName) {
     case "windows": {
-        // @ts-expect-error
+        // @ts-expect-error DDG custom global
         const fn = window.chrome?.webview?.postMessage || window.windowsInteropPostMessage;
         fn(subscriptionEvent)
         break;
diff --git a/messaging/lib/webkit.js b/messaging/lib/webkit.js
index 73573b0dd..33adeafd7 100644
--- a/messaging/lib/webkit.js
+++ b/messaging/lib/webkit.js
@@ -189,7 +189,7 @@ export class WebkitMessagingTransport {
              * @param {any[]} args
              */
             value: (...args) => {
-                // eslint-disable-next-line n/no-callback-literal
+                 
                 callback(...args)
                 delete this.globals.window[randomMethodName]
             }
diff --git a/messaging/lib/windows.js b/messaging/lib/windows.js
index 0af6fb525..33d7d8944 100644
--- a/messaging/lib/windows.js
+++ b/messaging/lib/windows.js
@@ -169,13 +169,13 @@ export class WindowsMessagingTransport {
         }
 
         // console.log('DEBUG: handler setup', { config, comparator })
-        // eslint-disable-next-line no-undef
+         
         this.config.methods.addEventListener('message', idHandler)
         options?.signal?.addEventListener('abort', abortHandler)
 
         teardown = () => {
             // console.log('DEBUG: handler teardown', { config, comparator })
-            // eslint-disable-next-line no-undef
+             
             this.config.methods.removeEventListener('message', idHandler)
             options?.signal?.removeEventListener('abort', abortHandler)
         }
diff --git a/package-lock.json b/package-lock.json
index 0dc2ab5ca..eae356e9f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,19 +14,17 @@
         "types-generator"
       ],
       "devDependencies": {
+        "@duckduckgo/eslint-config": "github:duckduckgo/eslint-config#51dc868a4342379d6aec1d0f4214156e7046d5f6",
         "@playwright/test": "^1.48.2",
-        "@typescript-eslint/eslint-plugin": "^6.9.1",
-        "eslint": "^8.57.1",
-        "eslint-config-standard": "^17.1.0",
-        "eslint-plugin-import": "^2.31.0",
-        "eslint-plugin-node": "^11.1.0",
-        "eslint-plugin-promise": "^6.1.1",
+        "@types/eslint__js": "^8.42.3",
+        "eslint": "^9.13.0",
         "minimist": "^1.2.8",
         "stylelint": "^15.11.0",
         "stylelint-config-standard": "^34.0.0",
         "stylelint-csstree-validator": "^3.0.0",
         "typedoc": "^0.26.10",
-        "typescript": "^5.6.3"
+        "typescript": "^5.6.3",
+        "typescript-eslint": "^8.12.2"
       }
     },
     "injected": {
@@ -56,6 +54,203 @@
         "rollup-plugin-svg-import": "^3.0.0"
       }
     },
+    "injected/node_modules/@eslint/js": {
+      "version": "8.57.1",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
+      "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "injected/node_modules/@typescript-eslint/eslint-plugin": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
+      "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/regexpp": "^4.5.1",
+        "@typescript-eslint/scope-manager": "6.21.0",
+        "@typescript-eslint/type-utils": "6.21.0",
+        "@typescript-eslint/utils": "6.21.0",
+        "@typescript-eslint/visitor-keys": "6.21.0",
+        "debug": "^4.3.4",
+        "graphemer": "^1.4.0",
+        "ignore": "^5.2.4",
+        "natural-compare": "^1.4.0",
+        "semver": "^7.5.4",
+        "ts-api-utils": "^1.0.1"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
+        "eslint": "^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "injected/node_modules/@typescript-eslint/parser": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
+      "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@typescript-eslint/scope-manager": "6.21.0",
+        "@typescript-eslint/types": "6.21.0",
+        "@typescript-eslint/typescript-estree": "6.21.0",
+        "@typescript-eslint/visitor-keys": "6.21.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "injected/node_modules/@typescript-eslint/type-utils": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz",
+      "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/typescript-estree": "6.21.0",
+        "@typescript-eslint/utils": "6.21.0",
+        "debug": "^4.3.4",
+        "ts-api-utils": "^1.0.1"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "injected/node_modules/@typescript-eslint/utils": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz",
+      "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "@types/json-schema": "^7.0.12",
+        "@types/semver": "^7.5.0",
+        "@typescript-eslint/scope-manager": "6.21.0",
+        "@typescript-eslint/types": "6.21.0",
+        "@typescript-eslint/typescript-estree": "6.21.0",
+        "semver": "^7.5.4"
+      },
+      "engines": {
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^7.0.0 || ^8.0.0"
+      }
+    },
+    "injected/node_modules/eslint": {
+      "version": "8.57.1",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
+      "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
+      "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.2.0",
+        "@eslint-community/regexpp": "^4.6.1",
+        "@eslint/eslintrc": "^2.1.4",
+        "@eslint/js": "8.57.1",
+        "@humanwhocodes/config-array": "^0.13.0",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@nodelib/fs.walk": "^1.2.8",
+        "@ungap/structured-clone": "^1.2.0",
+        "ajv": "^6.12.4",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.3.2",
+        "doctrine": "^3.0.0",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^7.2.2",
+        "eslint-visitor-keys": "^3.4.3",
+        "espree": "^9.6.1",
+        "esquery": "^1.4.2",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "globals": "^13.19.0",
+        "graphemer": "^1.4.0",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "is-path-inside": "^3.0.3",
+        "js-yaml": "^4.1.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3",
+        "strip-ansi": "^6.0.1",
+        "text-table": "^0.2.0"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "injected/node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "peer": true,
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
     "messaging": {
       "name": "@duckduckgo/messaging",
       "version": "1.0.0",
@@ -301,6 +496,22 @@
         "postcss-selector-parser": "^6.0.13"
       }
     },
+    "node_modules/@duckduckgo/eslint-config": {
+      "version": "1.0.0",
+      "resolved": "git+ssh://git@github.com/duckduckgo/eslint-config.git#51dc868a4342379d6aec1d0f4214156e7046d5f6",
+      "integrity": "sha512-4LiMeyUHFI/sBat9IWu0kVE4CbYhVOQ0dSIBETTBxvAOvolVARZZZ7tlsaag6/5EU2gvHwTRLSVTOVUhLdzBRA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@eslint/js": "^9.13.0",
+        "eslint-plugin-import": "^2.31.0",
+        "eslint-plugin-n": "^17.11.1",
+        "eslint-plugin-promise": "^7.1.0"
+      },
+      "peerDependencies": {
+        "eslint": ">= 9"
+      }
+    },
     "node_modules/@duckduckgo/messaging": {
       "resolved": "messaging",
       "link": true
@@ -705,19 +916,43 @@
       }
     },
     "node_modules/@eslint-community/regexpp": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz",
-      "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==",
+      "version": "4.12.1",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+      "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
       "dev": true,
       "engines": {
         "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
       }
     },
+    "node_modules/@eslint/config-array": {
+      "version": "0.18.0",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz",
+      "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==",
+      "dev": true,
+      "dependencies": {
+        "@eslint/object-schema": "^2.1.4",
+        "debug": "^4.3.1",
+        "minimatch": "^3.1.2"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/core": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz",
+      "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==",
+      "dev": true,
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
     "node_modules/@eslint/eslintrc": {
       "version": "2.1.4",
       "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
       "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
       "dev": true,
+      "peer": true,
       "dependencies": {
         "ajv": "^6.12.4",
         "debug": "^4.3.2",
@@ -737,12 +972,34 @@
       }
     },
     "node_modules/@eslint/js": {
-      "version": "8.57.1",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
-      "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+      "version": "9.13.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz",
+      "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/object-schema": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
+      "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
       "dev": true,
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/plugin-kit": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz",
+      "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==",
+      "dev": true,
+      "dependencies": {
+        "levn": "^0.4.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
     },
     "node_modules/@fingerprintjs/fingerprintjs": {
@@ -759,12 +1016,35 @@
       "resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.8.2.tgz",
       "integrity": "sha512-SwPWfeRa5veb1hOIBMdzI+73te5puUBHmqqaF1Bu7FjvxlYSz/kJcZKSa9Cg60zL0uRNeJL2SbRxV6Jp6Q1nFQ=="
     },
+    "node_modules/@humanfs/core": {
+      "version": "0.19.1",
+      "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+      "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+      "dev": true,
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanfs/node": {
+      "version": "0.16.6",
+      "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+      "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+      "dev": true,
+      "dependencies": {
+        "@humanfs/core": "^0.19.1",
+        "@humanwhocodes/retry": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
     "node_modules/@humanwhocodes/config-array": {
       "version": "0.13.0",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
       "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
       "deprecated": "Use @eslint/config-array instead",
       "dev": true,
+      "peer": true,
       "dependencies": {
         "@humanwhocodes/object-schema": "^2.0.3",
         "debug": "^4.3.1",
@@ -792,7 +1072,21 @@
       "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
       "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
       "deprecated": "Use @eslint/object-schema instead",
-      "dev": true
+      "dev": true,
+      "peer": true
+    },
+    "node_modules/@humanwhocodes/retry": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+      "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+      "dev": true,
+      "engines": {
+        "node": ">=18.18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
     },
     "node_modules/@isaacs/cliui": {
       "version": "8.0.2",
@@ -1325,7 +1619,8 @@
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
       "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
-      "dev": true
+      "dev": true,
+      "license": "MIT"
     },
     "node_modules/@shikijs/core": {
       "version": "1.22.0",
@@ -1393,6 +1688,27 @@
         "@types/har-format": "*"
       }
     },
+    "node_modules/@types/eslint": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
+      "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "*",
+        "@types/json-schema": "*"
+      }
+    },
+    "node_modules/@types/eslint__js": {
+      "version": "8.42.3",
+      "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz",
+      "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/eslint": "*"
+      }
+    },
     "node_modules/@types/estree": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
@@ -1446,7 +1762,8 @@
       "version": "0.0.29",
       "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
       "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
-      "dev": true
+      "dev": true,
+      "license": "MIT"
     },
     "node_modules/@types/lodash": {
       "version": "4.17.9",
@@ -1494,9 +1811,9 @@
       "dev": true
     },
     "node_modules/@types/semver": {
-      "version": "7.5.4",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz",
-      "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==",
+      "version": "7.5.8",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+      "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
       "dev": true
     },
     "node_modules/@types/unist": {
@@ -1507,33 +1824,31 @@
       "license": "MIT"
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.1.tgz",
-      "integrity": "sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz",
+      "integrity": "sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==",
       "dev": true,
       "dependencies": {
-        "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "6.9.1",
-        "@typescript-eslint/type-utils": "6.9.1",
-        "@typescript-eslint/utils": "6.9.1",
-        "@typescript-eslint/visitor-keys": "6.9.1",
-        "debug": "^4.3.4",
+        "@eslint-community/regexpp": "^4.10.0",
+        "@typescript-eslint/scope-manager": "8.12.2",
+        "@typescript-eslint/type-utils": "8.12.2",
+        "@typescript-eslint/utils": "8.12.2",
+        "@typescript-eslint/visitor-keys": "8.12.2",
         "graphemer": "^1.4.0",
-        "ignore": "^5.2.4",
+        "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
-        "eslint": "^7.0.0 || ^8.0.0"
+        "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+        "eslint": "^8.57.0 || ^9.0.0"
       },
       "peerDependenciesMeta": {
         "typescript": {
@@ -1542,16 +1857,16 @@
       }
     },
     "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz",
-      "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz",
+      "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.9.1",
-        "@typescript-eslint/visitor-keys": "6.9.1"
+        "@typescript-eslint/types": "8.12.2",
+        "@typescript-eslint/visitor-keys": "8.12.2"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
@@ -1559,12 +1874,12 @@
       }
     },
     "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz",
-      "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz",
+      "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==",
       "dev": true,
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
@@ -1572,16 +1887,16 @@
       }
     },
     "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz",
-      "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz",
+      "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.9.1",
-        "eslint-visitor-keys": "^3.4.1"
+        "@typescript-eslint/types": "8.12.2",
+        "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
@@ -1589,27 +1904,26 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.4.0.tgz",
-      "integrity": "sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.12.2.tgz",
+      "integrity": "sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==",
       "dev": true,
-      "peer": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "6.4.0",
-        "@typescript-eslint/types": "6.4.0",
-        "@typescript-eslint/typescript-estree": "6.4.0",
-        "@typescript-eslint/visitor-keys": "6.4.0",
+        "@typescript-eslint/scope-manager": "8.12.2",
+        "@typescript-eslint/types": "8.12.2",
+        "@typescript-eslint/typescript-estree": "8.12.2",
+        "@typescript-eslint/visitor-keys": "8.12.2",
         "debug": "^4.3.4"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "eslint": "^7.0.0 || ^8.0.0"
+        "eslint": "^8.57.0 || ^9.0.0"
       },
       "peerDependenciesMeta": {
         "typescript": {
@@ -1617,15 +1931,113 @@
         }
       }
     },
+    "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz",
+      "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "8.12.2",
+        "@typescript-eslint/visitor-keys": "8.12.2"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz",
+      "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==",
+      "dev": true,
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz",
+      "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "8.12.2",
+        "@typescript-eslint/visitor-keys": "8.12.2",
+        "debug": "^4.3.4",
+        "fast-glob": "^3.3.2",
+        "is-glob": "^4.0.3",
+        "minimatch": "^9.0.4",
+        "semver": "^7.6.0",
+        "ts-api-utils": "^1.3.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz",
+      "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "8.12.2",
+        "eslint-visitor-keys": "^3.4.3"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/parser/node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz",
-      "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==",
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz",
+      "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==",
       "dev": true,
-      "peer": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.4.0",
-        "@typescript-eslint/visitor-keys": "6.4.0"
+        "@typescript-eslint/types": "6.21.0",
+        "@typescript-eslint/visitor-keys": "6.21.0"
       },
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
@@ -1636,26 +2048,23 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.1.tgz",
-      "integrity": "sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz",
+      "integrity": "sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "6.9.1",
-        "@typescript-eslint/utils": "6.9.1",
+        "@typescript-eslint/typescript-estree": "8.12.2",
+        "@typescript-eslint/utils": "8.12.2",
         "debug": "^4.3.4",
-        "ts-api-utils": "^1.0.1"
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
-      "peerDependencies": {
-        "eslint": "^7.0.0 || ^8.0.0"
-      },
       "peerDependenciesMeta": {
         "typescript": {
           "optional": true
@@ -1663,12 +2072,12 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz",
-      "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz",
+      "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==",
       "dev": true,
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
@@ -1676,21 +2085,22 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz",
-      "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz",
+      "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.9.1",
-        "@typescript-eslint/visitor-keys": "6.9.1",
+        "@typescript-eslint/types": "8.12.2",
+        "@typescript-eslint/visitor-keys": "8.12.2",
         "debug": "^4.3.4",
-        "globby": "^11.1.0",
+        "fast-glob": "^3.3.2",
         "is-glob": "^4.0.3",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
+        "minimatch": "^9.0.4",
+        "semver": "^7.6.0",
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
@@ -1703,28 +2113,51 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz",
-      "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz",
+      "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.9.1",
-        "eslint-visitor-keys": "^3.4.1"
+        "@typescript-eslint/types": "8.12.2",
+        "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       }
     },
+    "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/@typescript-eslint/types": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz",
-      "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==",
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz",
+      "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==",
       "dev": true,
-      "peer": true,
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
       },
@@ -1734,17 +2167,17 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz",
-      "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==",
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz",
+      "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==",
       "dev": true,
-      "peer": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.4.0",
-        "@typescript-eslint/visitor-keys": "6.4.0",
+        "@typescript-eslint/types": "6.21.0",
+        "@typescript-eslint/visitor-keys": "6.21.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
+        "minimatch": "9.0.3",
         "semver": "^7.5.4",
         "ts-api-utils": "^1.0.1"
       },
@@ -1761,42 +2194,63 @@
         }
       }
     },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/@typescript-eslint/utils": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.1.tgz",
-      "integrity": "sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.12.2.tgz",
+      "integrity": "sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@types/json-schema": "^7.0.12",
-        "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "6.9.1",
-        "@typescript-eslint/types": "6.9.1",
-        "@typescript-eslint/typescript-estree": "6.9.1",
-        "semver": "^7.5.4"
+        "@typescript-eslint/scope-manager": "8.12.2",
+        "@typescript-eslint/types": "8.12.2",
+        "@typescript-eslint/typescript-estree": "8.12.2"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       },
       "peerDependencies": {
-        "eslint": "^7.0.0 || ^8.0.0"
+        "eslint": "^8.57.0 || ^9.0.0"
       }
     },
     "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz",
-      "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz",
+      "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.9.1",
-        "@typescript-eslint/visitor-keys": "6.9.1"
+        "@typescript-eslint/types": "8.12.2",
+        "@typescript-eslint/visitor-keys": "8.12.2"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
@@ -1804,12 +2258,12 @@
       }
     },
     "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz",
-      "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz",
+      "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==",
       "dev": true,
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
@@ -1817,21 +2271,22 @@
       }
     },
     "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz",
-      "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz",
+      "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.9.1",
-        "@typescript-eslint/visitor-keys": "6.9.1",
+        "@typescript-eslint/types": "8.12.2",
+        "@typescript-eslint/visitor-keys": "8.12.2",
         "debug": "^4.3.4",
-        "globby": "^11.1.0",
+        "fast-glob": "^3.3.2",
         "is-glob": "^4.0.3",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
+        "minimatch": "^9.0.4",
+        "semver": "^7.6.0",
+        "ts-api-utils": "^1.3.0"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
@@ -1844,30 +2299,53 @@
       }
     },
     "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz",
-      "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz",
+      "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.9.1",
-        "eslint-visitor-keys": "^3.4.1"
+        "@typescript-eslint/types": "8.12.2",
+        "eslint-visitor-keys": "^3.4.3"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/typescript-eslint"
       }
     },
+    "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/utils/node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz",
-      "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==",
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz",
+      "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==",
       "dev": true,
-      "peer": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.4.0",
+        "@typescript-eslint/types": "6.21.0",
         "eslint-visitor-keys": "^3.4.1"
       },
       "engines": {
@@ -1885,9 +2363,9 @@
       "dev": true
     },
     "node_modules/acorn": {
-      "version": "8.12.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
-      "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
+      "version": "8.14.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+      "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
       "dev": true,
       "bin": {
         "acorn": "bin/acorn"
@@ -1965,6 +2443,7 @@
       "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
       "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.5",
         "is-array-buffer": "^3.0.4"
@@ -1981,6 +2460,7 @@
       "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz",
       "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
@@ -2010,6 +2490,7 @@
       "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz",
       "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
@@ -2030,6 +2511,7 @@
       "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
       "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.2.0",
@@ -2048,6 +2530,7 @@
       "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz",
       "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.2.0",
@@ -2066,6 +2549,7 @@
       "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz",
       "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "array-buffer-byte-length": "^1.0.1",
         "call-bind": "^1.0.5",
@@ -2117,6 +2601,7 @@
       "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
       "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "possible-typed-array-names": "^1.0.0"
       },
@@ -2173,16 +2658,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/builtins": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz",
-      "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==",
-      "dev": true,
-      "peer": true,
-      "dependencies": {
-        "semver": "^7.0.0"
-      }
-    },
     "node_modules/call-bind": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
@@ -2487,6 +2962,7 @@
       "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
       "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.6",
         "es-errors": "^1.3.0",
@@ -2504,6 +2980,7 @@
       "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz",
       "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "es-errors": "^1.3.0",
@@ -2521,6 +2998,7 @@
       "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz",
       "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.6",
         "es-errors": "^1.3.0",
@@ -2637,6 +3115,7 @@
       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
       "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "define-data-property": "^1.0.1",
         "has-property-descriptors": "^1.0.0",
@@ -2690,6 +3169,7 @@
       "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
       "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
       "dev": true,
+      "peer": true,
       "dependencies": {
         "esutils": "^2.0.2"
       },
@@ -2794,6 +3274,20 @@
       "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
       "dev": true
     },
+    "node_modules/enhanced-resolve": {
+      "version": "5.17.1",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
+      "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
     "node_modules/entities": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
@@ -2818,6 +3312,7 @@
       "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
       "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "array-buffer-byte-length": "^1.0.1",
         "arraybuffer.prototype.slice": "^1.0.3",
@@ -2899,6 +3394,7 @@
       "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
       "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "es-errors": "^1.3.0"
       },
@@ -2911,6 +3407,7 @@
       "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
       "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "get-intrinsic": "^1.2.4",
         "has-tostringtag": "^1.0.2",
@@ -2925,6 +3422,7 @@
       "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz",
       "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "hasown": "^2.0.0"
       }
@@ -2934,6 +3432,7 @@
       "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
       "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "is-callable": "^1.1.4",
         "is-date-object": "^1.0.1",
@@ -3010,87 +3509,80 @@
       }
     },
     "node_modules/eslint": {
-      "version": "8.57.1",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
-      "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
+      "version": "9.13.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz",
+      "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.2.0",
-        "@eslint-community/regexpp": "^4.6.1",
-        "@eslint/eslintrc": "^2.1.4",
-        "@eslint/js": "8.57.1",
-        "@humanwhocodes/config-array": "^0.13.0",
+        "@eslint-community/regexpp": "^4.11.0",
+        "@eslint/config-array": "^0.18.0",
+        "@eslint/core": "^0.7.0",
+        "@eslint/eslintrc": "^3.1.0",
+        "@eslint/js": "9.13.0",
+        "@eslint/plugin-kit": "^0.2.0",
+        "@humanfs/node": "^0.16.5",
         "@humanwhocodes/module-importer": "^1.0.1",
-        "@nodelib/fs.walk": "^1.2.8",
-        "@ungap/structured-clone": "^1.2.0",
+        "@humanwhocodes/retry": "^0.3.1",
+        "@types/estree": "^1.0.6",
+        "@types/json-schema": "^7.0.15",
         "ajv": "^6.12.4",
         "chalk": "^4.0.0",
         "cross-spawn": "^7.0.2",
         "debug": "^4.3.2",
-        "doctrine": "^3.0.0",
         "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^7.2.2",
-        "eslint-visitor-keys": "^3.4.3",
-        "espree": "^9.6.1",
-        "esquery": "^1.4.2",
+        "eslint-scope": "^8.1.0",
+        "eslint-visitor-keys": "^4.1.0",
+        "espree": "^10.2.0",
+        "esquery": "^1.5.0",
         "esutils": "^2.0.2",
         "fast-deep-equal": "^3.1.3",
-        "file-entry-cache": "^6.0.1",
+        "file-entry-cache": "^8.0.0",
         "find-up": "^5.0.0",
         "glob-parent": "^6.0.2",
-        "globals": "^13.19.0",
-        "graphemer": "^1.4.0",
         "ignore": "^5.2.0",
         "imurmurhash": "^0.1.4",
         "is-glob": "^4.0.0",
-        "is-path-inside": "^3.0.3",
-        "js-yaml": "^4.1.0",
         "json-stable-stringify-without-jsonify": "^1.0.1",
-        "levn": "^0.4.1",
         "lodash.merge": "^4.6.2",
         "minimatch": "^3.1.2",
         "natural-compare": "^1.4.0",
         "optionator": "^0.9.3",
-        "strip-ansi": "^6.0.1",
         "text-table": "^0.2.0"
       },
       "bin": {
         "eslint": "bin/eslint.js"
       },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
-        "url": "https://opencollective.com/eslint"
+        "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "jiti": "*"
+      },
+      "peerDependenciesMeta": {
+        "jiti": {
+          "optional": true
+        }
       }
     },
-    "node_modules/eslint-config-standard": {
-      "version": "17.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz",
-      "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==",
+    "node_modules/eslint-compat-utils": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz",
+      "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==",
       "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.5.4"
+      },
       "engines": {
-        "node": ">=12.0.0"
+        "node": ">=12"
       },
       "peerDependencies": {
-        "eslint": "^8.0.1",
-        "eslint-plugin-import": "^2.25.2",
-        "eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
-        "eslint-plugin-promise": "^6.0.0"
+        "eslint": ">=6.0.0"
       }
     },
     "node_modules/eslint-import-resolver-node": {
@@ -3098,6 +3590,7 @@
       "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
       "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "debug": "^3.2.7",
         "is-core-module": "^2.13.0",
@@ -3109,6 +3602,7 @@
       "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
       "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "ms": "^2.1.1"
       }
@@ -3118,6 +3612,7 @@
       "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz",
       "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "debug": "^3.2.7"
       },
@@ -3135,27 +3630,31 @@
       "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
       "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "ms": "^2.1.1"
       }
     },
-    "node_modules/eslint-plugin-es": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz",
-      "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==",
+    "node_modules/eslint-plugin-es-x": {
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz",
+      "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==",
       "dev": true,
+      "funding": [
+        "https://github.com/sponsors/ota-meshi",
+        "https://opencollective.com/eslint"
+      ],
+      "license": "MIT",
       "dependencies": {
-        "eslint-utils": "^2.0.0",
-        "regexpp": "^3.0.0"
+        "@eslint-community/eslint-utils": "^4.1.2",
+        "@eslint-community/regexpp": "^4.11.0",
+        "eslint-compat-utils": "^0.5.1"
       },
       "engines": {
-        "node": ">=8.10.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
+        "node": "^14.18.0 || >=16.0.0"
       },
       "peerDependencies": {
-        "eslint": ">=4.19.1"
+        "eslint": ">=8"
       }
     },
     "node_modules/eslint-plugin-import": {
@@ -3163,6 +3662,7 @@
       "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz",
       "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "@rtsao/scc": "^1.1.0",
         "array-includes": "^3.1.8",
@@ -3196,6 +3696,7 @@
       "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
       "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "ms": "^2.1.1"
       }
@@ -3205,6 +3706,7 @@
       "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
       "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
       "dev": true,
+      "license": "Apache-2.0",
       "dependencies": {
         "esutils": "^2.0.2"
       },
@@ -3217,202 +3719,212 @@
       "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
       "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
       "dev": true,
+      "license": "ISC",
       "bin": {
         "semver": "bin/semver.js"
       }
     },
     "node_modules/eslint-plugin-n": {
-      "version": "15.6.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.6.1.tgz",
-      "integrity": "sha512-R9xw9OtCRxxaxaszTQmQAlPgM+RdGjaL1akWuY/Fv9fRAi8Wj4CUKc6iYVG8QNRjRuo8/BqVYIpfqberJUEacA==",
+      "version": "17.12.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.12.0.tgz",
+      "integrity": "sha512-zNAtz/erDn0v78bIY3MASSQlyaarV4IOTvP5ldHsqblRFrXriikB6ghkDTkHjUad+nMRrIbOy9euod2azjRfBg==",
       "dev": true,
-      "peer": true,
+      "license": "MIT",
       "dependencies": {
-        "builtins": "^5.0.1",
-        "eslint-plugin-es": "^4.1.0",
-        "eslint-utils": "^3.0.0",
-        "ignore": "^5.1.1",
-        "is-core-module": "^2.11.0",
-        "minimatch": "^3.1.2",
-        "resolve": "^1.22.1",
-        "semver": "^7.3.8"
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "enhanced-resolve": "^5.17.1",
+        "eslint-plugin-es-x": "^7.8.0",
+        "get-tsconfig": "^4.8.1",
+        "globals": "^15.11.0",
+        "ignore": "^5.3.2",
+        "minimatch": "^9.0.5",
+        "semver": "^7.6.3"
       },
       "engines": {
-        "node": ">=12.22.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
+        "url": "https://opencollective.com/eslint"
       },
       "peerDependencies": {
-        "eslint": ">=7.0.0"
+        "eslint": ">=8.23.0"
       }
     },
-    "node_modules/eslint-plugin-n/node_modules/eslint-plugin-es": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz",
-      "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==",
+    "node_modules/eslint-plugin-n/node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
       "dev": true,
-      "peer": true,
+      "license": "MIT",
       "dependencies": {
-        "eslint-utils": "^2.0.0",
-        "regexpp": "^3.0.0"
-      },
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-n/node_modules/globals": {
+      "version": "15.11.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz",
+      "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==",
+      "dev": true,
+      "license": "MIT",
       "engines": {
-        "node": ">=8.10.0"
+        "node": ">=18"
       },
       "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
-      },
-      "peerDependencies": {
-        "eslint": ">=4.19.1"
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/eslint-plugin-n/node_modules/eslint-plugin-es/node_modules/eslint-utils": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
-      "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+    "node_modules/eslint-plugin-n/node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
       "dev": true,
-      "peer": true,
+      "license": "ISC",
       "dependencies": {
-        "eslint-visitor-keys": "^1.1.0"
+        "brace-expansion": "^2.0.1"
       },
       "engines": {
-        "node": ">=6"
+        "node": ">=16 || 14 >=14.17"
       },
       "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/eslint-plugin-n/node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
-      "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+    "node_modules/eslint-plugin-promise": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.1.0.tgz",
+      "integrity": "sha512-8trNmPxdAy3W620WKDpaS65NlM5yAumod6XeC4LOb+jxlkG4IVcp68c6dXY2ev+uT4U1PtG57YDV6EGAXN0GbQ==",
       "dev": true,
-      "peer": true,
+      "license": "ISC",
       "engines": {
-        "node": ">=4"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0"
       }
     },
-    "node_modules/eslint-plugin-n/node_modules/eslint-utils": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
-      "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+    "node_modules/eslint-scope": {
+      "version": "7.2.2",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+      "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
       "dev": true,
       "peer": true,
       "dependencies": {
-        "eslint-visitor-keys": "^2.0.0"
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
       },
       "engines": {
-        "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       },
       "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
-      },
-      "peerDependencies": {
-        "eslint": ">=5"
+        "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/eslint-plugin-n/node_modules/eslint-visitor-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
-      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+    "node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
       "dev": true,
-      "peer": true,
       "engines": {
-        "node": ">=10"
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/eslint-plugin-node": {
-      "version": "11.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
-      "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==",
+    "node_modules/eslint/node_modules/@eslint/eslintrc": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
+      "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
       "dev": true,
       "dependencies": {
-        "eslint-plugin-es": "^3.0.0",
-        "eslint-utils": "^2.0.0",
-        "ignore": "^5.1.1",
-        "minimatch": "^3.0.4",
-        "resolve": "^1.10.1",
-        "semver": "^6.1.0"
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^10.0.1",
+        "globals": "^14.0.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
       },
       "engines": {
-        "node": ">=8.10.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
-      "peerDependencies": {
-        "eslint": ">=5.16.0"
-      }
-    },
-    "node_modules/eslint-plugin-node/node_modules/semver": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver.js"
+      "funding": {
+        "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/eslint-plugin-promise": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz",
-      "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==",
+    "node_modules/eslint/node_modules/eslint-scope": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
+      "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
       "dev": true,
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
-      "peerDependencies": {
-        "eslint": "^7.0.0 || ^8.0.0"
+      "funding": {
+        "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/eslint-scope": {
-      "version": "7.2.2",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
-      "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+    "node_modules/eslint/node_modules/eslint-visitor-keys": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+      "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
       "dev": true,
-      "dependencies": {
-        "esrecurse": "^4.3.0",
-        "estraverse": "^5.2.0"
-      },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
         "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/eslint-utils": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
-      "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+    "node_modules/eslint/node_modules/espree": {
+      "version": "10.3.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+      "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
       "dev": true,
       "dependencies": {
-        "eslint-visitor-keys": "^1.1.0"
+        "acorn": "^8.14.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^4.2.0"
       },
       "engines": {
-        "node": ">=6"
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
       "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
+        "url": "https://opencollective.com/eslint"
       }
     },
-    "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
-      "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+    "node_modules/eslint/node_modules/file-entry-cache": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+      "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
       "dev": true,
+      "dependencies": {
+        "flat-cache": "^4.0.0"
+      },
       "engines": {
-        "node": ">=4"
+        "node": ">=16.0.0"
       }
     },
-    "node_modules/eslint-visitor-keys": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
-      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+    "node_modules/eslint/node_modules/flat-cache": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+      "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
       "dev": true,
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      "dependencies": {
+        "flatted": "^3.2.9",
+        "keyv": "^4.5.4"
       },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
+      "engines": {
+        "node": ">=16"
       }
     },
     "node_modules/eslint/node_modules/glob-parent": {
@@ -3427,11 +3939,24 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/eslint/node_modules/globals": {
+      "version": "14.0.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/espree": {
       "version": "9.6.1",
       "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
       "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
       "dev": true,
+      "peer": true,
       "dependencies": {
         "acorn": "^8.9.0",
         "acorn-jsx": "^5.3.2",
@@ -3445,9 +3970,9 @@
       }
     },
     "node_modules/esquery": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz",
-      "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+      "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
       "dev": true,
       "dependencies": {
         "estraverse": "^5.1.0"
@@ -3527,9 +4052,9 @@
       "dev": true
     },
     "node_modules/fast-glob": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
-      "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+      "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
       "dev": true,
       "dependencies": {
         "@nodelib/fs.stat": "^2.0.2",
@@ -3608,6 +4133,7 @@
       "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
       "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
       "dev": true,
+      "peer": true,
       "dependencies": {
         "flat-cache": "^3.0.4"
       },
@@ -3691,6 +4217,7 @@
       "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
       "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "is-callable": "^1.1.3"
       }
@@ -3757,6 +4284,7 @@
       "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz",
       "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.2.0",
@@ -3775,6 +4303,7 @@
       "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
       "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
       "dev": true,
+      "license": "MIT",
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -3803,6 +4332,7 @@
       "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
       "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.5",
         "es-errors": "^1.3.0",
@@ -3815,6 +4345,19 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/get-tsconfig": {
+      "version": "4.8.1",
+      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz",
+      "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "resolve-pkg-maps": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+      }
+    },
     "node_modules/glob": {
       "version": "7.2.3",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -3893,6 +4436,7 @@
       "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
       "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
       "dev": true,
+      "peer": true,
       "dependencies": {
         "type-fest": "^0.20.2"
       },
@@ -3904,12 +4448,14 @@
       }
     },
     "node_modules/globalthis": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
-      "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+      "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "define-properties": "^1.1.3"
+        "define-properties": "^1.2.1",
+        "gopd": "^1.0.1"
       },
       "engines": {
         "node": ">= 0.4"
@@ -3957,6 +4503,13 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true,
+      "license": "ISC"
+    },
     "node_modules/graphemer": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
@@ -3978,6 +4531,7 @@
       "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
       "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
       "dev": true,
+      "license": "MIT",
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -4032,6 +4586,7 @@
       "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
       "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "has-symbols": "^1.0.3"
       },
@@ -4219,9 +4774,9 @@
       }
     },
     "node_modules/ignore": {
-      "version": "5.2.4",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
-      "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+      "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
       "dev": true,
       "engines": {
         "node": ">= 4"
@@ -4312,6 +4867,7 @@
       "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
       "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "es-errors": "^1.3.0",
         "hasown": "^2.0.0",
@@ -4326,6 +4882,7 @@
       "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
       "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.2",
         "get-intrinsic": "^1.2.1"
@@ -4349,6 +4906,7 @@
       "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
       "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "has-bigints": "^1.0.1"
       },
@@ -4361,6 +4919,7 @@
       "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
       "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.2",
         "has-tostringtag": "^1.0.0"
@@ -4377,6 +4936,7 @@
       "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
       "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">= 0.4"
       },
@@ -4404,6 +4964,7 @@
       "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz",
       "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "is-typed-array": "^1.1.13"
       },
@@ -4419,6 +4980,7 @@
       "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
       "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "has-tostringtag": "^1.0.0"
       },
@@ -4470,6 +5032,7 @@
       "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
       "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">= 0.4"
       },
@@ -4491,6 +5054,7 @@
       "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
       "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "has-tostringtag": "^1.0.0"
       },
@@ -4506,6 +5070,7 @@
       "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
       "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
       "dev": true,
+      "peer": true,
       "engines": {
         "node": ">=8"
       }
@@ -4544,6 +5109,7 @@
       "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
       "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.2",
         "has-tostringtag": "^1.0.0"
@@ -4560,6 +5126,7 @@
       "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz",
       "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7"
       },
@@ -4575,6 +5142,7 @@
       "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
       "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "has-tostringtag": "^1.0.0"
       },
@@ -4590,6 +5158,7 @@
       "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
       "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "has-symbols": "^1.0.2"
       },
@@ -4605,6 +5174,7 @@
       "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
       "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "which-typed-array": "^1.1.14"
       },
@@ -4620,6 +5190,7 @@
       "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
       "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.2"
       },
@@ -4631,7 +5202,8 @@
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
       "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
-      "dev": true
+      "dev": true,
+      "license": "MIT"
     },
     "node_modules/isexe": {
       "version": "2.0.0",
@@ -4834,6 +5406,7 @@
       "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
       "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "minimist": "^1.2.0"
       },
@@ -5396,6 +5969,7 @@
       "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
       "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">= 0.4"
       }
@@ -5405,6 +5979,7 @@
       "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz",
       "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.5",
         "define-properties": "^1.2.1",
@@ -5423,6 +5998,7 @@
       "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
       "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
@@ -5441,6 +6017,7 @@
       "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
       "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
@@ -5455,6 +6032,7 @@
       "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz",
       "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
@@ -5736,6 +6314,7 @@
       "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
       "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">= 0.4"
       }
@@ -6033,6 +6612,7 @@
       "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz",
       "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
@@ -6046,18 +6626,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/regexpp": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
-      "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/mysticatea"
-      }
-    },
     "node_modules/require-from-string": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@@ -6100,6 +6668,16 @@
         "node": ">=4"
       }
     },
+    "node_modules/resolve-pkg-maps": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+      }
+    },
     "node_modules/reusify": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -6220,6 +6798,7 @@
       "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
       "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "get-intrinsic": "^1.2.4",
@@ -6238,6 +6817,7 @@
       "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
       "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.6",
         "es-errors": "^1.3.0",
@@ -6268,13 +6848,11 @@
       "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="
     },
     "node_modules/semver": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
-      "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+      "version": "7.6.3",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+      "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
       "dev": true,
-      "dependencies": {
-        "lru-cache": "^6.0.0"
-      },
+      "license": "ISC",
       "bin": {
         "semver": "bin/semver.js"
       },
@@ -6304,6 +6882,7 @@
       "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
       "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "define-data-property": "^1.1.4",
         "es-errors": "^1.3.0",
@@ -6506,6 +7085,7 @@
       "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz",
       "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
@@ -6524,6 +7104,7 @@
       "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz",
       "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
@@ -6538,6 +7119,7 @@
       "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
       "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "define-properties": "^1.2.1",
@@ -6595,6 +7177,7 @@
       "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
       "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">=4"
       }
@@ -6857,6 +7440,16 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/tapable": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+      "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -6926,12 +7519,12 @@
       }
     },
     "node_modules/ts-api-utils": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz",
-      "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+      "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
       "dev": true,
       "engines": {
-        "node": ">=16.13.0"
+        "node": ">=16"
       },
       "peerDependencies": {
         "typescript": ">=4.2.0"
@@ -6942,6 +7535,7 @@
       "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
       "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "@types/json5": "^0.0.29",
         "json5": "^1.0.2",
@@ -6972,6 +7566,7 @@
       "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
       "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
       "dev": true,
+      "peer": true,
       "engines": {
         "node": ">=10"
       },
@@ -6984,6 +7579,7 @@
       "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz",
       "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "es-errors": "^1.3.0",
@@ -6998,6 +7594,7 @@
       "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz",
       "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "for-each": "^0.3.3",
@@ -7017,6 +7614,7 @@
       "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz",
       "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "available-typed-arrays": "^1.0.7",
         "call-bind": "^1.0.7",
@@ -7037,6 +7635,7 @@
       "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz",
       "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.7",
         "for-each": "^0.3.3",
@@ -7119,6 +7718,30 @@
         "node": ">=14.17"
       }
     },
+    "node_modules/typescript-eslint": {
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.12.2.tgz",
+      "integrity": "sha512-UbuVUWSrHVR03q9CWx+JDHeO6B/Hr9p4U5lRH++5tq/EbFq1faYZe50ZSBePptgfIKLEti0aPQ3hFgnPVcd8ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/eslint-plugin": "8.12.2",
+        "@typescript-eslint/parser": "8.12.2",
+        "@typescript-eslint/utils": "8.12.2"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/uc.micro": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
@@ -7131,6 +7754,7 @@
       "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
       "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "call-bind": "^1.0.2",
         "has-bigints": "^1.0.2",
@@ -7391,6 +8015,7 @@
       "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
       "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "is-bigint": "^1.0.1",
         "is-boolean-object": "^1.1.0",
@@ -7407,6 +8032,7 @@
       "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
       "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "available-typed-arrays": "^1.0.7",
         "call-bind": "^1.0.7",
@@ -7702,6 +8328,18 @@
       "dev": true,
       "requires": {}
     },
+    "@duckduckgo/eslint-config": {
+      "version": "git+ssh://git@github.com/duckduckgo/eslint-config.git#51dc868a4342379d6aec1d0f4214156e7046d5f6",
+      "integrity": "sha512-4LiMeyUHFI/sBat9IWu0kVE4CbYhVOQ0dSIBETTBxvAOvolVARZZZ7tlsaag6/5EU2gvHwTRLSVTOVUhLdzBRA==",
+      "dev": true,
+      "from": "@duckduckgo/eslint-config@github:duckduckgo/eslint-config#51dc868a4342379d6aec1d0f4214156e7046d5f6",
+      "requires": {
+        "@eslint/js": "^9.13.0",
+        "eslint-plugin-import": "^2.31.0",
+        "eslint-plugin-n": "^17.11.1",
+        "eslint-plugin-promise": "^7.1.0"
+      }
+    },
     "@duckduckgo/messaging": {
       "version": "file:messaging"
     },
@@ -7883,9 +8521,26 @@
       }
     },
     "@eslint-community/regexpp": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz",
-      "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==",
+      "version": "4.12.1",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+      "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+      "dev": true
+    },
+    "@eslint/config-array": {
+      "version": "0.18.0",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz",
+      "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==",
+      "dev": true,
+      "requires": {
+        "@eslint/object-schema": "^2.1.4",
+        "debug": "^4.3.1",
+        "minimatch": "^3.1.2"
+      }
+    },
+    "@eslint/core": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz",
+      "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==",
       "dev": true
     },
     "@eslint/eslintrc": {
@@ -7893,6 +8548,7 @@
       "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
       "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
       "dev": true,
+      "peer": true,
       "requires": {
         "ajv": "^6.12.4",
         "debug": "^4.3.2",
@@ -7906,11 +8562,26 @@
       }
     },
     "@eslint/js": {
-      "version": "8.57.1",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
-      "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+      "version": "9.13.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz",
+      "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==",
+      "dev": true
+    },
+    "@eslint/object-schema": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
+      "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
       "dev": true
     },
+    "@eslint/plugin-kit": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz",
+      "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==",
+      "dev": true,
+      "requires": {
+        "levn": "^0.4.1"
+      }
+    },
     "@fingerprintjs/fingerprintjs": {
       "version": "4.5.1",
       "resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs/-/fingerprintjs-4.5.1.tgz",
@@ -7925,11 +8596,28 @@
       "resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.8.2.tgz",
       "integrity": "sha512-SwPWfeRa5veb1hOIBMdzI+73te5puUBHmqqaF1Bu7FjvxlYSz/kJcZKSa9Cg60zL0uRNeJL2SbRxV6Jp6Q1nFQ=="
     },
+    "@humanfs/core": {
+      "version": "0.19.1",
+      "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+      "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+      "dev": true
+    },
+    "@humanfs/node": {
+      "version": "0.16.6",
+      "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+      "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+      "dev": true,
+      "requires": {
+        "@humanfs/core": "^0.19.1",
+        "@humanwhocodes/retry": "^0.3.0"
+      }
+    },
     "@humanwhocodes/config-array": {
       "version": "0.13.0",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
       "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
       "dev": true,
+      "peer": true,
       "requires": {
         "@humanwhocodes/object-schema": "^2.0.3",
         "debug": "^4.3.1",
@@ -7946,6 +8634,13 @@
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
       "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+      "dev": true,
+      "peer": true
+    },
+    "@humanwhocodes/retry": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+      "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
       "dev": true
     },
     "@isaacs/cliui": {
@@ -8329,6 +9024,25 @@
         "@types/har-format": "*"
       }
     },
+    "@types/eslint": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
+      "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
+      "dev": true,
+      "requires": {
+        "@types/estree": "*",
+        "@types/json-schema": "*"
+      }
+    },
+    "@types/eslint__js": {
+      "version": "8.42.3",
+      "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz",
+      "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==",
+      "dev": true,
+      "requires": {
+        "@types/eslint": "*"
+      }
+    },
     "@types/estree": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
@@ -8426,9 +9140,9 @@
       "dev": true
     },
     "@types/semver": {
-      "version": "7.5.4",
-      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz",
-      "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==",
+      "version": "7.5.8",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+      "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
       "dev": true
     },
     "@types/unist": {
@@ -8438,211 +9152,322 @@
       "dev": true
     },
     "@typescript-eslint/eslint-plugin": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.1.tgz",
-      "integrity": "sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz",
+      "integrity": "sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==",
       "dev": true,
       "requires": {
-        "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "6.9.1",
-        "@typescript-eslint/type-utils": "6.9.1",
-        "@typescript-eslint/utils": "6.9.1",
-        "@typescript-eslint/visitor-keys": "6.9.1",
-        "debug": "^4.3.4",
+        "@eslint-community/regexpp": "^4.10.0",
+        "@typescript-eslint/scope-manager": "8.12.2",
+        "@typescript-eslint/type-utils": "8.12.2",
+        "@typescript-eslint/utils": "8.12.2",
+        "@typescript-eslint/visitor-keys": "8.12.2",
         "graphemer": "^1.4.0",
-        "ignore": "^5.2.4",
+        "ignore": "^5.3.1",
         "natural-compare": "^1.4.0",
-        "semver": "^7.5.4",
-        "ts-api-utils": "^1.0.1"
+        "ts-api-utils": "^1.3.0"
       },
       "dependencies": {
         "@typescript-eslint/scope-manager": {
-          "version": "6.9.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz",
-          "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==",
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz",
+          "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==",
           "dev": true,
           "requires": {
-            "@typescript-eslint/types": "6.9.1",
-            "@typescript-eslint/visitor-keys": "6.9.1"
+            "@typescript-eslint/types": "8.12.2",
+            "@typescript-eslint/visitor-keys": "8.12.2"
           }
         },
         "@typescript-eslint/types": {
-          "version": "6.9.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz",
-          "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==",
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz",
+          "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==",
           "dev": true
         },
         "@typescript-eslint/visitor-keys": {
-          "version": "6.9.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz",
-          "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==",
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz",
+          "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==",
           "dev": true,
           "requires": {
-            "@typescript-eslint/types": "6.9.1",
-            "eslint-visitor-keys": "^3.4.1"
+            "@typescript-eslint/types": "8.12.2",
+            "eslint-visitor-keys": "^3.4.3"
           }
         }
       }
     },
     "@typescript-eslint/parser": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.4.0.tgz",
-      "integrity": "sha512-I1Ah1irl033uxjxO9Xql7+biL3YD7w9IU8zF+xlzD/YxY6a4b7DYA08PXUUCbm2sEljwJF6ERFy2kTGAGcNilg==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.12.2.tgz",
+      "integrity": "sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==",
       "dev": true,
-      "peer": true,
       "requires": {
-        "@typescript-eslint/scope-manager": "6.4.0",
-        "@typescript-eslint/types": "6.4.0",
-        "@typescript-eslint/typescript-estree": "6.4.0",
-        "@typescript-eslint/visitor-keys": "6.4.0",
+        "@typescript-eslint/scope-manager": "8.12.2",
+        "@typescript-eslint/types": "8.12.2",
+        "@typescript-eslint/typescript-estree": "8.12.2",
+        "@typescript-eslint/visitor-keys": "8.12.2",
         "debug": "^4.3.4"
+      },
+      "dependencies": {
+        "@typescript-eslint/scope-manager": {
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz",
+          "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==",
+          "dev": true,
+          "requires": {
+            "@typescript-eslint/types": "8.12.2",
+            "@typescript-eslint/visitor-keys": "8.12.2"
+          }
+        },
+        "@typescript-eslint/types": {
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz",
+          "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==",
+          "dev": true
+        },
+        "@typescript-eslint/typescript-estree": {
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz",
+          "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==",
+          "dev": true,
+          "requires": {
+            "@typescript-eslint/types": "8.12.2",
+            "@typescript-eslint/visitor-keys": "8.12.2",
+            "debug": "^4.3.4",
+            "fast-glob": "^3.3.2",
+            "is-glob": "^4.0.3",
+            "minimatch": "^9.0.4",
+            "semver": "^7.6.0",
+            "ts-api-utils": "^1.3.0"
+          }
+        },
+        "@typescript-eslint/visitor-keys": {
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz",
+          "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==",
+          "dev": true,
+          "requires": {
+            "@typescript-eslint/types": "8.12.2",
+            "eslint-visitor-keys": "^3.4.3"
+          }
+        },
+        "brace-expansion": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+          "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0"
+          }
+        },
+        "minimatch": {
+          "version": "9.0.5",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+          "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^2.0.1"
+          }
+        }
       }
     },
     "@typescript-eslint/scope-manager": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz",
-      "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==",
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz",
+      "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==",
       "dev": true,
-      "peer": true,
       "requires": {
-        "@typescript-eslint/types": "6.4.0",
-        "@typescript-eslint/visitor-keys": "6.4.0"
+        "@typescript-eslint/types": "6.21.0",
+        "@typescript-eslint/visitor-keys": "6.21.0"
       }
     },
     "@typescript-eslint/type-utils": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.1.tgz",
-      "integrity": "sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz",
+      "integrity": "sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/typescript-estree": "6.9.1",
-        "@typescript-eslint/utils": "6.9.1",
+        "@typescript-eslint/typescript-estree": "8.12.2",
+        "@typescript-eslint/utils": "8.12.2",
         "debug": "^4.3.4",
-        "ts-api-utils": "^1.0.1"
+        "ts-api-utils": "^1.3.0"
       },
       "dependencies": {
         "@typescript-eslint/types": {
-          "version": "6.9.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz",
-          "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==",
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz",
+          "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==",
           "dev": true
         },
         "@typescript-eslint/typescript-estree": {
-          "version": "6.9.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz",
-          "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==",
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz",
+          "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==",
+          "dev": true,
+          "requires": {
+            "@typescript-eslint/types": "8.12.2",
+            "@typescript-eslint/visitor-keys": "8.12.2",
+            "debug": "^4.3.4",
+            "fast-glob": "^3.3.2",
+            "is-glob": "^4.0.3",
+            "minimatch": "^9.0.4",
+            "semver": "^7.6.0",
+            "ts-api-utils": "^1.3.0"
+          }
+        },
+        "@typescript-eslint/visitor-keys": {
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz",
+          "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==",
+          "dev": true,
+          "requires": {
+            "@typescript-eslint/types": "8.12.2",
+            "eslint-visitor-keys": "^3.4.3"
+          }
+        },
+        "brace-expansion": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+          "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
           "dev": true,
           "requires": {
-            "@typescript-eslint/types": "6.9.1",
-            "@typescript-eslint/visitor-keys": "6.9.1",
-            "debug": "^4.3.4",
-            "globby": "^11.1.0",
-            "is-glob": "^4.0.3",
-            "semver": "^7.5.4",
-            "ts-api-utils": "^1.0.1"
+            "balanced-match": "^1.0.0"
           }
         },
-        "@typescript-eslint/visitor-keys": {
-          "version": "6.9.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz",
-          "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==",
+        "minimatch": {
+          "version": "9.0.5",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+          "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
           "dev": true,
           "requires": {
-            "@typescript-eslint/types": "6.9.1",
-            "eslint-visitor-keys": "^3.4.1"
+            "brace-expansion": "^2.0.1"
           }
         }
       }
     },
     "@typescript-eslint/types": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz",
-      "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==",
-      "dev": true,
-      "peer": true
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz",
+      "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==",
+      "dev": true
     },
     "@typescript-eslint/typescript-estree": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz",
-      "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==",
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz",
+      "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==",
       "dev": true,
-      "peer": true,
       "requires": {
-        "@typescript-eslint/types": "6.4.0",
-        "@typescript-eslint/visitor-keys": "6.4.0",
+        "@typescript-eslint/types": "6.21.0",
+        "@typescript-eslint/visitor-keys": "6.21.0",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
+        "minimatch": "9.0.3",
         "semver": "^7.5.4",
         "ts-api-utils": "^1.0.1"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+          "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0"
+          }
+        },
+        "minimatch": {
+          "version": "9.0.3",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+          "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^2.0.1"
+          }
+        }
       }
     },
     "@typescript-eslint/utils": {
-      "version": "6.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.1.tgz",
-      "integrity": "sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==",
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.12.2.tgz",
+      "integrity": "sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==",
       "dev": true,
       "requires": {
         "@eslint-community/eslint-utils": "^4.4.0",
-        "@types/json-schema": "^7.0.12",
-        "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "6.9.1",
-        "@typescript-eslint/types": "6.9.1",
-        "@typescript-eslint/typescript-estree": "6.9.1",
-        "semver": "^7.5.4"
+        "@typescript-eslint/scope-manager": "8.12.2",
+        "@typescript-eslint/types": "8.12.2",
+        "@typescript-eslint/typescript-estree": "8.12.2"
       },
       "dependencies": {
         "@typescript-eslint/scope-manager": {
-          "version": "6.9.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz",
-          "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==",
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz",
+          "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==",
           "dev": true,
           "requires": {
-            "@typescript-eslint/types": "6.9.1",
-            "@typescript-eslint/visitor-keys": "6.9.1"
+            "@typescript-eslint/types": "8.12.2",
+            "@typescript-eslint/visitor-keys": "8.12.2"
           }
         },
         "@typescript-eslint/types": {
-          "version": "6.9.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz",
-          "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==",
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz",
+          "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==",
           "dev": true
         },
         "@typescript-eslint/typescript-estree": {
-          "version": "6.9.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz",
-          "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==",
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz",
+          "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==",
           "dev": true,
           "requires": {
-            "@typescript-eslint/types": "6.9.1",
-            "@typescript-eslint/visitor-keys": "6.9.1",
+            "@typescript-eslint/types": "8.12.2",
+            "@typescript-eslint/visitor-keys": "8.12.2",
             "debug": "^4.3.4",
-            "globby": "^11.1.0",
+            "fast-glob": "^3.3.2",
             "is-glob": "^4.0.3",
-            "semver": "^7.5.4",
-            "ts-api-utils": "^1.0.1"
+            "minimatch": "^9.0.4",
+            "semver": "^7.6.0",
+            "ts-api-utils": "^1.3.0"
           }
         },
         "@typescript-eslint/visitor-keys": {
-          "version": "6.9.1",
-          "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz",
-          "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==",
+          "version": "8.12.2",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz",
+          "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==",
+          "dev": true,
+          "requires": {
+            "@typescript-eslint/types": "8.12.2",
+            "eslint-visitor-keys": "^3.4.3"
+          }
+        },
+        "brace-expansion": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+          "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
           "dev": true,
           "requires": {
-            "@typescript-eslint/types": "6.9.1",
-            "eslint-visitor-keys": "^3.4.1"
+            "balanced-match": "^1.0.0"
+          }
+        },
+        "minimatch": {
+          "version": "9.0.5",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+          "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^2.0.1"
           }
         }
       }
     },
     "@typescript-eslint/visitor-keys": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz",
-      "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==",
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz",
+      "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==",
       "dev": true,
-      "peer": true,
       "requires": {
-        "@typescript-eslint/types": "6.4.0",
+        "@typescript-eslint/types": "6.21.0",
         "eslint-visitor-keys": "^3.4.1"
       }
     },
@@ -8653,9 +9478,9 @@
       "dev": true
     },
     "acorn": {
-      "version": "8.12.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
-      "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
+      "version": "8.14.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+      "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
       "dev": true
     },
     "acorn-jsx": {
@@ -8860,16 +9685,6 @@
         "fill-range": "^7.0.1"
       }
     },
-    "builtins": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz",
-      "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==",
-      "dev": true,
-      "peer": true,
-      "requires": {
-        "semver": "^7.0.0"
-      }
-    },
     "call-bind": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
@@ -9201,6 +10016,7 @@
       "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
       "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
       "dev": true,
+      "peer": true,
       "requires": {
         "esutils": "^2.0.2"
       }
@@ -9276,6 +10092,16 @@
       "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
       "dev": true
     },
+    "enhanced-resolve": {
+      "version": "5.17.1",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
+      "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.2.0"
+      }
+    },
     "entities": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
@@ -9445,51 +10271,111 @@
       "dev": true
     },
     "eslint": {
-      "version": "8.57.1",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
-      "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
+      "version": "9.13.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz",
+      "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==",
       "dev": true,
       "requires": {
         "@eslint-community/eslint-utils": "^4.2.0",
-        "@eslint-community/regexpp": "^4.6.1",
-        "@eslint/eslintrc": "^2.1.4",
-        "@eslint/js": "8.57.1",
-        "@humanwhocodes/config-array": "^0.13.0",
+        "@eslint-community/regexpp": "^4.11.0",
+        "@eslint/config-array": "^0.18.0",
+        "@eslint/core": "^0.7.0",
+        "@eslint/eslintrc": "^3.1.0",
+        "@eslint/js": "9.13.0",
+        "@eslint/plugin-kit": "^0.2.0",
+        "@humanfs/node": "^0.16.5",
         "@humanwhocodes/module-importer": "^1.0.1",
-        "@nodelib/fs.walk": "^1.2.8",
-        "@ungap/structured-clone": "^1.2.0",
+        "@humanwhocodes/retry": "^0.3.1",
+        "@types/estree": "^1.0.6",
+        "@types/json-schema": "^7.0.15",
         "ajv": "^6.12.4",
         "chalk": "^4.0.0",
         "cross-spawn": "^7.0.2",
         "debug": "^4.3.2",
-        "doctrine": "^3.0.0",
         "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^7.2.2",
-        "eslint-visitor-keys": "^3.4.3",
-        "espree": "^9.6.1",
-        "esquery": "^1.4.2",
+        "eslint-scope": "^8.1.0",
+        "eslint-visitor-keys": "^4.1.0",
+        "espree": "^10.2.0",
+        "esquery": "^1.5.0",
         "esutils": "^2.0.2",
         "fast-deep-equal": "^3.1.3",
-        "file-entry-cache": "^6.0.1",
+        "file-entry-cache": "^8.0.0",
         "find-up": "^5.0.0",
         "glob-parent": "^6.0.2",
-        "globals": "^13.19.0",
-        "graphemer": "^1.4.0",
         "ignore": "^5.2.0",
         "imurmurhash": "^0.1.4",
         "is-glob": "^4.0.0",
-        "is-path-inside": "^3.0.3",
-        "js-yaml": "^4.1.0",
         "json-stable-stringify-without-jsonify": "^1.0.1",
-        "levn": "^0.4.1",
         "lodash.merge": "^4.6.2",
         "minimatch": "^3.1.2",
         "natural-compare": "^1.4.0",
         "optionator": "^0.9.3",
-        "strip-ansi": "^6.0.1",
         "text-table": "^0.2.0"
       },
       "dependencies": {
+        "@eslint/eslintrc": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
+          "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
+          "dev": true,
+          "requires": {
+            "ajv": "^6.12.4",
+            "debug": "^4.3.2",
+            "espree": "^10.0.1",
+            "globals": "^14.0.0",
+            "ignore": "^5.2.0",
+            "import-fresh": "^3.2.1",
+            "js-yaml": "^4.1.0",
+            "minimatch": "^3.1.2",
+            "strip-json-comments": "^3.1.1"
+          }
+        },
+        "eslint-scope": {
+          "version": "8.2.0",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
+          "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.3.0",
+            "estraverse": "^5.2.0"
+          }
+        },
+        "eslint-visitor-keys": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+          "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+          "dev": true
+        },
+        "espree": {
+          "version": "10.3.0",
+          "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+          "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+          "dev": true,
+          "requires": {
+            "acorn": "^8.14.0",
+            "acorn-jsx": "^5.3.2",
+            "eslint-visitor-keys": "^4.2.0"
+          }
+        },
+        "file-entry-cache": {
+          "version": "8.0.0",
+          "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+          "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+          "dev": true,
+          "requires": {
+            "flat-cache": "^4.0.0"
+          }
+        },
+        "flat-cache": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+          "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+          "dev": true,
+          "requires": {
+            "flatted": "^3.2.9",
+            "keyv": "^4.5.4"
+          }
+        },
         "glob-parent": {
           "version": "6.0.2",
           "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -9498,15 +10384,23 @@
           "requires": {
             "is-glob": "^4.0.3"
           }
+        },
+        "globals": {
+          "version": "14.0.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+          "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+          "dev": true
         }
       }
     },
-    "eslint-config-standard": {
-      "version": "17.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz",
-      "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==",
+    "eslint-compat-utils": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz",
+      "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==",
       "dev": true,
-      "requires": {}
+      "requires": {
+        "semver": "^7.5.4"
+      }
     },
     "eslint-import-resolver-node": {
       "version": "0.3.9",
@@ -9550,14 +10444,15 @@
         }
       }
     },
-    "eslint-plugin-es": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz",
-      "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==",
+    "eslint-plugin-es-x": {
+      "version": "7.8.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz",
+      "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==",
       "dev": true,
       "requires": {
-        "eslint-utils": "^2.0.0",
-        "regexpp": "^3.0.0"
+        "@eslint-community/eslint-utils": "^4.1.2",
+        "@eslint-community/regexpp": "^4.11.0",
+        "eslint-compat-utils": "^0.5.1"
       }
     },
     "eslint-plugin-import": {
@@ -9614,97 +10509,51 @@
       }
     },
     "eslint-plugin-n": {
-      "version": "15.6.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.6.1.tgz",
-      "integrity": "sha512-R9xw9OtCRxxaxaszTQmQAlPgM+RdGjaL1akWuY/Fv9fRAi8Wj4CUKc6iYVG8QNRjRuo8/BqVYIpfqberJUEacA==",
+      "version": "17.12.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.12.0.tgz",
+      "integrity": "sha512-zNAtz/erDn0v78bIY3MASSQlyaarV4IOTvP5ldHsqblRFrXriikB6ghkDTkHjUad+nMRrIbOy9euod2azjRfBg==",
       "dev": true,
-      "peer": true,
       "requires": {
-        "builtins": "^5.0.1",
-        "eslint-plugin-es": "^4.1.0",
-        "eslint-utils": "^3.0.0",
-        "ignore": "^5.1.1",
-        "is-core-module": "^2.11.0",
-        "minimatch": "^3.1.2",
-        "resolve": "^1.22.1",
-        "semver": "^7.3.8"
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "enhanced-resolve": "^5.17.1",
+        "eslint-plugin-es-x": "^7.8.0",
+        "get-tsconfig": "^4.8.1",
+        "globals": "^15.11.0",
+        "ignore": "^5.3.2",
+        "minimatch": "^9.0.5",
+        "semver": "^7.6.3"
       },
       "dependencies": {
-        "eslint-plugin-es": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz",
-          "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==",
+        "brace-expansion": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+          "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
           "dev": true,
-          "peer": true,
           "requires": {
-            "eslint-utils": "^2.0.0",
-            "regexpp": "^3.0.0"
-          },
-          "dependencies": {
-            "eslint-utils": {
-              "version": "2.1.0",
-              "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
-              "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
-              "dev": true,
-              "peer": true,
-              "requires": {
-                "eslint-visitor-keys": "^1.1.0"
-              }
-            },
-            "eslint-visitor-keys": {
-              "version": "1.3.0",
-              "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
-              "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
-              "dev": true,
-              "peer": true
-            }
+            "balanced-match": "^1.0.0"
           }
         },
-        "eslint-utils": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
-          "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+        "globals": {
+          "version": "15.11.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz",
+          "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==",
+          "dev": true
+        },
+        "minimatch": {
+          "version": "9.0.5",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+          "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
           "dev": true,
-          "peer": true,
           "requires": {
-            "eslint-visitor-keys": "^2.0.0"
+            "brace-expansion": "^2.0.1"
           }
-        },
-        "eslint-visitor-keys": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
-          "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
-          "dev": true,
-          "peer": true
-        }
-      }
-    },
-    "eslint-plugin-node": {
-      "version": "11.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
-      "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==",
-      "dev": true,
-      "requires": {
-        "eslint-plugin-es": "^3.0.0",
-        "eslint-utils": "^2.0.0",
-        "ignore": "^5.1.1",
-        "minimatch": "^3.0.4",
-        "resolve": "^1.10.1",
-        "semver": "^6.1.0"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "6.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
-          "dev": true
         }
       }
     },
     "eslint-plugin-promise": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz",
-      "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.1.0.tgz",
+      "integrity": "sha512-8trNmPxdAy3W620WKDpaS65NlM5yAumod6XeC4LOb+jxlkG4IVcp68c6dXY2ev+uT4U1PtG57YDV6EGAXN0GbQ==",
       "dev": true,
       "requires": {}
     },
@@ -9713,28 +10562,12 @@
       "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
       "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
       "dev": true,
+      "peer": true,
       "requires": {
         "esrecurse": "^4.3.0",
         "estraverse": "^5.2.0"
       }
     },
-    "eslint-utils": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
-      "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
-      "dev": true,
-      "requires": {
-        "eslint-visitor-keys": "^1.1.0"
-      },
-      "dependencies": {
-        "eslint-visitor-keys": {
-          "version": "1.3.0",
-          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
-          "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
-          "dev": true
-        }
-      }
-    },
     "eslint-visitor-keys": {
       "version": "3.4.3",
       "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
@@ -9746,6 +10579,7 @@
       "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
       "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
       "dev": true,
+      "peer": true,
       "requires": {
         "acorn": "^8.9.0",
         "acorn-jsx": "^5.3.2",
@@ -9753,9 +10587,9 @@
       }
     },
     "esquery": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz",
-      "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+      "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
       "dev": true,
       "requires": {
         "estraverse": "^5.1.0"
@@ -9810,9 +10644,9 @@
       "dev": true
     },
     "fast-glob": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
-      "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+      "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
       "dev": true,
       "requires": {
         "@nodelib/fs.stat": "^2.0.2",
@@ -9870,6 +10704,7 @@
       "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
       "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
       "dev": true,
+      "peer": true,
       "requires": {
         "flat-cache": "^3.0.4"
       }
@@ -10005,6 +10840,15 @@
         "get-intrinsic": "^1.2.4"
       }
     },
+    "get-tsconfig": {
+      "version": "4.8.1",
+      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz",
+      "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==",
+      "dev": true,
+      "requires": {
+        "resolve-pkg-maps": "^1.0.0"
+      }
+    },
     "glob": {
       "version": "7.2.3",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -10064,17 +10908,19 @@
       "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
       "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
       "dev": true,
+      "peer": true,
       "requires": {
         "type-fest": "^0.20.2"
       }
     },
     "globalthis": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
-      "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+      "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
       "dev": true,
       "requires": {
-        "define-properties": "^1.1.3"
+        "define-properties": "^1.2.1",
+        "gopd": "^1.0.1"
       }
     },
     "globby": {
@@ -10106,6 +10952,12 @@
         "get-intrinsic": "^1.1.3"
       }
     },
+    "graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true
+    },
     "graphemer": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
@@ -10287,9 +11139,9 @@
       }
     },
     "ignore": {
-      "version": "5.2.4",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
-      "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+      "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
       "dev": true
     },
     "immutable-json-patch": {
@@ -10370,6 +11222,132 @@
         "rollup-plugin-svg-import": "^3.0.0",
         "seedrandom": "^3.0.5",
         "sjcl": "^1.0.8"
+      },
+      "dependencies": {
+        "@eslint/js": {
+          "version": "8.57.1",
+          "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
+          "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+          "dev": true,
+          "peer": true
+        },
+        "@typescript-eslint/eslint-plugin": {
+          "version": "6.21.0",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
+          "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==",
+          "dev": true,
+          "requires": {
+            "@eslint-community/regexpp": "^4.5.1",
+            "@typescript-eslint/scope-manager": "6.21.0",
+            "@typescript-eslint/type-utils": "6.21.0",
+            "@typescript-eslint/utils": "6.21.0",
+            "@typescript-eslint/visitor-keys": "6.21.0",
+            "debug": "^4.3.4",
+            "graphemer": "^1.4.0",
+            "ignore": "^5.2.4",
+            "natural-compare": "^1.4.0",
+            "semver": "^7.5.4",
+            "ts-api-utils": "^1.0.1"
+          }
+        },
+        "@typescript-eslint/parser": {
+          "version": "6.21.0",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
+          "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
+          "dev": true,
+          "peer": true,
+          "requires": {
+            "@typescript-eslint/scope-manager": "6.21.0",
+            "@typescript-eslint/types": "6.21.0",
+            "@typescript-eslint/typescript-estree": "6.21.0",
+            "@typescript-eslint/visitor-keys": "6.21.0",
+            "debug": "^4.3.4"
+          }
+        },
+        "@typescript-eslint/type-utils": {
+          "version": "6.21.0",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz",
+          "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==",
+          "dev": true,
+          "requires": {
+            "@typescript-eslint/typescript-estree": "6.21.0",
+            "@typescript-eslint/utils": "6.21.0",
+            "debug": "^4.3.4",
+            "ts-api-utils": "^1.0.1"
+          }
+        },
+        "@typescript-eslint/utils": {
+          "version": "6.21.0",
+          "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz",
+          "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==",
+          "dev": true,
+          "requires": {
+            "@eslint-community/eslint-utils": "^4.4.0",
+            "@types/json-schema": "^7.0.12",
+            "@types/semver": "^7.5.0",
+            "@typescript-eslint/scope-manager": "6.21.0",
+            "@typescript-eslint/types": "6.21.0",
+            "@typescript-eslint/typescript-estree": "6.21.0",
+            "semver": "^7.5.4"
+          }
+        },
+        "eslint": {
+          "version": "8.57.1",
+          "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
+          "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
+          "dev": true,
+          "peer": true,
+          "requires": {
+            "@eslint-community/eslint-utils": "^4.2.0",
+            "@eslint-community/regexpp": "^4.6.1",
+            "@eslint/eslintrc": "^2.1.4",
+            "@eslint/js": "8.57.1",
+            "@humanwhocodes/config-array": "^0.13.0",
+            "@humanwhocodes/module-importer": "^1.0.1",
+            "@nodelib/fs.walk": "^1.2.8",
+            "@ungap/structured-clone": "^1.2.0",
+            "ajv": "^6.12.4",
+            "chalk": "^4.0.0",
+            "cross-spawn": "^7.0.2",
+            "debug": "^4.3.2",
+            "doctrine": "^3.0.0",
+            "escape-string-regexp": "^4.0.0",
+            "eslint-scope": "^7.2.2",
+            "eslint-visitor-keys": "^3.4.3",
+            "espree": "^9.6.1",
+            "esquery": "^1.4.2",
+            "esutils": "^2.0.2",
+            "fast-deep-equal": "^3.1.3",
+            "file-entry-cache": "^6.0.1",
+            "find-up": "^5.0.0",
+            "glob-parent": "^6.0.2",
+            "globals": "^13.19.0",
+            "graphemer": "^1.4.0",
+            "ignore": "^5.2.0",
+            "imurmurhash": "^0.1.4",
+            "is-glob": "^4.0.0",
+            "is-path-inside": "^3.0.3",
+            "js-yaml": "^4.1.0",
+            "json-stable-stringify-without-jsonify": "^1.0.1",
+            "levn": "^0.4.1",
+            "lodash.merge": "^4.6.2",
+            "minimatch": "^3.1.2",
+            "natural-compare": "^1.4.0",
+            "optionator": "^0.9.3",
+            "strip-ansi": "^6.0.1",
+            "text-table": "^0.2.0"
+          }
+        },
+        "glob-parent": {
+          "version": "6.0.2",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+          "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+          "dev": true,
+          "peer": true,
+          "requires": {
+            "is-glob": "^4.0.3"
+          }
+        }
       }
     },
     "internal-slot": {
@@ -10503,7 +11481,8 @@
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
       "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
-      "dev": true
+      "dev": true,
+      "peer": true
     },
     "is-plain-obj": {
       "version": "1.1.0",
@@ -11521,12 +12500,6 @@
         "set-function-name": "^2.0.2"
       }
     },
-    "regexpp": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
-      "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
-      "dev": true
-    },
     "require-from-string": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@@ -11556,6 +12529,12 @@
       "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
       "dev": true
     },
+    "resolve-pkg-maps": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+      "dev": true
+    },
     "reusify": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -11667,13 +12646,10 @@
       "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="
     },
     "semver": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
-      "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
-      "dev": true,
-      "requires": {
-        "lru-cache": "^6.0.0"
-      }
+      "version": "7.6.3",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+      "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+      "dev": true
     },
     "set-function-length": {
       "version": "1.2.2",
@@ -12098,6 +13074,12 @@
         }
       }
     },
+    "tapable": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+      "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+      "dev": true
+    },
     "text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -12147,9 +13129,9 @@
       "dev": true
     },
     "ts-api-utils": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz",
-      "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+      "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
       "dev": true,
       "requires": {}
     },
@@ -12184,7 +13166,8 @@
       "version": "0.20.2",
       "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
       "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
-      "dev": true
+      "dev": true,
+      "peer": true
     },
     "typed-array-buffer": {
       "version": "1.0.2",
@@ -12285,6 +13268,17 @@
       "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
       "dev": true
     },
+    "typescript-eslint": {
+      "version": "8.12.2",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.12.2.tgz",
+      "integrity": "sha512-UbuVUWSrHVR03q9CWx+JDHeO6B/Hr9p4U5lRH++5tq/EbFq1faYZe50ZSBePptgfIKLEti0aPQ3hFgnPVcd8ZQ==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/eslint-plugin": "8.12.2",
+        "@typescript-eslint/parser": "8.12.2",
+        "@typescript-eslint/utils": "8.12.2"
+      }
+    },
     "uc.micro": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
diff --git a/package.json b/package.json
index e8af51315..e8d99d22a 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,8 @@
     "docs-watch": "typedoc --watch",
     "tsc": "tsc",
     "tsc-watch": "tsc --watch",
-    "lint": "eslint . && npm run tsc",
+    "lint": "eslint . && npm run tsc && npm run lint-no-output-globals",
+    "lint-no-output-globals": "eslint --no-inline-config --config build-output.eslint.config.js Sources/ContentScopeScripts/dist/contentScope.js",
     "postlint": "npm run lint --workspaces --if-present",
     "lint-fix": "eslint . --fix && npm run tsc",
     "stylelint": "npx stylelint \"**/*.css\"",
@@ -31,20 +32,17 @@
     "messaging",
     "types-generator"
   ],
-  "dependencies": {},
   "devDependencies": {
-    "@typescript-eslint/eslint-plugin": "^6.9.1",
-    "eslint": "^8.57.1",
-    "eslint-config-standard": "^17.1.0",
-    "eslint-plugin-import": "^2.31.0",
-    "eslint-plugin-node": "^11.1.0",
-    "eslint-plugin-promise": "^6.1.1",
+    "@duckduckgo/eslint-config": "github:duckduckgo/eslint-config#51dc868a4342379d6aec1d0f4214156e7046d5f6",
+    "@types/eslint__js": "^8.42.3",
+    "eslint": "^9.13.0",
+    "minimist": "^1.2.8",
     "stylelint": "^15.11.0",
     "stylelint-config-standard": "^34.0.0",
     "stylelint-csstree-validator": "^3.0.0",
-    "minimist": "^1.2.8",
     "typedoc": "^0.26.10",
     "typescript": "^5.6.3",
+    "typescript-eslint": "^8.12.2",
     "@playwright/test": "^1.48.2"
   }
 }
diff --git a/special-pages/index.mjs b/special-pages/index.mjs
index 247cdf4d2..a89d0dda7 100644
--- a/special-pages/index.mjs
+++ b/special-pages/index.mjs
@@ -20,40 +20,40 @@ const DEBUG = Boolean(args.debug);
 export const support = {
     /** @type {Partial<Record<ImportMeta['injectName'], string[]>>} */
     duckplayer: {
-        'integration': ['copy', 'build-js'],
-        'windows': ['copy', 'build-js'],
-        'apple': ['copy', 'build-js', 'inline-html'],
-        'android': ['copy', 'build-js']
+        integration: ['copy', 'build-js'],
+        windows: ['copy', 'build-js'],
+        apple: ['copy', 'build-js', 'inline-html'],
+        android: ['copy', 'build-js']
     },
     /** @type {Partial<Record<ImportMeta['injectName'], string[]>>} */
     errorpage: {
-        'integration': ['copy'],
-        'apple': ['copy', 'inline-html'],
+        integration: ['copy'],
+        apple: ['copy', 'inline-html'],
     },
     /** @type {Partial<Record<ImportMeta['injectName'], string[]>>} */
     onboarding: {
-        'integration': ['copy', 'build-js'],
-        'windows': ['copy', 'build-js'],
-        'apple': ['copy', 'build-js'],
+        integration: ['copy', 'build-js'],
+        windows: ['copy', 'build-js'],
+        apple: ['copy', 'build-js'],
     },
     /** @type {Partial<Record<ImportMeta['injectName'], string[]>>} */
     example: {
-        'integration': ['copy', 'build-js']
+        integration: ['copy', 'build-js']
     },
     /** @type {Partial<Record<ImportMeta['injectName'], string[]>>} */
     'release-notes': {
-        'integration': ['copy', 'build-js'],
-        'apple': ['copy', 'build-js'],
+        integration: ['copy', 'build-js'],
+        apple: ['copy', 'build-js'],
     },
     /** @type {Partial<Record<ImportMeta['injectName'], string[]>>} */
     'special-error': {
-        'integration': ['copy', 'build-js'],
-        'apple': ['copy', 'build-js', 'inline-html'],
+        integration: ['copy', 'build-js'],
+        apple: ['copy', 'build-js', 'inline-html'],
     },
     /** @type {Partial<Record<ImportMeta['injectName'], string[]>>} */
     'new-tab': {
-        'integration': ['copy', 'build-js'],
-        'windows': ['copy', 'build-js'],
+        integration: ['copy', 'build-js'],
+        windows: ['copy', 'build-js'],
     },
 }
 
@@ -81,7 +81,7 @@ for (const [pageName, injectNames] of Object.entries(support)) {
 
         const pageOutputDirectory = join(buildDir, 'pages', pageName)
 
-        for (let job of jobs) {
+        for (const job of jobs) {
             if (job === 'copy') {
                 copyJobs.push({
                     src: pageSrc,
diff --git a/special-pages/pages/duckplayer/app/features/iframe.js b/special-pages/pages/duckplayer/app/features/iframe.js
index bae6b85af..0f6744076 100644
--- a/special-pages/pages/duckplayer/app/features/iframe.js
+++ b/special-pages/pages/duckplayer/app/features/iframe.js
@@ -14,7 +14,7 @@ export class IframeFeature {
      * @param {HTMLIFrameElement} iframe
      * @returns {(() => void) | null}
      */
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+     
     iframeDidLoad (iframe) {
         return () => {
             console.log('teardown')
diff --git a/special-pages/pages/duckplayer/app/features/title-capture.js b/special-pages/pages/duckplayer/app/features/title-capture.js
index 1e6a0fded..6a0c8bbdf 100644
--- a/special-pages/pages/duckplayer/app/features/title-capture.js
+++ b/special-pages/pages/duckplayer/app/features/title-capture.js
@@ -28,7 +28,7 @@ export class TitleCapture {
         }
 
         if (doc.title) {
-            // eslint-disable-next-line n/no-callback-literal
+             
             setter(doc.title)
         }
         if (win && doc) {
diff --git a/special-pages/pages/new-tab/app/privacy-stats/PrivacyStats.js b/special-pages/pages/new-tab/app/privacy-stats/PrivacyStats.js
index 90a929d92..cdbd6f588 100644
--- a/special-pages/pages/new-tab/app/privacy-stats/PrivacyStats.js
+++ b/special-pages/pages/new-tab/app/privacy-stats/PrivacyStats.js
@@ -142,6 +142,7 @@ export function Heading ({ expansion, trackerCompanies, totalCount, onToggle, bu
  * @param {import("preact").ComponentProps<'ul'>} [props.listAttrs]
  * @param {TrackerCompany[]} props.trackerCompanies
  */
+// eslint-disable-next-line no-redeclare
 export function Body ({ trackerCompanies, listAttrs = {} }) {
     const max = trackerCompanies[0]?.count ?? 0
     const [formatter] = useState(() => new Intl.NumberFormat())
diff --git a/special-pages/pages/new-tab/app/widget-list/widget-config.provider.js b/special-pages/pages/new-tab/app/widget-list/widget-config.provider.js
index 7d01df279..66271e524 100644
--- a/special-pages/pages/new-tab/app/widget-list/widget-config.provider.js
+++ b/special-pages/pages/new-tab/app/widget-list/widget-config.provider.js
@@ -16,7 +16,7 @@ export const WidgetConfigContext = createContext({
     widgetConfigItems: [],
 
     /** @type {(id:string) => void} */
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+     
     toggle: (_id) => {
 
     }
@@ -68,7 +68,7 @@ const WidgetVisibilityContext = createContext({
     visibility: /** @type {WidgetConfigItem['visibility']} */('visible'),
     id: /** @type {WidgetConfigItem['id']} */(''),
     /** @type {(id: string) => void} */
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+     
     toggle: (_id) => {},
     /** @type {number} */
     index: -1
diff --git a/special-pages/pages/new-tab/src/js/index.js b/special-pages/pages/new-tab/src/js/index.js
index 92c75c229..e63901a47 100644
--- a/special-pages/pages/new-tab/src/js/index.js
+++ b/special-pages/pages/new-tab/src/js/index.js
@@ -69,7 +69,7 @@ const messaging = createSpecialPageMessaging({
         // only in integration environments
         if (baseEnvironment.injectName !== 'integration') return null
         let mock = null
-        // eslint-disable-next-line no-labels
+        // eslint-disable-next-line no-labels,no-unused-labels
         $INTEGRATION: mock = mockTransport()
         return mock
     }
diff --git a/special-pages/pages/onboarding/app/components/App.js b/special-pages/pages/onboarding/app/components/App.js
index a00946fc3..d4b2ae01b 100644
--- a/special-pages/pages/onboarding/app/components/App.js
+++ b/special-pages/pages/onboarding/app/components/App.js
@@ -1,4 +1,4 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
+ 
 import { h } from 'preact'
 import { useContext, useRef } from 'preact/hooks'
 import styles from './App.module.css'
diff --git a/special-pages/pages/onboarding/app/components/App2.js b/special-pages/pages/onboarding/app/components/App2.js
index f7fbebc5b..db6a8b25a 100644
--- a/special-pages/pages/onboarding/app/components/App2.js
+++ b/special-pages/pages/onboarding/app/components/App2.js
@@ -1,4 +1,4 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
+ 
 import { h } from 'preact'
 import { useContext } from 'preact/hooks'
 import { GlobalContext, GlobalDispatch } from '../global'
diff --git a/special-pages/pages/onboarding/app/components/Background.js b/special-pages/pages/onboarding/app/components/Background.js
index 14b326c61..4027563e1 100644
--- a/special-pages/pages/onboarding/app/components/Background.js
+++ b/special-pages/pages/onboarding/app/components/Background.js
@@ -1,4 +1,4 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
+ 
 import { h } from 'preact'
 import styles from './Background.module.css'
 import cn from 'classnames'
diff --git a/special-pages/pages/onboarding/app/components/ListItem.js b/special-pages/pages/onboarding/app/components/ListItem.js
index 8f6f896db..06c18e1e8 100644
--- a/special-pages/pages/onboarding/app/components/ListItem.js
+++ b/special-pages/pages/onboarding/app/components/ListItem.js
@@ -3,7 +3,7 @@ import cn from 'classnames'
 import styles from './ListItem.module.css'
 import { Check } from './Icons'
 
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
+ 
 export const availableIcons = /** @type {const} */ ([
     'bookmarks.png',
     'browsing.png',
diff --git a/special-pages/pages/onboarding/app/components/Typed.js b/special-pages/pages/onboarding/app/components/Typed.js
index 8ddc2f8c6..7c3d6a9e6 100644
--- a/special-pages/pages/onboarding/app/components/Typed.js
+++ b/special-pages/pages/onboarding/app/components/Typed.js
@@ -1,4 +1,4 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
+ 
 import { h } from 'preact'
 import { useState, useEffect, useRef, useContext } from 'preact/hooks'
 
diff --git a/special-pages/pages/onboarding/app/components/v3/Animation.js b/special-pages/pages/onboarding/app/components/v3/Animation.js
index 1d98144ad..705f63305 100644
--- a/special-pages/pages/onboarding/app/components/v3/Animation.js
+++ b/special-pages/pages/onboarding/app/components/v3/Animation.js
@@ -21,6 +21,7 @@ export function SlideIn ({ children, onAnimationEnd }) {
 
     const animationEnd = useCallback(() => {
         setAnimationState('done')
+        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
         onAnimationEnd && onAnimationEnd()
     }, [onAnimationEnd])
 
diff --git a/special-pages/pages/onboarding/app/components/v3/Background.js b/special-pages/pages/onboarding/app/components/v3/Background.js
index 14b326c61..4027563e1 100644
--- a/special-pages/pages/onboarding/app/components/v3/Background.js
+++ b/special-pages/pages/onboarding/app/components/v3/Background.js
@@ -1,4 +1,4 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
+ 
 import { h } from 'preact'
 import styles from './Background.module.css'
 import cn from 'classnames'
diff --git a/special-pages/pages/onboarding/app/components/v3/Heading.js b/special-pages/pages/onboarding/app/components/v3/Heading.js
index d4392cf70..5ab7aa9e1 100644
--- a/special-pages/pages/onboarding/app/components/v3/Heading.js
+++ b/special-pages/pages/onboarding/app/components/v3/Heading.js
@@ -18,6 +18,7 @@ import styles from './Heading.module.css'
  */
 export function Heading ({ title, subtitle, speechBubble = false, onTitleComplete, children }) {
     const onComplete = () => {
+        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
         onTitleComplete && onTitleComplete()
     }
     const HeadingComponent = speechBubble ? SpeechBubble : PlainHeading
@@ -55,6 +56,7 @@ function PlainHeading ({ title, subtitle, onComplete, children }) {
     const [typingDone, setTypingDone] = useState(false)
     const onTypingComplete = () => {
         setTypingDone(true)
+        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
         onComplete && onComplete()
     }
 
@@ -141,6 +143,7 @@ function SpeechBubble ({ title, subtitle, onComplete, children }) {
 
     const onTypingComplete = () => {
         setAnimationState('typing-done')
+        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
         onComplete && onComplete()
     }
 
@@ -190,6 +193,7 @@ export function TypedTitle ({ title, paused = true, onComplete }) {
         setTextIndex(value => (value += 1))
 
         if (textIndex >= title.length - 1) {
+            // eslint-disable-next-line @typescript-eslint/no-unused-expressions
             onComplete && onComplete()
         }
     }
diff --git a/special-pages/pages/onboarding/app/components/v3/SettingsStep.js b/special-pages/pages/onboarding/app/components/v3/SettingsStep.js
index 8d4b65a27..fffb27efd 100644
--- a/special-pages/pages/onboarding/app/components/v3/SettingsStep.js
+++ b/special-pages/pages/onboarding/app/components/v3/SettingsStep.js
@@ -1,4 +1,4 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
+ 
 import { h } from 'preact'
 import { useGlobalDispatch, useGlobalState } from '../../global'
 import { useTypedTranslation } from '../../types'
diff --git a/special-pages/pages/onboarding/app/pages/CleanBrowsing.js b/special-pages/pages/onboarding/app/pages/CleanBrowsing.js
index 17f969999..95591fec7 100644
--- a/special-pages/pages/onboarding/app/pages/CleanBrowsing.js
+++ b/special-pages/pages/onboarding/app/pages/CleanBrowsing.js
@@ -1,4 +1,4 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
+ 
 import { h } from 'preact'
 import { ListItem } from '../components/ListItem'
 import { BounceIn, Check, SlideUp } from '../components/Icons'
diff --git a/special-pages/pages/onboarding/app/pages/PrivacyDefault.js b/special-pages/pages/onboarding/app/pages/PrivacyDefault.js
index 1f67381dc..b703fc36b 100644
--- a/special-pages/pages/onboarding/app/pages/PrivacyDefault.js
+++ b/special-pages/pages/onboarding/app/pages/PrivacyDefault.js
@@ -1,4 +1,4 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
+ 
 import { h } from 'preact'
 import { ListItem } from '../components/ListItem'
 import { BounceIn, Check, SlideUp } from '../components/Icons'
diff --git a/special-pages/pages/onboarding/app/pages/Summary.js b/special-pages/pages/onboarding/app/pages/Summary.js
index 0c38000be..d480b0eed 100644
--- a/special-pages/pages/onboarding/app/pages/Summary.js
+++ b/special-pages/pages/onboarding/app/pages/Summary.js
@@ -1,4 +1,4 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
+ 
 import { h } from 'preact'
 import { Stack } from '../components/Stack'
 import { Launch, SlideUp } from '../components/Icons'
diff --git a/special-pages/pages/release-notes/app/components/App.js b/special-pages/pages/release-notes/app/components/App.js
index 8df343cbb..47c7ccbe4 100644
--- a/special-pages/pages/release-notes/app/components/App.js
+++ b/special-pages/pages/release-notes/app/components/App.js
@@ -1,4 +1,4 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
+ 
 import { h } from 'preact'
 import { useEffect, useState } from 'preact/hooks'
 import { useMessaging } from '../index'
diff --git a/special-pages/pages/release-notes/app/components/ReleaseNotes.js b/special-pages/pages/release-notes/app/components/ReleaseNotes.js
index 257b5ed62..24114649e 100644
--- a/special-pages/pages/release-notes/app/components/ReleaseNotes.js
+++ b/special-pages/pages/release-notes/app/components/ReleaseNotes.js
@@ -1,8 +1,9 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
+ 
 import { Fragment, h } from 'preact'
 import { useMessaging } from '../index'
 import classNames from 'classnames'
 import { useTypedTranslation } from '../types'
+// eslint-disable-next-line no-redeclare
 import { Text } from '../../../../shared/components/Text/Text'
 import { Card } from '../../../../shared/components/Card/Card'
 import { Button } from '../../../../shared/components/Button/Button'
diff --git a/special-pages/pages/release-notes/src/js/index.js b/special-pages/pages/release-notes/src/js/index.js
index 64fe7d73c..d0217db7c 100644
--- a/special-pages/pages/release-notes/src/js/index.js
+++ b/special-pages/pages/release-notes/src/js/index.js
@@ -105,7 +105,7 @@ const messaging = createSpecialPageMessaging({
         // only in integration environments
         if (baseEnvironment.injectName !== 'integration') return null
         let mock = null
-        // eslint-disable-next-line no-labels
+        // eslint-disable-next-line no-labels, no-unused-labels
         $INTEGRATION: mock = mockTransport()
         return mock
     }
diff --git a/special-pages/pages/release-notes/src/js/mock-transport.js b/special-pages/pages/release-notes/src/js/mock-transport.js
index 4daa57d58..04581cc09 100644
--- a/special-pages/pages/release-notes/src/js/mock-transport.js
+++ b/special-pages/pages/release-notes/src/js/mock-transport.js
@@ -25,7 +25,7 @@ export function mockTransport () {
     }
 
     return new TestTransportConfig({
-        // eslint-disable-next-line @typescript-eslint/no-unused-vars
+         
         notify (_msg) {
         },
         request (_msg) {
diff --git a/special-pages/playwright.config.js b/special-pages/playwright.config.js
index a3650acfc..5f13da3a9 100644
--- a/special-pages/playwright.config.js
+++ b/special-pages/playwright.config.js
@@ -1,3 +1,4 @@
+/* global process */
 import { defineConfig, devices } from '@playwright/test'
 
 export default defineConfig({
diff --git a/special-pages/shared/components/Text/Text.js b/special-pages/shared/components/Text/Text.js
index 6ca49e853..f0fc58862 100644
--- a/special-pages/shared/components/Text/Text.js
+++ b/special-pages/shared/components/Text/Text.js
@@ -10,6 +10,7 @@ import styles from './Text.module.css'
  * @param {boolean} [props.strictSpacing] - Apply Design System letter spacing. Default: true
  * @param {import("preact").ComponentChild} [props.children]
  */
+// eslint-disable-next-line no-redeclare
 export function Text ({ as: Comp = 'p', variant, strictSpacing = true, className, children }) {
     return (
         <Comp className={classNames({ [styles[`${variant}`]]: variant, [styles.strictSpacing]: strictSpacing }, className)}>
diff --git a/special-pages/shared/create-special-page-messaging.js b/special-pages/shared/create-special-page-messaging.js
index 7ad91a695..febce6a36 100644
--- a/special-pages/shared/create-special-page-messaging.js
+++ b/special-pages/shared/create-special-page-messaging.js
@@ -66,7 +66,7 @@ export function createSpecialPageMessaging (opts) {
         /**
          * @param {import('@duckduckgo/messaging').RequestMessage} msg
          */
-        // eslint-disable-next-line @typescript-eslint/no-unused-vars
+         
         request: (msg) => {
             console.log(msg)
             if (msg.method === 'initialSetup') {
diff --git a/special-pages/tests/duckplayer-screenshots.spec.js b/special-pages/tests/duckplayer-screenshots.spec.js
index 7da7c5322..2848aa5f9 100644
--- a/special-pages/tests/duckplayer-screenshots.spec.js
+++ b/special-pages/tests/duckplayer-screenshots.spec.js
@@ -1,3 +1,4 @@
+/* global process */
 import { expect, test } from '@playwright/test'
 import { DuckPlayerPage } from './page-objects/duck-player.js'
 
diff --git a/special-pages/types.mjs b/special-pages/types.mjs
index bf40c517a..d48af5421 100644
--- a/special-pages/types.mjs
+++ b/special-pages/types.mjs
@@ -1,3 +1,4 @@
+/* eslint-disable promise/prefer-await-to-then */
 import { join } from "node:path";
 import { cwd, isLaunchFile } from "../scripts/script-utils.js";
 import { buildTypes } from "../types-generator/build-types.mjs";
diff --git a/tsconfig.json b/tsconfig.json
index 1f3354287..b3aed1e0a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -30,7 +30,8 @@
     "injected",
     "injected/src/globals.d.ts",
     "injected/src/features/duckplayer/duckplayer-settings.ts",
-    "typedoc.js"
+    "typedoc.js",
+    ".github/scripts"
   ],
   "exclude": [
     "injected/integration-test/test-pages",
diff --git a/types-generator/build-types.mjs b/types-generator/build-types.mjs
index 21a94fa17..373d48102 100644
--- a/types-generator/build-types.mjs
+++ b/types-generator/build-types.mjs
@@ -32,23 +32,23 @@ const ROOT = join(cwd(import.meta.url), '..')
  * @param {Record<string, Mapping>} mapping
  */
 export async function buildTypes(mapping) {
-    for (let [featureName, manifest] of Object.entries(mapping)) {
+    for (const [featureName, manifest] of Object.entries(mapping)) {
         if (manifest.exclude) continue;
         if (manifest.kind === 'settings') {
             const typescript = await createTypesForSchemaFile(featureName, manifest.schema);
-            let content = typescript.replace(/\r\n/g, '\n')
+            const content = typescript.replace(/\r\n/g, '\n')
             write([manifest.types], content)
             console.log('✅ %s schema written to `%s` from schema `%s`', featureName, relative(ROOT, manifest.types), manifest.schema)
         }
         if (manifest.kind === 'messages') {
             // create a job for each sub-folder that contains schemas
-            const schemas = await createSchemasFromFiles(manifest.schemaDir)
+            const schemas = createSchemasFromFiles(manifest.schemaDir)
 
             // for each folder
-            for (let schema of schemas) {
+            for (const schema of schemas) {
                 const typescript = await createTypesForSchemaMessages(schema.featureName, schema.schema, manifest.schemaDir)
-                let featurePath = manifest.resolve(schema.dirname)
-                let className = manifest.className(schema.topLevelType)
+                const featurePath = manifest.resolve(schema.dirname)
+                const className = manifest.className(schema.topLevelType)
                 const messageTypes = createMessagingTypes(schema, { featurePath, className })
                 const content = [typescript.replace(/\r\n/g, '\n'), messageTypes].join('')
                 const filename = schema.dirname + '.ts'
diff --git a/types-generator/json-schema-fs.mjs b/types-generator/json-schema-fs.mjs
index a6e668aa1..5f44750a2 100644
--- a/types-generator/json-schema-fs.mjs
+++ b/types-generator/json-schema-fs.mjs
@@ -34,20 +34,20 @@ export function createFileList(rootDir, featureDirName) {
 
 /**
  * @param {string} rootDir
- * @return {Promise<{
+ * @return {{
  *   schema: import("json-schema-to-typescript").JSONSchema;
  *   featureName: string;
  *   dirname: string;
  *   topLevelType: string;
- * }[]>}
+ * }[]}
  */
-export async function createSchemasFromFiles(rootDir) {
+export function createSchemasFromFiles(rootDir) {
     const dirList = readdirSync(rootDir, { withFileTypes: true });
     const dirs = dirList.filter(x => x.isDirectory());
 
     const outputs = [];
 
-    for (let dir of dirs) {
+    for (const dir of dirs) {
         const fileList = createFileList(rootDir, dir.name);
         const valid = fileList.filter(x => x.valid);
         if (valid.length === 0) continue;
@@ -73,6 +73,7 @@ export async function createSchemasFromFiles(rootDir) {
 export function isValidFileName(file) {
     if (!file.isFile()) return {result: false}
     if (!file.name.endsWith('json')) return {result: false}
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
     const [method, kind, ext] = file.name.split('.');
     if (kind === 'request' || kind === 'response' || kind === 'notify' || kind === 'subscribe') {
         return { result: true, method, kind }
diff --git a/types-generator/json-schema.mjs b/types-generator/json-schema.mjs
index 011802a28..ff9dea3bb 100644
--- a/types-generator/json-schema.mjs
+++ b/types-generator/json-schema.mjs
@@ -1,18 +1,20 @@
-import {createFileList} from "./json-schema-fs.mjs";
+/**
+ * @typedef {import("./json-schema-fs.mjs").createFileList} createFileList
+ */
 
 /**
  * @param {ReturnType<createFileList>[number]} file
  */
 function title(file) {
     switch (file.kind) {
-        case "request":
-        case "response":
-            return file.method + '_' + file.kind
-        case "subscribe":
-            return file.method + '_' + "subscription"
-        case "notify":
-            return file.method + '_' + "notification"
-        default: return file.kind
+    case "request":
+    case "response":
+        return file.method + '_' + file.kind
+    case "subscribe":
+        return file.method + '_' + "subscription"
+    case "notify":
+        return file.method + '_' + "notification"
+    default: return file.kind
     }
 }
 
@@ -41,14 +43,14 @@ function hasParams(schema) {
  */
 function baseSchema(file) {
     return {
-        "type": "object",
-        "title": title(file),
-        "description": description(file),
-        "additionalProperties": false,
-        "required": ["method"],
-        "properties": {
-            "method": {
-                "const": file.method
+        type: "object",
+        title: title(file),
+        description: description(file),
+        additionalProperties: false,
+        required: ["method"],
+        properties: {
+            method: {
+                const: file.method
             },
         }
     }
@@ -58,14 +60,14 @@ function baseSchema(file) {
  */
 function subscribeBaseSchema(file) {
     return {
-        "type": "object",
-        "title": title(file),
-        "description": description(file),
-        "additionalProperties": false,
-        "required": ["subscriptionEvent"],
-        "properties": {
-            "subscriptionEvent": {
-                "const": file.method
+        type: "object",
+        title: title(file),
+        description: description(file),
+        additionalProperties: false,
+        required: ["subscriptionEvent"],
+        properties: {
+            subscriptionEvent: {
+                const: file.method
             },
         }
     }
@@ -79,7 +81,7 @@ function createNotification(file) {
     const json = file.json;
     const base = baseSchema(file)
     if (hasParams(json)) {
-        base.properties.params = { "$ref": "./" + file.relative }
+        base.properties.params = { $ref: "./" + file.relative }
         base.required.push('params')
     }
     return base;
@@ -92,7 +94,7 @@ function createNotification(file) {
 function createRequest(file, response) {
     const base = createNotification(file);
     if (response && response.valid) {
-        base.properties.result = { "$ref": "./" + response.relative }
+        base.properties.result = { $ref: "./" + response.relative }
         base.required.push('result')
     }
     return base;
@@ -106,7 +108,7 @@ function createSubscription(file) {
     const json = file.json;
     const base = subscribeBaseSchema(file)
     if (hasParams(json)) {
-        base.properties.params = { "$ref": "./" + file.relative }
+        base.properties.params = { $ref: "./" + file.relative }
         base.required.push('params')
     }
     return base;
@@ -122,7 +124,7 @@ export function generateSchema(featureName, fileList) {
     const requests = [];
     const subscriptions = [];
 
-    for (let file of fileList.filter(x => x.valid)) {
+    for (const file of fileList.filter(x => x.valid)) {
         if (file.valid === false) continue; // ts
         if (file.kind === 'notify') {
             notifications.push(createNotification(file))
@@ -137,14 +139,14 @@ export function generateSchema(featureName, fileList) {
     }
 
     const base = {
-        "$schema": "http://json-schema.org/draft-07/schema#",
-        "type": "object",
-        "title": featureName + "_messages",
-        "description": `Requests, Notifications and Subscriptions from the ${featureName} feature`,
-        "additionalProperties": false,
-        "properties": {},
+        $schema: "http://json-schema.org/draft-07/schema#",
+        type: "object",
+        title: featureName + "_messages",
+        description: `Requests, Notifications and Subscriptions from the ${featureName} feature`,
+        additionalProperties: false,
+        properties: {},
         /** @type {string[]} */
-        "required": []
+        required: []
     }
 
     if (notifications.length) {
@@ -171,9 +173,9 @@ export function generateSchema(featureName, fileList) {
  */
 export function createMessagingTypes(job, { featurePath, className }) {
     const json = job.schema
-    const notifications = json.properties?.notifications?.oneOf?.length ?? 0 > 0;
-    const requests = json.properties?.requests?.oneOf?.length ?? 0 > 0;
-    const subscriptions = json.properties?.subscriptions?.oneOf?.length ?? 0 > 0;
+    const notifications = (json.properties?.notifications?.oneOf?.length ?? 0) > 0;
+    const requests = (json.properties?.requests?.oneOf?.length ?? 0) > 0;
+    const subscriptions = (json.properties?.subscriptions?.oneOf?.length ?? 0) > 0;
     const lines = [];
     if (notifications) {
         lines.push(`notify: import("@duckduckgo/messaging/lib/shared-types").MessagingBase<${job.topLevelType}>['notify']`)