diff --git a/.editorconfig b/.editorconfig
index 293886f83b3b3401ff6943fa937b1f5f5b0a6b0b..72bdfe164277ba15220143b9d2fe203f6f02d248 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -13,7 +13,7 @@ insert_final_newline = true
 trim_trailing_whitespace = true
 
 # TS/JS-Files
-[*.{ts,js}]
+[*.{ts,js,mjs}]
 indent_size = 2
 
 # JSON-Files
diff --git a/Build/Gruntfile.js b/Build/Gruntfile.js
index e6c5648608c9eaefa1e02a3d778dae079195f4fe..e98eba3da78ef46540b7b20bd27712906e6295b7 100644
--- a/Build/Gruntfile.js
+++ b/Build/Gruntfile.js
@@ -309,9 +309,8 @@ module.exports = function (grunt) {
 
             source = require('./util/map-import.js').mapImports(source, srcpath, imports);
 
-            // Workaround for https://github.com/microsoft/TypeScript/issues/35802 to avoid
-            // rollup from complaining in karma/jsunit test setup:
-            //   The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
+            // Workaround for https://github.com/microsoft/TypeScript/issues/35802
+            // > The 'this' keyword is equivalent to 'undefined' at the top level of an ES module
             source = source.replace('__decorate=this&&this.__decorate||function', '__decorate=function');
 
             return source;
@@ -320,11 +319,10 @@ module.exports = function (grunt) {
         files: [{
           expand: true,
           cwd: '<%= paths.root %>Build/JavaScript/',
-          src: ['**/*.js', '**/*.js.map'],
+          src: ['**/*.js', '**/*.js.map', '!*/tests/**/*'],
           dest: '<%= paths.sysext %>',
           rename: (dest, src) => dest + src
             .replace('/', '/Resources/Public/JavaScript/')
-            .replace('/Resources/Public/JavaScript/tests/', '/Tests/JavaScript/')
         }]
       },
       core_icons: {
@@ -788,6 +786,7 @@ module.exports = function (grunt) {
             expand: true,
             src: [
               '<%= paths.root %>Build/JavaScript/**/*.js',
+              '!<%= paths.root %>Build/JavaScript/*/tests/**/*',
             ],
             dest: '<%= paths.root %>Build',
             cwd: '.',
diff --git a/Build/JSUnit/Globals.js b/Build/JSUnit/Globals.js
deleted file mode 100644
index 2ceac77355650dcc06d2772edf20101bc302f3a3..0000000000000000000000000000000000000000
--- a/Build/JSUnit/Globals.js
+++ /dev/null
@@ -1,17 +0,0 @@
-if (typeof globalThis.TYPO3 === 'undefined') {
-  globalThis.TYPO3 = globalThis.TYPO3 || {};
-  globalThis.TYPO3.settings = {
-    'FormEngine': {
-      'formName': 'Test'
-    },
-    'DateTimePicker': {
-      'DateFormat': 'd.m.Y'
-    },
-    'ajaxUrls': {
-    }
-  };
-  globalThis.TYPO3.lang = {};
-}
-
-top.TYPO3 = globalThis.TYPO3;
-globalThis.TBE_EDITOR = {};
diff --git a/Build/JSUnit/Helper.js b/Build/JSUnit/Helper.js
deleted file mode 100644
index 422867980844d2fc703e80c6355523d9128c2b5f..0000000000000000000000000000000000000000
--- a/Build/JSUnit/Helper.js
+++ /dev/null
@@ -1,34 +0,0 @@
-'use strict';
-
-/**
- * Helper function to implement DataProvider
- * @param {Function|Array|Object} values
- * @param {Function} func
- */
-function using(values, func) {
-  if (values instanceof Function) {
-    values = values();
-  }
-
-  if (values instanceof Array) {
-    values.forEach(function(value) {
-      if (!(value instanceof Array)) {
-        value = [value];
-      }
-
-      func.apply(this, value);
-    });
-  } else {
-    let objectKeys = Object.keys(values);
-
-    objectKeys.forEach(function(key) {
-      if (!(values[key] instanceof Array)) {
-        values[key] = [values[key]];
-      }
-
-      values[key].push(key);
-
-      func.apply(this, values[key]);
-    });
-  }
-}
diff --git a/Build/JSUnit/JQueryGlobal.js b/Build/JSUnit/JQueryGlobal.js
deleted file mode 100644
index 41c3095043683d4a6657495c43cab30bb81805da..0000000000000000000000000000000000000000
--- a/Build/JSUnit/JQueryGlobal.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import jQuery from 'jquery';
-globalThis.TYPO3 = globalThis.TYPO3 || {};
-globalThis.TYPO3.jQuery = jQuery;
diff --git a/Build/JSUnit/TestSetup.js b/Build/JSUnit/TestSetup.js
deleted file mode 100644
index 884007912247d79eb47a3f2ec7b8198871fb70c2..0000000000000000000000000000000000000000
--- a/Build/JSUnit/TestSetup.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import './JQueryGlobal.js'
-
-// glob-import interpolated by rollup-plugin-glob-import
-import '../../typo3/sysext/**/Tests/JavaScript/**/*.js'
diff --git a/Build/JSUnit/karma.conf.ci.js b/Build/JSUnit/karma.conf.ci.js
deleted file mode 100644
index 290c53b17af48475b60cf78409f5abd9f247cd36..0000000000000000000000000000000000000000
--- a/Build/JSUnit/karma.conf.ci.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/* eslint-env node, commonjs */
-/* eslint-disable @typescript-eslint/no-var-requires */
-module.exports = function (config) {
-  require('./karma.conf.js')(config);
-  config.set({
-    browsers: ['ChromeHeadlessCustom'],
-    customLaunchers: {
-      ChromeHeadlessCustom: {
-        base: 'ChromeHeadless',
-        flags: ['--no-sandbox']
-      }
-    },
-  });
-};
diff --git a/Build/JSUnit/karma.conf.js b/Build/JSUnit/karma.conf.js
deleted file mode 100644
index c53bf8487e1002096276a7eebd0993f855de47fb..0000000000000000000000000000000000000000
--- a/Build/JSUnit/karma.conf.js
+++ /dev/null
@@ -1,146 +0,0 @@
-/* eslint-env node, commonjs */
-/* eslint-disable @typescript-eslint/no-var-requires */
-'use strict';
-
-const fs = require('fs')
-const globImport = require('rollup-plugin-glob-import');
-
-/**
- * Karma configuration
- */
-
-module.exports = function(config) {
-  config.set({
-    // base path that will be used to resolve all patterns (eg. files, exclude)
-    basePath: '../../',
-
-    // frameworks to use
-    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
-    frameworks: ['jasmine'],
-
-    // list of files / patterns to load in the browser
-    files: [
-      { pattern: 'Build/JSUnit/Globals.js' },
-      { pattern: 'Build/JSUnit/Helper.js' },
-      { pattern: 'Build/JSUnit/TestSetup.js', watched: false },
-    ],
-
-    // list of files to exclude
-    exclude: [
-    ],
-
-    // preprocess matching files before serving them to the browser
-    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
-    preprocessors: {
-      'Build/JSUnit/TestSetup.js': ['coverage', 'rollup'],
-    },
-
-
-    rollupPreprocessor: {
-      /**
-       * This is just a normal Rollup config object,
-       * except that `input` is handled for you.
-       */
-      plugins: [
-        globImport({}),
-        {
-          name: 'resolve-modules',
-          resolveId: (source) => {
-
-            const importMap = {
-              'lit': 'lit/index',
-              'lit/': 'lit/',
-              'lit-html': 'lit-html/lit-html',
-              'lit-html/': 'lit-html/',
-              'lit-element': 'lit-element/lit-element',
-              'lit-element/': 'lit-element/',
-              '@lit/reactive-element': '@lit/reactive-element/reactive-element',
-              '@lit/reactive-element/': '@lit/reactive-element/',
-            };
-
-            if (source in importMap) {
-              source = importMap[source];
-            } else {
-              for (let prefix in importMap) {
-                if (prefix.endsWith('/') && source.startsWith(prefix)) {
-                  source = importMap[prefix] + source.substring(prefix.length)
-                  break;
-                }
-              }
-            }
-
-            if (source.startsWith('@typo3')) {
-              const parts = source.substr(7).split('/');
-              const extension = parts.shift().replace(/-/g, '_');
-              const path = parts.join('/');
-              const fullPath = `typo3/sysext/${extension}/Resources/Public/JavaScript/${path}`;
-
-              return { id: fullPath }
-            }
-
-            let contribPath = `typo3/sysext/core/Resources/Public/JavaScript/Contrib/${source}.js`;
-            if (fs.existsSync(contribPath)) {
-              return { id: contribPath }
-            }
-
-            contribPath = `typo3/sysext/core/Resources/Public/JavaScript/Contrib/${source}`;
-            if (fs.existsSync(contribPath)) {
-              return { id: contribPath }
-            }
-
-            return null
-          }
-        }
-      ],
-      output: {
-        format: 'iife',
-        name: 'TYPO3UnitTestBundle',
-        sourcemap: 'inline',
-        inlineDynamicImports: true,
-      },
-    },
-
-    // test results reporter to use
-    // possible values: 'dots', 'progress', 'coverage', 'junit'
-    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
-    reporters: ['progress', 'junit', 'coverage'],
-
-    junitReporter: {
-      outputDir: 'typo3temp/var/tests/',
-      useBrowserName: false,
-      outputFile: 'karma.junit.xml'
-    },
-
-    coverageReporter: {
-      reporters: [
-        { type: 'clover', dir: 'typo3temp', subdir: 'var/tests', file: 'karma.clover.xml' }
-      ]
-    },
-
-    // web server port
-    port: 9876,
-
-    // enable / disable colors in the output (reporters and logs)
-    colors: true,
-
-    // level of logging
-    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
-    logLevel: config.LOG_INFO,
-
-    // enable / disable watching file and executing tests whenever any file changes
-    autoWatch: true,
-
-    // start these browsers
-    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
-    // browsers: ['Firefox', 'Chrome', 'Safari', 'PhantomJS', 'Opera', 'IE'],
-    browsers: ['Chrome'],
-
-    // Continuous Integration mode
-    // if true, Karma captures browsers, runs the tests and exits
-    singleRun: false,
-
-    // Concurrency level
-    // how many browser should be started simultaneous
-    concurrency: Infinity
-  })
-};
diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh
index 35c9db7b2d2b96a98b44be443e4b1a132e63ac2c..602ad3b624879a8f7fe0c7510d5acb627f04829d 100755
--- a/Build/Scripts/runTests.sh
+++ b/Build/Scripts/runTests.sh
@@ -911,7 +911,7 @@ case ${TEST_SUITE} in
         SUITE_EXIT_CODE=$?
         ;;
     unitJavascript)
-        COMMAND="cd Build; npm ci || exit 1; cd ..; Build/node_modules/karma/bin/karma start Build/JSUnit/karma.conf.ci.js --single-run"
+        COMMAND="cd Build; npm ci || exit 1; CHROME_SANDBOX=false BROWSERS=chrome npm run test"
         ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-javascript-${SUFFIX} -e HOME=${CORE_ROOT}/.cache ${IMAGE_NODEJS_CHROME} /bin/sh -c "${COMMAND}"
         SUITE_EXIT_CODE=$?
         ;;
diff --git a/Build/Sources/TypeScript/backend/document-save-actions.ts b/Build/Sources/TypeScript/backend/document-save-actions.ts
index 70bc4b29962b44045fbacc3aff52d8489b5b11df..4d9b969009286fcdd86862e28befbd50dfe69327 100644
--- a/Build/Sources/TypeScript/backend/document-save-actions.ts
+++ b/Build/Sources/TypeScript/backend/document-save-actions.ts
@@ -60,6 +60,11 @@ class DocumentSaveActions {
    * Initializes the save handling
    */
   private initializeSaveHandling(): void {
+    const docHeader = document.querySelector('.t3js-module-docheader');
+    if (docHeader === null) {
+      return;
+    }
+
     const elements = [
       'button[form]',
       'button[name^="_save"]',
@@ -114,7 +119,7 @@ class DocumentSaveActions {
           // e.g. loading a new page
         });
       }, { once: true });
-    }).delegateTo(document.querySelector('.t3js-module-docheader'), elements);
+    }).delegateTo(docHeader, elements);
   }
 
   private getAttachedForm(trigger: SubmitTriggerHTMLElement): HTMLFormElement|null {
diff --git a/Build/Sources/TypeScript/backend/icons.ts b/Build/Sources/TypeScript/backend/icons.ts
index 71c39915d2f2009377f23909b86b46d188a44099..62588af1f8723f532986dbaabe77117b07310426 100644
--- a/Build/Sources/TypeScript/backend/icons.ts
+++ b/Build/Sources/TypeScript/backend/icons.ts
@@ -280,7 +280,9 @@ class Icons {
 let iconsObject: Icons;
 if (!iconsObject) {
   iconsObject = new Icons();
-  TYPO3.Icons = iconsObject;
+  if (typeof TYPO3 !== 'undefined') {
+    TYPO3.Icons = iconsObject;
+  }
 }
 
 export default iconsObject;
diff --git a/Build/Sources/TypeScript/backend/modal.ts b/Build/Sources/TypeScript/backend/modal.ts
index 658b6f7dc2eed71e87399272645c61ff844ed265..95c2e410a673becda9466cb421cf78ce9744efb2 100644
--- a/Build/Sources/TypeScript/backend/modal.ts
+++ b/Build/Sources/TypeScript/backend/modal.ts
@@ -658,8 +658,10 @@ try {
 if (!modalObject) {
   modalObject = new Modal();
 
-  // expose as global object
-  TYPO3.Modal = modalObject;
+  if (typeof TYPO3 !== 'undefined') {
+    // expose as global object
+    TYPO3.Modal = modalObject;
+  }
 }
 
 export default modalObject;
diff --git a/Build/Sources/TypeScript/backend/module-menu.ts b/Build/Sources/TypeScript/backend/module-menu.ts
index a405196e64a1fe5c29ba05367184db16ad7c295d..6b3275d15dd579a19ffdac452cf3d99f32d3a8f3 100644
--- a/Build/Sources/TypeScript/backend/module-menu.ts
+++ b/Build/Sources/TypeScript/backend/module-menu.ts
@@ -522,11 +522,15 @@ interface ModuleMenuNamespace {
   App: ModuleMenu;
 }
 
-if (!top.TYPO3.ModuleMenu) {
-  top.TYPO3.ModuleMenu = {
+let moduleMenuApp: ModuleMenuNamespace = top?.TYPO3?.ModuleMenu;
+
+if (!moduleMenuApp) {
+  moduleMenuApp = {
     App: new ModuleMenu(),
-  };
+  }
+  if (top.TYPO3 !== undefined) {
+    top.TYPO3.ModuleMenu = moduleMenuApp;
+  }
 }
-const moduleMenuApp = <ModuleMenuNamespace>top.TYPO3.ModuleMenu;
 
 export default moduleMenuApp;
diff --git a/Build/Sources/TypeScript/backend/tests/backend-exception-test.ts b/Build/Sources/TypeScript/backend/tests/backend-exception-test.ts
index 41a1ce582923a129cc957e04a1462b516625dc7c..55e9a91a0f4bad25ccd4fbcc593963b977abf55e 100644
--- a/Build/Sources/TypeScript/backend/tests/backend-exception-test.ts
+++ b/Build/Sources/TypeScript/backend/tests/backend-exception-test.ts
@@ -11,16 +11,18 @@
  * The TYPO3 project - inspiring people to share!
  */
 
-import { BackendException } from '@typo3/backend/backend-exception';
+import { BackendException } from '@typo3/backend/backend-exception.js';
+import { expect } from '@open-wc/testing';
+import type { } from 'mocha';
 
 describe('@typo3/backend/backend-exception', () => {
   it('sets exception message', () => {
     const backendException: BackendException = new BackendException('some message');
-    expect(backendException.message).toBe('some message');
+    expect(backendException.message).to.equal('some message');
   });
 
   it('sets exception code', () => {
     const backendException: BackendException = new BackendException('', 12345);
-    expect(backendException.code).toBe(12345);
+    expect(backendException.code).to.equal(12345);
   });
 });
diff --git a/Build/Sources/TypeScript/backend/tests/backend/storage/browser-storage-test.ts b/Build/Sources/TypeScript/backend/tests/backend/storage/browser-storage-test.ts
index 96455b5939b643aa7d5b360e87012e0b166304ab..8f02af7db3a134861d0f39f0e78bf21cf786ccb5 100644
--- a/Build/Sources/TypeScript/backend/tests/backend/storage/browser-storage-test.ts
+++ b/Build/Sources/TypeScript/backend/tests/backend/storage/browser-storage-test.ts
@@ -11,7 +11,9 @@
  * The TYPO3 project - inspiring people to share!
  */
 
-import BrowserSession from '@typo3/backend/storage/browser-session';
+import BrowserSession from '@typo3/backend/storage/browser-session.js';
+import { expect } from '@open-wc/testing';
+import type { } from 'mocha';
 
 describe('@typo3/backend/storage/browser-session', () => {
   afterEach((): void => {
@@ -21,14 +23,14 @@ describe('@typo3/backend/storage/browser-session', () => {
   it('can set and get item', () => {
     const key = 'test-key';
     BrowserSession.set(key, 'foo');
-    expect(BrowserSession.get(key)).toBe('foo');
+    expect(BrowserSession.get(key)).to.equal('foo');
   });
 
   it('can check if item is set', () => {
     const key = 'test-key';
-    expect(BrowserSession.isset(key)).toBeFalse();
+    expect(BrowserSession.isset(key)).to.be.false;
     BrowserSession.set(key, 'foo');
-    expect(BrowserSession.isset(key)).toBeTrue();
+    expect(BrowserSession.isset(key)).to.be.true;
   });
 
   it('can get multiple items by prefix', () => {
@@ -42,16 +44,16 @@ describe('@typo3/backend/storage/browser-session', () => {
     }
 
     const items = BrowserSession.getByPrefix('test-prefix-');
-    expect(items).toEqual(entries);
+    expect(items).to.eql(entries);
   });
 
   it('can remove item', () => {
     const key = 'item-to-be-removed';
     BrowserSession.set(key, 'foo');
-    expect(BrowserSession.get(key)).not.toBeNull();
+    expect(BrowserSession.get(key)).not.to.be.null;
 
     BrowserSession.unset(key);
-    expect(BrowserSession.get(key)).toBeNull();
+    expect(BrowserSession.get(key)).to.be.null;
   });
 
   it('can remove multiple items by prefix', () => {
@@ -66,7 +68,7 @@ describe('@typo3/backend/storage/browser-session', () => {
     BrowserSession.unsetByPrefix('test-prefix-');
 
     const items = BrowserSession.getByPrefix('test-prefix-');
-    expect(items).toHaveSize(0);
+    expect(items).to.be.empty;
   });
 
   it('can clear storage', () => {
@@ -80,6 +82,6 @@ describe('@typo3/backend/storage/browser-session', () => {
     }
     BrowserSession.clear();
 
-    expect(sessionStorage.length).toHaveSize(0);
+    expect(sessionStorage).to.be.empty;
   });
 });
diff --git a/Build/Sources/TypeScript/backend/tests/backend/storage/client-test.ts b/Build/Sources/TypeScript/backend/tests/backend/storage/client-test.ts
index b39a35fbe330bf35cbe9c913098b2e1c0c18a677..0c24273f0fd68f15ee7857948837b5a121a796ad 100644
--- a/Build/Sources/TypeScript/backend/tests/backend/storage/client-test.ts
+++ b/Build/Sources/TypeScript/backend/tests/backend/storage/client-test.ts
@@ -11,7 +11,9 @@
  * The TYPO3 project - inspiring people to share!
  */
 
-import Client from '@typo3/backend/storage/client';
+import Client from '@typo3/backend/storage/client.js';
+import { expect } from '@open-wc/testing';
+import type { } from 'mocha';
 
 describe('@typo3/backend/storage/client', () => {
   afterEach((): void => {
@@ -21,14 +23,14 @@ describe('@typo3/backend/storage/client', () => {
   it('can set and get item', () => {
     const key = 'test-key';
     Client.set(key, 'foo');
-    expect(Client.get(key)).toBe('foo');
+    expect(Client.get(key)).to.equal('foo');
   });
 
   it('can check if item is set', () => {
     const key = 'test-key';
-    expect(Client.isset(key)).toBeFalse();
+    expect(Client.isset(key)).to.be.false;
     Client.set(key, 'foo');
-    expect(Client.isset(key)).toBeTrue();
+    expect(Client.isset(key)).to.be.true;
   });
 
   it('can get multiple items by prefix', () => {
@@ -42,16 +44,16 @@ describe('@typo3/backend/storage/client', () => {
     }
 
     const items = Client.getByPrefix('test-prefix-');
-    expect(items).toEqual(entries);
+    expect(items).to.eql(entries);
   });
 
   it('can remove item', () => {
     const key = 'item-to-be-removed';
     Client.set(key, 'foo');
-    expect(Client.get(key)).not.toBeNull();
+    expect(Client.get(key)).not.to.be.null;
 
     Client.unset(key);
-    expect(Client.get(key)).toBeNull();
+    expect(Client.get(key)).to.be.null;
   });
 
   it('can remove multiple items by prefix', () => {
@@ -66,7 +68,7 @@ describe('@typo3/backend/storage/client', () => {
     Client.unsetByPrefix('test-prefix-');
 
     const items = Client.getByPrefix('test-prefix-');
-    expect(items).toHaveSize(0);
+    expect(items).to.be.empty;
   });
 
   it('can clear storage', () => {
@@ -80,6 +82,6 @@ describe('@typo3/backend/storage/client', () => {
     }
     Client.clear();
 
-    expect(localStorage.length).toHaveSize(0);
+    expect(localStorage).to.be.empty;
   });
 });
diff --git a/Build/Sources/TypeScript/backend/tests/element/immediate-action-element-test.ts b/Build/Sources/TypeScript/backend/tests/element/immediate-action-element-test.ts
index 50d59662de9e05780c6214007871c3b3107259ce..46b658d50d1cb1d05ddd50c43742d95e7c7dacbd 100644
--- a/Build/Sources/TypeScript/backend/tests/element/immediate-action-element-test.ts
+++ b/Build/Sources/TypeScript/backend/tests/element/immediate-action-element-test.ts
@@ -11,11 +11,14 @@
  * The TYPO3 project - inspiring people to share!
  */
 
-import { ImmediateActionElement } from '@typo3/backend/element/immediate-action-element';
-import moduleMenuApp from '@typo3/backend/module-menu';
-import viewportObject from '@typo3/backend/viewport';
+import { ImmediateActionElement } from '@typo3/backend/element/immediate-action-element.js';
+import moduleMenuApp from '@typo3/backend/module-menu.js';
+import viewportObject from '@typo3/backend/viewport.js';
+import { expect } from '@open-wc/testing';
+import { stub } from 'sinon';
+import type { } from 'mocha';
 
-describe('TYPO3/CMS/Backend/Element/ImmediateActionElement:', () => {
+describe('@typo3/backend/element/immediate-action-element', () => {
   let root: HTMLElement; // This will hold the actual element under test.
 
   beforeEach((): void => {
@@ -29,74 +32,50 @@ describe('TYPO3/CMS/Backend/Element/ImmediateActionElement:', () => {
   });
 
   it('dispatches action when created via constructor', async () => {
-    const backup = viewportObject.Topbar;
-    const observer = {
-      refresh: (): void => {
-        return;
-      },
-    };
-    spyOn(observer, 'refresh').and.callThrough();
-    (viewportObject as any).Topbar = observer;
+    const refreshStub = stub(viewportObject.Topbar, 'refresh')
+
     const element = new ImmediateActionElement;
     element.setAttribute('action', 'TYPO3.Backend.Topbar.refresh');
-    expect(observer.refresh).not.toHaveBeenCalled();
+    expect(refreshStub).not.to.have.been.called;
     root.appendChild(element);
-    await import('@typo3/backend/viewport');
+    await import('@typo3/backend/viewport.js');
     await new Promise((resolve) => setTimeout(resolve, 100));
-    expect(observer.refresh).toHaveBeenCalled();
-    (viewportObject as any).Topbar = backup;
+    expect(refreshStub).to.have.been.called;
+
+    refreshStub.restore();
   });
 
   it('dispatches action when created via createElement', async () => {
-    const backup = viewportObject.Topbar;
-    const observer = {
-      refresh: (): void => {
-        return;
-      },
-    };
-    spyOn(observer, 'refresh').and.callThrough();
-    (viewportObject as any).Topbar = observer;
+    const refreshStub = stub(viewportObject.Topbar, 'refresh')
+
     const element = <ImmediateActionElement>document.createElement('typo3-immediate-action');
     element.setAttribute('action', 'TYPO3.Backend.Topbar.refresh');
-    expect(observer.refresh).not.toHaveBeenCalled();
+    expect(refreshStub).not.to.have.been.called;
     root.appendChild(element);
-    await import('@typo3/backend/viewport');
+    await import('@typo3/backend/viewport.js');
     await new Promise((resolve) => setTimeout(resolve, 100));
-    expect(observer.refresh).toHaveBeenCalled();
-    (viewportObject as any).Topbar = backup;
+    expect(refreshStub).to.have.been.called;
+
+    refreshStub.restore();
   });
 
   it('dispatches action when created from string', async () => {
-    const backup = moduleMenuApp.App;
-    const observer = {
-      refreshMenu: (): void => {
-        return;
-      },
-    };
-    spyOn(observer, 'refreshMenu').and.callThrough();
-    (moduleMenuApp as any).App = observer;
+    const refreshMenuStub = stub(moduleMenuApp.App, 'refreshMenu')
     const element = document.createRange().createContextualFragment('<typo3-immediate-action action="TYPO3.ModuleMenu.App.refreshMenu"></typo3-immediate-action>').querySelector('typo3-immediate-action');
-    expect(observer.refreshMenu).not.toHaveBeenCalled();
+    expect(refreshMenuStub).not.to.have.been.called;
     root.appendChild(element);
-    await import('@typo3/backend/module-menu');
+    await import('@typo3/backend/module-menu.js');
     await new Promise((resolve) => setTimeout(resolve, 100));
-    expect(observer.refreshMenu).toHaveBeenCalled();
-    (viewportObject as any).App = backup;
+    expect(refreshMenuStub).to.have.been.called;
+    refreshMenuStub.restore();
   });
 
   it('dispatches action when created via innerHTML', async () => {
-    const backup = moduleMenuApp.App;
-    const observer = {
-      refreshMenu: (): void => {
-        return;
-      },
-    };
-    spyOn(observer, 'refreshMenu').and.callThrough();
-    (moduleMenuApp as any).App = observer;
+    const refreshMenuStub = stub(moduleMenuApp.App, 'refreshMenu')
     root.innerHTML = '<typo3-immediate-action action="TYPO3.ModuleMenu.App.refreshMenu"></typo3-immediate-action>';
-    await import('@typo3/backend/module-menu');
+    await import('@typo3/backend/module-menu.js');
     await new Promise((resolve) => setTimeout(resolve, 100));
-    expect(observer.refreshMenu).toHaveBeenCalled();
-    (moduleMenuApp as any).App = backup;
+    expect(refreshMenuStub).to.have.been.called;
+    refreshMenuStub.restore();
   });
 });
diff --git a/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts b/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts
index e5102f02fdef8f02df335c14da968b55069e0293..9617009d82fe323676b92832ab58af43147101c5 100644
--- a/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts
+++ b/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts
@@ -1,6 +1,6 @@
-import FormEngineValidation from '@typo3/backend/form-engine-validation';
-
-declare function using(values: (() => Array<object>)|Array<object>|object, func: (testCase: FormatValueData|ProcessValueData) => void): void;
+import FormEngineValidation from '@typo3/backend/form-engine-validation.js';
+import { expect } from '@open-wc/testing';
+import type { } from 'mocha';
 
 interface FormEngineConfig {
   [x: string]: any;
@@ -22,6 +22,42 @@ interface ProcessValueData {
   result: string
 }
 
+//type Values = Array<object>|object;
+type Values = any;
+
+/**
+ * Helper function to implement DataProvider
+ * @param {Function|Array|Object} values
+ * @param {Function} func
+ */
+function using(values: (() => Values)|Values, func: (...args: unknown[]) => void): void {
+  if (values instanceof Function) {
+    values = values();
+  }
+
+  if (values instanceof Array) {
+    values.forEach(function(value) {
+      if (!(value instanceof Array)) {
+        value = [value];
+      }
+
+      func(...value);
+    });
+  } else {
+    const objectKeys = Object.keys(values);
+
+    objectKeys.forEach(function(key) {
+      if (!(values[key] instanceof Array)) {
+        values[key] = [values[key]];
+      }
+
+      values[key].push(key);
+
+      func(...values[key]);
+    });
+  }
+}
+
 describe('TYPO3/CMS/Backend/FormEngineValidationTest:', () => {
   const formatValueDataProvider: Array<FormatValueData> = [
     {
@@ -110,16 +146,12 @@ describe('TYPO3/CMS/Backend/FormEngineValidationTest:', () => {
     }
   ];
 
-  /**
-   * @dataProvider formatValueDataProvider
-   * @test
-   */
   describe('tests for formatValue', () => {
     using(formatValueDataProvider, function(testCase: FormatValueData) {
       it(testCase.description, () => {
         FormEngineValidation.initialize();
         const result = FormEngineValidation.formatValue(testCase.type, testCase.value, testCase.config);
-        expect(result).toBe(testCase.result);
+        expect(result).to.equal(testCase.result);
       });
     });
   });
@@ -155,136 +187,108 @@ describe('TYPO3/CMS/Backend/FormEngineValidationTest:', () => {
     }
   ];
 
-  /**
-   * @dataProvider processValueDataProvider
-   * @test
-   */
   describe('test for processValue', () => {
     using(processValueDataProvider, function(testCase: ProcessValueData) {
       it(testCase.description, () => {
         const result = FormEngineValidation.processValue(testCase.command, testCase.value, testCase.config);
-        expect(result).toBe(testCase.result);
+        expect(result).to.equal(testCase.result);
       });
     });
   });
 
-  /**
-   * @test
-   */
   describe('tests for parseInt', () => {
     it('works for value 0', () => {
-      expect(FormEngineValidation.parseInt(0)).toBe(0);
+      expect(FormEngineValidation.parseInt(0)).to.equal(0);
     });
     it('works for value 1', () => {
-      expect(FormEngineValidation.parseInt(1)).toBe(1);
+      expect(FormEngineValidation.parseInt(1)).to.equal(1);
     });
     it('works for value -1', () => {
-      expect(FormEngineValidation.parseInt(-1)).toBe(-1);
+      expect(FormEngineValidation.parseInt(-1)).to.equal(-1);
     });
     it('works for value "0"', () => {
-      expect(FormEngineValidation.parseInt('0')).toBe(0);
+      expect(FormEngineValidation.parseInt('0')).to.equal(0);
     });
     it('works for value "1"', () => {
-      expect(FormEngineValidation.parseInt('1')).toBe(1);
+      expect(FormEngineValidation.parseInt('1')).to.equal(1);
     });
     it('works for value "-1"', () => {
-      expect(FormEngineValidation.parseInt('-1')).toBe(-1);
+      expect(FormEngineValidation.parseInt('-1')).to.equal(-1);
     });
     it('works for value 0.5', () => {
-      expect(FormEngineValidation.parseInt(0.5)).toBe(0);
+      expect(FormEngineValidation.parseInt(0.5)).to.equal(0);
     });
     it('works for value "0.5"', () => {
-      expect(FormEngineValidation.parseInt('0.5')).toBe(0);
+      expect(FormEngineValidation.parseInt('0.5')).to.equal(0);
     });
     it('works for value "foo"', () => {
-      expect(FormEngineValidation.parseInt('foo')).toBe(0);
+      expect(FormEngineValidation.parseInt('foo')).to.equal(0);
     });
     it('works for value true', () => {
-      expect(FormEngineValidation.parseInt(true)).toBe(0);
+      expect(FormEngineValidation.parseInt(true)).to.equal(0);
     });
     it('works for value false', () => {
-      expect(FormEngineValidation.parseInt(false)).toBe(0);
+      expect(FormEngineValidation.parseInt(false)).to.equal(0);
     });
     it('works for value null', () => {
-      expect(FormEngineValidation.parseInt(null)).toBe(0);
+      expect(FormEngineValidation.parseInt(null)).to.equal(0);
     });
   });
 
-  /**
-   * @test
-   */
   describe('tests for parseDouble', () => {
     it('works for value 0', () => {
-      expect(FormEngineValidation.parseDouble(0)).toBe('0.00');
+      expect(FormEngineValidation.parseDouble(0)).to.equal('0.00');
     });
     it('works for value 1', () => {
-      expect(FormEngineValidation.parseDouble(1)).toBe('1.00');
+      expect(FormEngineValidation.parseDouble(1)).to.equal('1.00');
     });
     it('works for value -1', () => {
-      expect(FormEngineValidation.parseDouble(-1)).toBe('-1.00');
+      expect(FormEngineValidation.parseDouble(-1)).to.equal('-1.00');
     });
     it('works for value "0"', () => {
-      expect(FormEngineValidation.parseDouble('0')).toBe('0.00');
+      expect(FormEngineValidation.parseDouble('0')).to.equal('0.00');
     });
     it('works for value "1"', () => {
-      expect(FormEngineValidation.parseDouble('1')).toBe('1.00');
+      expect(FormEngineValidation.parseDouble('1')).to.equal('1.00');
     });
     it('works for value "-1"', () => {
-      expect(FormEngineValidation.parseDouble('-1')).toBe('-1.00');
+      expect(FormEngineValidation.parseDouble('-1')).to.equal('-1.00');
     });
     it('works for value 0.5', () => {
-      expect(FormEngineValidation.parseDouble(0.5)).toBe('0.50');
+      expect(FormEngineValidation.parseDouble(0.5)).to.equal('0.50');
     });
     it('works for value "0.5"', () => {
-      expect(FormEngineValidation.parseDouble('0.5')).toBe('0.50');
+      expect(FormEngineValidation.parseDouble('0.5')).to.equal('0.50');
     });
     it('works for value "foo"', () => {
-      expect(FormEngineValidation.parseDouble('foo')).toBe('0.00');
+      expect(FormEngineValidation.parseDouble('foo')).to.equal('0.00');
     });
     it('works for value true', () => {
-      expect(FormEngineValidation.parseDouble(true)).toBe('0.00');
+      expect(FormEngineValidation.parseDouble(true)).to.equal('0.00');
     });
     it('works for value false', () => {
-      expect(FormEngineValidation.parseDouble(false)).toBe('0.00');
+      expect(FormEngineValidation.parseDouble(false)).to.equal('0.00');
     });
     it('works for value null', () => {
-      expect(FormEngineValidation.parseDouble(null)).toBe('0.00');
+      expect(FormEngineValidation.parseDouble(null)).to.equal('0.00');
     });
   });
 
-  /**
-   * @test
-   */
   describe('tests for getYear', () => {
-    const currentDate = new Date();
-    afterEach(() => {
-      jasmine.clock().mockDate(currentDate);
-    });
-
     it('works for current date', () => {
       const date = new Date();
-      expect(FormEngineValidation.getYear(date)).toBe(date.getFullYear());
+      expect(FormEngineValidation.getYear(date)).to.equal(date.getFullYear());
     });
     it('works for year 2013', () => {
-      const baseTime = new Date(2013, 9, 23);
-      jasmine.clock().mockDate(baseTime);
-      expect(FormEngineValidation.getYear(baseTime)).toBe(2013);
+      const baseTime = new Date(2013, 9, 23, 1, 0, 0);
+      expect(FormEngineValidation.getYear(baseTime)).to.equal(2013);
     });
   });
 
-  /**
-   * @test
-   */
   describe('tests for getDate', () => {
-    const currentDate = new Date();
-    afterEach(() => {
-      jasmine.clock().mockDate(currentDate);
-    });
-
     xit('works for year 2013', () => {
       const baseTime = new Date(2013, 9, 23, 13, 13, 13);
-      jasmine.clock().mockDate(baseTime);
-      expect(FormEngineValidation.getDate(baseTime)).toBe(1382479200);
+      expect(FormEngineValidation.getDate(baseTime)).to.equal(1382486400);
     });
   });
 });
diff --git a/Build/Sources/TypeScript/backend/tests/grid-editor-test.ts b/Build/Sources/TypeScript/backend/tests/grid-editor-test.ts
index 131e6580c86e22c5cd9b613f1fc2946d48854503..9b40706c45143c0eb4332717d269c7179dc3ebe6 100644
--- a/Build/Sources/TypeScript/backend/tests/grid-editor-test.ts
+++ b/Build/Sources/TypeScript/backend/tests/grid-editor-test.ts
@@ -11,16 +11,18 @@
  * The TYPO3 project - inspiring people to share!
  */
 
-import { GridEditor } from '@typo3/backend/grid-editor';
+import { GridEditor } from '@typo3/backend/grid-editor.js';
+import { expect } from '@open-wc/testing';
+import type { } from 'mocha';
 
-describe('TYPO3/CMS/Backend/GridEditorTest:', () => {
+describe('@typo3/backend/grid-editor-test:', () => {
 
   describe('tests for stripMarkup', () => {
     it('works with string which contains html markup only', () => {
-      expect(GridEditor.stripMarkup('<b>\'formula\': "x > y"</b>')).toBe('\'formula\': "x > y"');
+      expect(GridEditor.stripMarkup('<b>\'formula\': "x > y"</b>')).to.equal('\'formula\': "x > y"');
     });
     it('works with string which contains html markup and normal text', () => {
-      expect(GridEditor.stripMarkup('<b>foo</b> bar')).toBe('foo bar');
+      expect(GridEditor.stripMarkup('<b>foo</b> bar')).to.equal('foo bar');
     });
   });
 
diff --git a/Build/Sources/TypeScript/backend/tests/hashing/md5Test.ts b/Build/Sources/TypeScript/backend/tests/hashing/md5Test.ts
index 74befbcaefcbd546970d5b8b32acd26650f88952..6acdaaebfb21c3046b84346b5d0416e268cd6709 100644
--- a/Build/Sources/TypeScript/backend/tests/hashing/md5Test.ts
+++ b/Build/Sources/TypeScript/backend/tests/hashing/md5Test.ts
@@ -11,16 +11,18 @@
  * The TYPO3 project - inspiring people to share!
  */
 
-import Md5 from '@typo3/backend/hashing/md5';
+import Md5 from '@typo3/backend/hashing/md5.js';
+import { expect } from '@open-wc/testing';
+import type { } from 'mocha';
 
-describe('TYPO3/CMS/Backend/Hashing/Md5:', () => {
+describe('@typo3/backend/hashing/md5', () => {
   describe('tests for hash', () => {
     it('hashes a value as expected', () => {
-      expect(Md5.hash('Hello World')).toBe('b10a8db164e0754105b7a99be72e3fe5');
+      expect(Md5.hash('Hello World')).to.equal('b10a8db164e0754105b7a99be72e3fe5');
       expect(
         Md5.hash('TYPO3 CMS is an Open Source Enterprise Content Management System with a large global community,'
           + ' backed by the approximately 900 members of the TYPO3 Association.'),
-      ).toBe('65b0beb76ada01bd7b5f44fb37da6139');
+      ).to.equal('65b0beb76ada01bd7b5f44fb37da6139');
     });
   });
 });
diff --git a/Build/Sources/TypeScript/backend/tests/icons-test.ts b/Build/Sources/TypeScript/backend/tests/icons-test.ts
index 8e7f76642a0cac9038328068fd65163b8a8258eb..6c5a355067e2687522317d5cc62c2ba7104f371c 100644
--- a/Build/Sources/TypeScript/backend/tests/icons-test.ts
+++ b/Build/Sources/TypeScript/backend/tests/icons-test.ts
@@ -1,63 +1,38 @@
-import Icons from '@typo3/backend/icons';
+import Icons from '@typo3/backend/icons.js';
+import { expect } from '@open-wc/testing';
+import type { } from 'mocha';
 
-describe('TYPO3/CMS/Backend/IconsTest:', () => {
-  /**
-   * @test
-   */
+describe('@typo3/backend/icons-test', () => {
   describe('tests for Icons object', () => {
     it('has all sizes', () => {
-      expect(Icons.sizes.small).toBe('small');
-      expect(Icons.sizes.default).toBe('default');
-      expect(Icons.sizes.large).toBe('large');
-      expect(Icons.sizes.overlay).toBe('overlay');
+      expect(Icons.sizes.small).to.equal('small');
+      expect(Icons.sizes.default).to.equal('default');
+      expect(Icons.sizes.large).to.equal('large');
+      expect(Icons.sizes.overlay).to.equal('overlay');
     });
     it('has all states', () => {
-      expect(Icons.states.default).toBe('default');
-      expect(Icons.states.disabled).toBe('disabled');
+      expect(Icons.states.default).to.equal('default');
+      expect(Icons.states.disabled).to.equal('disabled');
     });
     it('has all markupIdentifiers', () => {
-      expect(Icons.markupIdentifiers.default).toBe('default');
-      expect(Icons.markupIdentifiers.inline).toBe('inline');
+      expect(Icons.markupIdentifiers.default).to.equal('default');
+      expect(Icons.markupIdentifiers.inline).to.equal('inline');
     });
   });
 
-  /**
-   * @test
-   */
-  describe('tests for Icons::getIcon', () => {
-    beforeEach(() => {
-      spyOn(Icons, 'getIcon');
-      Icons.getIcon('test', Icons.sizes.small, null, Icons.states.default, Icons.markupIdentifiers.default);
-    });
-
-    it('tracks that the spy was called', () => {
-      expect(Icons.getIcon).toHaveBeenCalled();
-    });
-    it('tracks all the arguments of its calls', () => {
-      expect(Icons.getIcon).toHaveBeenCalledWith('test', Icons.sizes.small, null, Icons.states.default, Icons.markupIdentifiers.default);
-    });
-    xit('works get icon from remote server');
-  });
-
-  /**
-   * @test
-   */
   describe('tests for Icons::putInCache', () => {
     it('works for simply identifier and markup', () => {
       const promise = new Promise<void>((reveal) => reveal());
       (Icons as any).putInPromiseCache('foo', promise);
-      expect((Icons as any).getFromPromiseCache('foo')).toBe(promise);
-      expect((Icons as any).isPromiseCached('foo')).toBe(true);
+      expect((Icons as any).getFromPromiseCache('foo')).to.equal(promise);
+      expect((Icons as any).isPromiseCached('foo')).to.be.true;
     });
   });
 
-  /**
-   * @test
-   */
   describe('tests for Icons::getFromPromiseCache', () => {
     it('return undefined for uncached promise', () => {
-      expect((Icons as any).getFromPromiseCache('bar')).not.toBeDefined();
-      expect((Icons as any).isPromiseCached('bar')).toBe(false);
+      expect((Icons as any).getFromPromiseCache('bar')).to.be.undefined;
+      expect((Icons as any).isPromiseCached('bar')).to.be.false;
     });
   });
 });
diff --git a/Build/Sources/TypeScript/backend/tests/notification-test.ts b/Build/Sources/TypeScript/backend/tests/notification-test.ts
index e4ee1dea0e5cf78e74dd82ceb4d06609c227ad3f..f002ba8cf6d8ff293c82450c8ca906b13939593f 100644
--- a/Build/Sources/TypeScript/backend/tests/notification-test.ts
+++ b/Build/Sources/TypeScript/backend/tests/notification-test.ts
@@ -11,22 +11,30 @@
  * The TYPO3 project - inspiring people to share!
  */
 
-import DeferredAction from '@typo3/backend/action-button/deferred-action';
-import ImmediateAction from '@typo3/backend/action-button/immediate-action';
-import Notification from '@typo3/backend/notification';
+import DeferredAction from '@typo3/backend/action-button/deferred-action.js';
+import ImmediateAction from '@typo3/backend/action-button/immediate-action.js';
+import Notification from '@typo3/backend/notification.js';
+import Icons from '@typo3/backend/icons.js';
 import type { LitElement } from 'lit';
-import Icons from '@typo3/backend/icons';
+import { expect } from '@open-wc/testing';
+import { stub, SinonStub } from 'sinon';
+import type { } from 'mocha';
+
+describe('@typo3/backend/notification:', () => {
+  let getIconStub: SinonStub<Parameters<typeof Icons['getIcon']>, Promise<string>>;
 
-describe('TYPO3/CMS/Backend/Notification:', () => {
   beforeEach((): void => {
+    getIconStub = stub(Icons, 'getIcon');
+    getIconStub.returns(Promise.resolve('X'));
+  });
+
+  afterEach((): void => {
+    getIconStub.restore();
+
     const alertContainer = document.getElementById('alert-container');
     while (alertContainer !== null && alertContainer.firstChild) {
       alertContainer.removeChild(alertContainer.firstChild);
     }
-
-    spyOn(Icons, 'getIcon').and.callFake((): Promise<string> => {
-      return Promise.resolve('X');
-    });
   });
 
   describe('can render notifications with dismiss after 1000ms', () => {
@@ -79,12 +87,12 @@ describe('TYPO3/CMS/Backend/Notification:', () => {
         await (document.querySelector('#alert-container typo3-notification-message:last-child') as LitElement).updateComplete;
         const alertSelector = 'div.alert.' + dataSet.class;
         const alertBox = document.querySelector(alertSelector);
-        expect(alertBox).not.toBe(null);
-        expect(alertBox.querySelector('.alert-title').textContent).toEqual(dataSet.title);
-        expect(alertBox.querySelector('.alert-message').textContent).toEqual(dataSet.message);
+        expect(alertBox).not.to.be.null;
+        expect(alertBox.querySelector('.alert-title').textContent).to.equal(dataSet.title);
+        expect(alertBox.querySelector('.alert-message').textContent).to.equal(dataSet.message);
         // wait for the notification to disappear for the next assertion (which tests for auto dismiss)
         await new Promise(resolve => window.setTimeout(resolve, 2000));
-        expect(document.querySelector(alertSelector)).toBe(null);
+        expect(document.querySelector(alertSelector)).to.be.null;
       });
     }
   });
@@ -112,20 +120,14 @@ describe('TYPO3/CMS/Backend/Notification:', () => {
 
     await (document.querySelector('#alert-container typo3-notification-message:last-child') as LitElement).updateComplete;
     const alertBox = document.querySelector('div.alert');
-    expect(alertBox.querySelector('.alert-actions')).not.toBe(null);
-    expect(alertBox.querySelectorAll('.alert-actions a').length).toEqual(2);
-    expect(alertBox.querySelectorAll('.alert-actions a')[0].textContent).toEqual('My action');
-    expect(alertBox.querySelectorAll('.alert-actions a')[1].textContent).toEqual('My other action');
+    expect(alertBox.querySelector('.alert-actions')).not.to.be.null;
+    expect(alertBox.querySelectorAll('.alert-actions a').length).to.equal(2);
+    expect(alertBox.querySelectorAll('.alert-actions a')[0].textContent).to.equal('My action');
+    expect(alertBox.querySelectorAll('.alert-actions a')[1].textContent).to.equal('My other action');
   });
 
   it('immediate action is called', async () => {
-    const observer = {
-      callback: (): void => {
-        return;
-      },
-    };
-
-    spyOn(observer, 'callback').and.callThrough();
+    let called = false;
 
     Notification.info(
       'Info message',
@@ -134,7 +136,9 @@ describe('TYPO3/CMS/Backend/Notification:', () => {
       [
         {
           label: 'My immediate action',
-          action: new ImmediateAction(observer.callback),
+          action: new ImmediateAction(() => {
+            called = true
+          }),
         },
       ],
     );
@@ -143,6 +147,6 @@ describe('TYPO3/CMS/Backend/Notification:', () => {
     const alertBox = document.querySelector('div.alert');
     (<HTMLAnchorElement>alertBox.querySelector('.alert-actions a')).click();
     await (document.querySelector('#alert-container typo3-notification-message:last-child') as LitElement).updateComplete;
-    expect(observer.callback).toHaveBeenCalled();
+    expect(called).to.be.true;
   });
 });
diff --git a/Build/Sources/TypeScript/backend/tests/popover-test.ts b/Build/Sources/TypeScript/backend/tests/popover-test.ts
index f7d303ac579d68605796b7d05e9781fe9f03ddd2..1a628e83cddc2a1132ba2f86e43567b9a9c0eb54 100644
--- a/Build/Sources/TypeScript/backend/tests/popover-test.ts
+++ b/Build/Sources/TypeScript/backend/tests/popover-test.ts
@@ -1,10 +1,9 @@
 import { Popover as BootstrapPopover } from 'bootstrap';
-import Popover from '@typo3/backend/popover';
+import Popover from '@typo3/backend/popover.js';
+import { expect } from '@open-wc/testing';
+import type { } from 'mocha';
 
-describe('TYPO3/CMS/Backend/PopoverTest:', () => {
-  /**
-   * @test
-   */
+describe('@typo3/backend/popover-test', () => {
   describe('initialize', () => {
     const element = document.createElement('div');
     element.dataset.bsToggle = 'popover';
@@ -12,7 +11,7 @@ describe('TYPO3/CMS/Backend/PopoverTest:', () => {
 
     it('works with default selector', () => {
       Popover.initialize();
-      expect(element.outerHTML).toBe('<div data-bs-toggle="popover"></div>');
+      expect(element.outerHTML).to.equal('<div data-bs-toggle="popover"></div>');
     });
 
     const element2 = document.createElement('div');
@@ -21,7 +20,7 @@ describe('TYPO3/CMS/Backend/PopoverTest:', () => {
     document.body.append(element2);
     it('works with default selector and title attribute', () => {
       Popover.initialize();
-      expect(element2.outerHTML).toBe('<div data-bs-toggle="popover" data-title="foo" data-bs-title="foo"></div>');
+      expect(element2.outerHTML).to.equal('<div data-bs-toggle="popover" data-title="foo" data-bs-title="foo"></div>');
     });
 
     const element3 = document.createElement('div');
@@ -30,7 +29,7 @@ describe('TYPO3/CMS/Backend/PopoverTest:', () => {
     document.body.append(element3);
     it('works with default selector and content attribute', () => {
       Popover.initialize();
-      expect(element3.outerHTML).toBe('<div data-bs-toggle="popover" data-bs-content="foo"></div>');
+      expect(element3.outerHTML).to.equal('<div data-bs-toggle="popover" data-bs-content="foo"></div>');
     });
 
     const element4 = document.createElement('div');
@@ -38,7 +37,7 @@ describe('TYPO3/CMS/Backend/PopoverTest:', () => {
     document.body.append(element4);
     it('works with custom selector', () => {
       Popover.initialize('.t3js-popover');
-      expect(element4.outerHTML).toBe('<div class="t3js-popover"></div>');
+      expect(element4.outerHTML).to.equal('<div class="t3js-popover"></div>');
     });
   });
 
@@ -51,14 +50,14 @@ describe('TYPO3/CMS/Backend/PopoverTest:', () => {
 
     it('can set title', () => {
       Popover.initialize('.t3js-test-set-options');
-      expect(element.getAttribute('data-title')).toBe('foo-title');
-      expect(element.getAttribute('data-bs-content')).toBe('foo-content');
+      expect(element.getAttribute('data-title')).to.equal('foo-title');
+      expect(element.getAttribute('data-bs-content')).to.equal('foo-content');
       Popover.setOptions(element, <BootstrapPopover.Options>{
         'title': 'bar-title'
       });
-      expect(element.getAttribute('data-title')).toBe('foo-title');
-      expect(element.getAttribute('data-bs-content')).toBe('foo-content');
-      expect(element.getAttribute('data-bs-original-title')).toBe('bar-title');
+      expect(element.getAttribute('data-title')).to.equal('foo-title');
+      expect(element.getAttribute('data-bs-content')).to.equal('foo-content');
+      expect(element.getAttribute('data-bs-original-title')).to.equal('bar-title');
     });
 
     const element2 = document.createElement('div');
@@ -71,14 +70,14 @@ describe('TYPO3/CMS/Backend/PopoverTest:', () => {
       Popover.initialize('.t3js-test-set-options2');
       // Popover must be visible before the content can be updated manually via setOptions()
       Popover.show(element2);
-      expect(element2.getAttribute('data-title')).toBe('foo-title');
-      expect(element2.getAttribute('data-bs-content')).toBe('foo-content');
+      expect(element2.getAttribute('data-title')).to.equal('foo-title');
+      expect(element2.getAttribute('data-bs-content')).to.equal('foo-content');
       Popover.setOptions(element2, <BootstrapPopover.Options>{
         'content': 'bar-content'
       });
-      expect(element2.getAttribute('data-title')).toBe('foo-title');
-      expect(element2.getAttribute('data-bs-content')).toBe('bar-content');
-      expect(element2.getAttribute('data-bs-original-title')).toBe('foo-title');
+      expect(element2.getAttribute('data-title')).to.equal('foo-title');
+      expect(element2.getAttribute('data-bs-content')).to.equal('bar-content');
+      expect(element2.getAttribute('data-bs-original-title')).to.equal('foo-title');
     });
   });
 });
diff --git a/Build/Sources/TypeScript/backend/viewport.ts b/Build/Sources/TypeScript/backend/viewport.ts
index 854aebd68bc15a2a0dc6cea64d07af9024ab6314..4cd6d331b8aa423e433f095c9f564ab297378a95 100644
--- a/Build/Sources/TypeScript/backend/viewport.ts
+++ b/Build/Sources/TypeScript/backend/viewport.ts
@@ -34,9 +34,11 @@ class Viewport {
 
 let viewportObject: Viewport;
 
-if (!top.TYPO3.Backend) {
+if (!top.TYPO3 || !top.TYPO3.Backend) {
   viewportObject = new Viewport();
-  top.TYPO3.Backend = viewportObject;
+  if (typeof top.TYPO3 !== 'undefined') {
+    top.TYPO3.Backend = viewportObject;
+  }
 } else {
   viewportObject = top.TYPO3.Backend;
 }
diff --git a/Build/Sources/TypeScript/core/tests/ajax/ajax-request-test.ts b/Build/Sources/TypeScript/core/tests/ajax/ajax-request-test.ts
index ea42034a5297fd5cfd83febb9cc38bc5fda0c0b9..91e5e0f84973db91e41cbeb46c482bcdab5f2357 100644
--- a/Build/Sources/TypeScript/core/tests/ajax/ajax-request-test.ts
+++ b/Build/Sources/TypeScript/core/tests/ajax/ajax-request-test.ts
@@ -11,8 +11,11 @@
  * The TYPO3 project - inspiring people to share!
  */
 
-import AjaxRequest from '@typo3/core/ajax/ajax-request';
-import { AjaxResponse } from '@typo3/core/ajax/ajax-response';
+import AjaxRequest from '@typo3/core/ajax/ajax-request.js';
+import { AjaxResponse } from '@typo3/core/ajax/ajax-response.js';
+import { expect } from '@open-wc/testing';
+import { stub, SinonStub } from 'sinon';
+import type { } from 'mocha';
 
 describe('@typo3/core/ajax/ajax-request', (): void => {
   let promiseHelper: {
@@ -20,21 +23,29 @@ describe('@typo3/core/ajax/ajax-request', (): void => {
     reject: (error: Error) => void
   };
 
+  let fetchStub: SinonStub<Parameters<typeof fetch>, Promise<Response>>;
+
   beforeEach((): void => {
-    const fetchPromise: Promise<Response> = new Promise(((resolve: (response: Response) => void, reject: (error: Error) => void): void => {
+    const fetchPromise: Promise<Response> = new Promise((resolve, reject): void => {
       promiseHelper = {
         resolve: resolve,
         reject: reject,
       };
-    }));
-    spyOn(window, 'fetch').and.returnValue(fetchPromise);
+    });
+    fetchStub = stub(window, 'fetch');
+    fetchStub.returns(fetchPromise);
+  });
+
+  afterEach((): void => {
+    fetchStub.restore();
   });
 
   it('sends GET request', (): void => {
     (new AjaxRequest('https://example.com')).get();
-    expect(window.fetch).toHaveBeenCalledWith(new URL('https://example.com/'), jasmine.objectContaining({ method: 'GET' }));
+    expect(fetchStub).calledWithMatch(new URL('https://example.com/'), { method: 'GET' });
   });
 
+
   for (const requestMethod of ['POST', 'PUT', 'DELETE']) {
     describe(`send a ${requestMethod} request`, (): void => {
       function* requestDataProvider(): any {
@@ -74,10 +85,10 @@ describe('@typo3/core/ajax/ajax-request', (): void => {
       for (const providedData of requestDataProvider()) {
         const [name, requestMethod, payload, expectedFn, headers] = providedData;
         const requestFn: string = requestMethod.toLowerCase();
-        it(`with ${name}`, (done: DoneFn): void => {
+        it(`with ${name}`, (done): void => {
           const request: any = (new AjaxRequest('https://example.com'));
           request[requestFn](payload, { headers: headers });
-          expect(window.fetch).toHaveBeenCalledWith(new URL('https://example.com/'), jasmine.objectContaining({ method: requestMethod, body: expectedFn() }));
+          expect(fetchStub).calledWithMatch(new URL('https://example.com/'), { method: requestMethod, body: expectedFn() });
           done();
         });
       }
@@ -91,8 +102,8 @@ describe('@typo3/core/ajax/ajax-request', (): void => {
         'foobar huselpusel',
         {},
         (data: any, responseBody: any): void => {
-          expect(typeof data === 'string').toBeTruthy();
-          expect(data).toEqual(responseBody);
+          expect(typeof data === 'string').to.be.true;
+          expect(data).to.be.equal(responseBody);
         }
       ];
       yield [
@@ -100,8 +111,8 @@ describe('@typo3/core/ajax/ajax-request', (): void => {
         JSON.stringify({ foo: 'bar', baz: 'bencer' }),
         { 'Content-Type': 'application/json' },
         (data: any, responseBody: any): void => {
-          expect(typeof data === 'object').toBeTruthy();
-          expect(JSON.stringify(data)).toEqual(responseBody);
+          expect(typeof data === 'object').to.be.true;
+          expect(JSON.stringify(data)).to.equal(responseBody);
         }
       ];
       yield [
@@ -109,21 +120,21 @@ describe('@typo3/core/ajax/ajax-request', (): void => {
         JSON.stringify({ foo: 'bar', baz: 'bencer' }),
         { 'Content-Type': 'application/json; charset=utf-8' },
         (data: any, responseBody: any): void => {
-          expect(typeof data === 'object').toBeTruthy();
-          expect(JSON.stringify(data)).toEqual(responseBody);
+          expect(typeof data === 'object').to.be.true;
+          expect(JSON.stringify(data)).to.equal(responseBody);
         }
       ];
     }
 
     for (const providedData of responseDataProvider()) {
       const [name, responseText, headers, onfulfill] = providedData;
-      it('receives a ' + name + ' response', (done: DoneFn): void => {
+      it('receives a ' + name + ' response', (done): void => {
         const response = new Response(responseText, { headers: headers });
         promiseHelper.resolve(response);
 
         (new AjaxRequest(new URL('https://example.com'))).get().then(async (response: AjaxResponse): Promise<void> => {
           const data = await response.resolve();
-          expect(window.fetch).toHaveBeenCalledWith(new URL('https://example.com/'), jasmine.objectContaining({ method: 'GET' }));
+          expect(fetchStub).calledWithMatch(new URL('https://example.com/'), { method: 'GET' });
           onfulfill(data, responseText);
           done();
         });
@@ -181,7 +192,7 @@ describe('@typo3/core/ajax/ajax-request', (): void => {
       const [name, input, queryParameter, expected] = providedData;
       it('with ' + name, (): void => {
         (new AjaxRequest(input)).withQueryArguments(queryParameter).get();
-        expect(window.fetch).toHaveBeenCalledWith(expected, jasmine.objectContaining({ method: 'GET' }));
+        expect(fetchStub).calledWithMatch(expected, { method: 'GET' });
       });
     }
   });
@@ -250,7 +261,7 @@ describe('@typo3/core/ajax/ajax-request', (): void => {
       const [name, input, expected] = providedData;
       it('with ' + name, (): void => {
         (new AjaxRequest('https://example.com/')).withQueryArguments(input).get();
-        expect(window.fetch).toHaveBeenCalledWith(expected, jasmine.objectContaining({ method: 'GET' }));
+        expect(fetchStub).calledWithMatch(expected, { method: 'GET' });
       });
     }
   });
diff --git a/Build/Sources/TypeScript/core/tests/ajax/input-transformer-test.ts b/Build/Sources/TypeScript/core/tests/ajax/input-transformer-test.ts
index 95e19d282b3fc4e2b6b9169de2bb21c2219c8236..8dddb0661739c4f3c1b9d78740d4f36bf913eab1 100644
--- a/Build/Sources/TypeScript/core/tests/ajax/input-transformer-test.ts
+++ b/Build/Sources/TypeScript/core/tests/ajax/input-transformer-test.ts
@@ -11,7 +11,9 @@
  * The TYPO3 project - inspiring people to share!
  */
 
-import { GenericKeyValue, InputTransformer } from '@typo3/core/ajax/input-transformer';
+import { GenericKeyValue, InputTransformer } from '@typo3/core/ajax/input-transformer.js';
+import { expect } from '@open-wc/testing';
+import type { } from 'mocha';
 
 describe('@typo3/core/ajax/input-transformer', (): void => {
   it('converts object to FormData', (): void => {
@@ -21,7 +23,7 @@ describe('@typo3/core/ajax/input-transformer', (): void => {
     expected.set('bar', 'baz');
     expected.set('nested[works]', 'yes');
 
-    expect(InputTransformer.toFormData(input)).toEqual(expected);
+    expect(InputTransformer.toFormData(input)).to.eql(expected);
   });
 
   it('undefined values are removed in FormData', (): void => {
@@ -30,33 +32,33 @@ describe('@typo3/core/ajax/input-transformer', (): void => {
     expected.set('foo', 'bar');
     expected.set('bar', 'baz');
 
-    expect(InputTransformer.toFormData(input)).toEqual(expected);
+    expect(InputTransformer.toFormData(input)).to.eql(expected);
   });
 
   it('converts object to SearchParams', (): void => {
     const input: GenericKeyValue = { foo: 'bar', bar: 'baz', nested: { works: 'yes' } };
     const expected = 'foo=bar&bar=baz&nested[works]=yes';
 
-    expect(InputTransformer.toSearchParams(input)).toEqual(expected);
+    expect(InputTransformer.toSearchParams(input)).to.equal(expected);
   });
 
   it('merges array to SearchParams', (): void => {
     const input: Array<string> = ['foo=bar', 'bar=baz'];
     const expected = 'foo=bar&bar=baz';
 
-    expect(InputTransformer.toSearchParams(input)).toEqual(expected);
+    expect(InputTransformer.toSearchParams(input)).to.equal(expected);
   });
 
   it('keeps string in SearchParams', (): void => {
     const input: string = 'foo=bar&bar=baz';
     const expected = 'foo=bar&bar=baz';
 
-    expect(InputTransformer.toSearchParams(input)).toEqual(expected);
+    expect(InputTransformer.toSearchParams(input)).to.equal(expected);
   });
 
   it('undefined values are removed in SearchParams', (): void => {
     const input: GenericKeyValue = { foo: 'bar', bar: 'baz', removeme: undefined };
     const expected = 'foo=bar&bar=baz';
-    expect(InputTransformer.toSearchParams(input)).toEqual(expected);
+    expect(InputTransformer.toSearchParams(input)).to.equal(expected);
   });
 });
diff --git a/Build/Sources/TypeScript/core/tests/security-utility-test.ts b/Build/Sources/TypeScript/core/tests/security-utility-test.ts
index 94631b66b1e200a233039397a0083f9b88a01bae..3d545968b51a6fa40f3f424434594cc2cf2d9afb 100644
--- a/Build/Sources/TypeScript/core/tests/security-utility-test.ts
+++ b/Build/Sources/TypeScript/core/tests/security-utility-test.ts
@@ -11,7 +11,9 @@
  * The TYPO3 project - inspiring people to share!
  */
 
-import SecurityUtility from '@typo3/core/security-utility';
+import SecurityUtility from '@typo3/core/security-utility.js';
+import { expect } from '@open-wc/testing';
+import type { } from 'mocha';
 
 describe('@typo3/core/security-utility', (): void => {
   it('generates random hex value', (): void => {
@@ -22,7 +24,7 @@ describe('@typo3/core/security-utility', (): void => {
     }
     for (const validLength of validLengthDataProvider()) {
       const randomHexValue = (new SecurityUtility()).getRandomHexValue(validLength);
-      expect(randomHexValue.length).toBe(validLength);
+      expect(randomHexValue.length).to.equal(validLength);
     }
   });
 
@@ -33,16 +35,16 @@ describe('@typo3/core/security-utility', (): void => {
       yield 10.3; // length is "ceiled", 10.3 => 11, 10 != 11
     }
     for (const invalidLength of invalidLengthDataProvider()) {
-      expect(() => (new SecurityUtility()).getRandomHexValue(invalidLength)).toThrowError(SyntaxError);
+      expect(() => (new SecurityUtility()).getRandomHexValue(invalidLength)).to.throw(SyntaxError);
     }
   });
 
   it('encodes HTML', (): void => {
-    expect((new SecurityUtility).encodeHtml('<>"\'&')).toBe('&lt;&gt;&quot;&apos;&amp;');
+    expect((new SecurityUtility).encodeHtml('<>"\'&')).to.equal('&lt;&gt;&quot;&apos;&amp;');
   });
 
   it('removes HTML from string', (): void => {
-    expect((new SecurityUtility).stripHtml('<img src="" onerror="alert(\'1\')">oh noes')).toBe('oh noes');
-    expect((new SecurityUtility).encodeHtml('<>"\'&')).toBe('&lt;&gt;&quot;&apos;&amp;');
+    expect((new SecurityUtility).stripHtml('<img src="" onerror="alert(\'1\')">oh noes')).to.equal('oh noes');
+    expect((new SecurityUtility).encodeHtml('<>"\'&')).to.equal('&lt;&gt;&quot;&apos;&amp;');
   });
 });
diff --git a/Build/package-lock.json b/Build/package-lock.json
index 77996f8fd7be5e386b96693c4bb9095b2c7887d5..9f162f39ea09f37667e83601bd6cac0c25a23e1c 100644
--- a/Build/package-lock.json
+++ b/Build/package-lock.json
@@ -88,6 +88,7 @@
         "taboverride": "^4.0.3"
       },
       "devDependencies": {
+        "@open-wc/testing": "^3.2.0",
         "@rollup/plugin-commonjs": "^25.0.0",
         "@rollup/plugin-node-resolve": "^14.1.0",
         "@rollup/plugin-replace": "^2.4.2",
@@ -97,7 +98,6 @@
         "@types/d3-dispatch": "^3.0.2",
         "@types/d3-drag": "^3.0.2",
         "@types/d3-selection": "^3.0.4",
-        "@types/jasmine": "^4.3.0",
         "@types/jquery": "2.0.47",
         "@types/luxon": "^3.1.0",
         "@types/nprogress": "^0.2.0",
@@ -105,6 +105,10 @@
         "@typescript-eslint/eslint-plugin": "^6.6.0",
         "@typescript-eslint/parser": "^6.6.0",
         "@typescript-eslint/typescript-estree": "^6.6.0",
+        "@web/dev-server-esbuild": "^0.4.1",
+        "@web/dev-server-import-maps": "^0.1.1",
+        "@web/test-runner": "^0.17.1",
+        "@web/test-runner-playwright": "^0.10.1",
         "autoprefixer": "^10.4.14",
         "es-module-lexer": "^1.2.1",
         "eslint": "^8.38.0",
@@ -126,13 +130,6 @@
         "grunt-sass": "^3.1.0",
         "grunt-stylelint": "^0.18.0",
         "grunt-terser": "^2.0.0",
-        "jasmine-core": "^4.6.0",
-        "karma": "^6.4.1",
-        "karma-chrome-launcher": "^3.1.1",
-        "karma-coverage": "^2.2.0",
-        "karma-jasmine": "^5.1.0",
-        "karma-junit-reporter": "^2.0.1",
-        "karma-rollup-preprocessor": "^7.0.8",
         "lintspaces-cli": "^0.8.0",
         "mime-db": "^1.46.0",
         "pofile": "^1.1.4",
@@ -145,6 +142,7 @@
         "rollup-plugin-terser": "^7.0.2",
         "sass": "^1.62.0",
         "sharp": "^0.32.0",
+        "sinon": "^16.0.0",
         "stylelint": "^14.11.0",
         "stylelint-order": "^5.0.0",
         "stylelint-scss": "^4.6.0",
@@ -156,30 +154,26 @@
         "npm": ">=9.0.0 <10.0.0"
       }
     },
-    "node_modules/@ampproject/remapping": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
-      "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+    "node_modules/@75lb/deep-merge": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@75lb/deep-merge/-/deep-merge-1.1.1.tgz",
+      "integrity": "sha512-xvgv6pkMGBA6GwdyJbNAnDmfAIR/DfWhrj9jgWh3TY7gRm3KO46x/GPjRg6wJ0nOepwqrNxFfojebh0Df4h4Tw==",
       "dev": true,
       "dependencies": {
-        "@jridgewell/gen-mapping": "^0.1.0",
-        "@jridgewell/trace-mapping": "^0.3.9"
+        "lodash.assignwith": "^4.2.0",
+        "typical": "^7.1.1"
       },
       "engines": {
-        "node": ">=6.0.0"
+        "node": ">=12.17"
       }
     },
-    "node_modules/@ampproject/remapping/node_modules/@jridgewell/gen-mapping": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
-      "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+    "node_modules/@75lb/deep-merge/node_modules/typical": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/typical/-/typical-7.1.1.tgz",
+      "integrity": "sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA==",
       "dev": true,
-      "dependencies": {
-        "@jridgewell/set-array": "^1.0.0",
-        "@jridgewell/sourcemap-codec": "^1.4.10"
-      },
       "engines": {
-        "node": ">=6.0.0"
+        "node": ">=12.17"
       }
     },
     "node_modules/@babel/code-frame": {
@@ -193,54 +187,6 @@
         "node": ">=6.9.0"
       }
     },
-    "node_modules/@babel/compat-data": {
-      "version": "7.19.4",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz",
-      "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==",
-      "dev": true,
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/core": {
-      "version": "7.19.6",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz",
-      "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==",
-      "dev": true,
-      "dependencies": {
-        "@ampproject/remapping": "^2.1.0",
-        "@babel/code-frame": "^7.18.6",
-        "@babel/generator": "^7.19.6",
-        "@babel/helper-compilation-targets": "^7.19.3",
-        "@babel/helper-module-transforms": "^7.19.6",
-        "@babel/helpers": "^7.19.4",
-        "@babel/parser": "^7.19.6",
-        "@babel/template": "^7.18.10",
-        "@babel/traverse": "^7.19.6",
-        "@babel/types": "^7.19.4",
-        "convert-source-map": "^1.7.0",
-        "debug": "^4.1.0",
-        "gensync": "^1.0.0-beta.2",
-        "json5": "^2.2.1",
-        "semver": "^6.3.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/babel"
-      }
-    },
-    "node_modules/@babel/core/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
     "node_modules/@babel/generator": {
       "version": "7.19.6",
       "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.6.tgz",
@@ -254,33 +200,6 @@
         "node": ">=6.9.0"
       }
     },
-    "node_modules/@babel/helper-compilation-targets": {
-      "version": "7.19.3",
-      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz",
-      "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/compat-data": "^7.19.3",
-        "@babel/helper-validator-option": "^7.18.6",
-        "browserslist": "^4.21.3",
-        "semver": "^6.3.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.0.0"
-      }
-    },
-    "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
     "node_modules/@babel/helper-environment-visitor": {
       "version": "7.18.9",
       "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
@@ -312,49 +231,6 @@
         "node": ">=6.9.0"
       }
     },
-    "node_modules/@babel/helper-module-imports": {
-      "version": "7.18.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
-      "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
-      "dev": true,
-      "dependencies": {
-        "@babel/types": "^7.18.6"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-module-transforms": {
-      "version": "7.19.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz",
-      "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-environment-visitor": "^7.18.9",
-        "@babel/helper-module-imports": "^7.18.6",
-        "@babel/helper-simple-access": "^7.19.4",
-        "@babel/helper-split-export-declaration": "^7.18.6",
-        "@babel/helper-validator-identifier": "^7.19.1",
-        "@babel/template": "^7.18.10",
-        "@babel/traverse": "^7.19.6",
-        "@babel/types": "^7.19.4"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-simple-access": {
-      "version": "7.19.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz",
-      "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/types": "^7.19.4"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/@babel/helper-split-export-declaration": {
       "version": "7.18.6",
       "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
@@ -382,29 +258,6 @@
         "node": ">=6.9.0"
       }
     },
-    "node_modules/@babel/helper-validator-option": {
-      "version": "7.18.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
-      "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
-      "dev": true,
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helpers": {
-      "version": "7.19.4",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz",
-      "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/template": "^7.18.10",
-        "@babel/traverse": "^7.19.4",
-        "@babel/types": "^7.19.4"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/@babel/highlight": {
       "version": "7.18.6",
       "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
@@ -615,19 +468,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/@ckeditor/ckeditor5-dev-utils/node_modules/fs-extra": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
-      "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
-      "dependencies": {
-        "graceful-fs": "^4.2.0",
-        "jsonfile": "^4.0.0",
-        "universalify": "^0.1.0"
-      },
-      "engines": {
-        "node": ">=6 <7 || >=8"
-      }
-    },
     "node_modules/@ckeditor/ckeditor5-editor-classic": {
       "version": "39.0.2",
       "resolved": "https://registry.npmjs.org/@ckeditor/ckeditor5-editor-classic/-/ckeditor5-editor-classic-39.0.2.tgz",
@@ -1090,15 +930,6 @@
         "w3c-keyname": "^2.2.4"
       }
     },
-    "node_modules/@colors/colors": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
-      "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.1.90"
-      }
-    },
     "node_modules/@csstools/selector-specificity": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz",
@@ -1546,6 +1377,15 @@
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       }
     },
+    "node_modules/@esm-bundle/chai": {
+      "version": "4.3.4-fix.0",
+      "resolved": "https://registry.npmjs.org/@esm-bundle/chai/-/chai-4.3.4-fix.0.tgz",
+      "integrity": "sha512-26SKdM4uvDWlY8/OOOxSB1AqQWeBosCX3wRYUZO7enTAj03CtVxIiCimYVG2WpULcyV51qapK4qTovwkUr5Mlw==",
+      "dev": true,
+      "dependencies": {
+        "@types/chai": "^4.2.12"
+      }
+    },
     "node_modules/@gar/promisify": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
@@ -1584,20 +1424,17 @@
       "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
       "dev": true
     },
+    "node_modules/@import-maps/resolve": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@import-maps/resolve/-/resolve-1.0.1.tgz",
+      "integrity": "sha512-tWZNBIS1CoekcwlMuyG2mr0a1Wo5lb5lEHwwWvZo+5GLgr3e9LLDTtmgtCWEwBpXMkxn9D+2W9j2FY6eZQq0tA==",
+      "dev": true
+    },
     "node_modules/@interactjs/types": {
       "version": "1.10.17",
       "resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.17.tgz",
       "integrity": "sha512-X2JpoM7xUw0p9Me0tMaI0HNfcF/Hd07ZZlzpnpEMpGerUZOLoyeThrV9P+CrBHxZrluWJrigJbcdqXliFd0YMA=="
     },
-    "node_modules/@istanbuljs/schema": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
-      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/@jridgewell/gen-mapping": {
       "version": "0.3.2",
       "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
@@ -1761,6 +1598,12 @@
         "postcss": "^8.0.0"
       }
     },
+    "node_modules/@mdn/browser-compat-data": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-4.2.1.tgz",
+      "integrity": "sha512-EWUguj2kd7ldmrF9F+vI5hUOralPd+sdsUnYbRy33vZTuZkduC1shE9TtEMEjAQwyfyMb4ole5KtjF8MsnQOlA==",
+      "dev": true
+    },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1828,23 +1671,81 @@
         "node": ">=10"
       }
     },
-    "node_modules/@npmcli/move-file/node_modules/mkdirp": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
-      "bin": {
-        "mkdirp": "bin/cmd.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/@one-ini/wasm": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz",
       "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
       "dev": true
     },
+    "node_modules/@open-wc/chai-dom-equals": {
+      "version": "0.12.36",
+      "resolved": "https://registry.npmjs.org/@open-wc/chai-dom-equals/-/chai-dom-equals-0.12.36.tgz",
+      "integrity": "sha512-Gt1fa37h4rtWPQGETSU4n1L678NmMi9KwHM1sH+JCGcz45rs8DBPx7MUVeGZ+HxRlbEI5t9LU2RGGv6xT2OlyA==",
+      "dev": true,
+      "dependencies": {
+        "@open-wc/semantic-dom-diff": "^0.13.16",
+        "@types/chai": "^4.1.7"
+      }
+    },
+    "node_modules/@open-wc/chai-dom-equals/node_modules/@open-wc/semantic-dom-diff": {
+      "version": "0.13.21",
+      "resolved": "https://registry.npmjs.org/@open-wc/semantic-dom-diff/-/semantic-dom-diff-0.13.21.tgz",
+      "integrity": "sha512-BONpjHcGX2zFa9mfnwBCLEmlDsOHzT+j6Qt1yfK3MzFXFtAykfzFjAgaxPetu0YbBlCfXuMlfxI4vlRGCGMvFg==",
+      "dev": true
+    },
+    "node_modules/@open-wc/dedupe-mixin": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/@open-wc/dedupe-mixin/-/dedupe-mixin-1.4.0.tgz",
+      "integrity": "sha512-Sj7gKl1TLcDbF7B6KUhtvr+1UCxdhMbNY5KxdU5IfMFWqL8oy1ZeAcCANjoB1TL0AJTcPmcCFsCbHf8X2jGDUA==",
+      "dev": true
+    },
+    "node_modules/@open-wc/scoped-elements": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@open-wc/scoped-elements/-/scoped-elements-2.2.0.tgz",
+      "integrity": "sha512-Qe+vWsuVHFzUkdChwlmJGuQf9cA3I+QOsSHULV/6qf6wsqLM2/32svNRH+rbBIMwiPEwzZprZlkvkqQRucYnVA==",
+      "dev": true,
+      "dependencies": {
+        "@lit/reactive-element": "^1.0.0",
+        "@open-wc/dedupe-mixin": "^1.4.0"
+      }
+    },
+    "node_modules/@open-wc/semantic-dom-diff": {
+      "version": "0.20.0",
+      "resolved": "https://registry.npmjs.org/@open-wc/semantic-dom-diff/-/semantic-dom-diff-0.20.0.tgz",
+      "integrity": "sha512-qGHl3nkXluXsjpLY9bSZka/cnlrybPtJMs6RjmV/OP4ID7Gcz1uNWQks05pAhptDB1R47G6PQjdwxG8dXl1zGA==",
+      "dev": true,
+      "dependencies": {
+        "@types/chai": "^4.3.1",
+        "@web/test-runner-commands": "^0.7.0"
+      }
+    },
+    "node_modules/@open-wc/testing": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@open-wc/testing/-/testing-3.2.0.tgz",
+      "integrity": "sha512-9geTbFq8InbcfniPtS8KCfb5sbQ9WE6QMo1Tli8XMnfllnkZok7Az4kTRAskGQeMeQN/I2I//jE5xY/60qhrHg==",
+      "dev": true,
+      "dependencies": {
+        "@esm-bundle/chai": "^4.3.4-fix.0",
+        "@open-wc/chai-dom-equals": "^0.12.36",
+        "@open-wc/semantic-dom-diff": "^0.20.0",
+        "@open-wc/testing-helpers": "^2.3.0",
+        "@types/chai": "^4.2.11",
+        "@types/chai-dom": "^1.11.0",
+        "@types/sinon-chai": "^3.2.3",
+        "chai-a11y-axe": "^1.5.0"
+      }
+    },
+    "node_modules/@open-wc/testing-helpers": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@open-wc/testing-helpers/-/testing-helpers-2.3.0.tgz",
+      "integrity": "sha512-wkDipkia/OMWq5Z1KkAgvqNLfIOCiPGrrtfoCKuQje8u7F0Bz9Un44EwBtWcCdYtLc40quWP7XFpFsW8poIfUA==",
+      "dev": true,
+      "dependencies": {
+        "@open-wc/scoped-elements": "^2.2.0",
+        "lit": "^2.0.0",
+        "lit-html": "^2.0.0"
+      }
+    },
     "node_modules/@popperjs/core": {
       "version": "2.11.8",
       "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -1854,6 +1755,67 @@
         "url": "https://opencollective.com/popperjs"
       }
     },
+    "node_modules/@puppeteer/browsers": {
+      "version": "1.4.6",
+      "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz",
+      "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==",
+      "dev": true,
+      "dependencies": {
+        "debug": "4.3.4",
+        "extract-zip": "2.0.1",
+        "progress": "2.0.3",
+        "proxy-agent": "6.3.0",
+        "tar-fs": "3.0.4",
+        "unbzip2-stream": "1.4.3",
+        "yargs": "17.7.1"
+      },
+      "bin": {
+        "browsers": "lib/cjs/main-cli.js"
+      },
+      "engines": {
+        "node": ">=16.3.0"
+      },
+      "peerDependencies": {
+        "typescript": ">= 4.7.4"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@puppeteer/browsers/node_modules/pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "dev": true,
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/@puppeteer/browsers/node_modules/tar-fs": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz",
+      "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==",
+      "dev": true,
+      "dependencies": {
+        "mkdirp-classic": "^0.5.2",
+        "pump": "^3.0.0",
+        "tar-stream": "^3.1.5"
+      }
+    },
+    "node_modules/@puppeteer/browsers/node_modules/tar-stream": {
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz",
+      "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==",
+      "dev": true,
+      "dependencies": {
+        "b4a": "^1.6.4",
+        "fast-fifo": "^1.2.0",
+        "streamx": "^2.15.0"
+      }
+    },
     "node_modules/@rollup/plugin-commonjs": {
       "version": "25.0.0",
       "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.0.tgz",
@@ -2057,10 +2019,54 @@
       "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==",
       "dev": true
     },
-    "node_modules/@socket.io/component-emitter": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
-      "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
+    "node_modules/@sinonjs/commons": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
+      "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
+      "dev": true,
+      "dependencies": {
+        "type-detect": "4.0.8"
+      }
+    },
+    "node_modules/@sinonjs/fake-timers": {
+      "version": "10.3.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
+      "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
+      "dev": true,
+      "dependencies": {
+        "@sinonjs/commons": "^3.0.0"
+      }
+    },
+    "node_modules/@sinonjs/samsam": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz",
+      "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==",
+      "dev": true,
+      "dependencies": {
+        "@sinonjs/commons": "^2.0.0",
+        "lodash.get": "^4.4.2",
+        "type-detect": "^4.0.8"
+      }
+    },
+    "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+      "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+      "dev": true,
+      "dependencies": {
+        "type-detect": "4.0.8"
+      }
+    },
+    "node_modules/@sinonjs/text-encoding": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz",
+      "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==",
+      "dev": true
+    },
+    "node_modules/@tootallnate/quickjs-emscripten": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
+      "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
       "dev": true
     },
     "node_modules/@trysound/sax": {
@@ -2071,12 +2077,37 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/@types/accepts": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz",
+      "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/autosize": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/@types/autosize/-/autosize-4.0.1.tgz",
       "integrity": "sha512-iPJT/FCaSO79G6j+9n6gmFc5nhxZ1gDrA2UAvb5FslJ6FJQZnDfbBU0qp5vpp0Cbjj7+gOyjuWZ7RrXvRuETaA==",
       "dev": true
     },
+    "node_modules/@types/babel__code-frame": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/@types/babel__code-frame/-/babel__code-frame-7.0.4.tgz",
+      "integrity": "sha512-WBxINLlATjvmpCgBbb9tOPrKtcPfu4A/Yz2iRzmdaodfvjAS/Z0WZJClV9/EXvoC9viI3lgUs7B9Uo7G/RmMGg==",
+      "dev": true
+    },
+    "node_modules/@types/body-parser": {
+      "version": "1.19.3",
+      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz",
+      "integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/bootstrap": {
       "version": "5.2.6",
       "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.6.tgz",
@@ -2086,31 +2117,80 @@
         "@popperjs/core": "^2.9.2"
       }
     },
-    "node_modules/@types/color-name": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
-      "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
-      "license": "MIT"
-    },
-    "node_modules/@types/cookie": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
-      "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
+    "node_modules/@types/chai": {
+      "version": "4.3.6",
+      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.6.tgz",
+      "integrity": "sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==",
       "dev": true
     },
-    "node_modules/@types/cors": {
-      "version": "2.8.13",
-      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
-      "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
+    "node_modules/@types/chai-dom": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/@types/chai-dom/-/chai-dom-1.11.1.tgz",
+      "integrity": "sha512-q+fs4jdKZFDhXOWBehY0jDGCp8nxVe11Ia8MxqlIsJC3Y2JU149PSBYF2li2F3uxJFSAl2Rf8XeLWonHglpcGw==",
       "dev": true,
       "dependencies": {
-        "@types/node": "*"
+        "@types/chai": "*"
       }
     },
-    "node_modules/@types/d3-dispatch": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.2.tgz",
-      "integrity": "sha512-rxN6sHUXEZYCKV05MEh4z4WpPSqIw+aP7n9ZN6WYAAvZoEAghEK1WeVZMZcHRBwyaKflU43PCUAJNjFxCzPDjg==",
+    "node_modules/@types/co-body": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/@types/co-body/-/co-body-6.1.1.tgz",
+      "integrity": "sha512-I9A1k7o4m8m6YPYJIGb1JyNTLqRWtSPg1JOZPWlE19w8Su2VRgRVp/SkKftQSwoxWHGUxGbON4jltONMumC8bQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*"
+      }
+    },
+    "node_modules/@types/color-name": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
+      "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
+      "license": "MIT"
+    },
+    "node_modules/@types/command-line-args": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.1.tgz",
+      "integrity": "sha512-U2OcmS2tj36Yceu+mRuPyUV0ILfau/h5onStcSCzqTENsq0sBiAp2TmaXu1k8fY4McLcPKSYl9FRzn2hx5bI+w==",
+      "dev": true
+    },
+    "node_modules/@types/connect": {
+      "version": "3.4.36",
+      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz",
+      "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/content-disposition": {
+      "version": "0.5.6",
+      "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.6.tgz",
+      "integrity": "sha512-GmShTb4qA9+HMPPaV2+Up8tJafgi38geFi7vL4qAM7k8BwjoelgHZqEUKJZLvughUw22h6vD/wvwN4IUCaWpDA==",
+      "dev": true
+    },
+    "node_modules/@types/convert-source-map": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@types/convert-source-map/-/convert-source-map-2.0.1.tgz",
+      "integrity": "sha512-tm5Eb3AwhibN6ULRaad5TbNO83WoXVZLh2YRGAFH+qWkUz48l9Hu1jc+wJswB7T+ACWAG0cFnTeeQGpwedvlNw==",
+      "dev": true
+    },
+    "node_modules/@types/cookies": {
+      "version": "0.7.8",
+      "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.8.tgz",
+      "integrity": "sha512-y6KhF1GtsLERUpqOV+qZJrjUGzc0GE6UTa0b5Z/LZ7Nm2mKSdCXmS6Kdnl7fctPNnMSouHjxqEWI12/YqQfk5w==",
+      "dev": true,
+      "dependencies": {
+        "@types/connect": "*",
+        "@types/express": "*",
+        "@types/keygrip": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/d3-dispatch": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.2.tgz",
+      "integrity": "sha512-rxN6sHUXEZYCKV05MEh4z4WpPSqIw+aP7n9ZN6WYAAvZoEAghEK1WeVZMZcHRBwyaKflU43PCUAJNjFxCzPDjg==",
       "dev": true
     },
     "node_modules/@types/d3-drag": {
@@ -2128,6 +2208,12 @@
       "integrity": "sha512-ZeykX7286BCyMg9sH5fIAORyCB6hcATPSRQpN47jwBA2bMbAT0s+EvtDP5r1FZYJ95R8QoEE1CKJX+n0/M5Vhg==",
       "dev": true
     },
+    "node_modules/@types/debounce": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.1.tgz",
+      "integrity": "sha512-epMsEE85fi4lfmJUH/89/iV/LI+F5CvNIvmgs5g5jYFPfhO2S/ae8WSsLOKWdwtoaZw9Q2IhJ4tQ5tFCcS/4HA==",
+      "dev": true
+    },
     "node_modules/@types/eslint": {
       "version": "8.4.6",
       "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz",
@@ -2154,6 +2240,30 @@
       "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
       "license": "MIT"
     },
+    "node_modules/@types/express": {
+      "version": "4.17.17",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
+      "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
+      "dev": true,
+      "dependencies": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^4.17.33",
+        "@types/qs": "*",
+        "@types/serve-static": "*"
+      }
+    },
+    "node_modules/@types/express-serve-static-core": {
+      "version": "4.17.36",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.36.tgz",
+      "integrity": "sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*",
+        "@types/send": "*"
+      }
+    },
     "node_modules/@types/glob": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
@@ -2163,12 +2273,42 @@
         "@types/node": "*"
       }
     },
-    "node_modules/@types/jasmine": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.3.0.tgz",
-      "integrity": "sha512-u1jWakf8CWvLfSEZyxmzkgBzOEvXH/szpT0e6G8BTkx5Eu0BhDn7sbc5dz0JBN/6Wwm9rBe+JAsk9tJRyH9ZkA==",
+    "node_modules/@types/http-assert": {
+      "version": "1.5.3",
+      "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.3.tgz",
+      "integrity": "sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA==",
       "dev": true
     },
+    "node_modules/@types/http-errors": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz",
+      "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==",
+      "dev": true
+    },
+    "node_modules/@types/istanbul-lib-coverage": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
+      "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
+      "dev": true
+    },
+    "node_modules/@types/istanbul-lib-report": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+      "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
+      "dev": true,
+      "dependencies": {
+        "@types/istanbul-lib-coverage": "*"
+      }
+    },
+    "node_modules/@types/istanbul-reports": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
+      "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
+      "dev": true,
+      "dependencies": {
+        "@types/istanbul-lib-report": "*"
+      }
+    },
     "node_modules/@types/jquery": {
       "version": "2.0.47",
       "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-2.0.47.tgz",
@@ -2181,12 +2321,49 @@
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
       "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA=="
     },
+    "node_modules/@types/keygrip": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.3.tgz",
+      "integrity": "sha512-tfzBBb7OV2PbUfKbG6zRE5UbmtdLVCKT/XT364Z9ny6pXNbd9GnIB6aFYpq2A5lZ6mq9bhXgK6h5MFGNwhMmuQ==",
+      "dev": true
+    },
+    "node_modules/@types/koa": {
+      "version": "2.13.9",
+      "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.9.tgz",
+      "integrity": "sha512-tPX3cN1dGrMn+sjCDEiQqXH2AqlPoPd594S/8zxwUm/ZbPsQXKqHPUypr2gjCPhHUc+nDJLduhh5lXI/1olnGQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/accepts": "*",
+        "@types/content-disposition": "*",
+        "@types/cookies": "*",
+        "@types/http-assert": "*",
+        "@types/http-errors": "*",
+        "@types/keygrip": "*",
+        "@types/koa-compose": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/koa-compose": {
+      "version": "3.2.6",
+      "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.6.tgz",
+      "integrity": "sha512-PHiciWxH3NRyAaxUdEDE1NIZNfvhgtPlsdkjRPazHC6weqt90Jr0uLhIQs+SDwC8HQ/jnA7UQP6xOqGFB7ugWw==",
+      "dev": true,
+      "dependencies": {
+        "@types/koa": "*"
+      }
+    },
     "node_modules/@types/luxon": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.1.0.tgz",
       "integrity": "sha512-gCd/HcCgjqSxfMrgtqxCgYk/22NBQfypwFUG7ZAyG/4pqs51WLTcUzVp1hqTbieDYeHS3WoVEh2Yv/2l+7B0Vg==",
       "dev": true
     },
+    "node_modules/@types/mime": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+      "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
+      "dev": true
+    },
     "node_modules/@types/minimatch": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
@@ -2198,6 +2375,12 @@
       "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==",
       "dev": true
     },
+    "node_modules/@types/mocha": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz",
+      "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==",
+      "dev": true
+    },
     "node_modules/@types/node": {
       "version": "18.7.12",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.12.tgz",
@@ -2222,6 +2405,24 @@
       "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
       "license": "MIT"
     },
+    "node_modules/@types/parse5": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz",
+      "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==",
+      "dev": true
+    },
+    "node_modules/@types/qs": {
+      "version": "6.9.8",
+      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz",
+      "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==",
+      "dev": true
+    },
+    "node_modules/@types/range-parser": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+      "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+      "dev": true
+    },
     "node_modules/@types/resolve": {
       "version": "1.17.1",
       "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
@@ -2237,6 +2438,52 @@
       "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==",
       "dev": true
     },
+    "node_modules/@types/send": {
+      "version": "0.17.1",
+      "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz",
+      "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==",
+      "dev": true,
+      "dependencies": {
+        "@types/mime": "^1",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/serve-static": {
+      "version": "1.15.2",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz",
+      "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==",
+      "dev": true,
+      "dependencies": {
+        "@types/http-errors": "*",
+        "@types/mime": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/sinon": {
+      "version": "10.0.16",
+      "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.16.tgz",
+      "integrity": "sha512-j2Du5SYpXZjJVJtXBokASpPRj+e2z+VUhCPHmM6WMfe3dpHu6iVKJMU6AiBcMp/XTAYnEj6Wc1trJUWwZ0QaAQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/sinonjs__fake-timers": "*"
+      }
+    },
+    "node_modules/@types/sinon-chai": {
+      "version": "3.2.9",
+      "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.9.tgz",
+      "integrity": "sha512-/19t63pFYU0ikrdbXKBWj9PCdnKyTd0Qkz0X91Ta081cYsq90OxYdcWwK/dwEoDa6dtXgj2HJfmzgq+QZTHdmQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/chai": "*",
+        "@types/sinon": "*"
+      }
+    },
+    "node_modules/@types/sinonjs__fake-timers": {
+      "version": "8.1.2",
+      "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz",
+      "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==",
+      "dev": true
+    },
     "node_modules/@types/sortablejs": {
       "version": "1.10.6",
       "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.10.6.tgz",
@@ -2250,6 +2497,25 @@
       "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==",
       "license": "MIT"
     },
+    "node_modules/@types/ws": {
+      "version": "7.4.7",
+      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz",
+      "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/yauzl": {
+      "version": "2.10.0",
+      "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz",
+      "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==",
+      "dev": true,
+      "optional": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@typescript-eslint/eslint-plugin": {
       "version": "6.6.0",
       "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.6.0.tgz",
@@ -2497,17 +2763,511 @@
         "eslint-visitor-keys": "^3.4.1"
       },
       "engines": {
-        "node": "^16.0.0 || >=18.0.0"
+        "node": "^16.0.0 || >=18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typo3/icons": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@typo3/icons/-/icons-4.1.0.tgz",
+      "integrity": "sha512-QeCcfJKYuZqk/q49AYDmJ9rYLtZtoJf9fh97cJreq77riAd12TdR+YoZ/zurYA6L3nlYZPV+9EdvhBwHpksvEw=="
+    },
+    "node_modules/@web/browser-logs": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/@web/browser-logs/-/browser-logs-0.3.3.tgz",
+      "integrity": "sha512-wt8arj0x7ghXbnipgCvLR+xQ90cFg16ae23cFbInCrJvAxvyI22bAtT24W4XOXMPXwWLBVUJwBgBcXo3oKIvDw==",
+      "dev": true,
+      "dependencies": {
+        "errorstacks": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/config-loader": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/@web/config-loader/-/config-loader-0.2.1.tgz",
+      "integrity": "sha512-cQvTYA5lWLyyO8/R2aOReiudLa8r0LFHvMNYCwSAjzvrghb+AHxaW3BJWP9ORx6OaDcI7g5X8OATA81LSJce4A==",
+      "dev": true,
+      "dependencies": {
+        "semver": "^7.3.4"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/config-loader/node_modules/semver": {
+      "version": "7.5.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+      "dev": true,
+      "dependencies": {
+        "lru-cache": "^6.0.0"
+      },
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@web/dev-server": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/@web/dev-server/-/dev-server-0.3.1.tgz",
+      "integrity": "sha512-RYxamuwR/mGjVcYR8+VXXwluqjEzfpOJSBSSHr3F/U7ecC0veKSAbTOHCwv++SNNU8/PY+dg9g8Ss+X2gSwIaw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.12.11",
+        "@types/command-line-args": "^5.0.0",
+        "@web/config-loader": "^0.2.1",
+        "@web/dev-server-core": "^0.5.1",
+        "@web/dev-server-rollup": "^0.5.1",
+        "camelcase": "^6.2.0",
+        "command-line-args": "^5.1.1",
+        "command-line-usage": "^7.0.1",
+        "debounce": "^1.2.0",
+        "deepmerge": "^4.2.2",
+        "ip": "^1.1.5",
+        "nanocolors": "^0.2.1",
+        "open": "^8.0.2",
+        "portfinder": "^1.0.32"
+      },
+      "bin": {
+        "wds": "dist/bin.js",
+        "web-dev-server": "dist/bin.js"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/dev-server-core": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/@web/dev-server-core/-/dev-server-core-0.5.2.tgz",
+      "integrity": "sha512-7YjWmwzM+K5fPvBCXldUIMTK4EnEufi1aWQWinQE81oW1CqzEwmyUNCtnWV9fcPA4kJC4qrpcjWNGF4YDWxuSg==",
+      "dev": true,
+      "dependencies": {
+        "@types/koa": "^2.11.6",
+        "@types/ws": "^7.4.0",
+        "@web/parse5-utils": "^2.0.0",
+        "chokidar": "^3.4.3",
+        "clone": "^2.1.2",
+        "es-module-lexer": "^1.0.0",
+        "get-stream": "^6.0.0",
+        "is-stream": "^2.0.0",
+        "isbinaryfile": "^5.0.0",
+        "koa": "^2.13.0",
+        "koa-etag": "^4.0.0",
+        "koa-send": "^5.0.1",
+        "koa-static": "^5.0.0",
+        "lru-cache": "^8.0.4",
+        "mime-types": "^2.1.27",
+        "parse5": "^6.0.1",
+        "picomatch": "^2.2.2",
+        "ws": "^7.4.2"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/dev-server-core/node_modules/lru-cache": {
+      "version": "8.0.5",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
+      "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==",
+      "dev": true,
+      "engines": {
+        "node": ">=16.14"
+      }
+    },
+    "node_modules/@web/dev-server-esbuild": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/@web/dev-server-esbuild/-/dev-server-esbuild-0.4.1.tgz",
+      "integrity": "sha512-oUrxo7ggxeaWuQafu5bgiAJFatA+YEeYhgkUMB2CHq/SVleKgyPgQCqx42eVBJ2uWMyI1YWSpKtNueCmocwQzw==",
+      "dev": true,
+      "dependencies": {
+        "@mdn/browser-compat-data": "^4.0.0",
+        "@web/dev-server-core": "^0.5.1",
+        "esbuild": "^0.16 || ^0.17",
+        "parse5": "^6.0.1",
+        "ua-parser-js": "^1.0.33"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/dev-server-import-maps": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/@web/dev-server-import-maps/-/dev-server-import-maps-0.1.1.tgz",
+      "integrity": "sha512-Et/uswWE2K9tFLM2xNArsvoDtFmBPyFrwJRm5O5ls5u0F3ZNE8vXk4kUONakillQ/041uSE30wIfesljl1ZKsg==",
+      "dev": true,
+      "dependencies": {
+        "@import-maps/resolve": "^1.0.1",
+        "@types/parse5": "^6.0.1",
+        "@web/dev-server-core": "^0.5.1",
+        "@web/parse5-utils": "^2.0.0",
+        "parse5": "^6.0.1",
+        "picomatch": "^2.2.2"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/dev-server-rollup": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/@web/dev-server-rollup/-/dev-server-rollup-0.5.2.tgz",
+      "integrity": "sha512-R1heFIOmbExKJn2auDcOcF0EPoLQotZF1HE8Bpqq4TfLRkc7w+JClLdwkOMr/+Ip608cRw8VMkc7teYDFkvSXw==",
+      "dev": true,
+      "dependencies": {
+        "@rollup/plugin-node-resolve": "^15.0.1",
+        "@web/dev-server-core": "^0.5.0",
+        "nanocolors": "^0.2.1",
+        "parse5": "^6.0.1",
+        "rollup": "^3.15.0",
+        "whatwg-url": "^11.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/dev-server-rollup/node_modules/@rollup/plugin-node-resolve": {
+      "version": "15.2.1",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.1.tgz",
+      "integrity": "sha512-nsbUg588+GDSu8/NS8T4UAshO6xeaOfINNuXeVHcKV02LJtoRaM1SiOacClw4kws1SFiNhdLGxlbMY9ga/zs/w==",
+      "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"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "rollup": "^2.78.0||^3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "rollup": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@web/dev-server-rollup/node_modules/@rollup/pluginutils": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.4.tgz",
+      "integrity": "sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "^1.0.0",
+        "estree-walker": "^2.0.2",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "rollup": "^1.20.0||^2.0.0||^3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "rollup": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@web/dev-server-rollup/node_modules/@types/estree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
+      "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==",
+      "dev": true
+    },
+    "node_modules/@web/dev-server-rollup/node_modules/@types/resolve": {
+      "version": "1.20.2",
+      "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
+      "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
+      "dev": true
+    },
+    "node_modules/@web/dev-server-rollup/node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "dev": true
+    },
+    "node_modules/@web/dev-server-rollup/node_modules/rollup": {
+      "version": "3.29.2",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.2.tgz",
+      "integrity": "sha512-CJouHoZ27v6siztc21eEQGo0kIcE5D1gVPA571ez0mMYb25LGYGKnVNXpEj5MGlepmDWGXNjDB5q7uNiPHC11A==",
+      "dev": true,
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=14.18.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/@web/dev-server/node_modules/camelcase": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+      "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@web/parse5-utils": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@web/parse5-utils/-/parse5-utils-2.0.1.tgz",
+      "integrity": "sha512-FQI72BU5CXhpp7gLRskOQGGCcwvagLZnMnDwAfjrxo3pm1KOQzr8Vl+438IGpHV62xvjNdF1pjXwXcf7eekWGw==",
+      "dev": true,
+      "dependencies": {
+        "@types/parse5": "^6.0.1",
+        "parse5": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/test-runner": {
+      "version": "0.17.1",
+      "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.17.1.tgz",
+      "integrity": "sha512-xHlvdopBUuU7yexB7WpKdThY3CszWw2Cz9/CCDGnSUPzOFZgDLhcQKpbHjZdW21ggINoWq2nx+6eCTQ448o9kw==",
+      "dev": true,
+      "dependencies": {
+        "@web/browser-logs": "^0.3.3",
+        "@web/config-loader": "^0.2.1",
+        "@web/dev-server": "^0.3.1",
+        "@web/test-runner-chrome": "^0.14.0",
+        "@web/test-runner-commands": "^0.8.0",
+        "@web/test-runner-core": "^0.11.1",
+        "@web/test-runner-mocha": "^0.8.1",
+        "camelcase": "^6.2.0",
+        "command-line-args": "^5.1.1",
+        "command-line-usage": "^7.0.1",
+        "convert-source-map": "^2.0.0",
+        "diff": "^5.0.0",
+        "globby": "^11.0.1",
+        "nanocolors": "^0.2.1",
+        "portfinder": "^1.0.32",
+        "source-map": "^0.7.3"
+      },
+      "bin": {
+        "web-test-runner": "dist/bin.js",
+        "wtr": "dist/bin.js"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/test-runner-chrome": {
+      "version": "0.14.1",
+      "resolved": "https://registry.npmjs.org/@web/test-runner-chrome/-/test-runner-chrome-0.14.1.tgz",
+      "integrity": "sha512-snyQN5xyiTFVXQ1bmJ1T5dqgnCXofngTv2aevv5gW4FumKo96aFF7kBr+SRQnEd3yary+xJsGFT+50A6/mJ+1A==",
+      "dev": true,
+      "dependencies": {
+        "@web/test-runner-core": "^0.11.2",
+        "@web/test-runner-coverage-v8": "^0.7.0",
+        "async-mutex": "0.4.0",
+        "chrome-launcher": "^0.15.0",
+        "puppeteer-core": "^20.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/test-runner-commands": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/@web/test-runner-commands/-/test-runner-commands-0.7.0.tgz",
+      "integrity": "sha512-3aXeGrkynOdJ5jgZu5ZslcWmWuPVY9/HNdWDUqPyNePG08PKmLV9Ij342ODDL6OVsxF5dvYn1312PhDqu5AQNw==",
+      "dev": true,
+      "dependencies": {
+        "@web/test-runner-core": "^0.11.0",
+        "mkdirp": "^1.0.4"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/test-runner-core": {
+      "version": "0.11.4",
+      "resolved": "https://registry.npmjs.org/@web/test-runner-core/-/test-runner-core-0.11.4.tgz",
+      "integrity": "sha512-E7BsKAP8FAAEsfj4viASjmuaYfOw4UlKP9IFqo4W20eVyd1nbUWU3Amq4Jksh7W/j811qh3VaFNjDfCwklQXMg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.12.11",
+        "@types/babel__code-frame": "^7.0.2",
+        "@types/co-body": "^6.1.0",
+        "@types/convert-source-map": "^2.0.0",
+        "@types/debounce": "^1.2.0",
+        "@types/istanbul-lib-coverage": "^2.0.3",
+        "@types/istanbul-reports": "^3.0.0",
+        "@web/browser-logs": "^0.3.2",
+        "@web/dev-server-core": "^0.5.1",
+        "chokidar": "^3.4.3",
+        "cli-cursor": "^3.1.0",
+        "co-body": "^6.1.0",
+        "convert-source-map": "^2.0.0",
+        "debounce": "^1.2.0",
+        "dependency-graph": "^0.11.0",
+        "globby": "^11.0.1",
+        "ip": "^1.1.5",
+        "istanbul-lib-coverage": "^3.0.0",
+        "istanbul-lib-report": "^3.0.1",
+        "istanbul-reports": "^3.0.2",
+        "log-update": "^4.0.0",
+        "nanocolors": "^0.2.1",
+        "nanoid": "^3.1.25",
+        "open": "^8.0.2",
+        "picomatch": "^2.2.2",
+        "source-map": "^0.7.3"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/test-runner-core/node_modules/globby": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+      "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+      "dev": true,
+      "dependencies": {
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.2.9",
+        "ignore": "^5.2.0",
+        "merge2": "^1.4.1",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@web/test-runner-core/node_modules/source-map": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+      "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@web/test-runner-coverage-v8": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/@web/test-runner-coverage-v8/-/test-runner-coverage-v8-0.7.1.tgz",
+      "integrity": "sha512-R0laTOxbLg7kVKHCBILEmja3w1ihlwkB+eRc7J06/ByyZoQVWxkM9SrTAUx7qCFI6o738Jj24a6TfIDbu3CwSA==",
+      "dev": true,
+      "dependencies": {
+        "@web/test-runner-core": "^0.11.0",
+        "istanbul-lib-coverage": "^3.0.0",
+        "lru-cache": "^8.0.4",
+        "picomatch": "^2.2.2",
+        "v8-to-istanbul": "^9.0.1"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/test-runner-coverage-v8/node_modules/lru-cache": {
+      "version": "8.0.5",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
+      "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==",
+      "dev": true,
+      "engines": {
+        "node": ">=16.14"
+      }
+    },
+    "node_modules/@web/test-runner-mocha": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/@web/test-runner-mocha/-/test-runner-mocha-0.8.1.tgz",
+      "integrity": "sha512-CfYNZBbUSBiPNKkbF/dhxayecLCYZnu3g4cfgpfgmvLewlVOO6gNxaPt2c1/QhZutzTvXcMlsmaoWyk08F+V6A==",
+      "dev": true,
+      "dependencies": {
+        "@types/mocha": "^10.0.1",
+        "@web/test-runner-core": "^0.11.1"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/test-runner-playwright": {
+      "version": "0.10.1",
+      "resolved": "https://registry.npmjs.org/@web/test-runner-playwright/-/test-runner-playwright-0.10.1.tgz",
+      "integrity": "sha512-/sEfuKc60UT0gXdn7M6lFddh+nCepO73gLPe2Og7jfoFv2tDkkk41RYBG75jx11RMVOJ6+i1peluLZSVxLlcEg==",
+      "dev": true,
+      "dependencies": {
+        "@web/test-runner-core": "^0.11.0",
+        "@web/test-runner-coverage-v8": "^0.7.0",
+        "playwright": "^1.22.2"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/test-runner/node_modules/@web/test-runner-commands": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/@web/test-runner-commands/-/test-runner-commands-0.8.0.tgz",
+      "integrity": "sha512-R40Rz+Tf3Y3Z4Ka9ey0DQcwYz3BflkyaoL2TNc7CR33iN6zv9pzRkRrtt4M/cSQfOPBTEsLlVAi1FsbvXsCKuQ==",
+      "dev": true,
+      "dependencies": {
+        "@web/test-runner-core": "^0.11.1",
+        "mkdirp": "^1.0.4"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/@web/test-runner/node_modules/camelcase": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+      "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@web/test-runner/node_modules/globby": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+      "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+      "dev": true,
+      "dependencies": {
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.2.9",
+        "ignore": "^5.2.0",
+        "merge2": "^1.4.1",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
       },
       "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/@typo3/icons": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/@typo3/icons/-/icons-4.1.0.tgz",
-      "integrity": "sha512-QeCcfJKYuZqk/q49AYDmJ9rYLtZtoJf9fh97cJreq77riAd12TdR+YoZ/zurYA6L3nlYZPV+9EdvhBwHpksvEw=="
+    "node_modules/@web/test-runner/node_modules/source-map": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+      "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
     },
     "node_modules/@webassemblyjs/ast": {
       "version": "1.11.5",
@@ -2715,6 +3475,18 @@
         "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
       }
     },
+    "node_modules/agent-base": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
+      "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
     "node_modules/aggregate-error": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
@@ -2792,6 +3564,33 @@
       "resolved": "https://registry.npmjs.org/alwan/-/alwan-1.3.1.tgz",
       "integrity": "sha512-r3DQW37w7ju+2DNGGHNAc4cfgTVOUswicf8uf6TmdfQlq9CGNtsV2YsKLxJ4XKEjB+TqfaEMZaoiQw65OccwrA=="
     },
+    "node_modules/ansi-escapes": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+      "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+      "dev": true,
+      "dependencies": {
+        "type-fest": "^0.21.3"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ansi-escapes/node_modules/type-fest": {
+      "version": "0.21.3",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+      "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/ansi-regex": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
@@ -2853,6 +3652,15 @@
         "sprintf-js": "~1.0.2"
       }
     },
+    "node_modules/array-back": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz",
+      "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/array-each": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
@@ -2886,6 +3694,18 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/ast-types": {
+      "version": "0.13.4",
+      "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
+      "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/astral-regex": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
@@ -2903,6 +3723,15 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/async-mutex": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.4.0.tgz",
+      "integrity": "sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
     "node_modules/autobind-decorator": {
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/autobind-decorator/-/autobind-decorator-2.4.0.tgz",
@@ -2950,6 +3779,21 @@
       "resolved": "https://registry.npmjs.org/autosize/-/autosize-6.0.1.tgz",
       "integrity": "sha512-f86EjiUKE6Xvczc4ioP1JBlWG7FKrE13qe/DxBCpe8GCipCq2nFw73aO8QEBKHfSbYGDN5eB9jXWKen7tspDqQ=="
     },
+    "node_modules/axe-core": {
+      "version": "4.8.1",
+      "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.8.1.tgz",
+      "integrity": "sha512-9l850jDDPnKq48nbad8SiEelCv4OrUWrKab/cPj0GScVg6cb6NbCCt/Ulk26QEq5jP9NnGr04Bit1BHyV6r5CQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/b4a": {
+      "version": "1.6.4",
+      "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz",
+      "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==",
+      "dev": true
+    },
     "node_modules/balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -2976,13 +3820,13 @@
         }
       ]
     },
-    "node_modules/base64id": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
-      "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+    "node_modules/basic-ftp": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz",
+      "integrity": "sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==",
       "dev": true,
       "engines": {
-        "node": "^4.5.0 || >= 5.9"
+        "node": ">=10.0.0"
       }
     },
     "node_modules/big.js": {
@@ -3049,59 +3893,6 @@
         "safe-json-parse": "~1.0.1"
       }
     },
-    "node_modules/body-parser": {
-      "version": "1.20.1",
-      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
-      "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
-      "dev": true,
-      "dependencies": {
-        "bytes": "3.1.2",
-        "content-type": "~1.0.4",
-        "debug": "2.6.9",
-        "depd": "2.0.0",
-        "destroy": "1.2.0",
-        "http-errors": "2.0.0",
-        "iconv-lite": "0.4.24",
-        "on-finished": "2.4.1",
-        "qs": "6.11.0",
-        "raw-body": "2.5.1",
-        "type-is": "~1.6.18",
-        "unpipe": "1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.8",
-        "npm": "1.2.8000 || >= 1.4.16"
-      }
-    },
-    "node_modules/body-parser/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/body-parser/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/body-parser/node_modules/on-finished": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
-      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
-      "dev": true,
-      "dependencies": {
-        "ee-first": "1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
     "node_modules/body/node_modules/bytes": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz",
@@ -3222,6 +4013,15 @@
         "ieee754": "^1.1.13"
       }
     },
+    "node_modules/buffer-crc32": {
+      "version": "0.2.13",
+      "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+      "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/buffer-from": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -3276,17 +4076,6 @@
         "node": ">= 10"
       }
     },
-    "node_modules/cacache/node_modules/mkdirp": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
-      "bin": {
-        "mkdirp": "bin/cmd.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/cacache/node_modules/p-map": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
@@ -3301,6 +4090,19 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/cache-content-type": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz",
+      "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==",
+      "dev": true,
+      "dependencies": {
+        "mime-types": "^2.1.18",
+        "ylru": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 6.0.0"
+      }
+    },
     "node_modules/call-bind": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
@@ -3387,6 +4189,15 @@
         }
       ]
     },
+    "node_modules/chai-a11y-axe": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/chai-a11y-axe/-/chai-a11y-axe-1.5.0.tgz",
+      "integrity": "sha512-V/Vg/zJDr9aIkaHJ2KQu7lGTQQm5ZOH4u1k5iTMvIXuSVlSuUo0jcSpSqf9wUn9zl6oQXa4e4E0cqH18KOgKlQ==",
+      "dev": true,
+      "dependencies": {
+        "axe-core": "^4.3.3"
+      }
+    },
     "node_modules/chalk": {
       "version": "2.4.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -3401,6 +4212,37 @@
         "node": ">=4"
       }
     },
+    "node_modules/chalk-template": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz",
+      "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.1.2"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk-template?sponsor=1"
+      }
+    },
+    "node_modules/chalk-template/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
     "node_modules/chalk/node_modules/ansi-styles": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
@@ -3472,6 +4314,36 @@
         "node": ">=10"
       }
     },
+    "node_modules/chrome-launcher": {
+      "version": "0.15.2",
+      "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz",
+      "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*",
+        "escape-string-regexp": "^4.0.0",
+        "is-wsl": "^2.2.0",
+        "lighthouse-logger": "^1.0.0"
+      },
+      "bin": {
+        "print-chrome-path": "bin/print-chrome-path.js"
+      },
+      "engines": {
+        "node": ">=12.13.0"
+      }
+    },
+    "node_modules/chrome-launcher/node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/chrome-trace-event": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
@@ -3481,6 +4353,18 @@
         "node": ">=6.0"
       }
     },
+    "node_modules/chromium-bidi": {
+      "version": "0.4.16",
+      "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz",
+      "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==",
+      "dev": true,
+      "dependencies": {
+        "mitt": "3.0.0"
+      },
+      "peerDependencies": {
+        "devtools-protocol": "*"
+      }
+    },
     "node_modules/ckeditor5": {
       "version": "39.0.2",
       "resolved": "https://registry.npmjs.org/ckeditor5/-/ckeditor5-39.0.2.tgz",
@@ -3545,14 +4429,65 @@
       }
     },
     "node_modules/cliui": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
-      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
       "dev": true,
       "dependencies": {
         "string-width": "^4.2.0",
-        "strip-ansi": "^6.0.0",
+        "strip-ansi": "^6.0.1",
         "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/cliui/node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/clone": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+      "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/co": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+      "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+      "dev": true,
+      "engines": {
+        "iojs": ">= 1.0.0",
+        "node": ">= 0.12.0"
+      }
+    },
+    "node_modules/co-body": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/co-body/-/co-body-6.1.0.tgz",
+      "integrity": "sha512-m7pOT6CdLN7FuXUcpuz/8lfQ/L77x8SchHCF4G0RBTJO20Wzmhn5Sp4/5WsKy8OSpifBSUrmg83qEqaDHdyFuQ==",
+      "dev": true,
+      "dependencies": {
+        "inflation": "^2.0.0",
+        "qs": "^6.5.2",
+        "raw-body": "^2.3.3",
+        "type-is": "^1.6.16"
       }
     },
     "node_modules/color": {
@@ -3607,29 +4542,77 @@
         "simple-swizzle": "^0.2.2"
       }
     },
-    "node_modules/color/node_modules/color-convert": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+    "node_modules/color/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/colord": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
+      "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
+    },
+    "node_modules/colors": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+      "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==",
+      "engines": {
+        "node": ">=0.1.90"
+      }
+    },
+    "node_modules/command-line-args": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz",
+      "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==",
+      "dev": true,
+      "dependencies": {
+        "array-back": "^3.1.0",
+        "find-replace": "^3.0.0",
+        "lodash.camelcase": "^4.3.0",
+        "typical": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/command-line-usage": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.1.tgz",
+      "integrity": "sha512-NCyznE//MuTjwi3y84QVUGEOT+P5oto1e1Pk/jFPVdPPfsG03qpTIl3yw6etR+v73d0lXsoojRpvbru2sqePxQ==",
       "dev": true,
       "dependencies": {
-        "color-name": "~1.1.4"
+        "array-back": "^6.2.2",
+        "chalk-template": "^0.4.0",
+        "table-layout": "^3.0.0",
+        "typical": "^7.1.1"
       },
       "engines": {
-        "node": ">=7.0.0"
+        "node": ">=12.20.0"
       }
     },
-    "node_modules/colord": {
-      "version": "2.9.3",
-      "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
-      "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
+    "node_modules/command-line-usage/node_modules/array-back": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz",
+      "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.17"
+      }
     },
-    "node_modules/colors": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
-      "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==",
+    "node_modules/command-line-usage/node_modules/typical": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/typical/-/typical-7.1.1.tgz",
+      "integrity": "sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA==",
+      "dev": true,
       "engines": {
-        "node": ">=0.1.90"
+        "node": ">=12.17"
       }
     },
     "node_modules/commander": {
@@ -3658,45 +4641,23 @@
         "source-map": "^0.6.1"
       }
     },
-    "node_modules/connect": {
-      "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
-      "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+    "node_modules/content-disposition": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
-        "debug": "2.6.9",
-        "finalhandler": "1.1.2",
-        "parseurl": "~1.3.3",
-        "utils-merge": "1.0.1"
+        "safe-buffer": "5.2.1"
       },
       "engines": {
-        "node": ">= 0.10.0"
-      }
-    },
-    "node_modules/connect/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "2.0.0"
+        "node": ">= 0.6"
       }
     },
-    "node_modules/connect/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/content-type": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
-      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": ">= 0.6"
       }
@@ -3708,18 +4669,22 @@
       "dev": true
     },
     "node_modules/convert-source-map": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
-      "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
       "dev": true
     },
-    "node_modules/cookie": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
-      "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+    "node_modules/cookies": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz",
+      "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==",
       "dev": true,
+      "dependencies": {
+        "depd": "~2.0.0",
+        "keygrip": "~1.1.0"
+      },
       "engines": {
-        "node": ">= 0.6"
+        "node": ">= 0.8"
       }
     },
     "node_modules/core-util-is": {
@@ -3728,19 +4693,6 @@
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
       "license": "MIT"
     },
-    "node_modules/cors": {
-      "version": "2.8.5",
-      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
-      "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
-      "dev": true,
-      "dependencies": {
-        "object-assign": "^4",
-        "vary": "^1"
-      },
-      "engines": {
-        "node": ">= 0.10"
-      }
-    },
     "node_modules/cosmiconfig": {
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
@@ -3766,6 +4718,15 @@
       "resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.5.13.tgz",
       "integrity": "sha512-by7jKAo73y5/Do0K6sxdTKHgndY0NMjG2bEdgeJxycbcmHuCiMXqw8sxy5C5Y5WTOTcDGmbT7Sr5CgKOXR06OA=="
     },
+    "node_modules/cross-fetch": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
+      "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
+      "dev": true,
+      "dependencies": {
+        "node-fetch": "^2.6.12"
+      }
+    },
     "node_modules/css-color-list": {
       "version": "0.0.2",
       "resolved": "https://registry.npmjs.org/css-color-list/-/css-color-list-0.0.2.tgz",
@@ -3992,13 +4953,6 @@
         "node": ">=8.0.0"
       }
     },
-    "node_modules/custom-event": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
-      "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/d3-dispatch": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
@@ -4027,13 +4981,13 @@
         "node": ">=12"
       }
     },
-    "node_modules/date-format": {
-      "version": "4.0.13",
-      "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.13.tgz",
-      "integrity": "sha512-bnYCwf8Emc3pTD8pXnre+wfnjGtfi5ncMDKy7+cWZXbmRAsdWkOQHrfC1yz/KiwP5thDp2kCHWYWKBX4HP1hoQ==",
+    "node_modules/data-uri-to-buffer": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-5.0.1.tgz",
+      "integrity": "sha512-a9l6T1qqDogvvnw0nKlfZzqsyikEBZBClF39V3TFoKhDtGBqHu2HkuomJc02j5zft8zrUaXEuoicLeW54RkzPg==",
       "dev": true,
       "engines": {
-        "node": ">=4.0"
+        "node": ">= 14"
       }
     },
     "node_modules/dateformat": {
@@ -4048,8 +5002,7 @@
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
       "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
-      "dev": true,
-      "license": "MIT"
+      "dev": true
     },
     "node_modules/debug": {
       "version": "4.3.4",
@@ -4113,6 +5066,12 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/deep-equal": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
+      "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==",
+      "dev": true
+    },
     "node_modules/deep-extend": {
       "version": "0.6.0",
       "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
@@ -4138,6 +5097,29 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/define-lazy-prop": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+      "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/degenerator": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
+      "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
+      "dev": true,
+      "dependencies": {
+        "ast-types": "^0.13.4",
+        "escodegen": "^2.1.0",
+        "esprima": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
     "node_modules/del": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/del/-/del-5.1.0.tgz",
@@ -4174,6 +5156,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/delegates": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+      "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
+      "dev": true
+    },
     "node_modules/depd": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -4183,6 +5171,15 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/dependency-graph": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz",
+      "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
     "node_modules/destroy": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@@ -4210,12 +5207,11 @@
         "node": ">=8"
       }
     },
-    "node_modules/di": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
-      "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=",
-      "dev": true,
-      "license": "MIT"
+    "node_modules/devtools-protocol": {
+      "version": "0.0.1147663",
+      "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz",
+      "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==",
+      "dev": true
     },
     "node_modules/diff": {
       "version": "5.1.0",
@@ -4256,19 +5252,6 @@
         "node": ">=6.0.0"
       }
     },
-    "node_modules/dom-serialize": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
-      "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "custom-event": "~1.0.0",
-        "ent": "~2.2.0",
-        "extend": "^3.0.0",
-        "void-elements": "^2.0.0"
-      }
-    },
     "node_modules/dom-serializer": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@@ -4373,9 +5356,8 @@
     "node_modules/ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
-      "dev": true,
-      "license": "MIT"
+      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+      "dev": true
     },
     "node_modules/electron-to-chromium": {
       "version": "1.4.475",
@@ -4400,9 +5382,8 @@
     "node_modules/encodeurl": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+      "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": ">= 0.8"
       }
@@ -4417,36 +5398,6 @@
         "once": "^1.4.0"
       }
     },
-    "node_modules/engine.io": {
-      "version": "6.5.1",
-      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.1.tgz",
-      "integrity": "sha512-mGqhI+D7YxS9KJMppR6Iuo37Ed3abhU8NdfgSvJSDUafQutrN+sPTncJYTyM9+tkhSmWodKtVYGPPHyXJEwEQA==",
-      "dev": true,
-      "dependencies": {
-        "@types/cookie": "^0.4.1",
-        "@types/cors": "^2.8.12",
-        "@types/node": ">=10.0.0",
-        "accepts": "~1.3.4",
-        "base64id": "2.0.0",
-        "cookie": "~0.4.1",
-        "cors": "~2.8.5",
-        "debug": "~4.3.1",
-        "engine.io-parser": "~5.1.0",
-        "ws": "~8.11.0"
-      },
-      "engines": {
-        "node": ">=10.0.0"
-      }
-    },
-    "node_modules/engine.io-parser": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.1.0.tgz",
-      "integrity": "sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==",
-      "dev": true,
-      "engines": {
-        "node": ">=10.0.0"
-      }
-    },
     "node_modules/enhanced-resolve": {
       "version": "5.13.0",
       "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz",
@@ -4460,13 +5411,6 @@
         "node": ">=10.13.0"
       }
     },
-    "node_modules/ent": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
-      "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/entities": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
@@ -4493,6 +5437,12 @@
         "is-arrayish": "^0.2.1"
       }
     },
+    "node_modules/errorstacks": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/errorstacks/-/errorstacks-2.4.0.tgz",
+      "integrity": "sha512-5ecWhU5gt0a5G05nmQcgCxP5HperSMxLDzvWlT5U+ZSKkuDK0rJ3dbCQny6/vSCIXjwrhwSecXBbw1alr295hQ==",
+      "dev": true
+    },
     "node_modules/es-module-lexer": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz",
@@ -4584,9 +5534,8 @@
     "node_modules/escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
-      "dev": true,
-      "license": "MIT"
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+      "dev": true
     },
     "node_modules/escape-string-regexp": {
       "version": "1.0.5",
@@ -4597,6 +5546,27 @@
         "node": ">=0.8.0"
       }
     },
+    "node_modules/escodegen": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+      "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+      "dev": true,
+      "dependencies": {
+        "esprima": "^4.0.1",
+        "estraverse": "^5.2.0",
+        "esutils": "^2.0.2"
+      },
+      "bin": {
+        "escodegen": "bin/escodegen.js",
+        "esgenerate": "bin/esgenerate.js"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "optionalDependencies": {
+        "source-map": "~0.6.1"
+      }
+    },
     "node_modules/eslint": {
       "version": "8.38.0",
       "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
@@ -5006,6 +5976,15 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/eventemitter2": {
       "version": "0.4.14",
       "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
@@ -5062,12 +6041,63 @@
       "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
       "license": "MIT"
     },
+    "node_modules/extract-zip": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+      "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^4.1.1",
+        "get-stream": "^5.1.0",
+        "yauzl": "^2.10.0"
+      },
+      "bin": {
+        "extract-zip": "cli.js"
+      },
+      "engines": {
+        "node": ">= 10.17.0"
+      },
+      "optionalDependencies": {
+        "@types/yauzl": "^2.9.1"
+      }
+    },
+    "node_modules/extract-zip/node_modules/get-stream": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+      "dev": true,
+      "dependencies": {
+        "pump": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/extract-zip/node_modules/pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "dev": true,
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
     "node_modules/fast-deep-equal": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
       "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
       "license": "MIT"
     },
+    "node_modules/fast-fifo": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+      "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+      "dev": true
+    },
     "node_modules/fast-glob": {
       "version": "3.2.11",
       "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
@@ -5127,6 +6157,15 @@
         "node": ">=0.4.0"
       }
     },
+    "node_modules/fd-slicer": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+      "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+      "dev": true,
+      "dependencies": {
+        "pend": "~1.2.0"
+      }
+    },
     "node_modules/file-entry-cache": {
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -5168,42 +6207,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/finalhandler": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
-      "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "2.6.9",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "on-finished": "~2.3.0",
-        "parseurl": "~1.3.3",
-        "statuses": "~1.5.0",
-        "unpipe": "~1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/finalhandler/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/finalhandler/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/find-cache-dir": {
       "version": "3.3.2",
       "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
@@ -5220,6 +6223,18 @@
         "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
       }
     },
+    "node_modules/find-replace": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz",
+      "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==",
+      "dev": true,
+      "dependencies": {
+        "array-back": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
     "node_modules/find-up": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@@ -5301,26 +6316,6 @@
       "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
       "dev": true
     },
-    "node_modules/follow-redirects": {
-      "version": "1.15.1",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
-      "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://github.com/sponsors/RubenVerborgh"
-        }
-      ],
-      "engines": {
-        "node": ">=4.0"
-      },
-      "peerDependenciesMeta": {
-        "debug": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/for-in": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@@ -5354,12 +6349,34 @@
         "url": "https://www.patreon.com/infusion"
       }
     },
+    "node_modules/fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/fs-constants": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
       "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
       "dev": true
     },
+    "node_modules/fs-extra": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+      "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^4.0.0",
+        "universalify": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=6 <7 || >=8"
+      }
+    },
     "node_modules/fs-minipass": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
@@ -5419,15 +6436,6 @@
         "loader-utils": "^3.2.0"
       }
     },
-    "node_modules/gensync": {
-      "version": "1.0.0-beta.2",
-      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
-      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
-      "dev": true,
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/get-caller-file": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -5451,6 +6459,18 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/get-tsconfig": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.5.0.tgz",
@@ -5459,6 +6479,21 @@
         "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
       }
     },
+    "node_modules/get-uri": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.1.tgz",
+      "integrity": "sha512-7ZqONUVqaabogsYNWlYj0t3YZaL6dhuEueZXGF+/YVmf6dHmaFg8/6psJKqhx9QykIDKzpGcy2cn4oV4YC7V/Q==",
+      "dev": true,
+      "dependencies": {
+        "basic-ftp": "^5.0.2",
+        "data-uri-to-buffer": "^5.0.1",
+        "debug": "^4.3.4",
+        "fs-extra": "^8.1.0"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
     "node_modules/getobject": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz",
@@ -6213,6 +7248,21 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+      "dev": true,
+      "dependencies": {
+        "has-symbols": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/homedir-polyfill": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
@@ -6262,29 +7312,42 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/http-assert": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz",
+      "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==",
+      "dev": true,
+      "dependencies": {
+        "deep-equal": "~1.0.1",
+        "http-errors": "~1.8.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/http-errors": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
-      "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
+      "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
       "dev": true,
       "dependencies": {
-        "depd": "2.0.0",
+        "depd": "~1.1.2",
         "inherits": "2.0.4",
         "setprototypeof": "1.2.0",
-        "statuses": "2.0.1",
+        "statuses": ">= 1.5.0 < 2",
         "toidentifier": "1.0.1"
       },
       "engines": {
-        "node": ">= 0.8"
+        "node": ">= 0.6"
       }
     },
-    "node_modules/http-errors/node_modules/statuses": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
-      "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+    "node_modules/http-errors/node_modules/depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
       "dev": true,
       "engines": {
-        "node": ">= 0.8"
+        "node": ">= 0.6"
       }
     },
     "node_modules/http-parser-js": {
@@ -6294,19 +7357,30 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/http-proxy": {
-      "version": "1.18.1",
-      "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
-      "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+    "node_modules/http-proxy-agent": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz",
+      "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
-        "eventemitter3": "^4.0.0",
-        "follow-redirects": "^1.0.0",
-        "requires-port": "^1.0.0"
+        "agent-base": "^7.1.0",
+        "debug": "^4.3.4"
       },
       "engines": {
-        "node": ">=8.0.0"
+        "node": ">= 14"
+      }
+    },
+    "node_modules/https-proxy-agent": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz",
+      "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==",
+      "dev": true,
+      "dependencies": {
+        "agent-base": "^7.0.2",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 14"
       }
     },
     "node_modules/iconv-lite": {
@@ -6314,7 +7388,6 @@
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
       "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "safer-buffer": ">= 2.1.2 < 3"
       },
@@ -6452,6 +7525,15 @@
       "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
       "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A=="
     },
+    "node_modules/inflation": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/inflation/-/inflation-2.0.0.tgz",
+      "integrity": "sha512-m3xv4hJYR2oXw4o4Y5l6P5P16WYmazYof+el6Al3f+YlggGj6qT9kImBAnzDelRALnP5d3h4jGBPKzYCizjZZw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
     "node_modules/inflight": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -6486,6 +7568,12 @@
       "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
       "integrity": "sha512-CLM8SNMDu7C5psFCn6Wg/tgpj/bKAg7hc2gWqcuR9OD5Ft9PhBpIu8PLicPeis+xDd6YX2ncI8MCA64I9tftIA=="
     },
+    "node_modules/ip": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
+      "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==",
+      "dev": true
+    },
     "node_modules/is-absolute": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
@@ -6518,9 +7606,9 @@
       }
     },
     "node_modules/is-builtin-module": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz",
-      "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==",
+      "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"
@@ -6543,6 +7631,21 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-docker": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+      "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+      "dev": true,
+      "bin": {
+        "is-docker": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -6561,6 +7664,21 @@
         "node": ">=8"
       }
     },
+    "node_modules/is-generator-function": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+      "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+      "dev": true,
+      "dependencies": {
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/is-glob": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -6649,6 +7767,18 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/is-stream": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-unc-path": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
@@ -6678,18 +7808,30 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/is-wsl": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+      "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+      "dev": true,
+      "dependencies": {
+        "is-docker": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/isarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
       "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
     },
     "node_modules/isbinaryfile": {
-      "version": "4.0.10",
-      "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
-      "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.0.tgz",
+      "integrity": "sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg==",
       "dev": true,
       "engines": {
-        "node": ">= 8.0.0"
+        "node": ">= 14.0.0"
       },
       "funding": {
         "url": "https://github.com/sponsors/gjtorikian/"
@@ -6719,63 +7861,54 @@
         "node": ">=8"
       }
     },
-    "node_modules/istanbul-lib-instrument": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
-      "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+    "node_modules/istanbul-lib-report": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+      "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
       "dev": true,
       "dependencies": {
-        "@babel/core": "^7.12.3",
-        "@babel/parser": "^7.14.7",
-        "@istanbuljs/schema": "^0.1.2",
-        "istanbul-lib-coverage": "^3.2.0",
-        "semver": "^6.3.0"
+        "istanbul-lib-coverage": "^3.0.0",
+        "make-dir": "^4.0.0",
+        "supports-color": "^7.1.0"
       },
       "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/istanbul-lib-instrument/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver.js"
+        "node": ">=10"
       }
     },
-    "node_modules/istanbul-lib-report": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
-      "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+    "node_modules/istanbul-lib-report/node_modules/make-dir": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
+      "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
       "dev": true,
       "dependencies": {
-        "istanbul-lib-coverage": "^3.0.0",
-        "make-dir": "^3.0.0",
-        "supports-color": "^7.1.0"
+        "semver": "^7.5.3"
       },
       "engines": {
-        "node": ">=8"
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/istanbul-lib-source-maps": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
-      "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+    "node_modules/istanbul-lib-report/node_modules/semver": {
+      "version": "7.5.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
       "dev": true,
       "dependencies": {
-        "debug": "^4.1.1",
-        "istanbul-lib-coverage": "^3.0.0",
-        "source-map": "^0.6.1"
+        "lru-cache": "^6.0.0"
+      },
+      "bin": {
+        "semver": "bin/semver.js"
       },
       "engines": {
         "node": ">=10"
       }
     },
     "node_modules/istanbul-reports": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
-      "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz",
+      "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==",
       "dev": true,
       "dependencies": {
         "html-escaper": "^2.0.0",
@@ -6785,12 +7918,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/jasmine-core": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.0.tgz",
-      "integrity": "sha512-O236+gd0ZXS8YAjFx8xKaJ94/erqUliEkJTDedyE7iHvv4ZVqi+q+8acJxu05/WJDKm512EUNn809In37nWlAQ==",
-      "dev": true
-    },
     "node_modules/javascript-stringify": {
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-1.6.0.tgz",
@@ -6899,172 +8026,145 @@
         "graceful-fs": "^4.1.6"
       }
     },
-    "node_modules/karma": {
-      "version": "6.4.1",
-      "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz",
-      "integrity": "sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA==",
+    "node_modules/just-extend": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz",
+      "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==",
+      "dev": true
+    },
+    "node_modules/keygrip": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
+      "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==",
       "dev": true,
       "dependencies": {
-        "@colors/colors": "1.5.0",
-        "body-parser": "^1.19.0",
-        "braces": "^3.0.2",
-        "chokidar": "^3.5.1",
-        "connect": "^3.7.0",
-        "di": "^0.0.1",
-        "dom-serialize": "^2.2.1",
-        "glob": "^7.1.7",
-        "graceful-fs": "^4.2.6",
-        "http-proxy": "^1.18.1",
-        "isbinaryfile": "^4.0.8",
-        "lodash": "^4.17.21",
-        "log4js": "^6.4.1",
-        "mime": "^2.5.2",
-        "minimatch": "^3.0.4",
-        "mkdirp": "^0.5.5",
-        "qjobs": "^1.2.0",
-        "range-parser": "^1.2.1",
-        "rimraf": "^3.0.2",
-        "socket.io": "^4.4.1",
-        "source-map": "^0.6.1",
-        "tmp": "^0.2.1",
-        "ua-parser-js": "^0.7.30",
-        "yargs": "^16.1.1"
-      },
-      "bin": {
-        "karma": "bin/karma"
+        "tsscmp": "1.0.6"
       },
       "engines": {
-        "node": ">= 10"
+        "node": ">= 0.6"
       }
     },
-    "node_modules/karma-chrome-launcher": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz",
-      "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==",
-      "dev": true,
-      "dependencies": {
-        "which": "^1.2.1"
+    "node_modules/kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "node_modules/karma-coverage": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.0.tgz",
-      "integrity": "sha512-gPVdoZBNDZ08UCzdMHHhEImKrw1+PAOQOIiffv1YsvxFhBjqvo/SVXNk4tqn1SYqX0BJZT6S/59zgxiBe+9OuA==",
-      "dev": true,
-      "dependencies": {
-        "istanbul-lib-coverage": "^3.2.0",
-        "istanbul-lib-instrument": "^5.1.0",
-        "istanbul-lib-report": "^3.0.0",
-        "istanbul-lib-source-maps": "^4.0.1",
-        "istanbul-reports": "^3.0.5",
-        "minimatch": "^3.0.4"
-      },
+    "node_modules/klona": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
+      "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==",
       "engines": {
-        "node": ">=10.0.0"
+        "node": ">= 8"
       }
     },
-    "node_modules/karma-jasmine": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz",
-      "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==",
+    "node_modules/known-css-properties": {
+      "version": "0.25.0",
+      "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz",
+      "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==",
+      "dev": true
+    },
+    "node_modules/koa": {
+      "version": "2.14.2",
+      "resolved": "https://registry.npmjs.org/koa/-/koa-2.14.2.tgz",
+      "integrity": "sha512-VFI2bpJaodz6P7x2uyLiX6RLYpZmOJqNmoCst/Yyd7hQlszyPwG/I9CQJ63nOtKSxpt5M7NH67V6nJL2BwCl7g==",
       "dev": true,
       "dependencies": {
-        "jasmine-core": "^4.1.0"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "peerDependencies": {
-        "karma": "^6.0.0"
-      }
+        "accepts": "^1.3.5",
+        "cache-content-type": "^1.0.0",
+        "content-disposition": "~0.5.2",
+        "content-type": "^1.0.4",
+        "cookies": "~0.8.0",
+        "debug": "^4.3.2",
+        "delegates": "^1.0.0",
+        "depd": "^2.0.0",
+        "destroy": "^1.0.4",
+        "encodeurl": "^1.0.2",
+        "escape-html": "^1.0.3",
+        "fresh": "~0.5.2",
+        "http-assert": "^1.3.0",
+        "http-errors": "^1.6.3",
+        "is-generator-function": "^1.0.7",
+        "koa-compose": "^4.1.0",
+        "koa-convert": "^2.0.0",
+        "on-finished": "^2.3.0",
+        "only": "~0.0.2",
+        "parseurl": "^1.3.2",
+        "statuses": "^1.5.0",
+        "type-is": "^1.6.16",
+        "vary": "^1.1.2"
+      },
+      "engines": {
+        "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4"
+      }
+    },
+    "node_modules/koa-compose": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz",
+      "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==",
+      "dev": true
     },
-    "node_modules/karma-junit-reporter": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-2.0.1.tgz",
-      "integrity": "sha512-VtcGfE0JE4OE1wn0LK8xxDKaTP7slN8DO3I+4xg6gAi1IoAHAXOJ1V9G/y45Xg6sxdxPOR3THCFtDlAfBo9Afw==",
+    "node_modules/koa-convert": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz",
+      "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==",
       "dev": true,
       "dependencies": {
-        "path-is-absolute": "^1.0.0",
-        "xmlbuilder": "12.0.0"
+        "co": "^4.6.0",
+        "koa-compose": "^4.1.0"
       },
       "engines": {
-        "node": ">= 8"
-      },
-      "peerDependencies": {
-        "karma": ">=0.9"
+        "node": ">= 10"
       }
     },
-    "node_modules/karma-rollup-preprocessor": {
-      "version": "7.0.8",
-      "resolved": "https://registry.npmjs.org/karma-rollup-preprocessor/-/karma-rollup-preprocessor-7.0.8.tgz",
-      "integrity": "sha512-WiuBCS9qsatJuR17dghiTARBZ7LF+ml+eb7qJXhw7IbsdY0lTWELDRQC/93J9i6636CsAXVBL3VJF4WtaFLZzA==",
+    "node_modules/koa-etag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/koa-etag/-/koa-etag-4.0.0.tgz",
+      "integrity": "sha512-1cSdezCkBWlyuB9l6c/IFoe1ANCDdPBxkDkRiaIup40xpUub6U/wwRXoKBZw/O5BifX9OlqAjYnDyzM6+l+TAg==",
       "dev": true,
       "dependencies": {
-        "chokidar": "^3.3.1",
-        "debounce": "^1.2.0"
-      },
-      "engines": {
-        "node": ">= 8.0.0"
-      },
-      "peerDependencies": {
-        "rollup": ">= 1.0.0"
+        "etag": "^1.8.1"
       }
     },
-    "node_modules/karma/node_modules/glob": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+    "node_modules/koa-send": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.1.tgz",
+      "integrity": "sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==",
       "dev": true,
       "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
+        "debug": "^4.1.1",
+        "http-errors": "^1.7.3",
+        "resolve-path": "^1.4.0"
       },
       "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
+        "node": ">= 8"
       }
     },
-    "node_modules/karma/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+    "node_modules/koa-static": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/koa-static/-/koa-static-5.0.0.tgz",
+      "integrity": "sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==",
       "dev": true,
       "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "debug": "^3.1.0",
+        "koa-send": "^5.0.0"
       },
       "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/kind-of": {
-      "version": "6.0.3",
-      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
-      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
+        "node": ">= 7.6.0"
       }
     },
-    "node_modules/klona": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
-      "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==",
-      "engines": {
-        "node": ">= 8"
+    "node_modules/koa-static/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "dev": true,
+      "dependencies": {
+        "ms": "^2.1.1"
       }
     },
-    "node_modules/known-css-properties": {
-      "version": "0.25.0",
-      "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz",
-      "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==",
-      "dev": true
-    },
     "node_modules/levn": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -7111,6 +8211,31 @@
         "node": ">= 8"
       }
     },
+    "node_modules/lighthouse-logger": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz",
+      "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^2.6.9",
+        "marky": "^1.2.2"
+      }
+    },
+    "node_modules/lighthouse-logger/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/lighthouse-logger/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true
+    },
     "node_modules/lilconfig": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
@@ -7314,12 +8439,24 @@
       "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
       "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
     },
+    "node_modules/lodash.assignwith": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz",
+      "integrity": "sha512-ZznplvbvtjK2gMvnQ1BR/zqPFZmS6jbK4p+6Up4xcRYA7yMIwxHCfbTcrYxXKzzqLsQ05eJPVznEW3tuwV7k1g==",
+      "dev": true
+    },
     "node_modules/lodash.camelcase": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
       "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
       "dev": true
     },
+    "node_modules/lodash.get": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+      "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
+      "dev": true
+    },
     "node_modules/lodash.map": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
@@ -7349,20 +8486,22 @@
       "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
       "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="
     },
-    "node_modules/log4js": {
-      "version": "6.6.1",
-      "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.6.1.tgz",
-      "integrity": "sha512-J8VYFH2UQq/xucdNu71io4Fo+purYYudyErgBbswWKO0MC6QVOERRomt5su/z6d3RJSmLyTGmXl3Q/XjKCf+/A==",
+    "node_modules/log-update": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz",
+      "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==",
       "dev": true,
       "dependencies": {
-        "date-format": "^4.0.13",
-        "debug": "^4.3.4",
-        "flatted": "^3.2.6",
-        "rfdc": "^1.3.0",
-        "streamroller": "^3.1.2"
+        "ansi-escapes": "^4.3.0",
+        "cli-cursor": "^3.1.0",
+        "slice-ansi": "^4.0.0",
+        "wrap-ansi": "^6.2.0"
       },
       "engines": {
-        "node": ">=8.0"
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/lru-cache": {
@@ -7454,6 +8593,12 @@
       "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz",
       "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ=="
     },
+    "node_modules/marky": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz",
+      "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==",
+      "dev": true
+    },
     "node_modules/mathml-tag-names": {
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
@@ -7519,9 +8664,8 @@
     "node_modules/media-typer": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
-      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": ">= 0.6"
       }
@@ -7590,18 +8734,6 @@
         "node": ">=8.6"
       }
     },
-    "node_modules/mime": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
-      "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
-      "dev": true,
-      "bin": {
-        "mime": "cli.js"
-      },
-      "engines": {
-        "node": ">=4.0.0"
-      }
-    },
     "node_modules/mime-db": {
       "version": "1.52.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -7804,17 +8936,21 @@
         "node": ">= 8"
       }
     },
+    "node_modules/mitt": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz",
+      "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==",
+      "dev": true
+    },
     "node_modules/mkdirp": {
-      "version": "0.5.5",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-      "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "minimist": "^1.2.5"
-      },
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
       "bin": {
         "mkdirp": "bin/cmd.js"
+      },
+      "engines": {
+        "node": ">=10"
       }
     },
     "node_modules/mkdirp-classic": {
@@ -7834,6 +8970,12 @@
       "resolved": "https://registry.npmjs.org/muuri/-/muuri-0.9.5.tgz",
       "integrity": "sha512-nJL9/Pd5IaIXGAVunBs/LLQ+v6tPkvlqCYrlauWESgkVFr+F+CRf8HnayRh4AqiQ1S/PIEN39fhJSe4L5rLlxg=="
     },
+    "node_modules/nanocolors": {
+      "version": "0.2.13",
+      "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.2.13.tgz",
+      "integrity": "sha512-0n3mSAQLPpGLV9ORXT5+C/D4mwew7Ebws69Hx4E2sgz2ZA5+32Q80B9tL8PbL7XHnRDiAxH/pnrUJ9a4fkTNTA==",
+      "dev": true
+    },
     "node_modules/nanoid": {
       "version": "3.3.6",
       "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
@@ -7879,6 +9021,37 @@
       "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
       "peer": true
     },
+    "node_modules/netmask": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
+      "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/nise": {
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz",
+      "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==",
+      "dev": true,
+      "dependencies": {
+        "@sinonjs/commons": "^2.0.0",
+        "@sinonjs/fake-timers": "^10.0.2",
+        "@sinonjs/text-encoding": "^0.7.1",
+        "just-extend": "^4.0.2",
+        "path-to-regexp": "^1.7.0"
+      }
+    },
+    "node_modules/nise/node_modules/@sinonjs/commons": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+      "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+      "dev": true,
+      "dependencies": {
+        "type-detect": "4.0.8"
+      }
+    },
     "node_modules/node-abi": {
       "version": "3.31.0",
       "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.31.0.tgz",
@@ -7912,6 +9085,48 @@
       "integrity": "sha512-GyHvgPvUXBvAkXa0YvYnhilSB1A+FRYMpIVggKzPZqdaZfevZOuzfWzyvgzOwRLHBeo/MMswmJFsrNF4Nw1pmA==",
       "dev": true
     },
+    "node_modules/node-fetch": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+      "dev": true,
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/node-fetch/node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+      "dev": true
+    },
+    "node_modules/node-fetch/node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+      "dev": true
+    },
+    "node_modules/node-fetch/node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "dev": true,
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
     "node_modules/node-releases": {
       "version": "2.0.13",
       "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
@@ -8065,11 +9280,10 @@
       }
     },
     "node_modules/on-finished": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
-      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "ee-first": "1.1.1"
       },
@@ -8100,6 +9314,29 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/only": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz",
+      "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==",
+      "dev": true
+    },
+    "node_modules/open": {
+      "version": "8.4.2",
+      "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
+      "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
+      "dev": true,
+      "dependencies": {
+        "define-lazy-prop": "^2.0.0",
+        "is-docker": "^2.1.1",
+        "is-wsl": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/optionator": {
       "version": "0.9.1",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@@ -8226,6 +9463,39 @@
         "node": ">=6"
       }
     },
+    "node_modules/pac-proxy-agent": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz",
+      "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==",
+      "dev": true,
+      "dependencies": {
+        "@tootallnate/quickjs-emscripten": "^0.23.0",
+        "agent-base": "^7.0.2",
+        "debug": "^4.3.4",
+        "get-uri": "^6.0.1",
+        "http-proxy-agent": "^7.0.0",
+        "https-proxy-agent": "^7.0.2",
+        "pac-resolver": "^7.0.0",
+        "socks-proxy-agent": "^8.0.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/pac-resolver": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.0.tgz",
+      "integrity": "sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==",
+      "dev": true,
+      "dependencies": {
+        "degenerator": "^5.0.0",
+        "ip": "^1.1.8",
+        "netmask": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
     "node_modules/pad-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/pad-stream/-/pad-stream-2.0.0.tgz",
@@ -8322,7 +9592,6 @@
       "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
       "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": ">= 0.8"
       }
@@ -8368,6 +9637,21 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/path-to-regexp": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
+      "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
+      "dev": true,
+      "dependencies": {
+        "isarray": "0.0.1"
+      }
+    },
+    "node_modules/path-to-regexp/node_modules/isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
+      "dev": true
+    },
     "node_modules/path-type": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@@ -8377,6 +9661,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/pend": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+      "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+      "dev": true
+    },
     "node_modules/picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -8412,11 +9702,85 @@
         "node": ">=8"
       }
     },
+    "node_modules/playwright": {
+      "version": "1.38.0",
+      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.0.tgz",
+      "integrity": "sha512-fJGw+HO0YY+fU/F1N57DMO+TmXHTrmr905J05zwAQE9xkuwP/QLDk63rVhmyxh03dYnEhnRbsdbH9B0UVVRB3A==",
+      "dev": true,
+      "dependencies": {
+        "playwright-core": "1.38.0"
+      },
+      "bin": {
+        "playwright": "cli.js"
+      },
+      "engines": {
+        "node": ">=16"
+      },
+      "optionalDependencies": {
+        "fsevents": "2.3.2"
+      }
+    },
+    "node_modules/playwright-core": {
+      "version": "1.38.0",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.0.tgz",
+      "integrity": "sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g==",
+      "dev": true,
+      "bin": {
+        "playwright-core": "cli.js"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
     "node_modules/pofile": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.4.tgz",
       "integrity": "sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g=="
     },
+    "node_modules/portfinder": {
+      "version": "1.0.32",
+      "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz",
+      "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==",
+      "dev": true,
+      "dependencies": {
+        "async": "^2.6.4",
+        "debug": "^3.2.7",
+        "mkdirp": "^0.5.6"
+      },
+      "engines": {
+        "node": ">= 0.12.0"
+      }
+    },
+    "node_modules/portfinder/node_modules/async": {
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+      "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+      "dev": true,
+      "dependencies": {
+        "lodash": "^4.17.14"
+      }
+    },
+    "node_modules/portfinder/node_modules/debug": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "dev": true,
+      "dependencies": {
+        "ms": "^2.1.1"
+      }
+    },
+    "node_modules/portfinder/node_modules/mkdirp": {
+      "version": "0.5.6",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+      "dev": true,
+      "dependencies": {
+        "minimist": "^1.2.6"
+      },
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      }
+    },
     "node_modules/postcss": {
       "version": "8.4.27",
       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz",
@@ -9222,6 +10586,15 @@
       "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
       "license": "MIT"
     },
+    "node_modules/progress": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/promise-inflight": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@@ -9236,6 +10609,40 @@
         "node": ">=0.12"
       }
     },
+    "node_modules/proxy-agent": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz",
+      "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==",
+      "dev": true,
+      "dependencies": {
+        "agent-base": "^7.0.2",
+        "debug": "^4.3.4",
+        "http-proxy-agent": "^7.0.0",
+        "https-proxy-agent": "^7.0.0",
+        "lru-cache": "^7.14.1",
+        "pac-proxy-agent": "^7.0.0",
+        "proxy-from-env": "^1.1.0",
+        "socks-proxy-agent": "^8.0.1"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/proxy-agent/node_modules/lru-cache": {
+      "version": "7.18.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+      "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "dev": true
+    },
     "node_modules/pseudomap": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
@@ -9275,14 +10682,50 @@
         "node": ">=6"
       }
     },
-    "node_modules/qjobs": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
-      "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
+    "node_modules/puppeteer-core": {
+      "version": "20.9.0",
+      "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz",
+      "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==",
+      "dev": true,
+      "dependencies": {
+        "@puppeteer/browsers": "1.4.6",
+        "chromium-bidi": "0.4.16",
+        "cross-fetch": "4.0.0",
+        "debug": "4.3.4",
+        "devtools-protocol": "0.0.1147663",
+        "ws": "8.13.0"
+      },
+      "engines": {
+        "node": ">=16.3.0"
+      },
+      "peerDependencies": {
+        "typescript": ">= 4.7.4"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/puppeteer-core/node_modules/ws": {
+      "version": "8.13.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
+      "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
       "dev": true,
-      "license": "MIT",
       "engines": {
-        "node": ">=0.9"
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
       }
     },
     "node_modules/qs": {
@@ -9319,6 +10762,12 @@
         }
       ]
     },
+    "node_modules/queue-tick": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
+      "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==",
+      "dev": true
+    },
     "node_modules/quick-lru": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz",
@@ -9336,31 +10785,46 @@
         "safe-buffer": "^5.1.0"
       }
     },
-    "node_modules/range-parser": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
-      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+    "node_modules/raw-body": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+      "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
       "dev": true,
-      "license": "MIT",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      },
       "engines": {
-        "node": ">= 0.6"
+        "node": ">= 0.8"
       }
     },
-    "node_modules/raw-body": {
-      "version": "2.5.1",
-      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
-      "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+    "node_modules/raw-body/node_modules/http-errors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+      "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
       "dev": true,
       "dependencies": {
-        "bytes": "3.1.2",
-        "http-errors": "2.0.0",
-        "iconv-lite": "0.4.24",
-        "unpipe": "1.0.0"
+        "depd": "2.0.0",
+        "inherits": "2.0.4",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "toidentifier": "1.0.1"
       },
       "engines": {
         "node": ">= 0.8"
       }
     },
+    "node_modules/raw-body/node_modules/statuses": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+      "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/raw-loader": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz",
@@ -9594,13 +11058,6 @@
         "node": ">=0.10.5"
       }
     },
-    "node_modules/requires-port": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
-      "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/resolve": {
       "version": "1.22.1",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
@@ -9667,6 +11124,55 @@
         "node": ">=8"
       }
     },
+    "node_modules/resolve-path": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz",
+      "integrity": "sha512-i1xevIst/Qa+nA9olDxLWnLk8YZbi8R/7JPbCMcgyWaFR6bKWaexgJgEB5oc2PKMjYdrHynyz0NY+if+H98t1w==",
+      "dev": true,
+      "dependencies": {
+        "http-errors": "~1.6.2",
+        "path-is-absolute": "1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/resolve-path/node_modules/depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/resolve-path/node_modules/http-errors": {
+      "version": "1.6.3",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+      "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
+      "dev": true,
+      "dependencies": {
+        "depd": "~1.1.2",
+        "inherits": "2.0.3",
+        "setprototypeof": "1.1.0",
+        "statuses": ">= 1.4.0 < 2"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/resolve-path/node_modules/inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+      "dev": true
+    },
+    "node_modules/resolve-path/node_modules/setprototypeof": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+      "dev": true
+    },
     "node_modules/restore-cursor": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
@@ -9689,12 +11195,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/rfdc": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
-      "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==",
-      "dev": true
-    },
     "node_modules/rimraf": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -10127,6 +11627,24 @@
       "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
       "dev": true
     },
+    "node_modules/sinon": {
+      "version": "16.0.0",
+      "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.0.0.tgz",
+      "integrity": "sha512-B8AaZZm9CT5pqe4l4uWJztfD/mOTa7dL8Qo0W4+s+t74xECOgSZDDQCBjNgIK3+n4kyxQrSTv2V5ul8K25qkiQ==",
+      "dev": true,
+      "dependencies": {
+        "@sinonjs/commons": "^3.0.0",
+        "@sinonjs/fake-timers": "^10.3.0",
+        "@sinonjs/samsam": "^8.0.0",
+        "diff": "^5.1.0",
+        "nise": "^5.1.4",
+        "supports-color": "^7.2.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/sinon"
+      }
+    },
     "node_modules/slash": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@@ -10154,46 +11672,50 @@
         "url": "https://github.com/chalk/slice-ansi?sponsor=1"
       }
     },
-    "node_modules/socket.io": {
-      "version": "4.7.1",
-      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.1.tgz",
-      "integrity": "sha512-W+utHys2w//dhFjy7iQQu9sGd3eokCjGbl2r59tyLqNiJJBdIebn3GAKEXBr3osqHTObJi2die/25bCx2zsaaw==",
+    "node_modules/smart-buffer": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
       "dev": true,
-      "dependencies": {
-        "accepts": "~1.3.4",
-        "base64id": "~2.0.0",
-        "cors": "~2.8.5",
-        "debug": "~4.3.2",
-        "engine.io": "~6.5.0",
-        "socket.io-adapter": "~2.5.2",
-        "socket.io-parser": "~4.2.4"
-      },
       "engines": {
-        "node": ">=10.0.0"
+        "node": ">= 6.0.0",
+        "npm": ">= 3.0.0"
       }
     },
-    "node_modules/socket.io-adapter": {
-      "version": "2.5.2",
-      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
-      "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
+    "node_modules/socks": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
+      "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
       "dev": true,
       "dependencies": {
-        "ws": "~8.11.0"
+        "ip": "^2.0.0",
+        "smart-buffer": "^4.2.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0",
+        "npm": ">= 3.0.0"
       }
     },
-    "node_modules/socket.io-parser": {
-      "version": "4.2.4",
-      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
-      "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+    "node_modules/socks-proxy-agent": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz",
+      "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==",
       "dev": true,
       "dependencies": {
-        "@socket.io/component-emitter": "~3.1.0",
-        "debug": "~4.3.1"
+        "agent-base": "^7.0.2",
+        "debug": "^4.3.4",
+        "socks": "^2.7.1"
       },
       "engines": {
-        "node": ">=10.0.0"
+        "node": ">= 14"
       }
     },
+    "node_modules/socks/node_modules/ip": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
+      "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
+      "dev": true
+    },
     "node_modules/sortablejs": {
       "version": "1.15.0",
       "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz",
@@ -10322,9 +11844,8 @@
     "node_modules/statuses": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
-      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+      "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": ">= 0.6"
       }
@@ -10335,6 +11856,15 @@
       "integrity": "sha512-2bacd1TXzqOEsqRa+eEWkRdOSznwptrs4gqFcpMq5tOtmJUGPZd10W5Lam6wQ4YQ/+qjQt4e9u35yXCF6mrlfQ==",
       "dev": true
     },
+    "node_modules/stream-read-all": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/stream-read-all/-/stream-read-all-3.0.1.tgz",
+      "integrity": "sha512-EWZT9XOceBPlVJRrYcykW8jyRSZYbkb/0ZK36uLEmoWVO5gxBOnntNTseNzfREsqxqdfEGQrD8SXQ3QWbBmq8A==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/stream-shift": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
@@ -10342,32 +11872,14 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/streamroller": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.2.tgz",
-      "integrity": "sha512-wZswqzbgGGsXYIrBYhOE0yP+nQ6XRk7xDcYwuQAGTYXdyAUmvgVFE0YU1g5pvQT0m7GBaQfYcSnlHbapuK0H0A==",
-      "dev": true,
-      "dependencies": {
-        "date-format": "^4.0.13",
-        "debug": "^4.3.4",
-        "fs-extra": "^8.1.0"
-      },
-      "engines": {
-        "node": ">=8.0"
-      }
-    },
-    "node_modules/streamroller/node_modules/fs-extra": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
-      "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+    "node_modules/streamx": {
+      "version": "2.15.1",
+      "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.1.tgz",
+      "integrity": "sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==",
       "dev": true,
       "dependencies": {
-        "graceful-fs": "^4.2.0",
-        "jsonfile": "^4.0.0",
-        "universalify": "^0.1.0"
-      },
-      "engines": {
-        "node": ">=6 <7 || >=8"
+        "fast-fifo": "^1.1.0",
+        "queue-tick": "^1.0.1"
       }
     },
     "node_modules/string_decoder": {
@@ -10752,6 +12264,45 @@
         "node": ">=10.0.0"
       }
     },
+    "node_modules/table-layout": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-3.0.2.tgz",
+      "integrity": "sha512-rpyNZYRw+/C+dYkcQ3Pr+rLxW4CfHpXjPDnG7lYhdRoUcZTUt+KEsX+94RGp/aVp/MQU35JCITv2T/beY4m+hw==",
+      "dev": true,
+      "dependencies": {
+        "@75lb/deep-merge": "^1.1.1",
+        "array-back": "^6.2.2",
+        "command-line-args": "^5.2.1",
+        "command-line-usage": "^7.0.0",
+        "stream-read-all": "^3.0.1",
+        "typical": "^7.1.1",
+        "wordwrapjs": "^5.1.0"
+      },
+      "bin": {
+        "table-layout": "bin/cli.js"
+      },
+      "engines": {
+        "node": ">=12.17"
+      }
+    },
+    "node_modules/table-layout/node_modules/array-back": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz",
+      "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.17"
+      }
+    },
+    "node_modules/table-layout/node_modules/typical": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/typical/-/typical-7.1.1.tgz",
+      "integrity": "sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.17"
+      }
+    },
     "node_modules/table/node_modules/ajv": {
       "version": "8.11.0",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
@@ -10877,17 +12428,6 @@
         "safe-buffer": "~5.2.0"
       }
     },
-    "node_modules/tar/node_modules/mkdirp": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
-      "bin": {
-        "mkdirp": "bin/cmd.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/terser": {
       "version": "5.17.1",
       "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.1.tgz",
@@ -10969,6 +12509,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+      "dev": true
+    },
     "node_modules/through2": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
@@ -11003,19 +12549,6 @@
         "ms": "^2.1.1"
       }
     },
-    "node_modules/tmp": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
-      "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "rimraf": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=8.17.0"
-      }
-    },
     "node_modules/to-fast-properties": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -11055,6 +12588,18 @@
         "node": ">=0.6"
       }
     },
+    "node_modules/tr46": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
+      "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+      "dev": true,
+      "dependencies": {
+        "punycode": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/trim-newlines": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz",
@@ -11076,6 +12621,21 @@
         "typescript": ">=4.2.0"
       }
     },
+    "node_modules/tslib": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+      "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+      "dev": true
+    },
+    "node_modules/tsscmp": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
+      "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.6.x"
+      }
+    },
     "node_modules/tunnel-agent": {
       "version": "0.6.0",
       "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -11101,6 +12661,15 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/type-detect": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+      "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/type-fest": {
       "version": "0.20.2",
       "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
@@ -11118,7 +12687,6 @@
       "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
       "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "media-typer": "0.3.0",
         "mime-types": "~2.1.24"
@@ -11140,10 +12708,19 @@
         "node": ">=14.17"
       }
     },
+    "node_modules/typical": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz",
+      "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/ua-parser-js": {
-      "version": "0.7.33",
-      "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
-      "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==",
+      "version": "1.0.36",
+      "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.36.tgz",
+      "integrity": "sha512-znuyCIXzl8ciS3+y3fHJI/2OhQIXbXw9MWC/o3qwyR+RGppjZHrM27CGFSKCJXi2Kctiz537iOu2KnXs1lMQhw==",
       "dev": true,
       "funding": [
         {
@@ -11153,12 +12730,26 @@
         {
           "type": "paypal",
           "url": "https://paypal.me/faisalman"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/faisalman"
         }
       ],
       "engines": {
         "node": "*"
       }
     },
+    "node_modules/unbzip2-stream": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
+      "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
+      "dev": true,
+      "dependencies": {
+        "buffer": "^5.2.1",
+        "through": "^2.3.8"
+      }
+    },
     "node_modules/unc-path-regex": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
@@ -11214,9 +12805,8 @@
     "node_modules/unpipe": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
-      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": ">= 0.8"
       }
@@ -11265,22 +12855,32 @@
       "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
       "license": "MIT"
     },
-    "node_modules/utils-merge": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
-      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.4.0"
-      }
-    },
     "node_modules/v8-compile-cache": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
       "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
       "dev": true
     },
+    "node_modules/v8-to-istanbul": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz",
+      "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.12",
+        "@types/istanbul-lib-coverage": "^2.0.1",
+        "convert-source-map": "^1.6.0"
+      },
+      "engines": {
+        "node": ">=10.12.0"
+      }
+    },
+    "node_modules/v8-to-istanbul/node_modules/convert-source-map": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+      "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+      "dev": true
+    },
     "node_modules/v8flags": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
@@ -11316,16 +12916,6 @@
         "node": ">= 0.8"
       }
     },
-    "node_modules/void-elements": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
-      "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/w3c-keyname": {
       "version": "2.2.6",
       "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.6.tgz",
@@ -11344,6 +12934,15 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/webidl-conversions": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+      "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/webpack": {
       "version": "5.80.0",
       "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.80.0.tgz",
@@ -11546,6 +13145,19 @@
         "node": ">=0.8.0"
       }
     },
+    "node_modules/whatwg-url": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
+      "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
+      "dev": true,
+      "dependencies": {
+        "tr46": "^3.0.0",
+        "webidl-conversions": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/which": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -11567,10 +13179,19 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/wordwrapjs": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.0.tgz",
+      "integrity": "sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.17"
+      }
+    },
     "node_modules/wrap-ansi": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+      "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
       "dev": true,
       "dependencies": {
         "ansi-styles": "^4.0.0",
@@ -11578,10 +13199,7 @@
         "strip-ansi": "^6.0.0"
       },
       "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+        "node": ">=8"
       }
     },
     "node_modules/wrappy": {
@@ -11604,12 +13222,12 @@
       }
     },
     "node_modules/ws": {
-      "version": "8.11.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
-      "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
+      "version": "7.5.9",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
+      "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
       "dev": true,
       "engines": {
-        "node": ">=10.0.0"
+        "node": ">=8.3.0"
       },
       "peerDependencies": {
         "bufferutil": "^4.0.1",
@@ -11624,15 +13242,6 @@
         }
       }
     },
-    "node_modules/xmlbuilder": {
-      "version": "12.0.0",
-      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-12.0.0.tgz",
-      "integrity": "sha512-lMo8DJ8u6JRWp0/Y4XLa/atVDr75H9litKlb2E5j3V3MesoL50EBgZDWoLT3F/LztVnG67GjPXLZpqcky/UMnQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=6.0"
-      }
-    },
     "node_modules/xtend": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@@ -11668,21 +13277,21 @@
       }
     },
     "node_modules/yargs": {
-      "version": "16.2.0",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
-      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "version": "17.7.1",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
+      "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
       "dev": true,
       "dependencies": {
-        "cliui": "^7.0.2",
+        "cliui": "^8.0.1",
         "escalade": "^3.1.1",
         "get-caller-file": "^2.0.5",
         "require-directory": "^2.1.1",
-        "string-width": "^4.2.0",
+        "string-width": "^4.2.3",
         "y18n": "^5.0.5",
-        "yargs-parser": "^20.2.2"
+        "yargs-parser": "^21.1.1"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">=12"
       }
     },
     "node_modules/yargs-parser": {
@@ -11694,6 +13303,34 @@
         "node": ">=10"
       }
     },
+    "node_modules/yargs/node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yauzl": {
+      "version": "2.10.0",
+      "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+      "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+      "dev": true,
+      "dependencies": {
+        "buffer-crc32": "~0.2.3",
+        "fd-slicer": "~1.1.0"
+      }
+    },
+    "node_modules/ylru": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.3.2.tgz",
+      "integrity": "sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
     "node_modules/yocto-queue": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/Build/package.json b/Build/package.json
index 0ed27c9931344e6a6fac9494260a0c11dc2f8979..0cfa3e77b97f04df3f20570f2e2fc6e704aaa3f6 100644
--- a/Build/package.json
+++ b/Build/package.json
@@ -12,6 +12,7 @@
     "npm": ">=9.0.0 <10.0.0"
   },
   "devDependencies": {
+    "@open-wc/testing": "^3.2.0",
     "@rollup/plugin-commonjs": "^25.0.0",
     "@rollup/plugin-node-resolve": "^14.1.0",
     "@rollup/plugin-replace": "^2.4.2",
@@ -21,7 +22,6 @@
     "@types/d3-dispatch": "^3.0.2",
     "@types/d3-drag": "^3.0.2",
     "@types/d3-selection": "^3.0.4",
-    "@types/jasmine": "^4.3.0",
     "@types/jquery": "2.0.47",
     "@types/luxon": "^3.1.0",
     "@types/nprogress": "^0.2.0",
@@ -29,6 +29,10 @@
     "@typescript-eslint/eslint-plugin": "^6.6.0",
     "@typescript-eslint/parser": "^6.6.0",
     "@typescript-eslint/typescript-estree": "^6.6.0",
+    "@web/dev-server-esbuild": "^0.4.1",
+    "@web/dev-server-import-maps": "^0.1.1",
+    "@web/test-runner": "^0.17.1",
+    "@web/test-runner-playwright": "^0.10.1",
     "autoprefixer": "^10.4.14",
     "es-module-lexer": "^1.2.1",
     "eslint": "^8.38.0",
@@ -50,13 +54,6 @@
     "grunt-sass": "^3.1.0",
     "grunt-stylelint": "^0.18.0",
     "grunt-terser": "^2.0.0",
-    "jasmine-core": "^4.6.0",
-    "karma": "^6.4.1",
-    "karma-chrome-launcher": "^3.1.1",
-    "karma-coverage": "^2.2.0",
-    "karma-jasmine": "^5.1.0",
-    "karma-junit-reporter": "^2.0.1",
-    "karma-rollup-preprocessor": "^7.0.8",
     "lintspaces-cli": "^0.8.0",
     "mime-db": "^1.46.0",
     "pofile": "^1.1.4",
@@ -69,6 +66,7 @@
     "rollup-plugin-terser": "^7.0.2",
     "sass": "^1.62.0",
     "sharp": "^0.32.0",
+    "sinon": "^16.0.0",
     "stylelint": "^14.11.0",
     "stylelint-order": "^5.0.0",
     "stylelint-scss": "^4.6.0",
@@ -81,7 +79,10 @@
     "build-js": "./node_modules/.bin/grunt scripts",
     "build-flags": "./node_modules/.bin/grunt flags-build",
     "update": "./node_modules/.bin/grunt update",
-    "lint": "./node_modules/.bin/grunt lint"
+    "lint": "./node_modules/.bin/grunt lint",
+    "test": "wtr",
+    "watch:build": "grunt watch",
+    "watch:test": "wtr --watch"
   },
   "dependencies": {
     "@ckeditor/ckeditor5-alignment": "^39.0.2",
diff --git a/Build/tsconfig.json b/Build/tsconfig.json
index 52f541d92344ac29834ea3f56947c823b28deb41..a1360a1d124bdcc23546515650b762c4de862dda 100644
--- a/Build/tsconfig.json
+++ b/Build/tsconfig.json
@@ -97,8 +97,7 @@
         "outDir": "./JavaScript/",
         "types": [
             "TYPO3",
-            "ckeditor5-bundle",
-            "jasmine"
+            "ckeditor5-bundle"
         ],
         "typeRoots": [
             "node_modules/@types",
diff --git a/Build/web-test-runner.config.mjs b/Build/web-test-runner.config.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..e5429b890b4de643e029d9845d9beac7495afebb
--- /dev/null
+++ b/Build/web-test-runner.config.mjs
@@ -0,0 +1,216 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+import { chromeLauncher } from '@web/test-runner';
+import { playwrightLauncher } from '@web/test-runner-playwright';
+import { importMapsPlugin } from '@web/dev-server-import-maps';
+import { esbuildPlugin } from '@web/dev-server-esbuild';
+import { readdirSync, statSync, existsSync } from 'fs';
+
+const mode = process.env.MODE || 'prod';
+if (!['dev', 'prod'].includes(mode)) {
+  throw new Error(`MODE must be "dev" or "prod", was "${mode}"`);
+}
+
+const chromeSandboxRaw = process.env.CHROME_SANDBOX || 'true';
+if (!['true', 'false', '1', '0'].includes(chromeSandboxRaw)) {
+  throw new Error(`CHROME_SANDBOX must be "true" ("1") or "false" ("0"), was "${chromeSandboxRaw}"`);
+}
+const chromeSandbox = ['true', '1'].includes(chromeSandboxRaw);
+
+const launchOptions = {
+  headless: mode === 'prod',
+  devtools: mode === 'dev'
+};
+
+const chromeLaunchOptions = { ...launchOptions, args: chromeSandbox ? [] : ['--no-sandbox'] };
+
+const browsers = {
+  // Locally installed chrome
+  chrome: chromeLauncher({ launchOptions: chromeLaunchOptions }),
+
+  // Browser testing via playwright browsers (requires `npx playwright install`)
+  chromium: playwrightLauncher({ product: 'chromium', launchOptions: chromeLaunchOptions }),
+  firefox: playwrightLauncher({ product: 'firefox', launchOptions }),
+  webkit: playwrightLauncher({ product: 'webkit', launchOptions }),
+};
+
+const defaultBrowsers = [
+  browsers.chrome
+];
+
+// Prepend BROWSERS=x,y to `npm run test` to run a defined set of browsers
+// e.g. `BROWSERS=chromium,firefox npm run test`
+const noBrowser = (b) => {
+  throw new Error(`No browser configured named '${b}'; using defaults`);
+};
+let commandLineBrowsers;
+try {
+  commandLineBrowsers = process.env.BROWSERS?.split(',').map(
+    (b) => browsers[b] ?? noBrowser(b)
+  );
+} catch (e) {
+  console.warn(e);
+}
+
+const packages = readdirSync('Sources/TypeScript')
+  .filter(dir =>
+    statSync(`Sources/TypeScript/${dir}`).isDirectory() &&
+    existsSync(`Sources/TypeScript/${dir}/tests`)
+  );
+
+// https://modern-web.dev/docs/test-runner/cli-and-configuration/
+export default {
+  rootDir: '../',
+  groups: packages.map(pkg => ({
+    name: pkg,
+    files: `Sources/TypeScript/${pkg}/tests/**/*.ts`,
+  })),
+  nodeResolve: false,
+  preserveSymlinks: true,
+  browsers: commandLineBrowsers ?? defaultBrowsers,
+  plugins: [
+    esbuildPlugin({ ts: true }),
+    importMapsPlugin({
+      inject: {
+        importMap: {
+          imports: {
+            '@open-wc/testing': './Build/node_modules/@open-wc/testing/index.js',
+            '@open-wc/testing-helpers': './Build/node_modules/@open-wc/testing-helpers/index.js',
+            '@open-wc/semantic-dom-diff': './Build/node_modules/@open-wc/semantic-dom-diff/index.js',
+            '@open-wc/scoped-elements': '/Build/node_modules/@open-wc/scoped-elements/index.js',
+            '@open-wc/dedupe-mixin': '/Build/node_modules/@open-wc/dedupe-mixin/index.js',
+            '@web/test-runner-commands':  './Build/node_modules/@web/test-runner-commands/browser/commands.mjs',
+            '@web/test-runner-commands/plugins':  './Build/node_modules/@web/test-runner-commands/plugins.mjs',
+            '@esm-bundle/chai': './Build/node_modules/@esm-bundle/chai/esm/chai.js',
+            'chai-a11y-axe': './Build/node_modules/chai-a11y-axe/index.js',
+            'axe-core/': '/Build/node_modules/axe-core/',
+            'sinon': '/Build/node_modules/sinon/pkg/sinon-esm.js',
+
+            '@typo3/core/': './typo3/sysext/core/Resources/Public/JavaScript/',
+            'autosize': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/autosize.js',
+            'bootstrap': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/bootstrap.js',
+            'cropperjs': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/cropperjs.js',
+            'd3-dispatch': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/d3-dispatch.js',
+            'd3-drag': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/d3-drag.js',
+            'd3-selection': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/d3-selection.js',
+            'flatpickr/': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/flatpickr/',
+            'interactjs': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/interact.js',
+            'jquery': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/jquery.js',
+            'jquery/': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/jquery/',
+            'jquery/autocomplete.js': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/jquery.autocomplete.js',
+            'jquery-ui/': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/jquery-ui/',
+            '@lit/reactive-element': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/@lit/reactive-element/reactive-element.js',
+            '@lit/reactive-element/': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/@lit/reactive-element/',
+            'lit': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/lit/index.js',
+            'lit/': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/lit/',
+            'lit-element': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/lit-element/index.js',
+            'lit-element/': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/lit-element/',
+            'lit-html': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/lit-html/lit-html.js',
+            'lit-html/': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/lit-html/',
+            'luxon': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/luxon.js',
+            'nprogress': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/nprogress.js',
+            'sortablejs': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/sortablejs.js',
+            'tablesort.dotsep.js': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/tablesort.dotsep.js',
+            'tablesort.number.js': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/tablesort.number.js',
+            'tablesort': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/tablesort.js',
+            'taboverride': './typo3/sysext/core/Resources/Public/JavaScript/Contrib/taboverride.js',
+
+            '@typo3/scheduler/': './typo3/sysext/scheduler/Resources/Public/JavaScript/',
+            '@typo3/filelist/': './typo3/sysext/filelist/Resources/Public/JavaScript/',
+            '@typo3/impexp/': './typo3/sysext/impexp/Resources/Public/JavaScript/',
+            '@typo3/form/backend/': './typo3/sysext/form/Resources/Public/JavaScript/backend/',
+            '@typo3/install/': './typo3/sysext/install/Resources/Public/JavaScript/',
+            '@typo3/info/': './typo3/sysext/info/Resources/Public/JavaScript/',
+            '@typo3/linkvalidator/': './typo3/sysext/linkvalidator/Resources/Public/JavaScript/',
+            '@typo3/redirects/': './typo3/sysext/redirects/Resources/Public/JavaScript/',
+            '@typo3/recycler/': './typo3/sysext/recycler/Resources/Public/JavaScript/',
+            '@typo3/setup/': './typo3/sysext/setup/Resources/Public/JavaScript/',
+            '@typo3/rte-ckeditor/': './typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/',
+            '@typo3/ckeditor5-bundle.js': './typo3/sysext/rte_ckeditor/Resources/Public/Contrib/ckeditor5-bundle.js',
+            '@typo3/ckeditor5-inspector.js': './typo3/sysext/rte_ckeditor/Resources/Public/Contrib/ckeditor5-inspector.js',
+            '@typo3/ckeditor5/translations/': './typo3/sysext/rte_ckeditor/Resources/Public/Contrib/translations/',
+            '@typo3/adminpanel/': './typo3/sysext/adminpanel/Resources/Public/JavaScript/',
+            '@typo3/backend/': './typo3/sysext/backend/Resources/Public/JavaScript/',
+            '@typo3/backend/contrib/mark.js': './typo3/sysext/backend/Resources/Public/JavaScript/Contrib/mark.js',
+            'alwan': './typo3/sysext/backend/Resources/Public/JavaScript/Contrib/alwan.js',
+            'select-pure': './typo3/sysext/backend/Resources/Public/JavaScript/Contrib/select-pure.js',
+
+            '@typo3/belog/': './typo3/sysext/belog/Resources/Public/JavaScript/',
+            '@typo3/beuser/': './typo3/sysext/beuser/Resources/Public/JavaScript/',
+
+            '@typo3/dashboard/': './typo3/sysext/dashboard/Resources/Public/JavaScript/',
+            '@typo3/dashboard/contrib/chartjs.js': './typo3/sysext/dashboard/Resources/Public/JavaScript/Contrib/chartjs.js',
+            'muuri': './typo3/sysext/dashboard/Resources/Public/JavaScript/Contrib/muuri.js',
+
+            '@typo3/extensionmanager/': './typo3/sysext/extensionmanager/Resources/Public/JavaScript/',
+            '@typo3/lowlevel/': './typo3/sysext/lowlevel/Resources/Public/JavaScript/',
+            '@typo3/opendocs/': './typo3/sysext/opendocs/Resources/Public/JavaScript/',
+
+            '@typo3/t3editor/': './typo3/sysext/t3editor/Resources/Public/JavaScript/',
+            'crelt': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/crelt.js',
+            'style-mod': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/style-mod.js',
+            'w3c-keyname': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/w3c-keyname.js',
+            '@lezer/common': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@lezer/common.js',
+            '@lezer/css': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@lezer/css.js',
+            '@lezer/html': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@lezer/html.js',
+            '@lezer/javascript': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@lezer/javascript.js',
+            '@lezer/json': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@lezer/json.js',
+            '@lezer/php': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@lezer/php.js',
+            '@lezer/xml': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@lezer/xml.js',
+            '@lezer/lr': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@lezer/lr.js',
+            '@lezer/highlight': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@lezer/highlight.js',
+            '@codemirror/autocomplete': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/autocomplete.js',
+            '@codemirror/closebrackets': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/closebrackets.js',
+            '@codemirror/commands': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/commands.js',
+            '@codemirror/comment': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/comment.js',
+            '@codemirror/fold': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/fold.js',
+            '@codemirror/gutter': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/gutter.js',
+            '@codemirror/highlight': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/highlight.js',
+            '@codemirror/history': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/history.js',
+            '@codemirror/language': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/language.js',
+            '@codemirror/lang-css': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/lang-css.js',
+            '@codemirror/lang-html': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/lang-html.js',
+            '@codemirror/lang-javascript': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/lang-javascript.js',
+            '@codemirror/lang-json': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/lang-json.js',
+            '@codemirror/lang-php': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/lang-php.js',
+            '@codemirror/lang-sql': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/lang-sql.js',
+            '@codemirror/lang-xml': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/lang-xml.js',
+            '@codemirror/lint': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/lint.js',
+            '@codemirror/matchbrackets': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/matchbrackets.js',
+            '@codemirror/panel': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/panel.js',
+            '@codemirror/rangeset': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/rangeset.js',
+            '@codemirror/rectangular-selection': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/rectangular-selection.js',
+            '@codemirror/search': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/search.js',
+            '@codemirror/state': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/state.js',
+            '@codemirror/text': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/text.js',
+            '@codemirror/tooltip': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/tooltip.js',
+            '@codemirror/theme-one-dark': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/theme-one-dark.js',
+            '@codemirror/view': './typo3/sysext/t3editor/Resources/Public/JavaScript/Contrib/@codemirror/view.js',
+
+            '@typo3/tstemplate/': './typo3/sysext/tstemplate/Resources/Public/JavaScript/',
+            '@typo3/viewpage/': './typo3/sysext/viewpage/Resources/Public/JavaScript/',
+            '@typo3/workspaces/': './typo3/sysext/workspaces/Resources/Public/JavaScript/',
+          }
+        }
+      }
+    })
+  ],
+  testFramework: {
+    // https://mochajs.org/api/mocha
+    config: {
+      ui: 'bdd',
+      timeout: '60000'
+    },
+  },
+};
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/document-save-actions.js b/typo3/sysext/backend/Resources/Public/JavaScript/document-save-actions.js
index 90f0db1d63313fe725ff7ff7a0fc2c1063b812aa..343a2af79d482b778de5472599a75aef2e5de75d 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/document-save-actions.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/document-save-actions.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import DocumentService from"@typo3/core/document-service.js";import Icons from"@typo3/backend/icons.js";import RegularEvent from"@typo3/core/event/regular-event.js";class DocumentSaveActions{constructor(){this.preventDoubleClick=!1,this.preSubmitCallbacks=[],DocumentService.ready().then((()=>{this.initializeSaveHandling()}))}static getInstance(){return null===DocumentSaveActions.instance&&(DocumentSaveActions.instance=new DocumentSaveActions),DocumentSaveActions.instance}static registerEvents(){DocumentSaveActions.getInstance()}addPreSubmitCallback(e){if("function"!=typeof e)throw"callback must be a function.";this.preSubmitCallbacks.push(e)}initializeSaveHandling(){const e=["button[form]",'button[name^="_save"]','a[data-name^="_save"]','button[name="CMD"][value^="save"]','a[data-name="CMD"][data-value^="save"]'].join(",");new RegularEvent("click",((e,t)=>{if(this.preventDoubleClick)return;const n=this.getAttachedForm(t);if(null!==n){for(const t of this.preSubmitCallbacks){if(!t(e))return void e.preventDefault()}this.preventDoubleClick=!0,this.attachSaveFieldToForm(n,t),n.addEventListener("submit",(()=>{const e=t.closest(".t3js-splitbutton");let n;null!==e?(n=e.firstElementChild,e.querySelectorAll("button").forEach((e=>{e.disabled=!0}))):(n=t,n instanceof HTMLAnchorElement?n.classList.add("disabled"):n.disabled=!0),Icons.getIcon("spinner-circle",Icons.sizes.small).then((e=>{n.replaceChild(document.createRange().createContextualFragment(e),t.querySelector(".t3js-icon"))})).catch((()=>{}))}),{once:!0})}})).delegateTo(document.querySelector(".t3js-module-docheader"),e)}getAttachedForm(e){let t;return t=e instanceof HTMLAnchorElement?document.querySelector("#"+e.dataset.form):e.form,t||(t=e.closest("form")),t}attachSaveFieldToForm(e,t){const n=e.name+"_save_field";let a=document.getElementById(n);null===a&&(a=document.createElement("input"),a.id=n,a.type="hidden",e.append(a)),a.name=t instanceof HTMLAnchorElement?t.dataset.name:t.name,a.value=t instanceof HTMLAnchorElement?t.dataset.value:t.value}}DocumentSaveActions.instance=null;export default DocumentSaveActions;
\ No newline at end of file
+import DocumentService from"@typo3/core/document-service.js";import Icons from"@typo3/backend/icons.js";import RegularEvent from"@typo3/core/event/regular-event.js";class DocumentSaveActions{constructor(){this.preventDoubleClick=!1,this.preSubmitCallbacks=[],DocumentService.ready().then((()=>{this.initializeSaveHandling()}))}static getInstance(){return null===DocumentSaveActions.instance&&(DocumentSaveActions.instance=new DocumentSaveActions),DocumentSaveActions.instance}static registerEvents(){DocumentSaveActions.getInstance()}addPreSubmitCallback(e){if("function"!=typeof e)throw"callback must be a function.";this.preSubmitCallbacks.push(e)}initializeSaveHandling(){const e=document.querySelector(".t3js-module-docheader");if(null===e)return;const t=["button[form]",'button[name^="_save"]','a[data-name^="_save"]','button[name="CMD"][value^="save"]','a[data-name="CMD"][data-value^="save"]'].join(",");new RegularEvent("click",((e,t)=>{if(this.preventDoubleClick)return;const n=this.getAttachedForm(t);if(null!==n){for(const t of this.preSubmitCallbacks){if(!t(e))return void e.preventDefault()}this.preventDoubleClick=!0,this.attachSaveFieldToForm(n,t),n.addEventListener("submit",(()=>{const e=t.closest(".t3js-splitbutton");let n;null!==e?(n=e.firstElementChild,e.querySelectorAll("button").forEach((e=>{e.disabled=!0}))):(n=t,n instanceof HTMLAnchorElement?n.classList.add("disabled"):n.disabled=!0),Icons.getIcon("spinner-circle",Icons.sizes.small).then((e=>{n.replaceChild(document.createRange().createContextualFragment(e),t.querySelector(".t3js-icon"))})).catch((()=>{}))}),{once:!0})}})).delegateTo(e,t)}getAttachedForm(e){let t;return t=e instanceof HTMLAnchorElement?document.querySelector("#"+e.dataset.form):e.form,t||(t=e.closest("form")),t}attachSaveFieldToForm(e,t){const n=e.name+"_save_field";let a=document.getElementById(n);null===a&&(a=document.createElement("input"),a.id=n,a.type="hidden",e.append(a)),a.name=t instanceof HTMLAnchorElement?t.dataset.name:t.name,a.value=t instanceof HTMLAnchorElement?t.dataset.value:t.value}}DocumentSaveActions.instance=null;export default DocumentSaveActions;
\ No newline at end of file
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/icons.js b/typo3/sysext/backend/Resources/Public/JavaScript/icons.js
index 2b0e40753d5250777b70d2f576191e9dfc2e429f..73a4d41fc61018bff2ba038ef68d0f0f812c71d8 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/icons.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/icons.js
@@ -103,4 +103,4 @@ import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import ClientStorage f
         line-height: calc(var(--icon-size) / 1.6);
         font-size: calc((var(--icon-size) / 1.6) * var(--icon-unify-modifier))
       }
-    `}}class Icons{constructor(){this.sizes=Sizes,this.states=States,this.markupIdentifiers=MarkupIdentifiers,this.promiseCache={}}getIcon(e,i,t,s,n){const o=[e,i=i||Sizes.default,t,s=s||States.default,n=n||MarkupIdentifiers.default],r=o.join("_");return this.getIconRegistryCache().then((e=>(ClientStorage.isset("icon_registry_cache_identifier")&&ClientStorage.get("icon_registry_cache_identifier")===e||(ClientStorage.unsetByPrefix("icon_"),ClientStorage.set("icon_registry_cache_identifier",e)),this.fetchFromLocal(r).then(null,(()=>this.fetchFromRemote(o,r))))))}getIconRegistryCache(){const e="icon_registry_cache_identifier";return this.isPromiseCached(e)||this.putInPromiseCache(e,new AjaxRequest(TYPO3.settings.ajaxUrls.icons_cache).get().then((async e=>await e.resolve()))),this.getFromPromiseCache(e)}fetchFromRemote(e,i){if(!this.isPromiseCached(i)){const t={icon:JSON.stringify(e)};this.putInPromiseCache(i,new AjaxRequest(TYPO3.settings.ajaxUrls.icons).withQueryArguments(t).get().then((async e=>{const t=await e.resolve();return!e.response.redirected&&t.startsWith("<span")&&t.includes("t3js-icon")&&t.includes('<span class="icon-markup">')&&ClientStorage.set("icon_"+i,t),t})))}return this.getFromPromiseCache(i)}fetchFromLocal(e){return ClientStorage.isset("icon_"+e)?Promise.resolve(ClientStorage.get("icon_"+e)):Promise.reject()}isPromiseCached(e){return void 0!==this.promiseCache[e]}getFromPromiseCache(e){return this.promiseCache[e]}putInPromiseCache(e,i){this.promiseCache[e]=i}}let iconsObject;iconsObject||(iconsObject=new Icons,TYPO3.Icons=iconsObject);export default iconsObject;
\ No newline at end of file
+    `}}class Icons{constructor(){this.sizes=Sizes,this.states=States,this.markupIdentifiers=MarkupIdentifiers,this.promiseCache={}}getIcon(e,i,t,s,n){const o=[e,i=i||Sizes.default,t,s=s||States.default,n=n||MarkupIdentifiers.default],r=o.join("_");return this.getIconRegistryCache().then((e=>(ClientStorage.isset("icon_registry_cache_identifier")&&ClientStorage.get("icon_registry_cache_identifier")===e||(ClientStorage.unsetByPrefix("icon_"),ClientStorage.set("icon_registry_cache_identifier",e)),this.fetchFromLocal(r).then(null,(()=>this.fetchFromRemote(o,r))))))}getIconRegistryCache(){const e="icon_registry_cache_identifier";return this.isPromiseCached(e)||this.putInPromiseCache(e,new AjaxRequest(TYPO3.settings.ajaxUrls.icons_cache).get().then((async e=>await e.resolve()))),this.getFromPromiseCache(e)}fetchFromRemote(e,i){if(!this.isPromiseCached(i)){const t={icon:JSON.stringify(e)};this.putInPromiseCache(i,new AjaxRequest(TYPO3.settings.ajaxUrls.icons).withQueryArguments(t).get().then((async e=>{const t=await e.resolve();return!e.response.redirected&&t.startsWith("<span")&&t.includes("t3js-icon")&&t.includes('<span class="icon-markup">')&&ClientStorage.set("icon_"+i,t),t})))}return this.getFromPromiseCache(i)}fetchFromLocal(e){return ClientStorage.isset("icon_"+e)?Promise.resolve(ClientStorage.get("icon_"+e)):Promise.reject()}isPromiseCached(e){return void 0!==this.promiseCache[e]}getFromPromiseCache(e){return this.promiseCache[e]}putInPromiseCache(e,i){this.promiseCache[e]=i}}let iconsObject;iconsObject||(iconsObject=new Icons,"undefined"!=typeof TYPO3&&(TYPO3.Icons=iconsObject));export default iconsObject;
\ No newline at end of file
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/modal.js b/typo3/sysext/backend/Resources/Public/JavaScript/modal.js
index 9efe2495eaf982b25148a56b1272be1b150b752c..0175e47128379c32ae91e893e2d9a9869d37382c 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/modal.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/modal.js
@@ -50,4 +50,4 @@ var Identifiers,__decorate=function(t,e,a,o){var s,n=arguments.length,i=n<3?e:nu
           ${t.icon?html`<typo3-backend-icon identifier="${t.icon}" size="small"></typo3-backend-icon>`:nothing}
           ${t.text}
       </button>
-    `}trigger(t){this.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0}))}};__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"modalTitle",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"content",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"type",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"severity",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"variant",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"size",void 0),__decorate([property({type:Number,reflect:!0})],ModalElement.prototype,"zindex",void 0),__decorate([property({type:Boolean})],ModalElement.prototype,"staticBackdrop",void 0),__decorate([property({type:Boolean})],ModalElement.prototype,"hideCloseButton",void 0),__decorate([property({type:Array})],ModalElement.prototype,"additionalCssClasses",void 0),__decorate([property({type:Array,attribute:!1})],ModalElement.prototype,"buttons",void 0),__decorate([state()],ModalElement.prototype,"templateResultContent",void 0),__decorate([state()],ModalElement.prototype,"activeButton",void 0),ModalElement=__decorate([customElement("typo3-backend-modal")],ModalElement);export{ModalElement};class Modal{constructor(){this.sizes=Sizes,this.styles=Styles,this.types=Types,this.currentModal=null,this.instances=[],this.defaultConfiguration={type:Types.default,title:"Information",content:"No content provided, please check your <code>Modal</code> configuration.",severity:SeverityEnum.notice,buttons:[],style:Styles.default,size:Sizes.default,additionalCssClasses:[],callback:null,ajaxCallback:null,staticBackdrop:!1,hideCloseButton:!1},this.initializeMarkupTrigger(document)}static createModalResponseEventFromElement(t,e){return t.dataset.eventName?new CustomEvent(t.dataset.eventName,{bubbles:!0,detail:{result:e,payload:t.dataset.eventPayload||null}}):null}dismiss(){this.currentModal&&this.currentModal.hideModal()}confirm(t,e,a=SeverityEnum.warning,o=[],s){0===o.length&&o.push({text:TYPO3?.lang?.["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3?.lang?.["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(a),name:"ok"});const n=this.advanced({title:t,content:e,severity:a,buttons:o,additionalCssClasses:s});return n.addEventListener("button.clicked",(t=>{const e=t.target;"cancel"===e.getAttribute("name")?e.dispatchEvent(new CustomEvent("confirm.button.cancel",{bubbles:!0})):"ok"===e.getAttribute("name")&&e.dispatchEvent(new CustomEvent("confirm.button.ok",{bubbles:!0}))})),n}loadUrl(t,e=SeverityEnum.info,a,o,s){return this.advanced({type:Types.ajax,title:t,severity:e,buttons:a,ajaxCallback:s,content:o})}show(t,e,a=SeverityEnum.info,o,s){return this.advanced({type:Types.default,title:t,content:e,severity:a,buttons:o,additionalCssClasses:s})}advanced(t){return t.type="string"==typeof t.type&&t.type in Types?t.type:this.defaultConfiguration.type,t.title="string"==typeof t.title?t.title:this.defaultConfiguration.title,t.content="string"==typeof t.content||"object"==typeof t.content?t.content:this.defaultConfiguration.content,t.severity=void 0!==t.severity?t.severity:this.defaultConfiguration.severity,t.buttons=t.buttons||this.defaultConfiguration.buttons,t.size="string"==typeof t.size&&t.size in Sizes?t.size:this.defaultConfiguration.size,t.style="string"==typeof t.style&&t.style in Styles?t.style:this.defaultConfiguration.style,t.additionalCssClasses=t.additionalCssClasses||this.defaultConfiguration.additionalCssClasses,t.callback="function"==typeof t.callback?t.callback:this.defaultConfiguration.callback,t.ajaxCallback="function"==typeof t.ajaxCallback?t.ajaxCallback:this.defaultConfiguration.ajaxCallback,t.staticBackdrop=t.staticBackdrop||this.defaultConfiguration.staticBackdrop,t.hideCloseButton=t.hideCloseButton||this.defaultConfiguration.hideCloseButton,this.generate(t)}setButtons(t){return this.currentModal.buttons=t,this.currentModal}initializeMarkupTrigger(t){new RegularEvent("click",((t,e)=>{t.preventDefault();const a=e.dataset.bsContent||e.dataset.content||TYPO3?.lang?.["message.confirmation"]||"Are you sure?";let o=SeverityEnum.info;if(e.dataset.severity in SeverityEnum){const t=e.dataset.severity;o=SeverityEnum[t]}let s=Sizes.default;if(e.dataset.size in Sizes){const t=e.dataset.size;s=Sizes[t]}let n=e.dataset.url||null;if(null!==n){const t=n.includes("?")?"&":"?";n=n+t+new URLSearchParams(e.dataset).toString()}this.advanced({type:null!==n?Types.ajax:Types.default,title:e.dataset.title||"Alert",content:null!==n?n:a,size:s,severity:o,staticBackdrop:void 0!==e.dataset.staticBackdrop,buttons:[{text:e.dataset.buttonCloseText||TYPO3?.lang?.["button.close"]||"Close",active:!0,btnClass:"btn-default",trigger:(t,a)=>{a.hideModal();const o=Modal.createModalResponseEventFromElement(e,!1);null!==o&&e.dispatchEvent(o)}},{text:e.dataset.buttonOkText||TYPO3?.lang?.["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(o),trigger:(t,a)=>{a.hideModal();const o=Modal.createModalResponseEventFromElement(e,!0);null!==o&&e.dispatchEvent(o);const s=e.dataset.uri||e.dataset.href||e.getAttribute("href");s&&"#"!==s&&(e.ownerDocument.location.href=s),"submit"===e.getAttribute("type")&&(e.closest("form")?.submit(),"BUTTON"===e.tagName&&e.hasAttribute("form")&&e.ownerDocument.querySelector("form#"+e.getAttribute("form"))?.submit()),e.dataset.targetForm&&e.ownerDocument.querySelector("form#"+e.dataset.targetForm)?.submit()}}]})})).delegateTo(t,".t3js-modal-trigger")}generate(t){const e=document.createElement("typo3-backend-modal");return e.type=t.type,"string"==typeof t.content?e.content=t.content:t.type===Types.default&&(e.type=Types.template,e.templateResultContent=t.content),e.severity=t.severity,e.variant=t.style,e.size=t.size,e.modalTitle=t.title,e.additionalCssClasses=t.additionalCssClasses,e.buttons=t.buttons,e.staticBackdrop=t.staticBackdrop,e.hideCloseButton=t.hideCloseButton,t.callback&&(e.callback=t.callback),t.ajaxCallback&&(e.ajaxCallback=t.ajaxCallback),e.addEventListener("typo3-modal-shown",(()=>{const t=e.nextElementSibling,a=1e3+10*this.instances.length;e.zindex=a;const o=a-5;t.style.zIndex=o.toString(),e.querySelector(`${Identifiers.footer} .t3js-active`)?.focus()})),e.addEventListener("typo3-modal-hide",(()=>{if(this.instances.length>0){const t=this.instances.length-1;this.instances.splice(t,1),this.currentModal=this.instances[t-1]}})),e.addEventListener("typo3-modal-hidden",(()=>{e.remove(),this.instances.length>0&&document.body.classList.add("modal-open")})),e.addEventListener("typo3-modal-show",(()=>{this.currentModal=e,this.instances.push(e)})),document.body.appendChild(e),e}}let modalObject=null;try{parent&&parent.window.TYPO3&&parent.window.TYPO3.Modal?(parent.window.TYPO3.Modal.initializeMarkupTrigger(document),modalObject=parent.window.TYPO3.Modal):top&&top.TYPO3.Modal&&(top.TYPO3.Modal.initializeMarkupTrigger(document),modalObject=top.TYPO3.Modal)}catch{}modalObject||(modalObject=new Modal,TYPO3.Modal=modalObject);export default modalObject;
\ No newline at end of file
+    `}trigger(t){this.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0}))}};__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"modalTitle",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"content",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"type",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"severity",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"variant",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"size",void 0),__decorate([property({type:Number,reflect:!0})],ModalElement.prototype,"zindex",void 0),__decorate([property({type:Boolean})],ModalElement.prototype,"staticBackdrop",void 0),__decorate([property({type:Boolean})],ModalElement.prototype,"hideCloseButton",void 0),__decorate([property({type:Array})],ModalElement.prototype,"additionalCssClasses",void 0),__decorate([property({type:Array,attribute:!1})],ModalElement.prototype,"buttons",void 0),__decorate([state()],ModalElement.prototype,"templateResultContent",void 0),__decorate([state()],ModalElement.prototype,"activeButton",void 0),ModalElement=__decorate([customElement("typo3-backend-modal")],ModalElement);export{ModalElement};class Modal{constructor(){this.sizes=Sizes,this.styles=Styles,this.types=Types,this.currentModal=null,this.instances=[],this.defaultConfiguration={type:Types.default,title:"Information",content:"No content provided, please check your <code>Modal</code> configuration.",severity:SeverityEnum.notice,buttons:[],style:Styles.default,size:Sizes.default,additionalCssClasses:[],callback:null,ajaxCallback:null,staticBackdrop:!1,hideCloseButton:!1},this.initializeMarkupTrigger(document)}static createModalResponseEventFromElement(t,e){return t.dataset.eventName?new CustomEvent(t.dataset.eventName,{bubbles:!0,detail:{result:e,payload:t.dataset.eventPayload||null}}):null}dismiss(){this.currentModal&&this.currentModal.hideModal()}confirm(t,e,a=SeverityEnum.warning,o=[],s){0===o.length&&o.push({text:TYPO3?.lang?.["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3?.lang?.["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(a),name:"ok"});const n=this.advanced({title:t,content:e,severity:a,buttons:o,additionalCssClasses:s});return n.addEventListener("button.clicked",(t=>{const e=t.target;"cancel"===e.getAttribute("name")?e.dispatchEvent(new CustomEvent("confirm.button.cancel",{bubbles:!0})):"ok"===e.getAttribute("name")&&e.dispatchEvent(new CustomEvent("confirm.button.ok",{bubbles:!0}))})),n}loadUrl(t,e=SeverityEnum.info,a,o,s){return this.advanced({type:Types.ajax,title:t,severity:e,buttons:a,ajaxCallback:s,content:o})}show(t,e,a=SeverityEnum.info,o,s){return this.advanced({type:Types.default,title:t,content:e,severity:a,buttons:o,additionalCssClasses:s})}advanced(t){return t.type="string"==typeof t.type&&t.type in Types?t.type:this.defaultConfiguration.type,t.title="string"==typeof t.title?t.title:this.defaultConfiguration.title,t.content="string"==typeof t.content||"object"==typeof t.content?t.content:this.defaultConfiguration.content,t.severity=void 0!==t.severity?t.severity:this.defaultConfiguration.severity,t.buttons=t.buttons||this.defaultConfiguration.buttons,t.size="string"==typeof t.size&&t.size in Sizes?t.size:this.defaultConfiguration.size,t.style="string"==typeof t.style&&t.style in Styles?t.style:this.defaultConfiguration.style,t.additionalCssClasses=t.additionalCssClasses||this.defaultConfiguration.additionalCssClasses,t.callback="function"==typeof t.callback?t.callback:this.defaultConfiguration.callback,t.ajaxCallback="function"==typeof t.ajaxCallback?t.ajaxCallback:this.defaultConfiguration.ajaxCallback,t.staticBackdrop=t.staticBackdrop||this.defaultConfiguration.staticBackdrop,t.hideCloseButton=t.hideCloseButton||this.defaultConfiguration.hideCloseButton,this.generate(t)}setButtons(t){return this.currentModal.buttons=t,this.currentModal}initializeMarkupTrigger(t){new RegularEvent("click",((t,e)=>{t.preventDefault();const a=e.dataset.bsContent||e.dataset.content||TYPO3?.lang?.["message.confirmation"]||"Are you sure?";let o=SeverityEnum.info;if(e.dataset.severity in SeverityEnum){const t=e.dataset.severity;o=SeverityEnum[t]}let s=Sizes.default;if(e.dataset.size in Sizes){const t=e.dataset.size;s=Sizes[t]}let n=e.dataset.url||null;if(null!==n){const t=n.includes("?")?"&":"?";n=n+t+new URLSearchParams(e.dataset).toString()}this.advanced({type:null!==n?Types.ajax:Types.default,title:e.dataset.title||"Alert",content:null!==n?n:a,size:s,severity:o,staticBackdrop:void 0!==e.dataset.staticBackdrop,buttons:[{text:e.dataset.buttonCloseText||TYPO3?.lang?.["button.close"]||"Close",active:!0,btnClass:"btn-default",trigger:(t,a)=>{a.hideModal();const o=Modal.createModalResponseEventFromElement(e,!1);null!==o&&e.dispatchEvent(o)}},{text:e.dataset.buttonOkText||TYPO3?.lang?.["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(o),trigger:(t,a)=>{a.hideModal();const o=Modal.createModalResponseEventFromElement(e,!0);null!==o&&e.dispatchEvent(o);const s=e.dataset.uri||e.dataset.href||e.getAttribute("href");s&&"#"!==s&&(e.ownerDocument.location.href=s),"submit"===e.getAttribute("type")&&(e.closest("form")?.submit(),"BUTTON"===e.tagName&&e.hasAttribute("form")&&e.ownerDocument.querySelector("form#"+e.getAttribute("form"))?.submit()),e.dataset.targetForm&&e.ownerDocument.querySelector("form#"+e.dataset.targetForm)?.submit()}}]})})).delegateTo(t,".t3js-modal-trigger")}generate(t){const e=document.createElement("typo3-backend-modal");return e.type=t.type,"string"==typeof t.content?e.content=t.content:t.type===Types.default&&(e.type=Types.template,e.templateResultContent=t.content),e.severity=t.severity,e.variant=t.style,e.size=t.size,e.modalTitle=t.title,e.additionalCssClasses=t.additionalCssClasses,e.buttons=t.buttons,e.staticBackdrop=t.staticBackdrop,e.hideCloseButton=t.hideCloseButton,t.callback&&(e.callback=t.callback),t.ajaxCallback&&(e.ajaxCallback=t.ajaxCallback),e.addEventListener("typo3-modal-shown",(()=>{const t=e.nextElementSibling,a=1e3+10*this.instances.length;e.zindex=a;const o=a-5;t.style.zIndex=o.toString(),e.querySelector(`${Identifiers.footer} .t3js-active`)?.focus()})),e.addEventListener("typo3-modal-hide",(()=>{if(this.instances.length>0){const t=this.instances.length-1;this.instances.splice(t,1),this.currentModal=this.instances[t-1]}})),e.addEventListener("typo3-modal-hidden",(()=>{e.remove(),this.instances.length>0&&document.body.classList.add("modal-open")})),e.addEventListener("typo3-modal-show",(()=>{this.currentModal=e,this.instances.push(e)})),document.body.appendChild(e),e}}let modalObject=null;try{parent&&parent.window.TYPO3&&parent.window.TYPO3.Modal?(parent.window.TYPO3.Modal.initializeMarkupTrigger(document),modalObject=parent.window.TYPO3.Modal):top&&top.TYPO3.Modal&&(top.TYPO3.Modal.initializeMarkupTrigger(document),modalObject=top.TYPO3.Modal)}catch{}modalObject||(modalObject=new Modal,"undefined"!=typeof TYPO3&&(TYPO3.Modal=modalObject));export default modalObject;
\ No newline at end of file
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/module-menu.js b/typo3/sysext/backend/Resources/Public/JavaScript/module-menu.js
index 24ec543c1da397d93ad50478e0a421c0923c19c8..d09991e9ff48d2d6506e7bff8065c505c2182b49 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/module-menu.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/module-menu.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import{ScaffoldIdentifierEnum}from"@typo3/backend/enum/viewport/scaffold-identifier.js";import{flushModuleCache,ModuleSelector,ModuleUtility}from"@typo3/backend/module.js";import $ from"jquery";import PersistentStorage from"@typo3/backend/storage/persistent.js";import Viewport from"@typo3/backend/viewport.js";import ClientRequest from"@typo3/backend/event/client-request.js";import TriggerRequest from"@typo3/backend/event/trigger-request.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";var ModuleMenuSelector;!function(e){e.menu="[data-modulemenu]",e.item="[data-modulemenu-identifier]",e.collapsible='[data-modulemenu-collapsible="true"]'}(ModuleMenuSelector||(ModuleMenuSelector={}));class ModuleMenu{constructor(){this.loadedModule=null,$((()=>this.initialize()))}static getModuleMenuItemFromElement(e){return{identifier:e.dataset.modulemenuIdentifier,level:e.parentElement.dataset.modulemenuLevel?parseInt(e.parentElement.dataset.modulemenuLevel,10):null,collapsible:"true"===e.dataset.modulemenuCollapsible,expanded:"true"===e.attributes.getNamedItem("aria-expanded")?.value,element:e}}static getCollapsedMainMenuItems(){return PersistentStorage.isset("modulemenu")?JSON.parse(PersistentStorage.get("modulemenu")):{}}static addCollapsedMainMenuItem(e){const t=ModuleMenu.getCollapsedMainMenuItems();t[e]=!0,PersistentStorage.set("modulemenu",JSON.stringify(t))}static removeCollapseMainMenuItem(e){const t=this.getCollapsedMainMenuItems();delete t[e],PersistentStorage.set("modulemenu",JSON.stringify(t))}static includeId(e,t){if(!e.navigationComponentId)return t;let o="";o="@typo3/backend/page-tree/page-tree-element"===e.navigationComponentId?"web":e.name.split("_")[0];const n=ModuleStateStorage.current(o);return n.selection&&(t="id="+n.selection+"&"+t),t}static toggleMenu(e){const t=document.querySelector(ScaffoldIdentifierEnum.scaffold),o="scaffold-modulemenu-expanded";void 0===e&&(e=t.classList.contains(o)),t.classList.toggle(o,!e),e||t.classList.remove("scaffold-toolbar-expanded"),PersistentStorage.set("BackendComponents.States.typo3-module-menu",{collapsed:e})}static toggleModuleGroup(e){const t=ModuleMenu.getModuleMenuItemFromElement(e),o=t.element.closest(".modulemenu-group"),n=o.querySelector(".modulemenu-group-container");t.expanded?ModuleMenu.addCollapsedMainMenuItem(t.identifier):ModuleMenu.removeCollapseMainMenuItem(t.identifier),o.classList.toggle("modulemenu-group-collapsed",t.expanded),o.classList.toggle("modulemenu-group-expanded",!t.expanded),e.setAttribute("aria-expanded",(!t.expanded).toString()),$(n).stop().slideToggle()}static highlightModule(e){document.querySelector(ModuleMenuSelector.menu).querySelectorAll(ModuleMenuSelector.item).forEach((e=>{e.classList.remove("modulemenu-action-active"),e.removeAttribute("aria-current")}));document.querySelector(".t3js-scaffold-toolbar").querySelectorAll(ModuleSelector.link+".dropdown-item").forEach((e=>{e.classList.remove("active"),e.removeAttribute("aria-current")}));const t=ModuleUtility.getFromName(e);this.highlightModuleMenuItem(t,!0)}static highlightModuleMenuItem(e,t=!0){const o=document.querySelector(ModuleMenuSelector.menu).querySelectorAll(ModuleMenuSelector.item+'[data-modulemenu-identifier="'+e.name+'"]');o.forEach((e=>{e.classList.add("modulemenu-action-active"),t&&e.setAttribute("aria-current","location")}));const n=document.querySelector(".t3js-scaffold-toolbar").querySelectorAll(ModuleSelector.link+'[data-moduleroute-identifier="'+e.name+'"].dropdown-item');n.forEach((e=>{e.classList.add("active"),t&&e.setAttribute("aria-current","location")})),(o.length>0||n.length>0)&&(t=!1),""!==e.parent&&this.highlightModuleMenuItem(ModuleUtility.getFromName(e.parent),t)}static getPreviousItem(e){const t=e.parentElement.previousElementSibling;return null===t?ModuleMenu.getLastItem(e):t.firstElementChild}static getNextItem(e){const t=e.parentElement.nextElementSibling;return null===t?ModuleMenu.getFirstItem(e):t.firstElementChild}static getFirstItem(e){return e.parentElement.parentElement.firstElementChild.firstElementChild}static getLastItem(e){return e.parentElement.parentElement.lastElementChild.firstElementChild}static getParentItem(e){return e.parentElement.parentElement.parentElement.firstElementChild}static getFirstChildItem(e){return e.nextElementSibling.firstElementChild.firstElementChild}refreshMenu(){return new AjaxRequest(TYPO3.settings.ajaxUrls.modulemenu).get().then((async e=>{const t=await e.resolve();document.getElementById("modulemenu").outerHTML=t.menu,flushModuleCache(),this.initializeModuleMenuEvents(),this.loadedModule&&ModuleMenu.highlightModule(this.loadedModule)}))}getCurrentModule(){return this.loadedModule}reloadFrames(){Viewport.ContentContainer.refresh()}showModule(e,t,o=null){t=t||"";const n=ModuleUtility.getFromName(e);return this.loadModuleComponents(n,t,new ClientRequest("typo3.showModule",o))}initialize(){if(null===document.querySelector(ModuleMenuSelector.menu))return;const e=$.Deferred();e.resolve(),e.then((()=>{this.initializeModuleMenuEvents(),Viewport.Topbar.Toolbar.registerEvent((()=>{document.querySelector(".t3js-scaffold-toolbar")&&this.initializeTopBarEvents()}))}))}keyboardNavigation(e,t){const o=ModuleMenu.getModuleMenuItemFromElement(t);let n=null;switch(e.code){case"ArrowUp":n=ModuleMenu.getPreviousItem(o.element);break;case"ArrowDown":n=ModuleMenu.getNextItem(o.element);break;case"ArrowLeft":o.level>1&&(n=ModuleMenu.getParentItem(o.element));break;case"ArrowRight":o.collapsible&&(o.expanded||ModuleMenu.toggleModuleGroup(o.element),n=ModuleMenu.getFirstChildItem(o.element));break;case"Home":if(e.ctrlKey&&o.level>1){n=document.querySelector(ModuleMenuSelector.menu+" "+ModuleMenuSelector.item);break}n=ModuleMenu.getFirstItem(o.element);break;case"End":n=e.ctrlKey&&o.level>1?ModuleMenu.getLastItem(document.querySelector(ModuleMenuSelector.menu+" "+ModuleMenuSelector.item)):ModuleMenu.getLastItem(o.element);break;case"Space":case"Enter":("Space"===e.code||o.collapsible)&&e.preventDefault(),o.collapsible&&(ModuleMenu.toggleModuleGroup(o.element),"true"===o.element.attributes.getNamedItem("aria-expanded").value&&(n=ModuleMenu.getFirstChildItem(o.element)));break;case"Esc":case"Escape":o.level>1&&(n=ModuleMenu.getParentItem(o.element),ModuleMenu.toggleModuleGroup(n));break;default:n=null}null!==n&&n.focus()}initializeModuleMenuEvents(){const e=document.querySelector(ModuleMenuSelector.menu);new RegularEvent("keydown",this.keyboardNavigation).delegateTo(e,ModuleMenuSelector.item),new RegularEvent("click",((e,t)=>{e.preventDefault();const o=ModuleUtility.getRouteFromElement(t);this.showModule(o.identifier,o.params,e)})).delegateTo(e,ModuleSelector.link),new RegularEvent("click",((e,t)=>{e.preventDefault(),ModuleMenu.toggleModuleGroup(t)})).delegateTo(e,ModuleMenuSelector.collapsible)}initializeTopBarEvents(){const e=document.querySelector(".t3js-scaffold-toolbar");new RegularEvent("click",((e,t)=>{e.preventDefault();const o=ModuleUtility.getRouteFromElement(t);this.showModule(o.identifier,o.params,e)})).delegateTo(e,ModuleSelector.link),new RegularEvent("click",(e=>{e.preventDefault(),ModuleMenu.toggleMenu()})).bindTo(document.querySelector(".t3js-topbar-button-modulemenu")),new RegularEvent("click",(e=>{e.preventDefault(),ModuleMenu.toggleMenu(!0)})).bindTo(document.querySelector(".t3js-scaffold-content-overlay"));const t=e=>{const t=e.detail.module;if(!t||this.loadedModule===t)return;const o=ModuleUtility.getFromName(t);o.link&&(ModuleMenu.highlightModule(t),this.loadedModule=t,o.navigationComponentId?Viewport.NavigationContainer.showComponent(o.navigationComponentId):Viewport.NavigationContainer.hide())};document.addEventListener("typo3-module-load",t),document.addEventListener("typo3-module-loaded",t)}loadModuleComponents(e,t,o){const n=e.name,l=Viewport.ContentContainer.beforeSetUrl(o);return l.then((()=>{e.navigationComponentId?Viewport.NavigationContainer.showComponent(e.navigationComponentId):Viewport.NavigationContainer.hide(),ModuleMenu.highlightModule(n),this.loadedModule=n,t=ModuleMenu.includeId(e,t),this.openInContentContainer(n,e.link,t,new TriggerRequest("typo3.loadModuleComponents",o))})),l}openInContentContainer(e,t,o,n){const l=t+(o?(t.includes("?")?"&":"?")+o:"");return Viewport.ContentContainer.setUrl(l,new TriggerRequest("typo3.openInContentFrame",n),e)}}top.TYPO3.ModuleMenu||(top.TYPO3.ModuleMenu={App:new ModuleMenu});const moduleMenuApp=top.TYPO3.ModuleMenu;export default moduleMenuApp;
\ No newline at end of file
+import{ScaffoldIdentifierEnum}from"@typo3/backend/enum/viewport/scaffold-identifier.js";import{flushModuleCache,ModuleSelector,ModuleUtility}from"@typo3/backend/module.js";import $ from"jquery";import PersistentStorage from"@typo3/backend/storage/persistent.js";import Viewport from"@typo3/backend/viewport.js";import ClientRequest from"@typo3/backend/event/client-request.js";import TriggerRequest from"@typo3/backend/event/trigger-request.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";var ModuleMenuSelector;!function(e){e.menu="[data-modulemenu]",e.item="[data-modulemenu-identifier]",e.collapsible='[data-modulemenu-collapsible="true"]'}(ModuleMenuSelector||(ModuleMenuSelector={}));class ModuleMenu{constructor(){this.loadedModule=null,$((()=>this.initialize()))}static getModuleMenuItemFromElement(e){return{identifier:e.dataset.modulemenuIdentifier,level:e.parentElement.dataset.modulemenuLevel?parseInt(e.parentElement.dataset.modulemenuLevel,10):null,collapsible:"true"===e.dataset.modulemenuCollapsible,expanded:"true"===e.attributes.getNamedItem("aria-expanded")?.value,element:e}}static getCollapsedMainMenuItems(){return PersistentStorage.isset("modulemenu")?JSON.parse(PersistentStorage.get("modulemenu")):{}}static addCollapsedMainMenuItem(e){const t=ModuleMenu.getCollapsedMainMenuItems();t[e]=!0,PersistentStorage.set("modulemenu",JSON.stringify(t))}static removeCollapseMainMenuItem(e){const t=this.getCollapsedMainMenuItems();delete t[e],PersistentStorage.set("modulemenu",JSON.stringify(t))}static includeId(e,t){if(!e.navigationComponentId)return t;let o="";o="@typo3/backend/page-tree/page-tree-element"===e.navigationComponentId?"web":e.name.split("_")[0];const n=ModuleStateStorage.current(o);return n.selection&&(t="id="+n.selection+"&"+t),t}static toggleMenu(e){const t=document.querySelector(ScaffoldIdentifierEnum.scaffold),o="scaffold-modulemenu-expanded";void 0===e&&(e=t.classList.contains(o)),t.classList.toggle(o,!e),e||t.classList.remove("scaffold-toolbar-expanded"),PersistentStorage.set("BackendComponents.States.typo3-module-menu",{collapsed:e})}static toggleModuleGroup(e){const t=ModuleMenu.getModuleMenuItemFromElement(e),o=t.element.closest(".modulemenu-group"),n=o.querySelector(".modulemenu-group-container");t.expanded?ModuleMenu.addCollapsedMainMenuItem(t.identifier):ModuleMenu.removeCollapseMainMenuItem(t.identifier),o.classList.toggle("modulemenu-group-collapsed",t.expanded),o.classList.toggle("modulemenu-group-expanded",!t.expanded),e.setAttribute("aria-expanded",(!t.expanded).toString()),$(n).stop().slideToggle()}static highlightModule(e){document.querySelector(ModuleMenuSelector.menu).querySelectorAll(ModuleMenuSelector.item).forEach((e=>{e.classList.remove("modulemenu-action-active"),e.removeAttribute("aria-current")}));document.querySelector(".t3js-scaffold-toolbar").querySelectorAll(ModuleSelector.link+".dropdown-item").forEach((e=>{e.classList.remove("active"),e.removeAttribute("aria-current")}));const t=ModuleUtility.getFromName(e);this.highlightModuleMenuItem(t,!0)}static highlightModuleMenuItem(e,t=!0){const o=document.querySelector(ModuleMenuSelector.menu).querySelectorAll(ModuleMenuSelector.item+'[data-modulemenu-identifier="'+e.name+'"]');o.forEach((e=>{e.classList.add("modulemenu-action-active"),t&&e.setAttribute("aria-current","location")}));const n=document.querySelector(".t3js-scaffold-toolbar").querySelectorAll(ModuleSelector.link+'[data-moduleroute-identifier="'+e.name+'"].dropdown-item');n.forEach((e=>{e.classList.add("active"),t&&e.setAttribute("aria-current","location")})),(o.length>0||n.length>0)&&(t=!1),""!==e.parent&&this.highlightModuleMenuItem(ModuleUtility.getFromName(e.parent),t)}static getPreviousItem(e){const t=e.parentElement.previousElementSibling;return null===t?ModuleMenu.getLastItem(e):t.firstElementChild}static getNextItem(e){const t=e.parentElement.nextElementSibling;return null===t?ModuleMenu.getFirstItem(e):t.firstElementChild}static getFirstItem(e){return e.parentElement.parentElement.firstElementChild.firstElementChild}static getLastItem(e){return e.parentElement.parentElement.lastElementChild.firstElementChild}static getParentItem(e){return e.parentElement.parentElement.parentElement.firstElementChild}static getFirstChildItem(e){return e.nextElementSibling.firstElementChild.firstElementChild}refreshMenu(){return new AjaxRequest(TYPO3.settings.ajaxUrls.modulemenu).get().then((async e=>{const t=await e.resolve();document.getElementById("modulemenu").outerHTML=t.menu,flushModuleCache(),this.initializeModuleMenuEvents(),this.loadedModule&&ModuleMenu.highlightModule(this.loadedModule)}))}getCurrentModule(){return this.loadedModule}reloadFrames(){Viewport.ContentContainer.refresh()}showModule(e,t,o=null){t=t||"";const n=ModuleUtility.getFromName(e);return this.loadModuleComponents(n,t,new ClientRequest("typo3.showModule",o))}initialize(){if(null===document.querySelector(ModuleMenuSelector.menu))return;const e=$.Deferred();e.resolve(),e.then((()=>{this.initializeModuleMenuEvents(),Viewport.Topbar.Toolbar.registerEvent((()=>{document.querySelector(".t3js-scaffold-toolbar")&&this.initializeTopBarEvents()}))}))}keyboardNavigation(e,t){const o=ModuleMenu.getModuleMenuItemFromElement(t);let n=null;switch(e.code){case"ArrowUp":n=ModuleMenu.getPreviousItem(o.element);break;case"ArrowDown":n=ModuleMenu.getNextItem(o.element);break;case"ArrowLeft":o.level>1&&(n=ModuleMenu.getParentItem(o.element));break;case"ArrowRight":o.collapsible&&(o.expanded||ModuleMenu.toggleModuleGroup(o.element),n=ModuleMenu.getFirstChildItem(o.element));break;case"Home":if(e.ctrlKey&&o.level>1){n=document.querySelector(ModuleMenuSelector.menu+" "+ModuleMenuSelector.item);break}n=ModuleMenu.getFirstItem(o.element);break;case"End":n=e.ctrlKey&&o.level>1?ModuleMenu.getLastItem(document.querySelector(ModuleMenuSelector.menu+" "+ModuleMenuSelector.item)):ModuleMenu.getLastItem(o.element);break;case"Space":case"Enter":("Space"===e.code||o.collapsible)&&e.preventDefault(),o.collapsible&&(ModuleMenu.toggleModuleGroup(o.element),"true"===o.element.attributes.getNamedItem("aria-expanded").value&&(n=ModuleMenu.getFirstChildItem(o.element)));break;case"Esc":case"Escape":o.level>1&&(n=ModuleMenu.getParentItem(o.element),ModuleMenu.toggleModuleGroup(n));break;default:n=null}null!==n&&n.focus()}initializeModuleMenuEvents(){const e=document.querySelector(ModuleMenuSelector.menu);new RegularEvent("keydown",this.keyboardNavigation).delegateTo(e,ModuleMenuSelector.item),new RegularEvent("click",((e,t)=>{e.preventDefault();const o=ModuleUtility.getRouteFromElement(t);this.showModule(o.identifier,o.params,e)})).delegateTo(e,ModuleSelector.link),new RegularEvent("click",((e,t)=>{e.preventDefault(),ModuleMenu.toggleModuleGroup(t)})).delegateTo(e,ModuleMenuSelector.collapsible)}initializeTopBarEvents(){const e=document.querySelector(".t3js-scaffold-toolbar");new RegularEvent("click",((e,t)=>{e.preventDefault();const o=ModuleUtility.getRouteFromElement(t);this.showModule(o.identifier,o.params,e)})).delegateTo(e,ModuleSelector.link),new RegularEvent("click",(e=>{e.preventDefault(),ModuleMenu.toggleMenu()})).bindTo(document.querySelector(".t3js-topbar-button-modulemenu")),new RegularEvent("click",(e=>{e.preventDefault(),ModuleMenu.toggleMenu(!0)})).bindTo(document.querySelector(".t3js-scaffold-content-overlay"));const t=e=>{const t=e.detail.module;if(!t||this.loadedModule===t)return;const o=ModuleUtility.getFromName(t);o.link&&(ModuleMenu.highlightModule(t),this.loadedModule=t,o.navigationComponentId?Viewport.NavigationContainer.showComponent(o.navigationComponentId):Viewport.NavigationContainer.hide())};document.addEventListener("typo3-module-load",t),document.addEventListener("typo3-module-loaded",t)}loadModuleComponents(e,t,o){const n=e.name,l=Viewport.ContentContainer.beforeSetUrl(o);return l.then((()=>{e.navigationComponentId?Viewport.NavigationContainer.showComponent(e.navigationComponentId):Viewport.NavigationContainer.hide(),ModuleMenu.highlightModule(n),this.loadedModule=n,t=ModuleMenu.includeId(e,t),this.openInContentContainer(n,e.link,t,new TriggerRequest("typo3.loadModuleComponents",o))})),l}openInContentContainer(e,t,o,n){const l=t+(o?(t.includes("?")?"&":"?")+o:"");return Viewport.ContentContainer.setUrl(l,new TriggerRequest("typo3.openInContentFrame",n),e)}}let moduleMenuApp=top?.TYPO3?.ModuleMenu;moduleMenuApp||(moduleMenuApp={App:new ModuleMenu},void 0!==top.TYPO3&&(top.TYPO3.ModuleMenu=moduleMenuApp));export default moduleMenuApp;
\ No newline at end of file
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/viewport.js b/typo3/sysext/backend/Resources/Public/JavaScript/viewport.js
index fdca3adba14e65b102f247e57eb9999010feadbe..2d9887af596f98e9f38c52dc91d0dc2ab3a82e65 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/viewport.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/viewport.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import ContentContainer from"@typo3/backend/viewport/content-container.js";import ConsumerScope from"@typo3/backend/event/consumer-scope.js";import Loader from"@typo3/backend/viewport/loader.js";import NavigationContainer from"@typo3/backend/viewport/navigation-container.js";import Topbar from"@typo3/backend/viewport/topbar.js";class Viewport{constructor(){this.Loader=Loader,this.NavigationContainer=null,this.ContentContainer=null,this.consumerScope=ConsumerScope,this.Topbar=new Topbar,this.NavigationContainer=new NavigationContainer(this.consumerScope),this.ContentContainer=new ContentContainer(this.consumerScope)}}let viewportObject;top.TYPO3.Backend?viewportObject=top.TYPO3.Backend:(viewportObject=new Viewport,top.TYPO3.Backend=viewportObject);export default viewportObject;
\ No newline at end of file
+import ContentContainer from"@typo3/backend/viewport/content-container.js";import ConsumerScope from"@typo3/backend/event/consumer-scope.js";import Loader from"@typo3/backend/viewport/loader.js";import NavigationContainer from"@typo3/backend/viewport/navigation-container.js";import Topbar from"@typo3/backend/viewport/topbar.js";class Viewport{constructor(){this.Loader=Loader,this.NavigationContainer=null,this.ContentContainer=null,this.consumerScope=ConsumerScope,this.Topbar=new Topbar,this.NavigationContainer=new NavigationContainer(this.consumerScope),this.ContentContainer=new ContentContainer(this.consumerScope)}}let viewportObject;top.TYPO3&&top.TYPO3.Backend?viewportObject=top.TYPO3.Backend:(viewportObject=new Viewport,void 0!==top.TYPO3&&(top.TYPO3.Backend=viewportObject));export default viewportObject;
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/JavaScript/backend-exception-test.js b/typo3/sysext/backend/Tests/JavaScript/backend-exception-test.js
deleted file mode 100644
index c999bd9c3c9ccf522bc96f28fdd0ee20263b8564..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Tests/JavaScript/backend-exception-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import{BackendException}from"@typo3/backend/backend-exception.js";describe("@typo3/backend/backend-exception",(()=>{it("sets exception message",(()=>{const e=new BackendException("some message");expect(e.message).toBe("some message")})),it("sets exception code",(()=>{const e=new BackendException("",12345);expect(e.code).toBe(12345)}))}));
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/JavaScript/backend/storage/browser-storage-test.js b/typo3/sysext/backend/Tests/JavaScript/backend/storage/browser-storage-test.js
deleted file mode 100644
index 1077e766cb2906c60f6d41cd515122d5a561f9cc..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Tests/JavaScript/backend/storage/browser-storage-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import BrowserSession from"@typo3/backend/storage/browser-session.js";describe("@typo3/backend/storage/browser-session",(()=>{afterEach((()=>{BrowserSession.clear()})),it("can set and get item",(()=>{const e="test-key";BrowserSession.set(e,"foo"),expect(BrowserSession.get(e)).toBe("foo")})),it("can check if item is set",(()=>{const e="test-key";expect(BrowserSession.isset(e)).toBeFalse(),BrowserSession.set(e,"foo"),expect(BrowserSession.isset(e)).toBeTrue()})),it("can get multiple items by prefix",(()=>{const e={"test-prefix-foo":"foo","test-prefix-bar":"bar","test-prefix-baz":"baz"};for(const[s,o]of Object.entries(e))BrowserSession.set(s,o);const s=BrowserSession.getByPrefix("test-prefix-");expect(s).toEqual(e)})),it("can remove item",(()=>{const e="item-to-be-removed";BrowserSession.set(e,"foo"),expect(BrowserSession.get(e)).not.toBeNull(),BrowserSession.unset(e),expect(BrowserSession.get(e)).toBeNull()})),it("can remove multiple items by prefix",(()=>{const e={"test-prefix-foo":"foo","test-prefix-bar":"bar","test-prefix-baz":"baz"};for(const[s,o]of Object.entries(e))BrowserSession.set(s,o);BrowserSession.unsetByPrefix("test-prefix-");const s=BrowserSession.getByPrefix("test-prefix-");expect(s).toHaveSize(0)})),it("can clear storage",(()=>{const e={foo:"foo",baz:"bencer",huselpusel:"42"};for(const[s,o]of Object.entries(e))BrowserSession.set(s,o);BrowserSession.clear(),expect(sessionStorage.length).toHaveSize(0)}))}));
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/JavaScript/backend/storage/client-test.js b/typo3/sysext/backend/Tests/JavaScript/backend/storage/client-test.js
deleted file mode 100644
index b1d3969835ced542ea13cd188e1d1e165a063fb6..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Tests/JavaScript/backend/storage/client-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import Client from"@typo3/backend/storage/client.js";describe("@typo3/backend/storage/client",(()=>{afterEach((()=>{Client.clear()})),it("can set and get item",(()=>{const e="test-key";Client.set(e,"foo"),expect(Client.get(e)).toBe("foo")})),it("can check if item is set",(()=>{const e="test-key";expect(Client.isset(e)).toBeFalse(),Client.set(e,"foo"),expect(Client.isset(e)).toBeTrue()})),it("can get multiple items by prefix",(()=>{const e={"test-prefix-foo":"foo","test-prefix-bar":"bar","test-prefix-baz":"baz"};for(const[t,i]of Object.entries(e))Client.set(t,i);const t=Client.getByPrefix("test-prefix-");expect(t).toEqual(e)})),it("can remove item",(()=>{const e="item-to-be-removed";Client.set(e,"foo"),expect(Client.get(e)).not.toBeNull(),Client.unset(e),expect(Client.get(e)).toBeNull()})),it("can remove multiple items by prefix",(()=>{const e={"test-prefix-foo":"foo","test-prefix-bar":"bar","test-prefix-baz":"baz"};for(const[t,i]of Object.entries(e))Client.set(t,i);Client.unsetByPrefix("test-prefix-");const t=Client.getByPrefix("test-prefix-");expect(t).toHaveSize(0)})),it("can clear storage",(()=>{const e={foo:"foo",baz:"bencer",huselpusel:"42"};for(const[t,i]of Object.entries(e))Client.set(t,i);Client.clear(),expect(localStorage.length).toHaveSize(0)}))}));
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/JavaScript/element/immediate-action-element-test.js b/typo3/sysext/backend/Tests/JavaScript/element/immediate-action-element-test.js
deleted file mode 100644
index 548397f004a4771fade70a20a8903ec85bb32bce..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Tests/JavaScript/element/immediate-action-element-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import{ImmediateActionElement}from"@typo3/backend/element/immediate-action-element.js";import moduleMenuApp from"@typo3/backend/module-menu.js";import viewportObject from"@typo3/backend/viewport.js";describe("TYPO3/CMS/Backend/Element/ImmediateActionElement:",(()=>{let e;beforeEach((()=>{e=document.createElement("div"),document.body.appendChild(e)})),afterEach((()=>{e.remove(),e=null})),it("dispatches action when created via constructor",(async()=>{const t=viewportObject.Topbar,n={refresh:()=>{}};spyOn(n,"refresh").and.callThrough(),viewportObject.Topbar=n;const o=new ImmediateActionElement;o.setAttribute("action","TYPO3.Backend.Topbar.refresh"),expect(n.refresh).not.toHaveBeenCalled(),e.appendChild(o),await import("@typo3/backend/viewport.js"),await new Promise((e=>setTimeout(e,100))),expect(n.refresh).toHaveBeenCalled(),viewportObject.Topbar=t})),it("dispatches action when created via createElement",(async()=>{const t=viewportObject.Topbar,n={refresh:()=>{}};spyOn(n,"refresh").and.callThrough(),viewportObject.Topbar=n;const o=document.createElement("typo3-immediate-action");o.setAttribute("action","TYPO3.Backend.Topbar.refresh"),expect(n.refresh).not.toHaveBeenCalled(),e.appendChild(o),await import("@typo3/backend/viewport.js"),await new Promise((e=>setTimeout(e,100))),expect(n.refresh).toHaveBeenCalled(),viewportObject.Topbar=t})),it("dispatches action when created from string",(async()=>{const t=moduleMenuApp.App,n={refreshMenu:()=>{}};spyOn(n,"refreshMenu").and.callThrough(),moduleMenuApp.App=n;const o=document.createRange().createContextualFragment('<typo3-immediate-action action="TYPO3.ModuleMenu.App.refreshMenu"></typo3-immediate-action>').querySelector("typo3-immediate-action");expect(n.refreshMenu).not.toHaveBeenCalled(),e.appendChild(o),await import("@typo3/backend/module-menu.js"),await new Promise((e=>setTimeout(e,100))),expect(n.refreshMenu).toHaveBeenCalled(),viewportObject.App=t})),it("dispatches action when created via innerHTML",(async()=>{const t=moduleMenuApp.App,n={refreshMenu:()=>{}};spyOn(n,"refreshMenu").and.callThrough(),moduleMenuApp.App=n,e.innerHTML='<typo3-immediate-action action="TYPO3.ModuleMenu.App.refreshMenu"></typo3-immediate-action>',await import("@typo3/backend/module-menu.js"),await new Promise((e=>setTimeout(e,100))),expect(n.refreshMenu).toHaveBeenCalled(),moduleMenuApp.App=t}))}));
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/JavaScript/form-engine-validation-test.js b/typo3/sysext/backend/Tests/JavaScript/form-engine-validation-test.js
deleted file mode 100644
index 37361e0fe86a468f44d1958f56ef0c1c37b90dbd..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Tests/JavaScript/form-engine-validation-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import FormEngineValidation from"@typo3/backend/form-engine-validation.js";describe("TYPO3/CMS/Backend/FormEngineValidationTest:",(()=>{const e=[{description:"returns empty string with string 0",type:"date",value:"0",config:{},result:""},{description:"returns date with int 0",type:"date",value:0,config:{},result:"01-01-1970"},{description:"works for type date with timestamp",type:"date",value:1e7,config:{},result:"26-04-1970"},{description:"works for type date with iso date",type:"date",value:"2016-12-02T11:16:06+00:00",config:{},result:"02-12-2016"},{description:"works for type datetime",type:"datetime",value:"0",config:{},result:""},{description:"works for type datetime with timestamp",type:"datetime",value:1e7,config:{},result:"17:46 26-04-1970"},{description:"works for type datetime with iso date",type:"datetime",value:"2016-12-02T11:16:06+00:00",config:{},result:"11:16 02-12-2016"},{description:"resolves to empty result for zero value",type:"datetime",value:"0",config:{},result:""},{description:"resolves to empty result for invalid value",type:"datetime",value:"invalid",config:{},result:""},{description:"works for type time",type:"time",value:0,config:{},result:"00:00"},{description:"works for type time with timestamp",type:"time",value:1e7,config:{},result:"17:46"},{description:"works for type time with iso date",type:"time",value:"2016-12-02T11:16:06+00:00",config:{},result:"11:16"}];describe("tests for formatValue",(()=>{using(e,(function(e){it(e.description,(()=>{FormEngineValidation.initialize();const t=FormEngineValidation.formatValue(e.type,e.value,e.config);expect(t).toBe(e.result)}))}))}));const t=[{description:"works for command alpha with numeric value",command:"alpha",value:"1234",config:{},result:""},{description:"works for command alpha with string value",command:"alpha",value:"abc",config:{},result:"abc"},{description:"works for command alpha with alphanum input",command:"alpha",value:"abc123",config:{},result:"abc"},{description:"works for command alpha with alphanum input",command:"alpha",value:"123abc123",config:{},result:"abc"}];describe("test for processValue",(()=>{using(t,(function(e){it(e.description,(()=>{const t=FormEngineValidation.processValue(e.command,e.value,e.config);expect(t).toBe(e.result)}))}))})),describe("tests for parseInt",(()=>{it("works for value 0",(()=>{expect(FormEngineValidation.parseInt(0)).toBe(0)})),it("works for value 1",(()=>{expect(FormEngineValidation.parseInt(1)).toBe(1)})),it("works for value -1",(()=>{expect(FormEngineValidation.parseInt(-1)).toBe(-1)})),it('works for value "0"',(()=>{expect(FormEngineValidation.parseInt("0")).toBe(0)})),it('works for value "1"',(()=>{expect(FormEngineValidation.parseInt("1")).toBe(1)})),it('works for value "-1"',(()=>{expect(FormEngineValidation.parseInt("-1")).toBe(-1)})),it("works for value 0.5",(()=>{expect(FormEngineValidation.parseInt(.5)).toBe(0)})),it('works for value "0.5"',(()=>{expect(FormEngineValidation.parseInt("0.5")).toBe(0)})),it('works for value "foo"',(()=>{expect(FormEngineValidation.parseInt("foo")).toBe(0)})),it("works for value true",(()=>{expect(FormEngineValidation.parseInt(!0)).toBe(0)})),it("works for value false",(()=>{expect(FormEngineValidation.parseInt(!1)).toBe(0)})),it("works for value null",(()=>{expect(FormEngineValidation.parseInt(null)).toBe(0)}))})),describe("tests for parseDouble",(()=>{it("works for value 0",(()=>{expect(FormEngineValidation.parseDouble(0)).toBe("0.00")})),it("works for value 1",(()=>{expect(FormEngineValidation.parseDouble(1)).toBe("1.00")})),it("works for value -1",(()=>{expect(FormEngineValidation.parseDouble(-1)).toBe("-1.00")})),it('works for value "0"',(()=>{expect(FormEngineValidation.parseDouble("0")).toBe("0.00")})),it('works for value "1"',(()=>{expect(FormEngineValidation.parseDouble("1")).toBe("1.00")})),it('works for value "-1"',(()=>{expect(FormEngineValidation.parseDouble("-1")).toBe("-1.00")})),it("works for value 0.5",(()=>{expect(FormEngineValidation.parseDouble(.5)).toBe("0.50")})),it('works for value "0.5"',(()=>{expect(FormEngineValidation.parseDouble("0.5")).toBe("0.50")})),it('works for value "foo"',(()=>{expect(FormEngineValidation.parseDouble("foo")).toBe("0.00")})),it("works for value true",(()=>{expect(FormEngineValidation.parseDouble(!0)).toBe("0.00")})),it("works for value false",(()=>{expect(FormEngineValidation.parseDouble(!1)).toBe("0.00")})),it("works for value null",(()=>{expect(FormEngineValidation.parseDouble(null)).toBe("0.00")}))})),describe("tests for getYear",(()=>{const e=new Date;afterEach((()=>{jasmine.clock().mockDate(e)})),it("works for current date",(()=>{const e=new Date;expect(FormEngineValidation.getYear(e)).toBe(e.getFullYear())})),it("works for year 2013",(()=>{const e=new Date(2013,9,23);jasmine.clock().mockDate(e),expect(FormEngineValidation.getYear(e)).toBe(2013)}))})),describe("tests for getDate",(()=>{const e=new Date;afterEach((()=>{jasmine.clock().mockDate(e)})),xit("works for year 2013",(()=>{const e=new Date(2013,9,23,13,13,13);jasmine.clock().mockDate(e),expect(FormEngineValidation.getDate(e)).toBe(1382479200)}))}))}));
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/JavaScript/grid-editor-test.js b/typo3/sysext/backend/Tests/JavaScript/grid-editor-test.js
deleted file mode 100644
index 0a2019c8e7346a6cee33cdf65b88778f950bccb9..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Tests/JavaScript/grid-editor-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import{GridEditor}from"@typo3/backend/grid-editor.js";describe("TYPO3/CMS/Backend/GridEditorTest:",(()=>{describe("tests for stripMarkup",(()=>{it("works with string which contains html markup only",(()=>{expect(GridEditor.stripMarkup("<b>'formula': \"x > y\"</b>")).toBe("'formula': \"x > y\"")})),it("works with string which contains html markup and normal text",(()=>{expect(GridEditor.stripMarkup("<b>foo</b> bar")).toBe("foo bar")}))}))}));
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/JavaScript/hashing/md5Test.js b/typo3/sysext/backend/Tests/JavaScript/hashing/md5Test.js
deleted file mode 100644
index e7d9e7627dcd8346199305c77f1c088cc34cf1ac..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Tests/JavaScript/hashing/md5Test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import Md5 from"@typo3/backend/hashing/md5.js";describe("TYPO3/CMS/Backend/Hashing/Md5:",(()=>{describe("tests for hash",(()=>{it("hashes a value as expected",(()=>{expect(Md5.hash("Hello World")).toBe("b10a8db164e0754105b7a99be72e3fe5"),expect(Md5.hash("TYPO3 CMS is an Open Source Enterprise Content Management System with a large global community, backed by the approximately 900 members of the TYPO3 Association.")).toBe("65b0beb76ada01bd7b5f44fb37da6139")}))}))}));
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/JavaScript/icons-test.js b/typo3/sysext/backend/Tests/JavaScript/icons-test.js
deleted file mode 100644
index b524be41f95e407e51e55917c11780b0cf706769..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Tests/JavaScript/icons-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import Icons from"@typo3/backend/icons.js";describe("TYPO3/CMS/Backend/IconsTest:",(()=>{describe("tests for Icons object",(()=>{it("has all sizes",(()=>{expect(Icons.sizes.small).toBe("small"),expect(Icons.sizes.default).toBe("default"),expect(Icons.sizes.large).toBe("large"),expect(Icons.sizes.overlay).toBe("overlay")})),it("has all states",(()=>{expect(Icons.states.default).toBe("default"),expect(Icons.states.disabled).toBe("disabled")})),it("has all markupIdentifiers",(()=>{expect(Icons.markupIdentifiers.default).toBe("default"),expect(Icons.markupIdentifiers.inline).toBe("inline")}))})),describe("tests for Icons::getIcon",(()=>{beforeEach((()=>{spyOn(Icons,"getIcon"),Icons.getIcon("test",Icons.sizes.small,null,Icons.states.default,Icons.markupIdentifiers.default)})),it("tracks that the spy was called",(()=>{expect(Icons.getIcon).toHaveBeenCalled()})),it("tracks all the arguments of its calls",(()=>{expect(Icons.getIcon).toHaveBeenCalledWith("test",Icons.sizes.small,null,Icons.states.default,Icons.markupIdentifiers.default)})),xit("works get icon from remote server")})),describe("tests for Icons::putInCache",(()=>{it("works for simply identifier and markup",(()=>{const e=new Promise((e=>e()));Icons.putInPromiseCache("foo",e),expect(Icons.getFromPromiseCache("foo")).toBe(e),expect(Icons.isPromiseCached("foo")).toBe(!0)}))})),describe("tests for Icons::getFromPromiseCache",(()=>{it("return undefined for uncached promise",(()=>{expect(Icons.getFromPromiseCache("bar")).not.toBeDefined(),expect(Icons.isPromiseCached("bar")).toBe(!1)}))}))}));
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/JavaScript/notification-test.js b/typo3/sysext/backend/Tests/JavaScript/notification-test.js
deleted file mode 100644
index f1321bdad5516a19db5a78c92e6b62fa504d3780..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Tests/JavaScript/notification-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import DeferredAction from"@typo3/backend/action-button/deferred-action.js";import ImmediateAction from"@typo3/backend/action-button/immediate-action.js";import Notification from"@typo3/backend/notification.js";import Icons from"@typo3/backend/icons.js";describe("TYPO3/CMS/Backend/Notification:",(()=>{beforeEach((()=>{const e=document.getElementById("alert-container");for(;null!==e&&e.firstChild;)e.removeChild(e.firstChild);spyOn(Icons,"getIcon").and.callFake((()=>Promise.resolve("X")))})),describe("can render notifications with dismiss after 1000ms",(()=>{for(const e of[{method:Notification.notice,title:"Notice message",message:"This notification describes a notice",class:"alert-notice"},{method:Notification.info,title:"Info message",message:"This notification describes an informative action",class:"alert-info"},{method:Notification.success,title:"Success message",message:"This notification describes a successful action",class:"alert-success"},{method:Notification.warning,title:"Warning message",message:"This notification describes a harmful action",class:"alert-warning"},{method:Notification.error,title:"Error message",message:"This notification describes an erroneous action",class:"alert-danger"}])it("can render a notification of type "+e.class,(async()=>{e.method(e.title,e.message,1),await document.querySelector("#alert-container typo3-notification-message:last-child").updateComplete;const t="div.alert."+e.class,o=document.querySelector(t);expect(o).not.toBe(null),expect(o.querySelector(".alert-title").textContent).toEqual(e.title),expect(o.querySelector(".alert-message").textContent).toEqual(e.message),await new Promise((e=>window.setTimeout(e,2e3))),expect(document.querySelector(t)).toBe(null)}))})),it("can render action buttons",(async()=>{Notification.info("Info message","Some text",1,[{label:"My action",action:new ImmediateAction((e=>e))},{label:"My other action",action:new DeferredAction((e=>e))}]),await document.querySelector("#alert-container typo3-notification-message:last-child").updateComplete;const e=document.querySelector("div.alert");expect(e.querySelector(".alert-actions")).not.toBe(null),expect(e.querySelectorAll(".alert-actions a").length).toEqual(2),expect(e.querySelectorAll(".alert-actions a")[0].textContent).toEqual("My action"),expect(e.querySelectorAll(".alert-actions a")[1].textContent).toEqual("My other action")})),it("immediate action is called",(async()=>{const e={callback:()=>{}};spyOn(e,"callback").and.callThrough(),Notification.info("Info message","Some text",1,[{label:"My immediate action",action:new ImmediateAction(e.callback)}]),await document.querySelector("#alert-container typo3-notification-message:last-child").updateComplete;document.querySelector("div.alert").querySelector(".alert-actions a").click(),await document.querySelector("#alert-container typo3-notification-message:last-child").updateComplete,expect(e.callback).toHaveBeenCalled()}))}));
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/JavaScript/popover-test.js b/typo3/sysext/backend/Tests/JavaScript/popover-test.js
deleted file mode 100644
index d1953289a2bb4ce3709b6420f3b2d555cf67d30e..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Tests/JavaScript/popover-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import Popover from"@typo3/backend/popover.js";describe("TYPO3/CMS/Backend/PopoverTest:",(()=>{describe("initialize",(()=>{const t=document.createElement("div");t.dataset.bsToggle="popover",document.body.append(t),it("works with default selector",(()=>{Popover.initialize(),expect(t.outerHTML).toBe('<div data-bs-toggle="popover"></div>')}));const e=document.createElement("div");e.dataset.bsToggle="popover",e.dataset.title="foo",document.body.append(e),it("works with default selector and title attribute",(()=>{Popover.initialize(),expect(e.outerHTML).toBe('<div data-bs-toggle="popover" data-title="foo" data-bs-title="foo"></div>')}));const o=document.createElement("div");o.dataset.bsToggle="popover",o.dataset.bsContent="foo",document.body.append(o),it("works with default selector and content attribute",(()=>{Popover.initialize(),expect(o.outerHTML).toBe('<div data-bs-toggle="popover" data-bs-content="foo"></div>')}));const i=document.createElement("div");i.classList.add("t3js-popover"),document.body.append(i),it("works with custom selector",(()=>{Popover.initialize(".t3js-popover"),expect(i.outerHTML).toBe('<div class="t3js-popover"></div>')}))})),describe("call setOptions",(()=>{const t=document.createElement("div");t.classList.add("t3js-test-set-options"),t.dataset.title="foo-title",t.dataset.bsContent="foo-content",document.body.append(t),it("can set title",(()=>{Popover.initialize(".t3js-test-set-options"),expect(t.getAttribute("data-title")).toBe("foo-title"),expect(t.getAttribute("data-bs-content")).toBe("foo-content"),Popover.setOptions(t,{title:"bar-title"}),expect(t.getAttribute("data-title")).toBe("foo-title"),expect(t.getAttribute("data-bs-content")).toBe("foo-content"),expect(t.getAttribute("data-bs-original-title")).toBe("bar-title")}));const e=document.createElement("div");e.classList.add("t3js-test-set-options2"),e.dataset.title="foo-title",e.dataset.bsContent="foo-content",document.body.append(e),it("can set content",(()=>{Popover.initialize(".t3js-test-set-options2"),Popover.show(e),expect(e.getAttribute("data-title")).toBe("foo-title"),expect(e.getAttribute("data-bs-content")).toBe("foo-content"),Popover.setOptions(e,{content:"bar-content"}),expect(e.getAttribute("data-title")).toBe("foo-title"),expect(e.getAttribute("data-bs-content")).toBe("bar-content"),expect(e.getAttribute("data-bs-original-title")).toBe("foo-title")}))}))}));
\ No newline at end of file
diff --git a/typo3/sysext/core/Tests/JavaScript/ajax/ajax-request-test.js b/typo3/sysext/core/Tests/JavaScript/ajax/ajax-request-test.js
deleted file mode 100644
index eb7a6d2c4c0a7dea227ee4951edd55053660a897..0000000000000000000000000000000000000000
--- a/typo3/sysext/core/Tests/JavaScript/ajax/ajax-request-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import AjaxRequest from"@typo3/core/ajax/ajax-request.js";describe("@typo3/core/ajax/ajax-request",(()=>{let e;beforeEach((()=>{const t=new Promise(((t,o)=>{e={resolve:t,reject:o}}));spyOn(window,"fetch").and.returnValue(t)})),it("sends GET request",(()=>{new AjaxRequest("https://example.com").get(),expect(window.fetch).toHaveBeenCalledWith(new URL("https://example.com/"),jasmine.objectContaining({method:"GET"}))}));for(const e of["POST","PUT","DELETE"])describe(`send a ${e} request`,(()=>{for(const t of function*(){yield["object as payload",e,{foo:"bar",bar:"baz",nested:{works:"yes"}},()=>{const e=new FormData;return e.set("foo","bar"),e.set("bar","baz"),e.set("nested[works]","yes"),e},{}],yield["JSON object as payload",e,{foo:"bar",bar:"baz",nested:{works:"yes"}},()=>JSON.stringify({foo:"bar",bar:"baz",nested:{works:"yes"}}),{"Content-Type":"application/json"}],yield["JSON string as payload",e,JSON.stringify({foo:"bar",bar:"baz",nested:{works:"yes"}}),()=>JSON.stringify({foo:"bar",bar:"baz",nested:{works:"yes"}}),{"Content-Type":"application/json"}]}()){const[e,o,a,r,n]=t,s=o.toLowerCase();it(`with ${e}`,(e=>{new AjaxRequest("https://example.com")[s](a,{headers:n}),expect(window.fetch).toHaveBeenCalledWith(new URL("https://example.com/"),jasmine.objectContaining({method:o,body:r()})),e()}))}}));describe("send GET requests",(()=>{for(const t of function*(){yield["plaintext","foobar huselpusel",{},(e,t)=>{expect("string"==typeof e).toBeTruthy(),expect(e).toEqual(t)}],yield["JSON",JSON.stringify({foo:"bar",baz:"bencer"}),{"Content-Type":"application/json"},(e,t)=>{expect("object"==typeof e).toBeTruthy(),expect(JSON.stringify(e)).toEqual(t)}],yield["JSON with utf-8",JSON.stringify({foo:"bar",baz:"bencer"}),{"Content-Type":"application/json; charset=utf-8"},(e,t)=>{expect("object"==typeof e).toBeTruthy(),expect(JSON.stringify(e)).toEqual(t)}]}()){const[o,a,r,n]=t;it("receives a "+o+" response",(t=>{const o=new Response(a,{headers:r});e.resolve(o),new AjaxRequest(new URL("https://example.com")).get().then((async e=>{const o=await e.resolve();expect(window.fetch).toHaveBeenCalledWith(new URL("https://example.com/"),jasmine.objectContaining({method:"GET"})),n(o,a),t()}))}))}})),describe("send requests with different input urls",(()=>{for(const e of function*(){yield["absolute url with domain",new URL("https://example.com"),{},new URL("https://example.com/")],yield["absolute url with domain, with query parameter",new URL("https://example.com"),{foo:"bar",bar:{baz:"bencer"}},new URL("https://example.com/?foo=bar&bar%5Bbaz%5D=bencer")],yield["absolute url without domain","/foo/bar",{},new URL(window.location.origin+"/foo/bar")],yield["absolute url without domain, with query parameter","/foo/bar",{foo:"bar",bar:{baz:"bencer"}},new URL(window.location.origin+"/foo/bar?foo=bar&bar%5Bbaz%5D=bencer")],yield["relative url without domain","foo/bar",{},new URL(window.location.origin+"/foo/bar")],yield["relative url without domain, with query parameter","foo/bar",{foo:"bar",bar:{baz:"bencer"}},new URL(window.location.origin+"/foo/bar?foo=bar&bar%5Bbaz%5D=bencer")],yield["fallback to current script if not defined","?foo=bar&baz=bencer",{},new URL(window.location.origin+window.location.pathname+"?foo=bar&baz=bencer")]}()){const[t,o,a,r]=e;it("with "+t,(()=>{new AjaxRequest(o).withQueryArguments(a).get(),expect(window.fetch).toHaveBeenCalledWith(r,jasmine.objectContaining({method:"GET"}))}))}})),describe("send requests with query arguments",(()=>{for(const e of function*(){yield["single level of arguments",{foo:"bar",bar:"baz"},new URL("https://example.com/?foo=bar&bar=baz")],yield["nested arguments",{foo:"bar",bar:{baz:"bencer"}},new URL("https://example.com/?foo=bar&bar%5Bbaz%5D=bencer")],yield["string argument","hello=world&foo=bar",new URL("https://example.com/?hello=world&foo=bar")],yield["array of arguments",["foo=bar","husel=pusel"],new URL("https://example.com/?foo=bar&husel=pusel")],yield["object with array",{foo:["bar","baz"]},new URL("https://example.com/?foo%5B0%5D=bar&foo%5B1%5D=baz")],yield["complex object",{foo:"bar",nested:{husel:"pusel",bar:"baz",array:["5","6"]},array:["1","2"]},new URL("https://example.com/?foo=bar&nested%5Bhusel%5D=pusel&nested%5Bbar%5D=baz&nested%5Barray%5D%5B0%5D=5&nested%5Barray%5D%5B1%5D=6&array%5B0%5D=1&array%5B1%5D=2")],yield["complex, deeply nested object",{foo:"bar",nested:{husel:"pusel",bar:"baz",array:["5","6"],deep_nested:{husel:"pusel",bar:"baz",array:["5","6"]}},array:["1","2"]},new URL("https://example.com/?foo=bar&nested%5Bhusel%5D=pusel&nested%5Bbar%5D=baz&nested%5Barray%5D%5B0%5D=5&nested%5Barray%5D%5B1%5D=6&nested%5Bdeep_nested%5D%5Bhusel%5D=pusel&nested%5Bdeep_nested%5D%5Bbar%5D=baz&nested%5Bdeep_nested%5D%5Barray%5D%5B0%5D=5&nested%5Bdeep_nested%5D%5Barray%5D%5B1%5D=6&array%5B0%5D=1&array%5B1%5D=2")]}()){const[t,o,a]=e;it("with "+t,(()=>{new AjaxRequest("https://example.com/").withQueryArguments(o).get(),expect(window.fetch).toHaveBeenCalledWith(a,jasmine.objectContaining({method:"GET"}))}))}}))}));
\ No newline at end of file
diff --git a/typo3/sysext/core/Tests/JavaScript/ajax/input-transformer-test.js b/typo3/sysext/core/Tests/JavaScript/ajax/input-transformer-test.js
deleted file mode 100644
index 015544c13a28a8e16eedf15168395b64edef1a9a..0000000000000000000000000000000000000000
--- a/typo3/sysext/core/Tests/JavaScript/ajax/input-transformer-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import{InputTransformer}from"@typo3/core/ajax/input-transformer.js";describe("@typo3/core/ajax/input-transformer",(()=>{it("converts object to FormData",(()=>{const a=new FormData;a.set("foo","bar"),a.set("bar","baz"),a.set("nested[works]","yes"),expect(InputTransformer.toFormData({foo:"bar",bar:"baz",nested:{works:"yes"}})).toEqual(a)})),it("undefined values are removed in FormData",(()=>{const a={foo:"bar",bar:"baz",removeme:void 0},r=new FormData;r.set("foo","bar"),r.set("bar","baz"),expect(InputTransformer.toFormData(a)).toEqual(r)})),it("converts object to SearchParams",(()=>{expect(InputTransformer.toSearchParams({foo:"bar",bar:"baz",nested:{works:"yes"}})).toEqual("foo=bar&bar=baz&nested[works]=yes")})),it("merges array to SearchParams",(()=>{expect(InputTransformer.toSearchParams(["foo=bar","bar=baz"])).toEqual("foo=bar&bar=baz")})),it("keeps string in SearchParams",(()=>{expect(InputTransformer.toSearchParams("foo=bar&bar=baz")).toEqual("foo=bar&bar=baz")})),it("undefined values are removed in SearchParams",(()=>{const a={foo:"bar",bar:"baz",removeme:void 0};expect(InputTransformer.toSearchParams(a)).toEqual("foo=bar&bar=baz")}))}));
\ No newline at end of file
diff --git a/typo3/sysext/core/Tests/JavaScript/security-utility-test.js b/typo3/sysext/core/Tests/JavaScript/security-utility-test.js
deleted file mode 100644
index dbac3c87d2ce56baa85c22d630ca863817b89e56..0000000000000000000000000000000000000000
--- a/typo3/sysext/core/Tests/JavaScript/security-utility-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-import SecurityUtility from"@typo3/core/security-utility.js";describe("@typo3/core/security-utility",(()=>{it("generates random hex value",(()=>{for(const t of function*(){yield 1,yield 20,yield 39}()){const e=(new SecurityUtility).getRandomHexValue(t);expect(e.length).toBe(t)}})),it("throws SyntaxError on invalid length",(()=>{for(const t of function*(){yield 0,yield-90,yield 10.3}())expect((()=>(new SecurityUtility).getRandomHexValue(t))).toThrowError(SyntaxError)})),it("encodes HTML",(()=>{expect((new SecurityUtility).encodeHtml("<>\"'&")).toBe("&lt;&gt;&quot;&apos;&amp;")})),it("removes HTML from string",(()=>{expect((new SecurityUtility).stripHtml('<img src="" onerror="alert(\'1\')">oh noes')).toBe("oh noes"),expect((new SecurityUtility).encodeHtml("<>\"'&")).toBe("&lt;&gt;&quot;&apos;&amp;")}))}));
\ No newline at end of file