Merge dependabot/npm_and_yarn/types/tar-6.1.12 into combined-prs-branch
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Thu, 4 Apr 2024 16:16:26 +0000 (18:16 +0200)
committerGitHub <noreply@github.com>
Thu, 4 Apr 2024 16:16:26 +0000 (18:16 +0200)
137 files changed:
.eslintrc.cjs
.github/ISSUE_TEMPLATE/bug_report.yml
.github/ISSUE_TEMPLATE/feature_request.yml
.github/dependabot.yml
.gitignore
.husky/.gitignore [deleted file]
.husky/commit-msg
.husky/pre-commit
.npmrc
CHANGELOG.md
README.md
bundle.js
docker/Dockerfile
docker/autoconfig.sh
docker/start.sh
package.json
pnpm-lock.yaml
pnpm-workspace.yaml [new file with mode: 0644]
sonar-project.properties
src/assets/config-template.json
src/charging-station/AutomaticTransactionGenerator.ts
src/charging-station/Bootstrap.ts
src/charging-station/ChargingStation.ts
src/charging-station/ChargingStationWorker.ts
src/charging-station/ConfigurationKeyUtils.ts
src/charging-station/Helpers.ts
src/charging-station/IdTagsCache.ts
src/charging-station/SharedLRUCache.ts
src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts
src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts
src/charging-station/broadcast-channel/WorkerBroadcastChannel.ts
src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts
src/charging-station/ocpp/1.6/OCPP16RequestService.ts
src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts
src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts
src/charging-station/ocpp/2.0/OCPP20RequestService.ts
src/charging-station/ocpp/2.0/OCPP20ResponseService.ts
src/charging-station/ocpp/OCPPIncomingRequestService.ts
src/charging-station/ocpp/OCPPRequestService.ts
src/charging-station/ocpp/OCPPResponseService.ts
src/charging-station/ocpp/OCPPServiceUtils.ts
src/charging-station/ui-server/AbstractUIServer.ts
src/charging-station/ui-server/UIHttpServer.ts
src/charging-station/ui-server/UIServerFactory.ts
src/charging-station/ui-server/UIServerUtils.ts
src/charging-station/ui-server/UIWebSocketServer.ts
src/charging-station/ui-server/ui-services/AbstractUIService.ts
src/charging-station/ui-server/ui-services/UIService001.ts
src/charging-station/ui-server/ui-services/UIServiceFactory.ts
src/exception/OCPPError.ts
src/performance/PerformanceStatistics.ts
src/performance/index.ts
src/performance/storage/JsonFileStorage.ts
src/performance/storage/MikroOrmStorage.ts
src/performance/storage/MongoDBStorage.ts
src/performance/storage/None.ts
src/performance/storage/StorageFactory.ts
src/types/AutomaticTransactionGenerator.ts
src/types/ChargingStationInfo.ts
src/types/ChargingStationOcppConfiguration.ts
src/types/ChargingStationTemplate.ts
src/types/ChargingStationWorker.ts
src/types/ConfigurationData.ts
src/types/MapStringifyFormat.ts [new file with mode: 0644]
src/types/MeasurandPerPhaseSampledValueTemplates.ts
src/types/SimulatorState.ts [new file with mode: 0644]
src/types/Statistics.ts
src/types/UIProtocol.ts
src/types/WorkerBroadcastChannel.ts
src/types/index.ts
src/types/ocpp/1.6/Requests.ts
src/types/ocpp/1.6/Responses.ts
src/types/ocpp/1.6/Transaction.ts
src/types/ocpp/2.0/Common.ts
src/types/ocpp/2.0/Requests.ts
src/types/ocpp/2.0/Responses.ts
src/types/ocpp/2.0/Variables.ts
src/types/ocpp/Configuration.ts
src/types/ocpp/Requests.ts
src/types/ocpp/Responses.ts
src/utils/ChargingStationConfigurationUtils.ts
src/utils/Configuration.ts
src/utils/ConfigurationUtils.ts
src/utils/Constants.ts
src/utils/ErrorUtils.ts
src/utils/FileUtils.ts
src/utils/Logger.ts
src/utils/MessageChannelUtils.ts
src/utils/StatisticUtils.ts
src/utils/Utils.ts
src/utils/index.ts
src/worker/WorkerAbstract.ts
src/worker/WorkerConstants.ts
src/worker/WorkerDynamicPool.ts
src/worker/WorkerFactory.ts
src/worker/WorkerFixedPool.ts
src/worker/WorkerSet.ts
src/worker/WorkerTypes.ts
src/worker/index.ts
tests/utils/StatisticUtils.test.ts
tests/utils/Utils.test.ts
tsconfig.json
ui/web/.eslintrc.cjs
ui/web/README.md
ui/web/package.json
ui/web/pnpm-lock.yaml [deleted file]
ui/web/sonar-project.properties
ui/web/src/App.vue
ui/web/src/assets/webui.png
ui/web/src/components/actions/AddChargingStations.vue
ui/web/src/components/actions/SetSupervisionUrl.vue
ui/web/src/components/actions/StartTransaction.vue
ui/web/src/components/buttons/Button.vue
ui/web/src/components/buttons/FlatButton.vue [deleted file]
ui/web/src/components/buttons/ReloadButton.vue
ui/web/src/components/buttons/ToggleButton.vue [new file with mode: 0644]
ui/web/src/components/charging-stations/CSConnector.vue
ui/web/src/components/charging-stations/CSData.vue
ui/web/src/components/charging-stations/CSTable.vue
ui/web/src/composables/UIClient.ts
ui/web/src/composables/Utils.ts
ui/web/src/composables/index.ts
ui/web/src/main.ts
ui/web/src/router/index.ts
ui/web/src/shims-vue.d.ts
ui/web/src/types/ChargingStationType.ts
ui/web/src/types/ConfigurationType.ts
ui/web/src/types/JsonType.ts
ui/web/src/types/UIProtocol.ts
ui/web/src/types/index.ts
ui/web/src/views/ChargingStationsView.vue
ui/web/src/views/NotFoundView.vue [new file with mode: 0644]
ui/web/start.js
ui/web/tests/unit/CSTable.spec.ts
ui/web/vite.config.ts
ui/web/vitest.config.ts

index a9d87cf38fbbbd3101d27bc02910f1767b7898fc..d4ff8a2032e4de52a2d2999f9f99c36afce42c1c 100644 (file)
@@ -8,10 +8,10 @@ module.exports = defineConfig({
     node: true
   },
   parserOptions: {
-    ecmaVersion: 2022,
-    sourceType: 'module'
+    sourceType: 'module',
+    ecmaVersion: 2022
   },
-  plugins: ['import'],
+  plugins: ['simple-import-sort'],
   extends: ['eslint:recommended', 'plugin:import/recommended'],
   settings: {
     'import/resolver': {
@@ -21,36 +21,8 @@ module.exports = defineConfig({
     }
   },
   rules: {
-    'sort-imports': [
-      'error',
-      {
-        ignoreCase: false,
-        ignoreDeclarationSort: true,
-        ignoreMemberSort: false,
-        memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
-        allowSeparatedGroups: true
-      }
-    ],
-    'import/order': [
-      'error',
-      {
-        groups: [
-          'builtin', // Built-in imports (come from NodeJS native) go first
-          'external', // <- External imports
-          'internal', // <- Absolute imports
-          ['sibling', 'parent'], // <- Relative imports, the sibling and parent types they can be mingled together
-          'index', // <- Index imports
-          'unknown' // <- Unknown
-        ],
-        'newlines-between': 'always',
-        alphabetize: {
-          /* Sort in ascending order. Options: ["ignore", "asc", "desc"] */
-          order: 'asc',
-          /* Ignore case. Options: [true, false] */
-          caseInsensitive: true
-        }
-      }
-    ]
+    'simple-import-sort/imports': 'error',
+    'simple-import-sort/exports': 'error'
   },
   overrides: [
     {
@@ -64,7 +36,7 @@ module.exports = defineConfig({
         'plugin:@typescript-eslint/strict-type-checked',
         'plugin:@typescript-eslint/stylistic-type-checked',
         'plugin:import/typescript',
-        'standard-with-typescript'
+        'love'
       ],
       rules: {
         'operator-linebreak': 'off',
index dd821855bb0388711a2ed601138951d43daa8903..b0efef9b871e1adc37e7cd957fc77e77aff48b1a 100644 (file)
@@ -18,23 +18,40 @@ body:
       options:
         - label: I've searched for any related issues and avoided creating a duplicate issue.
           required: true
+  - type: dropdown
+    attributes:
+      label: Component
+      description: The component of the simulator that the bug is related to.
+      options:
+        - Simulator
+        - Web UI
+    validations:
+      required: true
   - type: textarea
     attributes:
       label: Description
       description: Description of the bug.
       placeholder: |
         - Please include error outputs in the 'Actual result' input field from 'pnpm start:dev', if any
+    validations:
+      required: true
   - type: input
     attributes:
-      label: e-mobility-charging-stations-simulator version
+      label: Version
+    validations:
+      required: true
   - type: input
     attributes:
       label: Node.js version
       description: Output of `node -v`.
+    validations:
+      required: true
   - type: textarea
     attributes:
       label: System
       description: Output of `npx --yes envinfo --system`.
+    validations:
+      required: true
   - type: textarea
     attributes:
       label: Expected result
index 09e61b0a6fae0aa40c1e2d5588ab78d5701a8339..0cee693fca2f14f700db8314dddec1f9470de383 100644 (file)
@@ -18,6 +18,15 @@ body:
       options:
         - label: I've searched for any related issues and avoided creating a duplicate issue.
           required: true
+  - type: dropdown
+    attributes:
+      label: Component
+      description: The component of the simulator that the feature is related to.
+      options:
+        - Simulator
+        - Web UI
+    validations:
+      required: true
   - type: textarea
     attributes:
       label: Description
@@ -25,6 +34,8 @@ body:
       placeholder: |
         - If your feature request is related to a problem, please describe it
         - If you have solutions in mind, please describe them
+    validations:
+      required: true
   - type: textarea
     attributes:
       label: Attachments
index ebbc8bdb015b04caffb10cb15e99e3a1b98b6cca..eca1f15217266f9bf9014357f125fd9209c2e469 100644 (file)
@@ -16,18 +16,6 @@ updates:
       interval: 'daily'
     labels:
       - 'dependencies'
-      - 'simulator'
-    reviewers:
-      - 'jerome-benoit'
-      - 'olivierbagot'
-    versioning-strategy: increase
-  - package-ecosystem: 'npm'
-    directory: '/ui/web'
-    schedule:
-      interval: 'daily'
-    labels:
-      - 'dependencies'
-      - 'webui'
     reviewers:
       - 'jerome-benoit'
       - 'olivierbagot'
index 2315d1829532b2aadd5027dff3b07597cd352569..c8f0f08948512c47aa7c4fcdfea8d092bac451da 100644 (file)
@@ -4,6 +4,7 @@ src/assets/config*.json
 src/scripts/scriptConfig.json
 src/assets/*idtags*.json
 !src/assets/idtags-template.json
+src/assets/configs-docker/*.json
 mikro-orm.config*.ts
 !mikro-orm.config-template.ts
 manifest*.yml
diff --git a/.husky/.gitignore b/.husky/.gitignore
deleted file mode 100644 (file)
index 31354ec..0000000
+++ /dev/null
@@ -1 +0,0 @@
-_
index c160a7712333eb282e933e1b5009ae81b9d4c677..0a4b97de53ae4f83db224a73981e6c0aaf581203 100755 (executable)
@@ -1,4 +1 @@
-#!/usr/bin/env sh
-. "$(dirname -- "$0")/_/husky.sh"
-
-npx --no -- commitlint --edit ${1}
+npx --no -- commitlint --edit $1
index cf0c46b936c647a48c38cfbf9d837b280fcbb754..041c660c92b36e939248b12a0fb99f203d210be2 100755 (executable)
@@ -1,4 +1 @@
-#!/usr/bin/env sh
-. "$(dirname -- "$0")/_/husky.sh"
-
 npx --no-install lint-staged
diff --git a/.npmrc b/.npmrc
index 22144e5675e2280bbc0cb39f84dab991399d619f..9b852ce8e2bf5cda6df176d411ccdd7cd15f5734 100644 (file)
--- a/.npmrc
+++ b/.npmrc
@@ -1,2 +1,3 @@
+ignore-workspace-root-check=true
 auto-install-peers=true
 legacy-peer-deps=true
index 5587d9c3cd79124edbe34f5586aa25dde1717f06..954c0b09ccb34c9529b81aa0c7ab66d4b447f6ab 100644 (file)
@@ -1,6 +1,219 @@
 # Changelog
 
-## [v1.2.37](https://github.com/sap/e-mobility-charging-stations-simulator/compare/v1.2.36...v1.2.37)
+## [v1.3.1](https://github.com/sap/e-mobility-charging-stations-simulator/compare/v1.3.0...v1.3.1)
+
+- build(deps-dev): bump @types/node from 20.11.29 to 20.11.30 [`#1016`](https://github.com/sap/e-mobility-charging-stations-simulator/pull/1016)
+- build(deps-dev): bump @types/node from 20.11.26 to 20.11.27 [`#1014`](https://github.com/sap/e-mobility-charging-stations-simulator/pull/1014)
+- build(deps-dev): apply updates [`657931d`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/657931d1618bc97852cfc27cef1d932a87cc1537)
+- build(deps-dev): apply updates [`ea32ea0`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ea32ea059bfdd7134abe2ba349985e5b15bc1d06)
+- build(deps-dev): apply updates [`cbeb1d7`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/cbeb1d7d6a3c17cbd5e69e159bdeb9e6dc837faa)
+- build(deps): apply updates [`647a34d`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/647a34debf383df8d7136d5d8f1a7735149f0182)
+- build(deps-dev): apply updates [`8ae4dcf`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/8ae4dcf159bf29835de7fd1edd7978aa8a3ee76b)
+- build(deps-dev): apply updates [`399583b`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/399583be5697acdad4151e3025a1011cd6f4461a)
+- refactor: convert home made helpers to rambda ones [`38ae4ce`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/38ae4ce2dba0f31e0f21c20490be0e7d376ce47c)
+- build(deps-dev): apply updates [`75e6075`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/75e6075c1ae340bca0ba41e9a836ae066ef9b6db)
+- fix: untangle worker set message from application message [`65d2250`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/65d2250205186eaab80febce407d1f7fdf830473)
+- build(deps): apply updates [`e689cef`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/e689cef921490106fcb092172fc93150eeedb78f)
+- feat: ensure charging station add op return its station info [`3b09e78`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/3b09e788c6dab8e5a8b3314e8e69ede16ff82fcc)
+- fix: only process worker message events when necessary [`ce0abd8`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ce0abd8248cbc1c976bb298e45daeb0749387619)
+- refactor: use more ramdba helpers [`c17a8d2`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/c17a8d29b2177a087f7fea1db4ada2b3795a7a31)
+- build(deps-dev): apply updates [`32ddfff`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/32ddfff1a1d314429239d98d6f5c56939aff17c4)
+- fix: fix simulator initialization ordering [`2bb3c92`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/2bb3c92f49572f8d81f40620df42de19217a6b4c)
+- refactor: use native node random integer generator [`fcda915`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/fcda9151b9eb6d13f06f7a5db45c89a77bda9eb8)
+- refactor: rename configuration elementStartDelay to elementAddDelay [`da47bc2`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/da47bc294fb5e439e96faaf9441ba0cd23e64f9c)
+- refactor: cleanup worker related type definitions [`551f2b6`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/551f2b6e68f544b559243a2634c8b52a15bcdeec)
+- build(deps-dev): apply updates [`aa711e3`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/aa711e399be6e394493fee4895b72eaf213df836)
+- refactor: improve configuration sanity checks log messages [`ae61fa2`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ae61fa2feb89927b7d56ffdc7a54f4dff6b662b4)
+- fix: ensure error at adding charging stations not stop further [`1091427`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/1091427efe95a8f1733282589dc1794a5be7e785)
+- fix: untangle worker pool/set init from start [`24dc52e`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/24dc52e9f4bd2ff4539955f169333ee3902bc95e)
+- build(deps-dev): apply updates [`c455d8a`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/c455d8a4a8c884defe864b7ef5c2b9b5a77cc354)
+- build(deps-dev): apply updates [`369e009`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/369e009abc7cdab7ffc2ed6f9e7939542893de4e)
+- build(deps-dev): apply updates [`05558d9`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/05558d929eb51343f080455aae1a4ec4fed62fa5)
+- build(deps-dev): apply updates [`21c9ace`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/21c9ace8422fd595c5052c8ac85b5e701299fa75)
+- refactor: cleanup app message handling code [`9e9194c`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/9e9194c977f2100a92a4549e6562750499607706)
+- build(deps): apply updates [`8b12e4c`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/8b12e4c338df18d16da0e59eb86512b7554e49e9)
+- refactor: cleanup logic at building station info [`bf2561e`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/bf2561ebbc6f51ce3ea68df68eacfe8a28d50bc6)
+- refactor: strong stype configuration key deprecation helper [`23c97c4`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/23c97c471a4b7194aaac0620e935341de07eb1ad)
+- refactor: cleanup worket set message handling [`1d7a504`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/1d7a504ba27c38e3f8617c80cf53aed85da580af)
+- refactor: move template firmware defaults to constants [`5dcb9d4`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/5dcb9d4945027c6be39e4342f1627ea2da3813b7)
+- refactor: remove unneeded isString() helper [`87bcd3b`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/87bcd3b41c269bdb32468c766dca8d307689e8c2)
+- refactor: use ramdba helper for builtin types [`8a4f882`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/8a4f882a2486682b72195ad978ec1d81604b452b)
+- fix: fix worker configuration merge issue [`56f9459`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/56f945900ccdbffb165bc42026fa20158c7032fe)
+- build(deps-dev): apply updates [`8d2a9e1`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/8d2a9e1c5a2b21932c77846bdf10cbe726797685)
+- refactor: spell fix in log messages [`81c7488`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/81c74884a3bc7441eb454ee90bda854d752e1908)
+- fix: skip worker message events processing [`a86eefa`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/a86eefabd6d0538803532e7f8ef6498215c54863)
+- refactor: cleanup utils export [`90dc299`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/90dc299a5f5c9990aa52911b2d8fdc401776972f)
+- fix: fix worker set elementsPerWorker sanity check [`195c627`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/195c627f62717d86ebc1c3dfd4a69f25277ceb70)
+- docs: refine README.md [`c193674`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/c1936747c6fb43fb39b67821e020b4a819ab4340)
+- fix: ensure add charging stations reponse display only the necessary [`2ea8735`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/2ea8735dffc06e2aa7442971134d2c0cd3100872)
+- docs: refine README.md [`5aeeae8`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/5aeeae8227c78b02e0a68a3b52a5f6114ff49f3a)
+- refactor: cleanup variables namespace [`9afcd55`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/9afcd55e48821caec56007dd8c2bc0381da3ed1f)
+- build: target ESNext in tsconfig.json [`cf86bef`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/cf86bef110e64ee5471f588dbb28ffea7dc01a35)
+- refactor: preincrement worker set counter [`2c60ea1`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/2c60ea1e18dbc9667e85fecf1c55462d6eb6e581)
+- refactor: cleanup worker set message handling code [`ccf1118`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ccf11189ed15c2e4706eac4b29dee2f972c68db7)
+- fix: ensure proper status in add charging stations op response [`43c49f2`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/43c49f23af9869b5a04da7c0e04b11b5243d9e34)
+- build: enfore tree shaking with esbuild [`c4d48ea`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/c4d48eaed98277b0e5ccd625048ba5e3fe94df66)
+
+## [v1.3.0](https://github.com/sap/e-mobility-charging-stations-simulator/compare/v1.2.38...v1.3.0) (2024-03-11)
+
+- fix: get diagnostics is trying to upload the incorrect folder [`#1006`](https://github.com/sap/e-mobility-charging-stations-simulator/issues/1006)
+- fix(simulator): ftp client incorrect access request when custom port present in the uri [`#1007`](https://github.com/sap/e-mobility-charging-stations-simulator/issues/1007)
+- build: use pnpm workspace [`749d723`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/749d72306546377b9a9a625b25658bcc55b61597)
+- refactor: cleanup eslint configuration [`4c3f6c2`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/4c3f6c20f9416e148a3d26b6a06acc13274ab469)
+- build(deps-dev): apply updates [`cc5703a`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/cc5703a7db561db2a6ae0f63519d42698d8bed2f)
+- build(deps-dev): apply updates [`3ca2b75`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/3ca2b752197d57b35aac592532ed232b0a0c5631)
+- build(deps-dev): apply updates [`6828b80`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/6828b80a67e83a031177d0dda6ec9488cb78bc28)
+- refactor: refine type definitions [`f4b3f35`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/f4b3f35d653138a946d74d5f3c313275ee9c03b2)
+- refactor: strong type UUID usage in UI protocol [`2c5c744`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/2c5c744359d7b1c65da4eb255db1d67da7b604f3)
+- feat!: handle Set at JSON serialization to string [`276e05a`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/276e05aec38f4e8b4a8d5ebd8cbbcb30592b414d)
+- build(deps-dev): apply updates [`b675e5f`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/b675e5fe85754bed9dcc7352c674639a9bc9046d)
+- refactor(ui): cleanup initial data fetching code [`97cd0ef`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/97cd0ef387b54392852368147eb51cea354bc804)
+- build(deps-dev): apply updates [`b87a504`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/b87a5041892c1df5fa8337130b4b81d499fb51b3)
+- refactor(ui): cleanup data fetching and display refreshing logic [`26cf7d9`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/26cf7d996edab284f7f616ab32702a6a979cad1f)
+- refactor(ui): cleanup eslint configuration [`84ec8d3`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/84ec8d3464903cfa8259ff622bff2b19b5c47132)
+- refactor: cleanup vue.js global properties usage [`7e31543`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/7e31543058b67b8ca9991b01820f88ab7f4f16b0)
+- build(deps): apply updates [`402912e`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/402912e9a1720b0cda8912b7b22119fdbec1e92f)
+- chore: version 1.3.0 [`9d70f58`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/9d70f587815b0ea85cb96bc72be514f866cee578)
+- build(deps-dev): apply updates [`b09d81a`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/b09d81ac96c649486b80b89915c3a3eb45325ef6)
+- refactor(ui): use watchers to refresh display [`f6cb176`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/f6cb17676bcd54d2aa28fe9f0eaa8797ac616873)
+- build(deps-dev): apply updates [`346a813`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/346a8133337b20f625b3f80f557970cb79d73415)
+- docs: add ToC to README.md [`99a3209`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/99a3209ecfae0c6de885d74a65dad9c47f0365e5)
+- build(deps-dev): apply updates [`ae430bb`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ae430bbbd3df0813d5237a205f92b2e928059f7f)
+- fix(ui): remove WS event listeners at main page unmount [`aa9b0a1`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/aa9b0a1ec352572d91076a4d0cfa78b53a46c759)
+- fix: fix template name consistency [`a33026f`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/a33026fe3cd6ce141efa55d27d35b4eb5c1a54b9)
+- refactor: move charging station helper to its right place [`ffb833e`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ffb833ec8daea8112feed14af15cdfdb67f9d41b)
+- refactor: removed commented out eslint configuration [`60a8349`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/60a8349bd3c77c271ca1207e7f93d203d303dfc3)
+- feat(ui): add 'Not found' catch all [`877b880`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/877b880ca5f6643f4aab913a3f92d027e30ccd28)
+- refactor(ui): refine types augmentation [`faf9c82`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/faf9c82d28d21aa0e0bf71c51c1f470c059fed7c)
+- refactor(ui): cleanup UI client instance getter [`39cd8fc`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/39cd8fcbac08d293817365362ee90260a07b189d)
+- build(deps-dev): apply updates [`985c876`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/985c876c77518c7c83a24c4018b1f50319273033)
+- refactor(ui): cleanup actions panel [`916f092`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/916f0920593cd041ba7be8a8ac28b62db0303d06)
+- docs: add ToC to ui/web/README.md [`a433785`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/a433785302e0f45eac5fab515cc6b725949f1e9e)
+- refactor(ui): validate UUID format [`2242771`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/22427713f4d879422dcdefa4c46d19d9af6f055d)
+- perf(ui): use computed ref when possible [`ec7366e`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ec7366ed6c79d62c203c8db30fb74504374427f3)
+- docs: document UI protocol simulator state RPC command [`a997fb6`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/a997fb633645882785e12ea3362d428233476e7c)
+- refactor: cleanup debug code [`28f384a`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/28f384aada699433ed099f6b5d630ee95f0a609e)
+- docs: refine README.md and ui/web/README.md [`aef0864`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/aef0864243a2fcc8a4e38871f45566156360f709)
+- refactor: add sanity checks at adding charging station(s) [`3b68e41`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/3b68e4162f8c9a67bc598c2ea4cab407611e0d31)
+- fix(ui): ensure the tool bar is sized at 100% width [`c630855`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/c63085548339556d66ced562e9649a84d0544ff4)
+- build(deps-dev): apply updates [`a7c5f3e`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/a7c5f3e1383fea87cf9fcd6ef4ec83cd8f41249c)
+- fix: ensure docker image configurations can be overriden from [`830edb9`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/830edb9829646c62d87eed61683df948ca6cf122)
+- refactor: cleanup eslint configuration [`b00a1ee`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/b00a1ee17c18ed50ef89cd708db06a1c6c266df7)
+- refactor(ui): refine error messages formatting [`08b5528`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/08b552892290e9558a41c3a58589baa9957ca917)
+- refactor(ui): always display the buttons bar [`558db46`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/558db46fe98fb6bdb10531c30b63658521fe0d5f)
+- build: fix linter error [`e2baeff`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/e2baeffc85dcc52c9b7c96a0f3de1a52e7ef5b89)
+- refactor(ui): refine action container styling [`ef2b2d0`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ef2b2d0395a57a5e94b2754262d91d831b11da52)
+- refactor(ui): refine simulator button style [`fec0313`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/fec03138c23258cf772f52e0f08f166781fa2bfe)
+- docs: refine README.md [`df893e6`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/df893e681229e94ec16b7902c79aed22b8694937)
+- refactor: cleanup some type casting [`61877a2`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/61877a2e1a2cf976a5e5f7f37828950d8aca9af5)
+- docs: refine README.md [`7b0ca52`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/7b0ca5238b6e88bfd624ca41d308a2401db5d82f)
+- docs: refine README.md [`ea20816`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ea208169f24516322ace901fbc75c7c3144992aa)
+- build(deps-dev): apply updates [`165e326`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/165e326ad178423ab95d552725c868fa01b16987)
+- fix(ui): always display the action container [`bfb2a85`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/bfb2a85ab6dad67c70eb082b89d958a546576ab9)
+- refactor: use 'join' to build template relative path [`8c7d214`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/8c7d214a2afc023ffdf34bbae8bfd4e16961bcc4)
+- refactor(ui): trivial code cleanup [`374c2a5`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/374c2a5b5bfff002bf2a687b3c0e357058672251)
+- docs: refine README.md and ui/web/README.md [`a122373`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/a122373629b1073b19a81c8fb376f1e7c4ffb8f6)
+- docs: refine README.md [`e35d61a`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/e35d61adf7ca6e7ed34527f53970552c802dbdae)
+- refactor(ui): cleanup type definitions [`e99a8ac`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/e99a8ac94ed48bc1dd74bca598b80eb648760e00)
+- fix(ui): fix global properties init [`68c6d50`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/68c6d508a8e4eb7e67c4f174c0f3d305829c2eaf)
+- refactor(ui): refine container border sizing [`7be35e5`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/7be35e502da734096424f2f8e6311f01fdab5b19)
+- fix: properly handle template relative file path within a directory [`d990f4b`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/d990f4bc8ad5366a200fd3d89be76c084fd71cea)
+- refactor(ui): refine toggle button styling [`754d819`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/754d819937907a0c8169ab39416e9ffbd2059142)
+- docs: refine README.md [`352ea56`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/352ea56deed119ba9172e1cbf85a89fadbd034d0)
+- docs: refine README.md [`738acbf`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/738acbf767e776699e06239a63cb35558018fcc0)
+- refactor(ui): resize buttons bar [`350f7bd`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/350f7bd5cff80ae49bb8e6145f2e59caa8e9842a)
+- refactor: refine error message [`2762ad6`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/2762ad626ae7c18006df4cfc86dcf85035c467b2)
+- fix(docker): fix Web UI path in start shell script [`148b999`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/148b9999c861bf027eca91259e4496ca077221ce)
+- fix(ui): handle missing version in simulator state [`1fa3425`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/1fa3425af7b41fd728168fae1cef29d603767609)
+- docs(ui): update Web UI screenshot [`53a8f2a`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/53a8f2aadb2fceace1a8884770680f7e70437f73)
+
+## [v1.2.38](https://github.com/sap/e-mobility-charging-stations-simulator/compare/v1.2.37...v1.2.38) (2024-03-02)
+
+- Combined PRs [`#1005`](https://github.com/sap/e-mobility-charging-stations-simulator/pull/1005)
+- build(deps-dev): bump @types/node from 20.11.19 to 20.11.20 [`#990`](https://github.com/sap/e-mobility-charging-stations-simulator/pull/990)
+- build(deps): bump poolifier from 3.1.20 to 3.1.21 [`#989`](https://github.com/sap/e-mobility-charging-stations-simulator/pull/989)
+- build(deps-dev): bump eslint-plugin-vue from 9.21.1 to 9.22.0 in /ui/web [`#988`](https://github.com/sap/e-mobility-charging-stations-simulator/pull/988)
+- build(deps-dev): bump @types/node from 20.11.19 to 20.11.20 in /ui/web [`#987`](https://github.com/sap/e-mobility-charging-stations-simulator/pull/987)
+- build(deps-dev): bump eslint-plugin-jsdoc from 48.1.0 to 48.2.0 [`#986`](https://github.com/sap/e-mobility-charging-stations-simulator/pull/986)
+- feat(ui): add support for multiple UI server configurations [`#978`](https://github.com/sap/e-mobility-charging-stations-simulator/issues/978)
+- build(deps-dev): apply updates [`ad1d31b`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ad1d31be86f7b4147955168a3ea887aecd4966be)
+- build(deps-dev): apply updates [`22af77b`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/22af77b3d6ec5982e03fa366f766539cf122e51d)
+- feat(ui): introduce toggle button and use it for actions [`2610da7`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/2610da71b813ad94e1a2a48755d5689da53b41fa)
+- feat: expose template stats to UI server simulatorState command [`e823764`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/e82376450bef7a755869accf8f662826147832d8)
+- fix(ui): ensure app is initialized independently of the WS status [`916fe45`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/916fe4560be0062eeb6979d88988584c286ccc50)
+- refactor(ui): put all main actions to the same bar [`239bd87`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/239bd875c259755721a45f2e677bc6cb73d88f7d)
+- feat(ui): use toggle button to star/stop simulator [`240fa4d`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/240fa4da80fd262158004eb576410d02afe0001f)
+- chore: version 1.2.38 [`f054629`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/f054629f83e26f345fe7d145005a1903907cc88a)
+- build(deps-dev): apply updates [`b123496`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/b123496005bfda83f9d5da158e5bc89b2727d10d)
+- refactor(ui): factor out app initialization [`3b0c6e1`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/3b0c6e17e092aaf540a4011d95236dd9ab4f7e7b)
+- build(deps-dev): apply updates [`9f1bc33`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/9f1bc33a893f30ebb6c006c4a8fa2cd3010e2602)
+- docs(ui): document multiple UI servers configuration [`4b08c55`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/4b08c55eeba9e1bad0e519c5dfcea9f73933b2a1)
+- refactor(ui): cleanup CSS styling [`3802683`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/3802683b5018295e8ebf6199939e43ac65db0763)
+- fix(ui): ensure the charging stations list re-rendered after UI server [`7e2e8c9`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/7e2e8c9b820004dc6a84d6a5629575460daf8167)
+- fix(ui): refresh add charging stations button on UI server switch [`d64ea57`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/d64ea57bcb4fd9151a43b16ff4d4b5ed059ad1d7)
+- docs: refine GitHub issue templates [`7ed430f`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/7ed430ff71a022333b8f318c75de07eb47203515)
+- build(deps-dev): bump @types/node from 20.11.22 to 20.11.24 [`49cb6d2`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/49cb6d204b3f99db356709e988cbf369024403f1)
+- build(deps-dev): bump @types/node from 20.11.22 to 20.11.24 in /ui/web [`96442cf`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/96442cf4f959be873ea0a3b4bd18f4134dc5fc6f)
+- fix(ui): rerender shared toggle buttons properly [`8346876`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/834687644463a925b90dafe0d5c60c249cea963e)
+- refactor(ui): trivial code cleanups [`3eea3eb`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/3eea3ebcd6172a2928aba166e26d5f06cd4dbe42)
+- refactor(ui): strong type pages ref [`cf95967`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/cf959670a5fe40c5279d6e08989f5b31ee06a64d)
+- build(deps-dev): apply updates [`2cd5640`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/2cd5640f2aa372aaff46638d7eac515825a627cf)
+- refactor(ui): cleanup props usage [`4b10e4f`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/4b10e4f94fe91fab12f8ba298137c49fc295774c)
+- docs: refine GitHub issue templates [`f5dabe7`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/f5dabe760ddd11646be22df45825dd29f8646b1c)
+- refactor(ui): cleanup helper namespace [`b767fda`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/b767fda721ad867b55ed76067721483767805930)
+- refactor(ui): cleanup CSS styling [`6027002`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/6027002ff1398a1cb3bb55d4994e4615efded2c0)
+- fix(ui): do not clear toggle button states on reload [`421fcdc`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/421fcdc9c9e7e9864147223a079d76c8b34810fc)
+- refactor(ui): refine CS table width sizing [`97ea01a`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/97ea01ab1ab0078f6b9c0b7c7c9fdfa2592abb3a)
+- feat(ui): allow to specificy a UI server configuration name [`258666f`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/258666f9962451dda19d0db528bbf07e41838ff2)
+- fix(ui): fix connector(s) column flex direction [`25dbae9`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/25dbae90206427d1069ac941ec38bee647894aa4)
+- refactor(ui): refine UI server selector styling [`bad93db`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/bad93db30828b729fa9611d849c76c3ea1965e3d)
+- docs: refine GitHub issue templates [`8482210`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/84822103592a269211a672a122d8ec0568088736)
+- fix(ui): ensure opened actions are closed at UI server switch [`a4edfbb`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/a4edfbb3a5e5c7fb55ef6b4cd4e3394defb09e03)
+- fix(ui): close add charging stations action at UI server switch [`de3e2a4`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/de3e2a49ef1c8b39ab1992c7e217aec48c67aa90)
+- fix(ui): ensure templates are refreshed at UI server change [`18c7fec`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/18c7fecb7a062a74cf13052270f8b1379f9fb2f6)
+- docs: update Web UI screenshot [`d8032ef`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/d8032ef9b693c7262ea0ca1f173b06cefb14e8da)
+- Merge dependabot/npm_and_yarn/types/node-20.11.24 into combined-prs-branch [`7338d15`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/7338d15af937a3add527a6d8389305771b054f79)
+- Merge dependabot/npm_and_yarn/ui/web/types/node-20.11.24 into combined-prs-branch [`ef86d39`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ef86d396cd15446b6c835fc01a6ddf3c7e971e6f)
+- build(deps-dev): apply updates [`f4bc686`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/f4bc686323fc1d9db694ee7d0737d54472c02a79)
+- build(deps-dev): apply updates [`ac54162`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ac54162159023a9915357447fba8f85bcdc39f77)
+- build(deps-dev): apply updates [`b042caf`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/b042cafa5aa720ca76b1cc3233a10c91ac19bff5)
+- build(deps): apply updates [`2e4f3c0`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/2e4f3c0952c61aad9157a8107e9909bb2a243551)
+- build(deps): apply updates [`ec338d7`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/ec338d760c076b231f13313a2559c853d7fc8593)
+- build(deps-dev): apply updates [`f0abd3b`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/f0abd3b5f1c7ccf4f3fa18988b2114bc1f79d3d6)
+- fix(ui): ensure UI server can only be started once [`a1cfaa1`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/a1cfaa16c9e91765bee3c54cd0208ad01c154aa4)
+- build(deps-dev): apply updates [`615d6cf`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/615d6cfe1466c30ed410acc66f789158eac51268)
+- feat(ui): add charging station options to add action [`093ca83`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/093ca832a5c03ea03f32368b4bf13c1d08ef3a54)
+- build(deps-dev): apply updates [`76dfd84`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/76dfd8402d29857721be9c632315070e2245c6b5)
+- feat(ui): support more charging station options at add op [`3d9f374`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/3d9f374123a8ee5bae6693ed81417e62000bbfee)
+- build(ui): add missing file [`4940561`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/4940561f080511ee4178df364222765d65289ffe)
+- fix: fix supervision url handling at charging stations adding [`2293fad`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/2293fadc545b3f531f3c94c2f9f622eeab331bd7)
+- fix(ui): fix initilisation logic [`5369437`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/5369437218a4bda8ac1adc1c2dcb33b1729f8f63)
+- fix: clear UI server cache at simulator stop [`5c0e935`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/5c0e9352ee4e933387ae3a20ac814bda7e43b72a)
+- fix(ui): ensure consistent styling on all buttons [`14ee627`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/14ee627af194a4d3aac36cb286bc7999e9481860)
+- build(deps-dev): apply updates [`9ed96ef`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/9ed96ef8d5ff598889cc9abf3d4e048b9f4c62d5)
+- refactor: improve error reporting in the UI server code [`776cdee`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/776cdee38610a544b49add6a5fa96010faff5c4f)
+- refactor(ui): revert wrongly introduced code to handle multiples UI [`c25a404`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/c25a4046983ef80409cbc4546f944231e8655604)
+- fix(ui): re-render charging stations table without reloading [`a4868fd`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/a4868fd7e236faf73e62efeeadad20119e2d96eb)
+- refactor(ui): improve the charging stations list styles [`6dba76a`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/6dba76a51714e00deefea659141f7119d7f5263f)
+- refactor(ui): refine action bar style [`229d8c3`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/229d8c3413d0b1908bfd2ea3239f1887bdb862a2)
+- refactor(ui): cleanup variables namespace [`8654502`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/86545028054a9b13982500366c51e7ea2503a4eb)
+- feat(ui): add helper to dynamically set configuration on UI client [`a4baab6`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/a4baab6300ff8bd111e0915f860b1cb05bb594e9)
+- fix(ui): handle vue.js app initialization error [`bcb671d`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/bcb671db37d3f4f60d5ac300df8b686404e787ff)
+- fix: ensure charging stations array global property is initialized [`30fc5f0`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/30fc5f08f040907e439a8fbedf66aa17c2100811)
+- fix(ui): ensure action bar is displayed [`7378b34`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/7378b34a676b65d8a4732e672c25c73096d7e59c)
+- fix(ui): move v-show to the UI server container component [`4752138`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/47521384ba9d093d453f8a71b847afcac37d68e4)
+- fix(ui): add missing style in SPA [`427a497`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/427a497022f0a5ff9454c618b6d3620521625923)
+- build: bump volta pnpm version [`e1fcdc4`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/e1fcdc4ef7705bce5c5639550e9ca4cd53035bf2)
+- docs(ui): update Web UI screenshot [`f8947d3`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/f8947d3078521a713834a84bdfdfa16d8b6caebf)
+- refactor: cleanup integer conversion helper [`f5ee140`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/f5ee14038709c759f86a9f98f90719c9f7f750be)
+- refactor: refine .cfignore [`0b18282`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/0b1828224edf798044ef54672ddfc69598cd99a5)
+- refactor(ui): cleanup toast messages [`182f846`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/182f84679517c8be5ebfa7b84a89fff6e355a415)
+- docs(ui): refine ui/web/README.md wording [`7176985`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/7176985f002d3a50d14a52504d756f16ab60ea19)
+- docs: refine README.md [`6eb6361`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/6eb6361504e4775e0ef162e3cbaf5fe37ec2bbb5)
+- fix: reset simulator state on stop/start cycle [`9d289c6`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/9d289c632492e76934967d23a8305bf28a352a83)
+- refactor(ui): refine vue.js app init error message [`88d3e6e`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/88d3e6ed5f1c8fad4c32311829e51d1fdcee2dcb)
+
+## [v1.2.37](https://github.com/sap/e-mobility-charging-stations-simulator/compare/v1.2.36...v1.2.37) (2024-02-19)
 
 - feat(ui): enhance charging stations list structure [`#977`](https://github.com/sap/e-mobility-charging-stations-simulator/issues/977)
 - feat: add a basic authentication scheme for UI WebSocket server [`#980`](https://github.com/sap/e-mobility-charging-stations-simulator/issues/980)
 - feat(ui): add set supervision url action [`f869617`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/f8696170fe4343a6fef450a6f0d4a47daccbcee0)
 - refactor(ui): remove uneeded encapsulation [`f1df317`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/f1df3177edd9a3bc78844505a4780a72ab24139a)
 - build: bump volta node version [`fb12687`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/fb12687ba6cd126c5ef5cad38549992636f18fe6)
+- chore: version 1.2.37 [`4395666`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/4395666c70cfbe603d1c98e6d2c44d11748e00b3)
 - feat(ui): add charging stations action support [`878855a`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/878855a218443d8d0a610f1e79d8d4afaf07153a)
 - fix(ui): fix configuration handling on the docker image [`0d7042e`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/0d7042e20aa7c9a4019c433b8c05b770b861d2a2)
 - refactor(ui): cleanup vue.js app initialization code and logic [`a64b9a6`](https://github.com/sap/e-mobility-charging-stations-simulator/commit/a64b9a64f0cb3b2b9b07ec38ec4001dcc7024a60)
index 750ad8e2968c5017a1101f864706cf357441ff4b..063c839d6d6ac599ec9bd83fa1930a41e99924dc 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,6 +1,11 @@
 <!-- markdownlint-disable-file MD033 MD024 -->
+<div align="center">
 
-# [e-mobility charging stations simulator](https://github.com/sap/e-mobility-charging-stations-simulator)
+# [e-Mobility charging stations simulator](https://github.com/sap/e-mobility-charging-stations-simulator)
+
+</div>
+
+<div align="center">
 
 [![GitHub Clones](https://img.shields.io/badge/dynamic/json?color=brightgreen&label=Clone&query=count&url=https://gist.githubusercontent.com/jerome-benoit/c7c669b881d5b27dc0b44a639504ff93/raw/clone.json&logo=github)](https://github.com/SAP/e-mobility-charging-stations-simulator/graphs/traffic)
 [![GitHub commit activity (main)](https://img.shields.io/github/commit-activity/m/SAP/e-mobility-charging-stations-simulator/main?color=brightgreen&logo=github)](https://github.com/SAP/e-mobility-charging-stations-simulator/graphs/commit-activity)
 [![REUSE status](https://api.reuse.software/badge/github.com/SAP/e-mobility-charging-stations-simulator)](https://api.reuse.software/info/github.com/SAP/e-mobility-charging-stations-simulator)
 [![Javascript Standard Style Guide](<https://badgen.net/static/code style/standard/green>)](https://standardjs.com)
 
-## Summary
+</div>
 
 Simple [node.js](https://nodejs.org/) software to simulate and scale a set of charging stations based on the OCPP-J protocol as part of [SAP e-Mobility](https://www.sap.com/products/scm/e-mobility.html) solution.
 
-## Prerequisites
+## Table of contents
+
+- [Installation](#installation)
+  - [Prerequisites](#prerequisites)
+    - [Windows](#windows)
+    - [MacOSX](#macosx)
+    - [GNU/Linux](#gnulinux)
+  - [Dependencies](#dependencies)
+- [Initial configuration](#initial-configuration)
+- [Start simulator](#start-simulator)
+- [Start Web UI](#start-web-ui)
+- [Configuration files syntax](#configuration-files-syntax)
+  - [Charging stations simulator configuration](#charging-stations-simulator-configuration)
+  - [Charging station configuration template](#charging-station-configuration-template)
+  - [Charging station configuration](#charging-station-configuration)
+- [Docker](#docker)
+- [OCPP-J commands supported](#ocpp-j-commands-supported)
+  - [Version 1.6](#version-16)
+  - [Version 2.x.x](#version-2xx)
+- [OCPP-J standard parameters supported](#ocpp-j-standard-parameters-supported)
+  - [Version 1.6](#version-16-1)
+  - [Version 2.x.x](#version-2xx-1)
+- [UI Protocol](#ui-protocol)
+  - [Websocket Protocol](#websocket-protocol)
+  - [HTTP Protocol](#http-protocol)
+- [Support, Feedback, Contributing](#support-feedback-contributing)
+- [Code of Conduct](#code-of-conduct)
+- [Licensing](#licensing)
+
+## Installation
+
+### Prerequisites
 
 Install the [node.js](https://nodejs.org/) current LTS or superior version runtime environment:
 
-### Windows
+#### Windows
 
 - [Chocolatey](https://chocolatey.org/):
 
@@ -24,7 +60,7 @@ Install the [node.js](https://nodejs.org/) current LTS or superior version runti
 choco install -y nodejs
 ```
 
-### MacOSX
+#### MacOSX
 
 - [Homebrew](https://brew.sh/):
 
@@ -32,11 +68,11 @@ choco install -y nodejs
 brew install node
 ```
 
-### GNU/Linux
+#### GNU/Linux
 
 - [NodeSource](https://github.com/nodesource/distributions) Node.js Binary Distributions for all supported versions.
 
-## Installation
+#### Dependencies
 
 Enable corepack if not already done and install latest pnpm version:
 
@@ -56,11 +92,13 @@ pnpm install
 Copy the configuration template file [src/assets/config-template.json](./src/assets/config-template.json) to [src/assets/config.json](./src/assets/config.json).  
 Copy the RFID tags template file [src/assets/idtags-template.json](./src/assets/idtags-template.json) to [src/assets/idtags.json](./src/assets/idtags.json).
 
-Tweak them to your needs by following the section [configuration files syntax](./README.md#configuration-files-syntax): OCPP server supervision URL(s), charging station templates, etc.
+Tweak them to your needs by following the section [configuration files syntax](#configuration-files-syntax): OCPP server supervision URL(s), charging station templates, etc.
 
-## Start
+## Start simulator
 
-To start the program, run: `pnpm start`.
+```shell
+pnpm start
+```
 
 ## Start Web UI
 
@@ -68,7 +106,7 @@ See Web UI [README.md](./ui/web/README.md) for more information.
 
 ## Configuration files syntax
 
-All configuration files are in the JSON standard format.
+All configuration files are using the JSON standard syntax.
 
 **Configuration files locations**:
 
@@ -101,15 +139,15 @@ But the modifications to test have to be done to the files in the build target d
 
 **src/assets/config.json**:
 
-| Key                        | Value(s)                                     | Default Value                                                                                                                                                                                                                 | Value type                                                                                                                                                                                                                                                                      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
-| -------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| supervisionUrls            |                                              | []                                                                                                                                                                                                                            | string \| string[]                                                                                                                                                                                                                                                              | string or strings array containing global connection URIs to OCPP-J servers                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
-| supervisionUrlDistribution | round-robin/random/charging-station-affinity | charging-station-affinity                                                                                                                                                                                                     | string                                                                                                                                                                                                                                                                          | supervision urls distribution policy to simulated charging stations                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
-| log                        |                                              | {<br />"enabled": true,<br />"file": "logs/combined.log",<br />"errorFile": "logs/error.log",<br />"statisticsInterval": 60,<br />"level": "info",<br />"console": false,<br />"format": "simple",<br />"rotate": true<br />} | {<br />enabled?: boolean;<br />file?: string;<br />errorFile?: string;<br />statisticsInterval?: number;<br />level?: string;<br />console?: boolean;<br />format?: string;<br />rotate?: boolean;<br />maxFiles?: string \| number;<br />maxSize?: string \| number;<br />}    | Log configuration section:<br />- _enabled_: enable logging<br />- _file_: log file relative path<br />- _errorFile_: error log file relative path<br />- _statisticsInterval_: seconds between charging stations statistics output in the logs<br />- _level_: emerg/alert/crit/error/warning/notice/info/debug [winston](https://github.com/winstonjs/winston) logging level</br >- _console_: output logs on the console<br />- _format_: [winston](https://github.com/winstonjs/winston) log format<br />- _rotate_: enable daily log files rotation<br />- _maxFiles_: maximum number of log files: https://github.com/winstonjs/winston-daily-rotate-file#options<br />- _maxSize_: maximum size of log files in bytes, or units of kb, mb, and gb: https://github.com/winstonjs/winston-daily-rotate-file#options                                                                                         |
-| worker                     |                                              | {<br />"processType": "workerSet",<br />"startDelay": 500,<br />"elementStartDelay": 0,<br />"elementsPerWorker": 'auto',<br />"poolMinSize": 4,<br />"poolMaxSize": 16<br />}                                                | {<br />processType?: WorkerProcessType;<br />startDelay?: number;<br />elementStartDelay?: number;<br />elementsPerWorker?: number \| 'auto' \| 'all';<br />poolMinSize?: number;<br />poolMaxSize?: number;<br />resourceLimits?: ResourceLimits;<br />}                       | Worker configuration section:<br />- _processType_: worker threads process type (`workerSet`/`fixedPool`/`dynamicPool`)<br />- _startDelay_: milliseconds to wait at worker threads startup (only for `workerSet` worker threads process type)<br />- _elementStartDelay_: milliseconds to wait at charging station startup<br />- _elementsPerWorker_: number of charging stations per worker threads for the `workerSet` process type (`auto` means (number of stations) / (number of CPUs) \* 1.5 if (number of stations) > (number of CPUs), otherwise 1; `all` means a unique worker will run all charging stations)<br />- _poolMinSize_: worker threads pool minimum number of threads</br >- _poolMaxSize_: worker threads pool maximum number of threads<br />- _resourceLimits_: worker threads [resource limits](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) object option |
-| uiServer                   |                                              | {<br />"enabled": false,<br />"type": "ws",<br />"version": "1.1",<br />"options": {<br />"host": "localhost",<br />"port": 8080<br />}<br />}                                                                                | {<br />enabled?: boolean;<br />type?: ApplicationProtocol;<br />version?: ApplicationProtocolVersion;<br />options?: ServerOptions;<br />authentication?: {<br />enabled: boolean;<br />type: AuthenticationType;<br />username?: string;<br />password?: string;<br />}<br />} | UI server configuration section:<br />- _enabled_: enable UI server<br />- _type_: 'http' or 'ws'<br />- _version_: HTTP version '1.1' or '2.0'<br />- _options_: node.js net module [listen options](https://nodejs.org/api/net.html#serverlistenoptions-callback)<br />- _authentication_: authentication type configuration section                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
-| performanceStorage         |                                              | {<br />"enabled": true,<br />"type": "none",<br />}                                                                                                                                                                           | {<br />enabled?: boolean;<br />type?: string;<br />uri?: string;<br />}                                                                                                                                                                                                         | Performance storage configuration section:<br />- _enabled_: enable performance storage<br />- _type_: 'jsonfile', 'mongodb' or 'none'<br />- _uri_: storage URI                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
-| stationTemplateUrls        |                                              | {}[]                                                                                                                                                                                                                          | {<br />file: string;<br />numberOfStations: number;<br />}[]                                                                                                                                                                                                                    | array of charging station configuration templates URIs configuration section (charging station configuration template file name and number of stations)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
+| Key                        | Value(s)                                     | Default Value                                                                                                                                                                                                                 | Value type                                                                                                                                                                                                                                                                      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
+| -------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| supervisionUrls            |                                              | []                                                                                                                                                                                                                            | string \| string[]                                                                                                                                                                                                                                                              | string or strings array containing global connection URIs to OCPP-J servers                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
+| supervisionUrlDistribution | round-robin/random/charging-station-affinity | charging-station-affinity                                                                                                                                                                                                     | string                                                                                                                                                                                                                                                                          | supervision urls distribution policy to simulated charging stations                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
+| log                        |                                              | {<br />"enabled": true,<br />"file": "logs/combined.log",<br />"errorFile": "logs/error.log",<br />"statisticsInterval": 60,<br />"level": "info",<br />"console": false,<br />"format": "simple",<br />"rotate": true<br />} | {<br />enabled?: boolean;<br />file?: string;<br />errorFile?: string;<br />statisticsInterval?: number;<br />level?: string;<br />console?: boolean;<br />format?: string;<br />rotate?: boolean;<br />maxFiles?: string \| number;<br />maxSize?: string \| number;<br />}    | Log configuration section:<br />- _enabled_: enable logging<br />- _file_: log file relative path<br />- _errorFile_: error log file relative path<br />- _statisticsInterval_: seconds between charging stations statistics output in the logs<br />- _level_: emerg/alert/crit/error/warning/notice/info/debug [winston](https://github.com/winstonjs/winston) logging level</br >- _console_: output logs on the console<br />- _format_: [winston](https://github.com/winstonjs/winston) log format<br />- _rotate_: enable daily log files rotation<br />- _maxFiles_: maximum number of log files: https://github.com/winstonjs/winston-daily-rotate-file#options<br />- _maxSize_: maximum size of log files in bytes, or units of kb, mb, and gb: https://github.com/winstonjs/winston-daily-rotate-file#options                                                                                        |
+| worker                     |                                              | {<br />"processType": "workerSet",<br />"startDelay": 500,<br />"elementAddDelay": 0,<br />"elementsPerWorker": 'auto',<br />"poolMinSize": 4,<br />"poolMaxSize": 16<br />}                                                  | {<br />processType?: WorkerProcessType;<br />startDelay?: number;<br />elementAddDelay?: number;<br />elementsPerWorker?: number \| 'auto' \| 'all';<br />poolMinSize?: number;<br />poolMaxSize?: number;<br />resourceLimits?: ResourceLimits;<br />}                         | Worker configuration section:<br />- _processType_: worker threads process type (`workerSet`/`fixedPool`/`dynamicPool`)<br />- _startDelay_: milliseconds to wait at worker threads startup (only for `workerSet` worker threads process type)<br />- _elementAddDelay_: milliseconds to wait between charging station add<br />- _elementsPerWorker_: number of charging stations per worker threads for the `workerSet` process type (`auto` means (number of stations) / (number of CPUs) \* 1.5 if (number of stations) > (number of CPUs), otherwise 1; `all` means a unique worker will run all charging stations)<br />- _poolMinSize_: worker threads pool minimum number of threads</br >- _poolMaxSize_: worker threads pool maximum number of threads<br />- _resourceLimits_: worker threads [resource limits](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) object option |
+| uiServer                   |                                              | {<br />"enabled": false,<br />"type": "ws",<br />"version": "1.1",<br />"options": {<br />"host": "localhost",<br />"port": 8080<br />}<br />}                                                                                | {<br />enabled?: boolean;<br />type?: ApplicationProtocol;<br />version?: ApplicationProtocolVersion;<br />options?: ServerOptions;<br />authentication?: {<br />enabled: boolean;<br />type: AuthenticationType;<br />username?: string;<br />password?: string;<br />}<br />} | UI server configuration section:<br />- _enabled_: enable UI server<br />- _type_: 'http' or 'ws'<br />- _version_: HTTP version '1.1' or '2.0'<br />- _options_: node.js net module [listen options](https://nodejs.org/api/net.html#serverlistenoptions-callback)<br />- _authentication_: authentication type configuration section                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
+| performanceStorage         |                                              | {<br />"enabled": true,<br />"type": "none",<br />}                                                                                                                                                                           | {<br />enabled?: boolean;<br />type?: string;<br />uri?: string;<br />}                                                                                                                                                                                                         | Performance storage configuration section:<br />- _enabled_: enable performance storage<br />- _type_: 'jsonfile', 'mongodb' or 'none'<br />- _uri_: storage URI                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
+| stationTemplateUrls        |                                              | {}[]                                                                                                                                                                                                                          | {<br />file: string;<br />numberOfStations: number;<br />provisionedNumberOfStations?: number;<br />}[]                                                                                                                                                                         | array of charging station templates URIs configuration section:<br />- _file_: charging station configuration template file relative path<br />- _numberOfStations_: template number of stations at startup<br />- _provisionedNumberOfStations_: template provisioned number of stations after startup                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
 
 #### Worker process model
 
@@ -497,7 +535,7 @@ All kind of OCPP parameters are supported in charging station configuration or c
 
 ### Version 2.x.x
 
-## UI protocol
+## UI Protocol
 
 Protocol to control the simulator via a Websocket or HTTP server:
 
@@ -505,7 +543,7 @@ Protocol to control the simulator via a Websocket or HTTP server:
 sequenceDiagram
 Client->>UI Server: request
 UI Server->>Client: response
-Note over UI Server,Client: Transport protocol: HTTP, Websocket
+Note over UI Server,Client: HTTP or Websocket
 ```
 
 ### Websocket Protocol
@@ -531,6 +569,23 @@ Set the Websocket header _Sec-Websocket-Protocol_ to `ui0.0.1`.
 
 ##### Procedures
 
+###### Simulator State
+
+- Request:  
+  `ProcedureName`: 'simulatorState'  
+  `PDU`: {}
+
+- Response:  
+  `PDU`: {  
+   `status`: 'success' | 'failure',  
+   `state`: {  
+   `version`: string,  
+   `configuration`: ConfigurationData,  
+   `started`: boolean,  
+   `templateStatistics`: Record<string, TemplateStatistics>  
+   }  
+  }
+
 ###### Start Simulator
 
 - Request:  
@@ -585,7 +640,9 @@ Set the Websocket header _Sec-Websocket-Protocol_ to `ui0.0.1`.
 
 - Response:  
   `PDU`: {  
-   `status`: 'success' | 'failure'  
+   `status`: 'success' | 'failure',  
+   `hashIdsSucceeded`: charging station unique identifier strings array (optional),  
+   `hashIdsFailed`: charging station unique identifier strings array (optional)  
   }
 
 ###### Delete Charging Stations
@@ -630,7 +687,7 @@ Set the Websocket header _Sec-Websocket-Protocol_ to `ui0.0.1`.
 
 - Response:  
   `PDU`: {  
-   `status`: 'success' | 'failure'  
+   `status`: 'success' | 'failure',  
    `performanceStatistics`: Statistics[]  
   }
 
@@ -822,7 +879,7 @@ Examples:
   - Request:  
     `ProcedureName`: 'heartbeat'  
     `PDU`: {  
-     `hashIds`: charging station unique identifier strings array (optional, default: all charging stations),  
+     `hashIds`: charging station unique identifier strings array (optional, default: all charging stations)  
     }
 
   - Response:  
index 912d16d95ae41f32ae40a562206c4950bb43d697..4443f985160205ae4e0356f8d133f221712a3e6f 100644 (file)
--- a/bundle.js
+++ b/bundle.js
@@ -22,6 +22,7 @@ await build({
     'basic-ftp',
     'chalk',
     'date-fns',
+    'date-fns/*',
     'http-status-codes',
     'logform',
     'mnemonist',
@@ -35,6 +36,7 @@ await build({
     'winston-daily-rotate-file',
     'ws'
   ],
+  treeShaking: true,
   minify: true,
   sourcemap,
   entryNames: '[name]',
@@ -47,7 +49,8 @@ await build({
         './dist/assets/*.json',
         './dist/assets/json-schemas',
         './dist/assets/station-templates',
-        './dist/assets/ui-protocol'
+        './dist/assets/ui-protocol',
+        './dist/assets/configs-docker'
       ]
     }),
     copy({
@@ -67,6 +70,10 @@ await build({
         {
           from: ['./src/assets/station-templates/**/*.json'],
           to: ['./assets/station-templates']
+        },
+        {
+          from: ['./src/assets/configs-docker/*.json'],
+          to: ['./assets/configs-docker']
         }
       ]
     })
index 0e13a0dd2963d36af1e7fb3caf3a26c0975583c2..baef3220648d1643421afee14539079a8b2aa7e2 100644 (file)
@@ -1,11 +1,8 @@
 FROM node:lts-alpine AS builder
 
-# Build simulator
+# Build simulator and dashboard
 WORKDIR /usr/builder
-COPY package.json pnpm-lock.yaml tsconfig.json bundle.js build-requirements.js skip-preinstall.js prepare.js ./
-COPY src ./src
-COPY docker/config.json ./src/assets/config.json
-COPY docker/idtags.json ./src/assets/idtags.json
+COPY . ./
 RUN set -ex \
   && apk add --no-cache --virtual .gyp build-base python3 \
   && corepack enable \
@@ -13,18 +10,11 @@ RUN set -ex \
   && pnpm set progress=false \
   && pnpm config set depth 0 \
   && pnpm install --ignore-scripts --frozen-lockfile \
+  && cp docker/config.json src/assets/config.json \
+  && cp docker/idtags.json src/assets/idtags.json \
   && pnpm build \
-  && apk del .gyp
-
-# Build simulator dashboard
-WORKDIR /usr/builder/webui
-COPY ui/web ./
-RUN set -ex \
-  && corepack enable \
-  && corepack prepare pnpm@latest --activate \
-  && pnpm set progress=false \
-  && pnpm config set depth 0 \
-  && pnpm install --ignore-scripts --frozen-lockfile \
+  && apk del .gyp \
+  && cd ui/web \
   && cp src/assets/config-template.json public/config.json \
   && pnpm build
 
@@ -36,7 +26,7 @@ ARG MAX_OLD_SPACE_SIZE=768
 ENV NODE_OPTIONS="--stack-trace-limit=${STACK_TRACE_LIMIT} --max-old-space-size=${MAX_OLD_SPACE_SIZE}"
 
 WORKDIR /usr/app
-COPY --from=builder /usr/builder/webui ./webui
+COPY --from=builder /usr/builder/ui/web ./ui/web
 COPY --from=builder /usr/builder/node_modules ./node_modules
 COPY --from=builder /usr/builder/dist ./dist
 COPY package.json README.md LICENSE ./
index 118159054402d2ed59987b162694723873ec0fae..24973efcee5b31cfec90f402cd5e9dc3a2089756 100755 (executable)
@@ -7,8 +7,9 @@ then
   [ -z $emobility_server_type ] && { echo "emobility env server type variable not found, exiting"; exit 1; }
   [ -z $emobility_service_type ] && { echo "emobility env service type variable not found, exiting"; exit 1; }
 
-  cp $emobility_install_dir/dist/assets/configs-aws/$emobility_server_type-$emobility_service_type-$emobility_landscape.json $emobility_install_dir/dist/assets/config.json
-  cp $emobility_install_dir/dist/assets/configs-aws/$emobility_server_type-$emobility_service_type-$emobility_landscape-idtags.json $emobility_install_dir/dist/assets/idtags.json
+  cp $emobility_install_dir/dist/assets/configs-docker/$emobility_server_type-$emobility_service_type-$emobility_landscape.json $emobility_install_dir/dist/assets/config.json
+  cp $emobility_install_dir/dist/assets/configs-docker/$emobility_server_type-$emobility_service_type-$emobility_landscape-idtags.json $emobility_install_dir/dist/assets/idtags.json
+  cp $emobility_install_dir/dist/assets/configs-docker/$emobility_server_type-$emobility_service_type-$emobility_landscape-webui.json $emobility_install_dir/ui/web/public/config.json
 else
   echo "no emobility env defined, start with default configuration"
 fi
index bcfe3b6407109a825d986eba9bfe804a3f61272e..65a466c9482247edb99c1bc595dc1ce4b4819770 100755 (executable)
@@ -1,4 +1,4 @@
 #!/usr/bin/env sh
 
 node dist/start.js &
-node webui/start.js
+node ui/web/start.js
index d357406883c8123368833b43147e52cc10668706..994aaae47a7e4c85e74a81ee726c978305738552 100644 (file)
@@ -1,15 +1,16 @@
 {
   "$schema": "https://json.schemastore.org/package",
   "name": "e-mobility-charging-stations-simulator",
-  "version": "1.2.37",
+  "version": "1.3.1",
   "engines": {
     "node": ">=18.18.0",
     "pnpm": ">=8.6.0"
   },
   "volta": {
-    "node": "20.11.1",
-    "pnpm": "8.15.4"
+    "node": "20.12.1",
+    "pnpm": "8.15.6"
   },
+  "packageManager": "pnpm@8.15.6",
   "repository": {
     "type": "git",
     "url": "https://github.com/sap/e-mobility-charging-stations-simulator.git"
@@ -51,7 +52,6 @@
     "useTsNode": true
   },
   "scripts": {
-    "preinstall": "node skip-preinstall.js || npx --yes only-allow pnpm",
     "prepare": "node prepare.js",
     "build-requirements": "node --no-warnings build-requirements.js",
     "start": "pnpm build && cross-env NODE_ENV=production node dist/start.js",
     }
   },
   "dependencies": {
-    "@mikro-orm/core": "^6.1.6",
-    "@mikro-orm/mariadb": "^6.1.6",
-    "@mikro-orm/reflection": "^6.1.6",
-    "@mikro-orm/sqlite": "^6.1.6",
+    "@mikro-orm/core": "^6.1.12",
+    "@mikro-orm/mariadb": "^6.1.12",
+    "@mikro-orm/reflection": "^6.1.12",
+    "@mikro-orm/sqlite": "^6.1.12",
     "ajv": "^8.12.0",
-    "ajv-formats": "^2.1.1",
+    "ajv-formats": "^3.0.1",
     "basic-ftp": "^5.0.5",
     "chalk": "^5.3.0",
-    "date-fns": "^3.3.1",
+    "date-fns": "^3.6.0",
     "http-status-codes": "^2.3.0",
     "logform": "^2.6.0",
     "mnemonist": "0.40.0-rc1",
-    "mongodb": "^6.3.0",
-    "poolifier": "^3.1.21",
-    "rambda": "^9.1.0",
-    "tar": "^6.2.0",
-    "winston": "^3.11.0",
+    "mongodb": "^6.5.0",
+    "poolifier": "^3.1.29",
+    "rambda": "^9.2.0",
+    "tar": "^6.2.1",
+    "winston": "^3.13.0",
     "winston-daily-rotate-file": "^5.0.0",
     "ws": "^8.16.0"
   },
     "utf-8-validate": "^6.0.3"
   },
   "devDependencies": {
-    "@commitlint/cli": "^19.0.3",
-    "@commitlint/config-conventional": "^19.0.3",
-    "@mikro-orm/cli": "^6.1.6",
+    "@commitlint/cli": "^19.2.1",
+    "@commitlint/config-conventional": "^19.1.0",
+    "@mikro-orm/cli": "^6.1.12",
     "@release-it/bumper": "^6.0.1",
-    "@types/node": "^20.11.24",
+    "@types/node": "^20.12.3",
     "@types/semver": "^7.5.8",
-    "@types/tar": "^6.1.11",
+    "@types/tar": "^6.1.12",
     "@types/ws": "^8.5.10",
-    "@typescript-eslint/eslint-plugin": "^7.1.0",
-    "@typescript-eslint/parser": "^7.1.0",
+    "@typescript-eslint/eslint-plugin": "^7.5.0",
+    "@typescript-eslint/parser": "^7.5.0",
     "auto-changelog": "^2.4.0",
     "c8": "^9.1.0",
     "clinic": "^13.0.0",
     "cross-env": "^7.0.3",
-    "esbuild": "^0.20.1",
+    "esbuild": "^0.20.2",
     "esbuild-plugin-clean": "^1.0.1",
     "esbuild-plugin-copy": "^2.1.1",
     "eslint": "^8.57.0",
+    "eslint-config-love": "^44.0.0",
     "eslint-config-standard": "^17.1.0",
-    "eslint-config-standard-with-typescript": "^43.0.1",
     "eslint-define-config": "^2.1.0",
     "eslint-import-resolver-typescript": "^3.6.1",
     "eslint-plugin-import": "^2.29.1",
-    "eslint-plugin-jsdoc": "^48.2.0",
+    "eslint-plugin-jsdoc": "^48.2.2",
     "eslint-plugin-n": "^16.6.2",
+    "eslint-plugin-simple-import-sort": "^12.0.0",
     "eslint-plugin-tsdoc": "^0.2.17",
     "expect": "^29.7.0",
-    "glob": "^10.3.10",
+    "glob": "^10.3.12",
     "husky": "^9.0.11",
     "lint-staged": "^15.2.2",
     "prettier": "^3.2.5",
     "semver": "^7.6.0",
     "ts-node": "^10.9.2",
     "tsx": "^4.7.1",
-    "typescript": "~5.3.3"
+    "typescript": "~5.4.3"
   }
 }
index 9796e3295ab97ac241576b284f5dc6f3410e5e9e..42071eb7d94669a7214729359209d12a276c1171 100644 (file)
@@ -13,185 +13,286 @@ overrides:
   uuid: ^9.0.0
   tough-cookie: ^4.1.3
 
-dependencies:
-  '@mikro-orm/core':
-    specifier: ^6.1.6
-    version: 6.1.6
-  '@mikro-orm/mariadb':
-    specifier: ^6.1.6
-    version: 6.1.6(@mikro-orm/core@6.1.6)
-  '@mikro-orm/reflection':
-    specifier: ^6.1.6
-    version: 6.1.6(@mikro-orm/core@6.1.6)
-  '@mikro-orm/sqlite':
-    specifier: ^6.1.6
-    version: 6.1.6(@mikro-orm/core@6.1.6)
-  ajv:
-    specifier: ^8.12.0
-    version: 8.12.0
-  ajv-formats:
-    specifier: ^2.1.1
-    version: 2.1.1(ajv@8.12.0)
-  basic-ftp:
-    specifier: ^5.0.5
-    version: 5.0.5
-  chalk:
-    specifier: ^5.3.0
-    version: 5.3.0
-  date-fns:
-    specifier: ^3.3.1
-    version: 3.3.1
-  http-status-codes:
-    specifier: ^2.3.0
-    version: 2.3.0
-  logform:
-    specifier: ^2.6.0
-    version: 2.6.0
-  mnemonist:
-    specifier: 0.40.0-rc1
-    version: 0.40.0-rc1
-  mongodb:
-    specifier: ^6.3.0
-    version: 6.3.0
-  poolifier:
-    specifier: ^3.1.21
-    version: 3.1.21
-  rambda:
-    specifier: ^9.1.0
-    version: 9.1.0
-  tar:
-    specifier: ^6.2.0
-    version: 6.2.0
-  winston:
-    specifier: ^3.11.0
-    version: 3.11.0
-  winston-daily-rotate-file:
-    specifier: ^5.0.0
-    version: 5.0.0(winston@3.11.0)
-  ws:
-    specifier: ^8.16.0
-    version: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)
-
-optionalDependencies:
-  bufferutil:
-    specifier: ^4.0.8
-    version: 4.0.8
-  utf-8-validate:
-    specifier: ^6.0.3
-    version: 6.0.3
-
-devDependencies:
-  '@commitlint/cli':
-    specifier: ^19.0.3
-    version: 19.0.3(@types/node@20.11.24)(typescript@5.3.3)
-  '@commitlint/config-conventional':
-    specifier: ^19.0.3
-    version: 19.0.3
-  '@mikro-orm/cli':
-    specifier: ^6.1.6
-    version: 6.1.6
-  '@release-it/bumper':
-    specifier: ^6.0.1
-    version: 6.0.1(release-it@17.1.1)
-  '@types/node':
-    specifier: ^20.11.24
-    version: 20.11.24
-  '@types/semver':
-    specifier: ^7.5.8
-    version: 7.5.8
-  '@types/tar':
-    specifier: ^6.1.11
-    version: 6.1.11
-  '@types/ws':
-    specifier: ^8.5.10
-    version: 8.5.10
-  '@typescript-eslint/eslint-plugin':
-    specifier: ^7.1.0
-    version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
-  '@typescript-eslint/parser':
-    specifier: ^7.1.0
-    version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-  auto-changelog:
-    specifier: ^2.4.0
-    version: 2.4.0
-  c8:
-    specifier: ^9.1.0
-    version: 9.1.0
-  clinic:
-    specifier: ^13.0.0
-    version: 13.0.0
-  cross-env:
-    specifier: ^7.0.3
-    version: 7.0.3
-  esbuild:
-    specifier: ^0.20.1
-    version: 0.20.1
-  esbuild-plugin-clean:
-    specifier: ^1.0.1
-    version: 1.0.1(esbuild@0.20.1)
-  esbuild-plugin-copy:
-    specifier: ^2.1.1
-    version: 2.1.1(esbuild@0.20.1)
-  eslint:
-    specifier: ^8.57.0
-    version: 8.57.0
-  eslint-config-standard:
-    specifier: ^17.1.0
-    version: 17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.0)
-  eslint-config-standard-with-typescript:
-    specifier: ^43.0.1
-    version: 43.0.1(@typescript-eslint/eslint-plugin@7.1.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.0)(typescript@5.3.3)
-  eslint-define-config:
-    specifier: ^2.1.0
-    version: 2.1.0
-  eslint-import-resolver-typescript:
-    specifier: ^3.6.1
-    version: 3.6.1(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
-  eslint-plugin-import:
-    specifier: ^2.29.1
-    version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
-  eslint-plugin-jsdoc:
-    specifier: ^48.2.0
-    version: 48.2.0(eslint@8.57.0)
-  eslint-plugin-n:
-    specifier: ^16.6.2
-    version: 16.6.2(eslint@8.57.0)
-  eslint-plugin-tsdoc:
-    specifier: ^0.2.17
-    version: 0.2.17
-  expect:
-    specifier: ^29.7.0
-    version: 29.7.0
-  glob:
-    specifier: ^10.3.10
-    version: 10.3.10
-  husky:
-    specifier: ^9.0.11
-    version: 9.0.11
-  lint-staged:
-    specifier: ^15.2.2
-    version: 15.2.2
-  prettier:
-    specifier: ^3.2.5
-    version: 3.2.5
-  release-it:
-    specifier: ^17.1.1
-    version: 17.1.1(typescript@5.3.3)
-  rimraf:
-    specifier: ^5.0.5
-    version: 5.0.5
-  semver:
-    specifier: ^7.5.3
-    version: 7.6.0
-  ts-node:
-    specifier: ^10.9.2
-    version: 10.9.2(@types/node@20.11.24)(typescript@5.3.3)
-  tsx:
-    specifier: ^4.7.1
-    version: 4.7.1
-  typescript:
-    specifier: ~5.3.3
-    version: 5.3.3
+importers:
+
+  .:
+    dependencies:
+      '@mikro-orm/core':
+        specifier: ^6.1.12
+        version: 6.1.12
+      '@mikro-orm/mariadb':
+        specifier: ^6.1.12
+        version: 6.1.12(@mikro-orm/core@6.1.12)
+      '@mikro-orm/reflection':
+        specifier: ^6.1.12
+        version: 6.1.12(@mikro-orm/core@6.1.12)
+      '@mikro-orm/sqlite':
+        specifier: ^6.1.12
+        version: 6.1.12(@mikro-orm/core@6.1.12)
+      ajv:
+        specifier: ^8.12.0
+        version: 8.12.0
+      ajv-formats:
+        specifier: ^3.0.1
+        version: 3.0.1(ajv@8.12.0)
+      basic-ftp:
+        specifier: ^5.0.5
+        version: 5.0.5
+      chalk:
+        specifier: ^5.3.0
+        version: 5.3.0
+      date-fns:
+        specifier: ^3.6.0
+        version: 3.6.0
+      http-status-codes:
+        specifier: ^2.3.0
+        version: 2.3.0
+      logform:
+        specifier: ^2.6.0
+        version: 2.6.0
+      mnemonist:
+        specifier: 0.40.0-rc1
+        version: 0.40.0-rc1
+      mongodb:
+        specifier: ^6.5.0
+        version: 6.5.0
+      poolifier:
+        specifier: ^3.1.29
+        version: 3.1.29
+      rambda:
+        specifier: ^9.2.0
+        version: 9.2.0
+      tar:
+        specifier: ^6.2.1
+        version: 6.2.1
+      winston:
+        specifier: ^3.13.0
+        version: 3.13.0
+      winston-daily-rotate-file:
+        specifier: ^5.0.0
+        version: 5.0.0(winston@3.13.0)
+      ws:
+        specifier: ^8.16.0
+        version: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)
+    optionalDependencies:
+      bufferutil:
+        specifier: ^4.0.8
+        version: 4.0.8
+      utf-8-validate:
+        specifier: ^6.0.3
+        version: 6.0.3
+    devDependencies:
+      '@commitlint/cli':
+        specifier: ^19.2.1
+        version: 19.2.1(@types/node@20.12.3)(typescript@5.4.3)
+      '@commitlint/config-conventional':
+        specifier: ^19.1.0
+        version: 19.1.0
+      '@mikro-orm/cli':
+        specifier: ^6.1.12
+        version: 6.1.12
+      '@release-it/bumper':
+        specifier: ^6.0.1
+        version: 6.0.1(release-it@17.1.1)
+      '@types/node':
+        specifier: ^20.12.3
+        version: 20.12.3
+      '@types/semver':
+        specifier: ^7.5.8
+        version: 7.5.8
+      '@types/tar':
+        specifier: ^6.1.12
+        version: 6.1.12
+      '@types/ws':
+        specifier: ^8.5.10
+        version: 8.5.10
+      '@typescript-eslint/eslint-plugin':
+        specifier: ^7.5.0
+        version: 7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.57.0)(typescript@5.4.3)
+      '@typescript-eslint/parser':
+        specifier: ^7.5.0
+        version: 7.5.0(eslint@8.57.0)(typescript@5.4.3)
+      auto-changelog:
+        specifier: ^2.4.0
+        version: 2.4.0
+      c8:
+        specifier: ^9.1.0
+        version: 9.1.0
+      clinic:
+        specifier: ^13.0.0
+        version: 13.0.0
+      cross-env:
+        specifier: ^7.0.3
+        version: 7.0.3
+      esbuild:
+        specifier: ^0.20.2
+        version: 0.20.2
+      esbuild-plugin-clean:
+        specifier: ^1.0.1
+        version: 1.0.1(esbuild@0.20.2)
+      esbuild-plugin-copy:
+        specifier: ^2.1.1
+        version: 2.1.1(esbuild@0.20.2)
+      eslint:
+        specifier: ^8.57.0
+        version: 8.57.0
+      eslint-config-love:
+        specifier: ^44.0.0
+        version: 44.0.0(@typescript-eslint/eslint-plugin@7.5.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.0)(typescript@5.4.3)
+      eslint-config-standard:
+        specifier: ^17.1.0
+        version: 17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.0)
+      eslint-define-config:
+        specifier: ^2.1.0
+        version: 2.1.0
+      eslint-import-resolver-typescript:
+        specifier: ^3.6.1
+        version: 3.6.1(@typescript-eslint/parser@7.5.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+      eslint-plugin-import:
+        specifier: ^2.29.1
+        version: 2.29.1(@typescript-eslint/parser@7.5.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+      eslint-plugin-jsdoc:
+        specifier: ^48.2.2
+        version: 48.2.2(eslint@8.57.0)
+      eslint-plugin-n:
+        specifier: ^16.6.2
+        version: 16.6.2(eslint@8.57.0)
+      eslint-plugin-simple-import-sort:
+        specifier: ^12.0.0
+        version: 12.0.0(eslint@8.57.0)
+      eslint-plugin-tsdoc:
+        specifier: ^0.2.17
+        version: 0.2.17
+      expect:
+        specifier: ^29.7.0
+        version: 29.7.0
+      glob:
+        specifier: ^10.3.12
+        version: 10.3.12
+      husky:
+        specifier: ^9.0.11
+        version: 9.0.11
+      lint-staged:
+        specifier: ^15.2.2
+        version: 15.2.2
+      prettier:
+        specifier: ^3.2.5
+        version: 3.2.5
+      release-it:
+        specifier: ^17.1.1
+        version: 17.1.1(typescript@5.4.3)
+      rimraf:
+        specifier: ^5.0.5
+        version: 5.0.5
+      semver:
+        specifier: ^7.5.3
+        version: 7.6.0
+      ts-node:
+        specifier: ^10.9.2
+        version: 10.9.2(@types/node@20.12.3)(typescript@5.4.3)
+      tsx:
+        specifier: ^4.7.1
+        version: 4.7.1
+      typescript:
+        specifier: ~5.4.3
+        version: 5.4.3
+
+  ui/web:
+    dependencies:
+      finalhandler:
+        specifier: ^1.2.0
+        version: 1.2.0
+      serve-static:
+        specifier: ^1.15.0
+        version: 1.15.0
+      vue:
+        specifier: ^3.4.21
+        version: 3.4.21(typescript@5.4.3)
+      vue-router:
+        specifier: ^4.3.0
+        version: 4.3.0(vue@3.4.21)
+      vue-toast-notification:
+        specifier: ^3.1.2
+        version: 3.1.2(vue@3.4.21)
+    devDependencies:
+      '@rushstack/eslint-patch':
+        specifier: ^1.10.1
+        version: 1.10.1
+      '@tsconfig/node20':
+        specifier: ^20.1.4
+        version: 20.1.4
+      '@types/jsdom':
+        specifier: ^21.1.6
+        version: 21.1.6
+      '@types/node':
+        specifier: ^20.12.3
+        version: 20.12.3
+      '@typescript-eslint/eslint-plugin':
+        specifier: ^7.5.0
+        version: 7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.57.0)(typescript@5.4.3)
+      '@typescript-eslint/parser':
+        specifier: ^7.5.0
+        version: 7.5.0(eslint@8.57.0)(typescript@5.4.3)
+      '@vitejs/plugin-vue':
+        specifier: ^5.0.4
+        version: 5.0.4(vite@5.2.8)(vue@3.4.21)
+      '@vitejs/plugin-vue-jsx':
+        specifier: ^3.1.0
+        version: 3.1.0(vite@5.2.8)(vue@3.4.21)
+      '@vitest/coverage-v8':
+        specifier: ^1.4.0
+        version: 1.4.0(vitest@1.4.0)
+      '@vue/eslint-config-prettier':
+        specifier: ^9.0.0
+        version: 9.0.0(eslint@8.57.0)(prettier@3.2.5)
+      '@vue/eslint-config-typescript':
+        specifier: ^13.0.0
+        version: 13.0.0(eslint-plugin-vue@9.24.0)(eslint@8.57.0)(typescript@5.4.3)
+      '@vue/test-utils':
+        specifier: ^2.4.5
+        version: 2.4.5
+      '@vue/tsconfig':
+        specifier: ^0.5.1
+        version: 0.5.1
+      cross-env:
+        specifier: ^7.0.3
+        version: 7.0.3
+      eslint:
+        specifier: ^8.57.0
+        version: 8.57.0
+      eslint-define-config:
+        specifier: ^2.1.0
+        version: 2.1.0
+      eslint-import-resolver-typescript:
+        specifier: ^3.6.1
+        version: 3.6.1(@typescript-eslint/parser@7.5.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+      eslint-plugin-import:
+        specifier: ^2.29.1
+        version: 2.29.1(@typescript-eslint/parser@7.5.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+      eslint-plugin-simple-import-sort:
+        specifier: ^12.0.0
+        version: 12.0.0(eslint@8.57.0)
+      eslint-plugin-vue:
+        specifier: ^9.24.0
+        version: 9.24.0(eslint@8.57.0)
+      jsdom:
+        specifier: ^24.0.0
+        version: 24.0.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)
+      prettier:
+        specifier: ^3.2.5
+        version: 3.2.5
+      rimraf:
+        specifier: ^5.0.5
+        version: 5.0.5
+      typescript:
+        specifier: ~5.4.3
+        version: 5.4.3
+      vite:
+        specifier: ^5.2.8
+        version: 5.2.8(@types/node@20.12.3)
+      vitest:
+        specifier: ^1.4.0
+        version: 1.4.0(@types/node@20.12.3)(jsdom@24.0.0)
 
 packages:
 
@@ -237,32 +338,309 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /@ampproject/remapping@2.3.0:
+    resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.5
+      '@jridgewell/trace-mapping': 0.3.25
+    dev: true
+
   /@assemblyscript/loader@0.19.23:
     resolution: {integrity: sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw==}
     dev: true
 
-  /@babel/code-frame@7.23.5:
-    resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
+  /@babel/code-frame@7.24.2:
+    resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/highlight': 7.23.4
-      chalk: 2.4.2
+      '@babel/highlight': 7.24.2
+      picocolors: 1.0.0
+    dev: true
+
+  /@babel/compat-data@7.24.4:
+    resolution: {integrity: sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/core@7.24.4:
+    resolution: {integrity: sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@ampproject/remapping': 2.3.0
+      '@babel/code-frame': 7.24.2
+      '@babel/generator': 7.24.4
+      '@babel/helper-compilation-targets': 7.23.6
+      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.4)
+      '@babel/helpers': 7.24.4
+      '@babel/parser': 7.24.4
+      '@babel/template': 7.24.0
+      '@babel/traverse': 7.24.1
+      '@babel/types': 7.24.0
+      convert-source-map: 2.0.0
+      debug: 4.3.4
+      gensync: 1.0.0-beta.2
+      json5: 2.2.3
+      semver: 7.6.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/generator@7.24.4:
+    resolution: {integrity: sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.24.0
+      '@jridgewell/gen-mapping': 0.3.5
+      '@jridgewell/trace-mapping': 0.3.25
+      jsesc: 2.5.2
     dev: true
 
+  /@babel/helper-annotate-as-pure@7.22.5:
+    resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.24.0
+    dev: true
+
+  /@babel/helper-compilation-targets@7.23.6:
+    resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/compat-data': 7.24.4
+      '@babel/helper-validator-option': 7.23.5
+      browserslist: 4.23.0
+      lru-cache: 5.1.1
+      semver: 7.6.0
+    dev: true
+
+  /@babel/helper-create-class-features-plugin@7.24.4(@babel/core@7.24.4):
+    resolution: {integrity: sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.24.4
+      '@babel/helper-annotate-as-pure': 7.22.5
+      '@babel/helper-environment-visitor': 7.22.20
+      '@babel/helper-function-name': 7.23.0
+      '@babel/helper-member-expression-to-functions': 7.23.0
+      '@babel/helper-optimise-call-expression': 7.22.5
+      '@babel/helper-replace-supers': 7.24.1(@babel/core@7.24.4)
+      '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
+      '@babel/helper-split-export-declaration': 7.22.6
+      semver: 7.6.0
+    dev: true
+
+  /@babel/helper-environment-visitor@7.22.20:
+    resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/helper-function-name@7.23.0:
+    resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/template': 7.24.0
+      '@babel/types': 7.24.0
+    dev: true
+
+  /@babel/helper-hoist-variables@7.22.5:
+    resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.24.0
+    dev: true
+
+  /@babel/helper-member-expression-to-functions@7.23.0:
+    resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.24.0
+    dev: true
+
+  /@babel/helper-module-imports@7.22.15:
+    resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.24.0
+    dev: true
+
+  /@babel/helper-module-imports@7.24.3:
+    resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.24.0
+    dev: true
+
+  /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.4):
+    resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.24.4
+      '@babel/helper-environment-visitor': 7.22.20
+      '@babel/helper-module-imports': 7.24.3
+      '@babel/helper-simple-access': 7.22.5
+      '@babel/helper-split-export-declaration': 7.22.6
+      '@babel/helper-validator-identifier': 7.22.20
+    dev: true
+
+  /@babel/helper-optimise-call-expression@7.22.5:
+    resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.24.0
+    dev: true
+
+  /@babel/helper-plugin-utils@7.24.0:
+    resolution: {integrity: sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/helper-replace-supers@7.24.1(@babel/core@7.24.4):
+    resolution: {integrity: sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.24.4
+      '@babel/helper-environment-visitor': 7.22.20
+      '@babel/helper-member-expression-to-functions': 7.23.0
+      '@babel/helper-optimise-call-expression': 7.22.5
+    dev: true
+
+  /@babel/helper-simple-access@7.22.5:
+    resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.24.0
+    dev: true
+
+  /@babel/helper-skip-transparent-expression-wrappers@7.22.5:
+    resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.24.0
+    dev: true
+
+  /@babel/helper-split-export-declaration@7.22.6:
+    resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.24.0
+    dev: true
+
+  /@babel/helper-string-parser@7.24.1:
+    resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==}
+    engines: {node: '>=6.9.0'}
+
   /@babel/helper-validator-identifier@7.22.20:
     resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
     engines: {node: '>=6.9.0'}
+
+  /@babel/helper-validator-option@7.23.5:
+    resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/helpers@7.24.4:
+    resolution: {integrity: sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/template': 7.24.0
+      '@babel/traverse': 7.24.1
+      '@babel/types': 7.24.0
+    transitivePeerDependencies:
+      - supports-color
     dev: true
 
-  /@babel/highlight@7.23.4:
-    resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
+  /@babel/highlight@7.24.2:
+    resolution: {integrity: sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==}
     engines: {node: '>=6.9.0'}
     dependencies:
       '@babel/helper-validator-identifier': 7.22.20
       chalk: 2.4.2
       js-tokens: 4.0.0
+      picocolors: 1.0.0
+    dev: true
+
+  /@babel/parser@7.24.4:
+    resolution: {integrity: sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+    dependencies:
+      '@babel/types': 7.24.0
+
+  /@babel/plugin-syntax-jsx@7.24.1(@babel/core@7.24.4):
+    resolution: {integrity: sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.4
+      '@babel/helper-plugin-utils': 7.24.0
+    dev: true
+
+  /@babel/plugin-syntax-typescript@7.24.1(@babel/core@7.24.4):
+    resolution: {integrity: sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.4
+      '@babel/helper-plugin-utils': 7.24.0
+    dev: true
+
+  /@babel/plugin-transform-typescript@7.24.4(@babel/core@7.24.4):
+    resolution: {integrity: sha512-79t3CQ8+oBGk/80SQ8MN3Bs3obf83zJ0YZjDmDaEZN8MqhMI760apl5z6a20kFeMXBwJX99VpKT8CKxEBp5H1g==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.4
+      '@babel/helper-annotate-as-pure': 7.22.5
+      '@babel/helper-create-class-features-plugin': 7.24.4(@babel/core@7.24.4)
+      '@babel/helper-plugin-utils': 7.24.0
+      '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.4)
+    dev: true
+
+  /@babel/template@7.24.0:
+    resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/code-frame': 7.24.2
+      '@babel/parser': 7.24.4
+      '@babel/types': 7.24.0
+    dev: true
+
+  /@babel/traverse@7.24.1:
+    resolution: {integrity: sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/code-frame': 7.24.2
+      '@babel/generator': 7.24.4
+      '@babel/helper-environment-visitor': 7.22.20
+      '@babel/helper-function-name': 7.23.0
+      '@babel/helper-hoist-variables': 7.22.5
+      '@babel/helper-split-export-declaration': 7.22.6
+      '@babel/parser': 7.24.4
+      '@babel/types': 7.24.0
+      debug: 4.3.4
+      globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
     dev: true
 
+  /@babel/types@7.24.0:
+    resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-string-parser': 7.24.1
+      '@babel/helper-validator-identifier': 7.22.20
+      to-fast-properties: 2.0.0
+
   /@bcoe/v8-coverage@0.2.3:
     resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
     dev: true
@@ -305,8 +683,8 @@ packages:
       chalk: 4.1.2
       lodash.debounce: 4.0.8
       loose-envify: 1.4.0
-      postcss: 8.4.35
-      postcss-import: 13.0.0(postcss@8.4.35)
+      postcss: 8.4.38
+      postcss-import: 13.0.0(postcss@8.4.38)
       stream-template: 0.0.10
       webfontloader: 1.6.28
     dev: true
@@ -412,15 +790,15 @@ packages:
     engines: {node: '>=0.1.90'}
     dev: false
 
-  /@commitlint/cli@19.0.3(@types/node@20.11.24)(typescript@5.3.3):
-    resolution: {integrity: sha512-mGhh/aYPib4Vy4h+AGRloMY+CqkmtdeKPV9poMcZeImF5e3knQ5VYaSeAM0mEzps1dbKsHvABwaDpafLUuM96g==}
+  /@commitlint/cli@19.2.1(@types/node@20.12.3)(typescript@5.4.3):
+    resolution: {integrity: sha512-cbkYUJsLqRomccNxvoJTyv5yn0bSy05BBizVyIcLACkRbVUqYorC351Diw/XFSWC/GtpwiwT2eOvQgFZa374bg==}
     engines: {node: '>=v18'}
     hasBin: true
     dependencies:
       '@commitlint/format': 19.0.3
-      '@commitlint/lint': 19.0.3
-      '@commitlint/load': 19.0.3(@types/node@20.11.24)(typescript@5.3.3)
-      '@commitlint/read': 19.0.3
+      '@commitlint/lint': 19.1.0
+      '@commitlint/load': 19.2.0(@types/node@20.12.3)(typescript@5.4.3)
+      '@commitlint/read': 19.2.1
       '@commitlint/types': 19.0.3
       execa: 8.0.1
       yargs: 17.7.2
@@ -429,8 +807,8 @@ packages:
       - typescript
     dev: true
 
-  /@commitlint/config-conventional@19.0.3:
-    resolution: {integrity: sha512-vh0L8XeLaEzTe8VCxSd0gAFvfTK0RFolrzw4o431bIuWJfi/yRCHJlsDwus7wW2eJaFFDR0VFXJyjGyDQhi4vA==}
+  /@commitlint/config-conventional@19.1.0:
+    resolution: {integrity: sha512-KIKD2xrp6Uuk+dcZVj3++MlzIr/Su6zLE8crEDQCZNvWHNQSeeGbzOlNtsR32TUy6H3JbP7nWgduAHCaiGQ6EA==}
     engines: {node: '>=v18'}
     dependencies:
       '@commitlint/types': 19.0.3
@@ -478,8 +856,8 @@ packages:
       semver: 7.6.0
     dev: true
 
-  /@commitlint/lint@19.0.3:
-    resolution: {integrity: sha512-uHPyRqIn57iIplYa5xBr6oNu5aPXKGC4WLeuHfqQHclwIqbJ33g3yA5fIA+/NYnp5ZM2EFiujqHFaVUYj6HlKA==}
+  /@commitlint/lint@19.1.0:
+    resolution: {integrity: sha512-ESjaBmL/9cxm+eePyEr6SFlBUIYlYpI80n+Ltm7IA3MAcrmiP05UMhJdAD66sO8jvo8O4xdGn/1Mt2G5VzfZKw==}
     engines: {node: '>=v18'}
     dependencies:
       '@commitlint/is-ignored': 19.0.3
@@ -488,17 +866,17 @@ packages:
       '@commitlint/types': 19.0.3
     dev: true
 
-  /@commitlint/load@19.0.3(@types/node@20.11.24)(typescript@5.3.3):
-    resolution: {integrity: sha512-18Tk/ZcDFRKIoKfEcl7kC+bYkEQ055iyKmGsYDoYWpKf6FUvBrP9bIWapuy/MB+kYiltmP9ITiUx6UXtqC9IRw==}
+  /@commitlint/load@19.2.0(@types/node@20.12.3)(typescript@5.4.3):
+    resolution: {integrity: sha512-XvxxLJTKqZojCxaBQ7u92qQLFMMZc4+p9qrIq/9kJDy8DOrEa7P1yx7Tjdc2u2JxIalqT4KOGraVgCE7eCYJyQ==}
     engines: {node: '>=v18'}
     dependencies:
       '@commitlint/config-validator': 19.0.3
       '@commitlint/execute-rule': 19.0.0
-      '@commitlint/resolve-extends': 19.0.3
+      '@commitlint/resolve-extends': 19.1.0
       '@commitlint/types': 19.0.3
       chalk: 5.3.0
-      cosmiconfig: 8.3.6(typescript@5.3.3)
-      cosmiconfig-typescript-loader: 5.0.0(@types/node@20.11.24)(cosmiconfig@8.3.6)(typescript@5.3.3)
+      cosmiconfig: 9.0.0(typescript@5.4.3)
+      cosmiconfig-typescript-loader: 5.0.0(@types/node@20.12.3)(cosmiconfig@9.0.0)(typescript@5.4.3)
       lodash.isplainobject: 4.0.6
       lodash.merge: 4.6.2
       lodash.uniq: 4.5.0
@@ -521,18 +899,19 @@ packages:
       conventional-commits-parser: 5.0.0
     dev: true
 
-  /@commitlint/read@19.0.3:
-    resolution: {integrity: sha512-b5AflTyAXkUx5qKw4TkjjcOccXZHql3JqMi522knTQktq2AubKXFz60Sws+K4FsefwPws6fGz9mqiI/NvsvxFA==}
+  /@commitlint/read@19.2.1:
+    resolution: {integrity: sha512-qETc4+PL0EUv7Q36lJbPG+NJiBOGg7SSC7B5BsPWOmei+Dyif80ErfWQ0qXoW9oCh7GTpTNRoaVhiI8RbhuaNw==}
     engines: {node: '>=v18'}
     dependencies:
       '@commitlint/top-level': 19.0.0
       '@commitlint/types': 19.0.3
+      execa: 8.0.1
       git-raw-commits: 4.0.0
       minimist: 1.2.8
     dev: true
 
-  /@commitlint/resolve-extends@19.0.3:
-    resolution: {integrity: sha512-18BKmta8OC8+Ub+Q3QGM9l27VjQaXobloVXOrMvu8CpEwJYv62vC/t7Ka5kJnsW0tU9q1eMqJFZ/nN9T/cOaIA==}
+  /@commitlint/resolve-extends@19.1.0:
+    resolution: {integrity: sha512-z2riI+8G3CET5CPgXJPlzftH+RiWYLMYv4C9tSLdLXdr6pBNimSKukYP9MS27ejmscqCTVA4almdLh0ODD2KYg==}
     engines: {node: '>=v18'}
     dependencies:
       '@commitlint/config-validator': 19.0.3
@@ -607,8 +986,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/aix-ppc64@0.20.1:
-    resolution: {integrity: sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==}
+  /@esbuild/aix-ppc64@0.20.2:
+    resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
     engines: {node: '>=12'}
     cpu: [ppc64]
     os: [aix]
@@ -625,8 +1004,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/android-arm64@0.20.1:
-    resolution: {integrity: sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==}
+  /@esbuild/android-arm64@0.20.2:
+    resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [android]
@@ -643,8 +1022,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/android-arm@0.20.1:
-    resolution: {integrity: sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==}
+  /@esbuild/android-arm@0.20.2:
+    resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
     engines: {node: '>=12'}
     cpu: [arm]
     os: [android]
@@ -661,8 +1040,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/android-x64@0.20.1:
-    resolution: {integrity: sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==}
+  /@esbuild/android-x64@0.20.2:
+    resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [android]
@@ -679,8 +1058,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/darwin-arm64@0.20.1:
-    resolution: {integrity: sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==}
+  /@esbuild/darwin-arm64@0.20.2:
+    resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [darwin]
@@ -697,8 +1076,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/darwin-x64@0.20.1:
-    resolution: {integrity: sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==}
+  /@esbuild/darwin-x64@0.20.2:
+    resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [darwin]
@@ -715,8 +1094,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/freebsd-arm64@0.20.1:
-    resolution: {integrity: sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==}
+  /@esbuild/freebsd-arm64@0.20.2:
+    resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [freebsd]
@@ -733,8 +1112,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/freebsd-x64@0.20.1:
-    resolution: {integrity: sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==}
+  /@esbuild/freebsd-x64@0.20.2:
+    resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [freebsd]
@@ -751,8 +1130,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/linux-arm64@0.20.1:
-    resolution: {integrity: sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w==}
+  /@esbuild/linux-arm64@0.20.2:
+    resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [linux]
@@ -769,8 +1148,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/linux-arm@0.20.1:
-    resolution: {integrity: sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==}
+  /@esbuild/linux-arm@0.20.2:
+    resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
     engines: {node: '>=12'}
     cpu: [arm]
     os: [linux]
@@ -787,8 +1166,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/linux-ia32@0.20.1:
-    resolution: {integrity: sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==}
+  /@esbuild/linux-ia32@0.20.2:
+    resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
     engines: {node: '>=12'}
     cpu: [ia32]
     os: [linux]
@@ -805,8 +1184,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/linux-loong64@0.20.1:
-    resolution: {integrity: sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==}
+  /@esbuild/linux-loong64@0.20.2:
+    resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
     engines: {node: '>=12'}
     cpu: [loong64]
     os: [linux]
@@ -823,8 +1202,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/linux-mips64el@0.20.1:
-    resolution: {integrity: sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==}
+  /@esbuild/linux-mips64el@0.20.2:
+    resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
     engines: {node: '>=12'}
     cpu: [mips64el]
     os: [linux]
@@ -841,8 +1220,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/linux-ppc64@0.20.1:
-    resolution: {integrity: sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==}
+  /@esbuild/linux-ppc64@0.20.2:
+    resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
     engines: {node: '>=12'}
     cpu: [ppc64]
     os: [linux]
@@ -859,8 +1238,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/linux-riscv64@0.20.1:
-    resolution: {integrity: sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==}
+  /@esbuild/linux-riscv64@0.20.2:
+    resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
     engines: {node: '>=12'}
     cpu: [riscv64]
     os: [linux]
@@ -877,8 +1256,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/linux-s390x@0.20.1:
-    resolution: {integrity: sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==}
+  /@esbuild/linux-s390x@0.20.2:
+    resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
     engines: {node: '>=12'}
     cpu: [s390x]
     os: [linux]
@@ -895,8 +1274,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/linux-x64@0.20.1:
-    resolution: {integrity: sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==}
+  /@esbuild/linux-x64@0.20.2:
+    resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [linux]
@@ -913,8 +1292,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/netbsd-x64@0.20.1:
-    resolution: {integrity: sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==}
+  /@esbuild/netbsd-x64@0.20.2:
+    resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [netbsd]
@@ -931,8 +1310,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/openbsd-x64@0.20.1:
-    resolution: {integrity: sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==}
+  /@esbuild/openbsd-x64@0.20.2:
+    resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [openbsd]
@@ -949,8 +1328,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/sunos-x64@0.20.1:
-    resolution: {integrity: sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==}
+  /@esbuild/sunos-x64@0.20.2:
+    resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [sunos]
@@ -967,8 +1346,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/win32-arm64@0.20.1:
-    resolution: {integrity: sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==}
+  /@esbuild/win32-arm64@0.20.2:
+    resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [win32]
@@ -985,8 +1364,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/win32-ia32@0.20.1:
-    resolution: {integrity: sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==}
+  /@esbuild/win32-ia32@0.20.2:
+    resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
     engines: {node: '>=12'}
     cpu: [ia32]
     os: [win32]
@@ -1003,8 +1382,8 @@ packages:
     dev: true
     optional: true
 
-  /@esbuild/win32-x64@0.20.1:
-    resolution: {integrity: sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==}
+  /@esbuild/win32-x64@0.20.2:
+    resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [win32]
@@ -1058,7 +1437,7 @@ packages:
     resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
     engines: {node: '>=10.10.0'}
     dependencies:
-      '@humanwhocodes/object-schema': 2.0.2
+      '@humanwhocodes/object-schema': 2.0.3
       debug: 4.3.4
       minimatch: 3.1.2
     transitivePeerDependencies:
@@ -1070,8 +1449,8 @@ packages:
     engines: {node: '>=12.22'}
     dev: true
 
-  /@humanwhocodes/object-schema@2.0.2:
-    resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
+  /@humanwhocodes/object-schema@2.0.3:
+    resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
     dev: true
 
   /@iarna/toml@2.2.5:
@@ -1124,22 +1503,35 @@ packages:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.6
       '@types/istanbul-reports': 3.0.4
-      '@types/node': 20.11.24
+      '@types/node': 20.12.3
       '@types/yargs': 17.0.32
       chalk: 4.1.2
     dev: true
 
+  /@jridgewell/gen-mapping@0.3.5:
+    resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      '@jridgewell/set-array': 1.2.1
+      '@jridgewell/sourcemap-codec': 1.4.15
+      '@jridgewell/trace-mapping': 0.3.25
+    dev: true
+
   /@jridgewell/resolve-uri@3.1.2:
     resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
     engines: {node: '>=6.0.0'}
     dev: true
 
+  /@jridgewell/set-array@1.2.1:
+    resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+    engines: {node: '>=6.0.0'}
+    dev: true
+
   /@jridgewell/sourcemap-codec@1.4.15:
     resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
-    dev: true
 
-  /@jridgewell/trace-mapping@0.3.23:
-    resolution: {integrity: sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==}
+  /@jridgewell/trace-mapping@0.3.25:
+    resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
     dependencies:
       '@jridgewell/resolve-uri': 3.1.2
       '@jridgewell/sourcemap-codec': 1.4.15
@@ -1152,8 +1544,8 @@ packages:
       '@jridgewell/sourcemap-codec': 1.4.15
     dev: true
 
-  /@ljharb/through@2.3.12:
-    resolution: {integrity: sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==}
+  /@ljharb/through@2.3.13:
+    resolution: {integrity: sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==}
     engines: {node: '>= 0.4'}
     dependencies:
       call-bind: 1.0.7
@@ -1172,14 +1564,14 @@ packages:
     resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==}
     dev: true
 
-  /@mikro-orm/cli@6.1.6:
-    resolution: {integrity: sha512-fA79d/F52Ab8apy1LgKgEP+CZYIvYwGLp9ZUXu7x6P0YCbOovimZwj/SfLyU7DdKyHH+ZZExnRqzaBjbgqvhbg==}
+  /@mikro-orm/cli@6.1.12:
+    resolution: {integrity: sha512-9Jf33ZLfEnX1cRwgBdImgpBDesILjm9IPeZxOGa9KI1hQHZ+MrWN9gRl3PFHwQvElHXEt3aU2IBiMO3h6iVXaQ==}
     engines: {node: '>= 18.12.0'}
     hasBin: true
     dependencies:
       '@jercle/yargonaut': 1.1.5
-      '@mikro-orm/core': 6.1.6
-      '@mikro-orm/knex': 6.1.6(@mikro-orm/core@6.1.6)(sqlite3@5.1.7)
+      '@mikro-orm/core': 6.1.12
+      '@mikro-orm/knex': 6.1.12(@mikro-orm/core@6.1.12)(sqlite3@5.1.7)
       fs-extra: 11.2.0
       tsconfig-paths: 4.2.0
       yargs: 17.7.2
@@ -1194,25 +1586,25 @@ packages:
       - tedious
     dev: true
 
-  /@mikro-orm/core@6.1.6:
-    resolution: {integrity: sha512-YpXXwVuGDCDn7wPBCIU/6kEST5ANOpKGCh0Kl+t+iqnwkOJF5RtoNvsSYSRpwdK7GIdlHVNMFmoX9p6om/bVXw==}
+  /@mikro-orm/core@6.1.12:
+    resolution: {integrity: sha512-51/1iBdXoF+bODJMpW8cUsr1TsieIJobiAX4g9A6CgBU6v95vwzyEQRo9v73i+YuPfrjH4YrrSbRaAr1tKe38A==}
     engines: {node: '>= 18.12.0'}
     dependencies:
       dataloader: 2.2.2
-      dotenv: 16.4.4
+      dotenv: 16.4.5
       esprima: 4.0.1
       fs-extra: 11.2.0
       globby: 11.1.0
-      mikro-orm: 6.1.6
+      mikro-orm: 6.1.12
       reflect-metadata: 0.2.1
 
-  /@mikro-orm/knex@6.1.6(@mikro-orm/core@6.1.6)(sqlite3@5.1.7):
-    resolution: {integrity: sha512-oLSke1/iIJRiV4ur/ywoKvFT2IPYCQns5S6PCLHV7hfxgmhySpK1JwmFiz2bROht5TDcadi5KN2JlG9XaHDx+Q==}
+  /@mikro-orm/knex@6.1.12(@mikro-orm/core@6.1.12)(sqlite3@5.1.7):
+    resolution: {integrity: sha512-bGRDTM13ASYcmte8BglikDwfoYmCo8YUW5LY4Mn5GUCyzjLV7XP23SrTaerLIxXlNiTnJhTO+On3cOyndFwHpw==}
     engines: {node: '>= 18.12.0'}
     peerDependencies:
       '@mikro-orm/core': ^6.0.0
     dependencies:
-      '@mikro-orm/core': 6.1.6
+      '@mikro-orm/core': 6.1.12
       fs-extra: 11.2.0
       knex: 3.1.0(sqlite3@5.1.7)
       sqlstring: 2.3.3
@@ -1226,14 +1618,14 @@ packages:
       - supports-color
       - tedious
 
-  /@mikro-orm/mariadb@6.1.6(@mikro-orm/core@6.1.6):
-    resolution: {integrity: sha512-9/LoISqCb0kUs/LaTaZs6TE3T922wUWAYIp9Sah05nuB7bU1BTVrdJRWfgNXLEAAytmDFZTq3rBYP5L/IBPa+Q==}
+  /@mikro-orm/mariadb@6.1.12(@mikro-orm/core@6.1.12):
+    resolution: {integrity: sha512-rcpH801qqLMFyer9cFkWeh1F16BQyj0q2UhrZXXE+57mJajgqmq/+zmot5sCq9jPxzi2SKEn+l7M30L9bmyHWw==}
     engines: {node: '>= 18.12.0'}
     peerDependencies:
       '@mikro-orm/core': ^6.0.0
     dependencies:
-      '@mikro-orm/core': 6.1.6
-      '@mikro-orm/knex': 6.1.6(@mikro-orm/core@6.1.6)(sqlite3@5.1.7)
+      '@mikro-orm/core': 6.1.12
+      '@mikro-orm/knex': 6.1.12(@mikro-orm/core@6.1.12)(sqlite3@5.1.7)
       mariadb: 2.5.6
     transitivePeerDependencies:
       - better-sqlite3
@@ -1246,25 +1638,25 @@ packages:
       - tedious
     dev: false
 
-  /@mikro-orm/reflection@6.1.6(@mikro-orm/core@6.1.6):
-    resolution: {integrity: sha512-b6GHuzNExWkNP6NDjciY3PXwMou46/DvvibW417q3FpJ8V+0/XzE53BgZ1abtb22m7ALF9xRUgL8XDGCHVfKQQ==}
+  /@mikro-orm/reflection@6.1.12(@mikro-orm/core@6.1.12):
+    resolution: {integrity: sha512-mm9rOeSqE4lqn/vNAG2G7Yws8DjH+YYoTE81GGkO8BIUCjT+F+cTKzW871Qj0JfGSuW4spsdDW10gKmXywrveQ==}
     engines: {node: '>= 18.12.0'}
     peerDependencies:
       '@mikro-orm/core': ^6.0.0
     dependencies:
-      '@mikro-orm/core': 6.1.6
+      '@mikro-orm/core': 6.1.12
       globby: 11.1.0
-      ts-morph: 21.0.1
+      ts-morph: 22.0.0
     dev: false
 
-  /@mikro-orm/sqlite@6.1.6(@mikro-orm/core@6.1.6):
-    resolution: {integrity: sha512-Wb4oZiHi/XeApUv8LbUUHwYpAVUEs7mSDuuLpVtQGxxMlA+Q1IBtV6tiP6nfapS092LJsVMs/lL0yaALmM+x/Q==}
+  /@mikro-orm/sqlite@6.1.12(@mikro-orm/core@6.1.12):
+    resolution: {integrity: sha512-IOv0s5UYdO2dzCiHGpTcceo2FOZtddnMmlNmRkBXhKzYkYUEut0qJJqt20d8oJsMJuEk8Et7/MAgdJT5FbWTzA==}
     engines: {node: '>= 18.12.0'}
     peerDependencies:
       '@mikro-orm/core': ^6.0.0
     dependencies:
-      '@mikro-orm/core': 6.1.6
-      '@mikro-orm/knex': 6.1.6(@mikro-orm/core@6.1.6)(sqlite3@5.1.7)
+      '@mikro-orm/core': 6.1.12
+      '@mikro-orm/knex': 6.1.12(@mikro-orm/core@6.1.12)(sqlite3@5.1.7)
       fs-extra: 11.2.0
       sqlite3: 5.1.7
       sqlstring-sqlite: 0.1.1
@@ -1279,8 +1671,8 @@ packages:
       - tedious
     dev: false
 
-  /@mongodb-js/saslprep@1.1.4:
-    resolution: {integrity: sha512-8zJ8N1x51xo9hwPh6AWnKdLGEC5N3lDa6kms1YHmFBoRhTpJR6HG8wWk0td1MVCu9cD4YBrvjZEtd5Obw0Fbnw==}
+  /@mongodb-js/saslprep@1.1.5:
+    resolution: {integrity: sha512-XLNOMH66KhJzUJNwT/qlMnS4WsNDWD5ASdyaSH3EtK+F4r/CFGa3jT4GNi4mfOitGvWXtdLgQJkQjxSVrio+jA==}
     requiresBuild: true
     dependencies:
       sparse-bitfield: 3.0.3
@@ -1369,30 +1761,30 @@ packages:
     resolution: {integrity: sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==}
     dev: true
 
-  /@octokit/plugin-paginate-rest@9.2.0(@octokit/core@5.1.0):
-    resolution: {integrity: sha512-NKi0bJEZqOSbBLMv9kdAcuocpe05Q2xAXNLTGi0HN2GSMFJHNZuSoPNa0tcQFTOFCKe+ZaYBZ3lpXh1yxgUDCA==}
+  /@octokit/plugin-paginate-rest@9.2.1(@octokit/core@5.1.0):
+    resolution: {integrity: sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw==}
     engines: {node: '>= 18'}
     peerDependencies:
-      '@octokit/core': '>=5'
+      '@octokit/core': '5'
     dependencies:
       '@octokit/core': 5.1.0
       '@octokit/types': 12.6.0
     dev: true
 
-  /@octokit/plugin-request-log@4.0.0(@octokit/core@5.1.0):
-    resolution: {integrity: sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA==}
+  /@octokit/plugin-request-log@4.0.1(@octokit/core@5.1.0):
+    resolution: {integrity: sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==}
     engines: {node: '>= 18'}
     peerDependencies:
-      '@octokit/core': '>=5'
+      '@octokit/core': '5'
     dependencies:
       '@octokit/core': 5.1.0
     dev: true
 
-  /@octokit/plugin-rest-endpoint-methods@10.4.0(@octokit/core@5.1.0):
-    resolution: {integrity: sha512-INw5rGXWlbv/p/VvQL63dhlXr38qYTHkQ5bANi9xofrF9OraqmjHsIGyenmjmul1JVRHpUlw5heFOj1UZLEolA==}
+  /@octokit/plugin-rest-endpoint-methods@10.4.1(@octokit/core@5.1.0):
+    resolution: {integrity: sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==}
     engines: {node: '>= 18'}
     peerDependencies:
-      '@octokit/core': '>=5'
+      '@octokit/core': '5'
     dependencies:
       '@octokit/core': 5.1.0
       '@octokit/types': 12.6.0
@@ -1422,9 +1814,9 @@ packages:
     engines: {node: '>= 18'}
     dependencies:
       '@octokit/core': 5.1.0
-      '@octokit/plugin-paginate-rest': 9.2.0(@octokit/core@5.1.0)
-      '@octokit/plugin-request-log': 4.0.0(@octokit/core@5.1.0)
-      '@octokit/plugin-rest-endpoint-methods': 10.4.0(@octokit/core@5.1.0)
+      '@octokit/plugin-paginate-rest': 9.2.1(@octokit/core@5.1.0)
+      '@octokit/plugin-request-log': 4.0.1(@octokit/core@5.1.0)
+      '@octokit/plugin-rest-endpoint-methods': 10.4.1(@octokit/core@5.1.0)
     dev: true
 
   /@octokit/types@12.6.0:
@@ -1433,6 +1825,10 @@ packages:
       '@octokit/openapi-types': 20.0.0
     dev: true
 
+  /@one-ini/wasm@0.1.1:
+    resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
+    dev: true
+
   /@pkgjs/parseargs@0.11.0:
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
     engines: {node: '>=14'}
@@ -1440,6 +1836,11 @@ packages:
     dev: true
     optional: true
 
+  /@pkgr/core@0.1.1:
+    resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
+    engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+    dev: true
+
   /@pnpm/config.env-replace@1.1.0:
     resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==}
     engines: {node: '>=12.22.0'}
@@ -1470,13 +1871,137 @@ packages:
       '@iarna/toml': 2.2.5
       detect-indent: 7.0.1
       fast-glob: 3.3.2
-      ini: 4.1.1
+      ini: 4.1.2
       js-yaml: 4.1.0
       lodash-es: 4.17.21
-      release-it: 17.1.1(typescript@5.3.3)
+      release-it: 17.1.1(typescript@5.4.3)
       semver: 7.6.0
     dev: true
 
+  /@rollup/rollup-android-arm-eabi@4.14.0:
+    resolution: {integrity: sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==}
+    cpu: [arm]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-android-arm64@4.14.0:
+    resolution: {integrity: sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==}
+    cpu: [arm64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-darwin-arm64@4.14.0:
+    resolution: {integrity: sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-darwin-x64@4.14.0:
+    resolution: {integrity: sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-arm-gnueabihf@4.14.0:
+    resolution: {integrity: sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-arm64-gnu@4.14.0:
+    resolution: {integrity: sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-arm64-musl@4.14.0:
+    resolution: {integrity: sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-powerpc64le-gnu@4.14.0:
+    resolution: {integrity: sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==}
+    cpu: [ppc64le]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-riscv64-gnu@4.14.0:
+    resolution: {integrity: sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==}
+    cpu: [riscv64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-s390x-gnu@4.14.0:
+    resolution: {integrity: sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==}
+    cpu: [s390x]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-x64-gnu@4.14.0:
+    resolution: {integrity: sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-x64-musl@4.14.0:
+    resolution: {integrity: sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-win32-arm64-msvc@4.14.0:
+    resolution: {integrity: sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==}
+    cpu: [arm64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-win32-ia32-msvc@4.14.0:
+    resolution: {integrity: sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-win32-x64-msvc@4.14.0:
+    resolution: {integrity: sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rushstack/eslint-patch@1.10.1:
+    resolution: {integrity: sha512-S3Kq8e7LqxkA9s7HKLqXGTGck1uwis5vAXan3FnU5yw1Ec5hsSGnq4s/UCaSqABPOnOTg7zASLyst7+ohgWexg==}
+    dev: true
+
   /@sinclair/typebox@0.27.8:
     resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
     dev: true
@@ -1535,17 +2060,17 @@ packages:
     resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==}
     dev: true
 
-  /@ts-morph/common@0.22.0:
-    resolution: {integrity: sha512-HqNBuV/oIlMKdkLshXd1zKBqNQCsuPEsgQOkfFQ/eUKjRlwndXW1AjN9LVkBEIukm00gGXSRmfkl0Wv5VXLnlw==}
+  /@ts-morph/common@0.23.0:
+    resolution: {integrity: sha512-m7Lllj9n/S6sOkCkRftpM7L24uvmfXQFedlW/4hENcuJH1HHm9u5EgxZb9uVjQSCGrbBWBkOGgcTxNg36r6ywA==}
     dependencies:
       fast-glob: 3.3.2
-      minimatch: 9.0.3
+      minimatch: 9.0.4
       mkdirp: 3.0.1
       path-browserify: 1.0.1
     dev: false
 
-  /@tsconfig/node10@1.0.9:
-    resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
+  /@tsconfig/node10@1.0.11:
+    resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
     dev: true
 
   /@tsconfig/node12@1.0.11:
@@ -1560,10 +2085,18 @@ packages:
     resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
     dev: true
 
+  /@tsconfig/node20@20.1.4:
+    resolution: {integrity: sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==}
+    dev: true
+
   /@types/conventional-commits-parser@5.0.0:
     resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.12.3
+    dev: true
+
+  /@types/estree@1.0.5:
+    resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
     dev: true
 
   /@types/geojson@7946.0.14:
@@ -1590,6 +2123,14 @@ packages:
       '@types/istanbul-lib-report': 3.0.3
     dev: true
 
+  /@types/jsdom@21.1.6:
+    resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==}
+    dependencies:
+      '@types/node': 20.12.3
+      '@types/tough-cookie': 4.0.5
+      parse5: 7.1.2
+    dev: true
+
   /@types/json-schema@7.0.15:
     resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
     dev: true
@@ -1606,8 +2147,8 @@ packages:
     resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==}
     dev: false
 
-  /@types/node@20.11.24:
-    resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==}
+  /@types/node@20.12.3:
+    resolution: {integrity: sha512-sD+ia2ubTeWrOu+YMF+MTAB7E+O7qsMqAbMfW7DG3K1URwhZ5hN1pLlRVGbf4wDFzSfikL05M17EyorS86jShw==}
     dependencies:
       undici-types: 5.26.5
     dev: true
@@ -1628,13 +2169,17 @@ packages:
     resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
     dev: true
 
-  /@types/tar@6.1.11:
-    resolution: {integrity: sha512-ThA1WD8aDdVU4VLuyq5NEqriwXErF5gEIJeyT6gHBWU7JtSmW2a5qjNv3/vR82O20mW+1vhmeZJfBQPT3HCugg==}
+  /@types/tar@6.1.12:
+    resolution: {integrity: sha512-FwbJPi9YuovB6ilnHrz8Y4pb0Fh6N7guFkbnlCl39ua893Qi5gkXui7LSDpTQMJCmA4z5f6SeSrTPQEWLdtFVw==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.12.3
       minipass: 4.2.8
     dev: true
 
+  /@types/tough-cookie@4.0.5:
+    resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
+    dev: true
+
   /@types/triple-beam@1.3.5:
     resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
     dev: false
@@ -1656,7 +2201,7 @@ packages:
   /@types/ws@8.5.10:
     resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
     dependencies:
-      '@types/node': 20.11.24
+      '@types/node': 20.12.3
     dev: true
 
   /@types/yargs-parser@21.0.3:
@@ -1669,9 +2214,9 @@ packages:
       '@types/yargs-parser': 21.0.3
     dev: true
 
-  /@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  /@typescript-eslint/eslint-plugin@7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.57.0)(typescript@5.4.3):
+    resolution: {integrity: sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==}
+    engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       '@typescript-eslint/parser': ^7.0.0
       eslint: ^8.56.0
@@ -1681,47 +2226,55 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.10.0
-      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/scope-manager': 7.1.0
-      '@typescript-eslint/type-utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/visitor-keys': 7.1.0
+      '@typescript-eslint/parser': 7.5.0(eslint@8.57.0)(typescript@5.4.3)
+      '@typescript-eslint/scope-manager': 7.5.0
+      '@typescript-eslint/type-utils': 7.5.0(eslint@8.57.0)(typescript@5.4.3)
+      '@typescript-eslint/utils': 7.5.0(eslint@8.57.0)(typescript@5.4.3)
+      '@typescript-eslint/visitor-keys': 7.5.0
       debug: 4.3.4
       eslint: 8.57.0
       graphemer: 1.4.0
       ignore: 5.3.1
       natural-compare: 1.4.0
       semver: 7.6.0
-      ts-api-utils: 1.2.1(typescript@5.3.3)
-      typescript: 5.3.3
+      ts-api-utils: 1.3.0(typescript@5.4.3)
+      typescript: 5.4.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  /@typescript-eslint/parser@7.5.0(eslint@8.57.0)(typescript@5.4.3):
+    resolution: {integrity: sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==}
+    engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
-      eslint: ^7.0.0 || ^8.0.0
+      eslint: ^8.56.0
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/scope-manager': 6.21.0
-      '@typescript-eslint/types': 6.21.0
-      '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3)
-      '@typescript-eslint/visitor-keys': 6.21.0
+      '@typescript-eslint/scope-manager': 7.5.0
+      '@typescript-eslint/types': 7.5.0
+      '@typescript-eslint/typescript-estree': 7.5.0(typescript@5.4.3)
+      '@typescript-eslint/visitor-keys': 7.5.0
       debug: 4.3.4
       eslint: 8.57.0
-      typescript: 5.3.3
+      typescript: 5.4.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  /@typescript-eslint/scope-manager@7.5.0:
+    resolution: {integrity: sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    dependencies:
+      '@typescript-eslint/types': 7.5.0
+      '@typescript-eslint/visitor-keys': 7.5.0
+    dev: true
+
+  /@typescript-eslint/type-utils@7.5.0(eslint@8.57.0)(typescript@5.4.3):
+    resolution: {integrity: sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==}
+    engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       eslint: ^8.56.0
       typescript: '*'
@@ -1729,144 +2282,317 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/scope-manager': 7.1.0
-      '@typescript-eslint/types': 7.1.0
-      '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
-      '@typescript-eslint/visitor-keys': 7.1.0
+      '@typescript-eslint/typescript-estree': 7.5.0(typescript@5.4.3)
+      '@typescript-eslint/utils': 7.5.0(eslint@8.57.0)(typescript@5.4.3)
       debug: 4.3.4
       eslint: 8.57.0
-      typescript: 5.3.3
+      ts-api-utils: 1.3.0(typescript@5.4.3)
+      typescript: 5.4.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/scope-manager@6.21.0:
-    resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    dependencies:
-      '@typescript-eslint/types': 6.21.0
-      '@typescript-eslint/visitor-keys': 6.21.0
-    dev: true
-
-  /@typescript-eslint/scope-manager@7.1.0:
-    resolution: {integrity: sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    dependencies:
-      '@typescript-eslint/types': 7.1.0
-      '@typescript-eslint/visitor-keys': 7.1.0
+  /@typescript-eslint/types@7.5.0:
+    resolution: {integrity: sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==}
+    engines: {node: ^18.18.0 || >=20.0.0}
     dev: true
 
-  /@typescript-eslint/type-utils@7.1.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  /@typescript-eslint/typescript-estree@7.5.0(typescript@5.4.3):
+    resolution: {integrity: sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==}
+    engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
-      eslint: ^8.56.0
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
-      '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/types': 7.5.0
+      '@typescript-eslint/visitor-keys': 7.5.0
       debug: 4.3.4
+      globby: 11.1.0
+      is-glob: 4.0.3
+      minimatch: 9.0.3
+      semver: 7.6.0
+      ts-api-utils: 1.3.0(typescript@5.4.3)
+      typescript: 5.4.3
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/utils@7.5.0(eslint@8.57.0)(typescript@5.4.3):
+    resolution: {integrity: sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
+      '@types/json-schema': 7.0.15
+      '@types/semver': 7.5.8
+      '@typescript-eslint/scope-manager': 7.5.0
+      '@typescript-eslint/types': 7.5.0
+      '@typescript-eslint/typescript-estree': 7.5.0(typescript@5.4.3)
       eslint: 8.57.0
-      ts-api-utils: 1.2.1(typescript@5.3.3)
-      typescript: 5.3.3
+      semver: 7.6.0
     transitivePeerDependencies:
       - supports-color
+      - typescript
     dev: true
 
-  /@typescript-eslint/types@6.21.0:
-    resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  /@typescript-eslint/visitor-keys@7.5.0:
+    resolution: {integrity: sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    dependencies:
+      '@typescript-eslint/types': 7.5.0
+      eslint-visitor-keys: 3.4.3
     dev: true
 
-  /@typescript-eslint/types@7.1.0:
-    resolution: {integrity: sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  /@ungap/structured-clone@1.2.0:
+    resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
     dev: true
 
-  /@typescript-eslint/typescript-estree@6.21.0(typescript@5.3.3):
-    resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  /@vitejs/plugin-vue-jsx@3.1.0(vite@5.2.8)(vue@3.4.21):
+    resolution: {integrity: sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==}
+    engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+      vite: ^4.0.0 || ^5.0.0
+      vue: ^3.0.0
+    dependencies:
+      '@babel/core': 7.24.4
+      '@babel/plugin-transform-typescript': 7.24.4(@babel/core@7.24.4)
+      '@vue/babel-plugin-jsx': 1.2.2(@babel/core@7.24.4)
+      vite: 5.2.8(@types/node@20.12.3)
+      vue: 3.4.21(typescript@5.4.3)
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@vitejs/plugin-vue@5.0.4(vite@5.2.8)(vue@3.4.21):
+    resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    peerDependencies:
+      vite: ^5.0.0
+      vue: ^3.2.25
+    dependencies:
+      vite: 5.2.8(@types/node@20.12.3)
+      vue: 3.4.21(typescript@5.4.3)
+    dev: true
+
+  /@vitest/coverage-v8@1.4.0(vitest@1.4.0):
+    resolution: {integrity: sha512-4hDGyH1SvKpgZnIByr9LhGgCEuF9DKM34IBLCC/fVfy24Z3+PZ+Ii9hsVBsHvY1umM1aGPEjceRkzxCfcQ10wg==}
+    peerDependencies:
+      vitest: 1.4.0
     dependencies:
-      '@typescript-eslint/types': 6.21.0
-      '@typescript-eslint/visitor-keys': 6.21.0
+      '@ampproject/remapping': 2.3.0
+      '@bcoe/v8-coverage': 0.2.3
       debug: 4.3.4
-      globby: 11.1.0
-      is-glob: 4.0.3
-      minimatch: 9.0.3
-      semver: 7.6.0
-      ts-api-utils: 1.2.1(typescript@5.3.3)
-      typescript: 5.3.3
+      istanbul-lib-coverage: 3.2.2
+      istanbul-lib-report: 3.0.1
+      istanbul-lib-source-maps: 5.0.4
+      istanbul-reports: 3.1.7
+      magic-string: 0.30.8
+      magicast: 0.3.3
+      picocolors: 1.0.0
+      std-env: 3.7.0
+      strip-literal: 2.1.0
+      test-exclude: 6.0.0
+      v8-to-istanbul: 9.2.0
+      vitest: 1.4.0(@types/node@20.12.3)(jsdom@24.0.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/typescript-estree@7.1.0(typescript@5.3.3):
-    resolution: {integrity: sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  /@vitest/expect@1.4.0:
+    resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==}
+    dependencies:
+      '@vitest/spy': 1.4.0
+      '@vitest/utils': 1.4.0
+      chai: 4.4.1
+    dev: true
+
+  /@vitest/runner@1.4.0:
+    resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==}
+    dependencies:
+      '@vitest/utils': 1.4.0
+      p-limit: 5.0.0
+      pathe: 1.1.2
+    dev: true
+
+  /@vitest/snapshot@1.4.0:
+    resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==}
+    dependencies:
+      magic-string: 0.30.8
+      pathe: 1.1.2
+      pretty-format: 29.7.0
+    dev: true
+
+  /@vitest/spy@1.4.0:
+    resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==}
+    dependencies:
+      tinyspy: 2.2.1
+    dev: true
+
+  /@vitest/utils@1.4.0:
+    resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==}
+    dependencies:
+      diff-sequences: 29.6.3
+      estree-walker: 3.0.3
+      loupe: 2.3.7
+      pretty-format: 29.7.0
+    dev: true
+
+  /@vue/babel-helper-vue-transform-on@1.2.2:
+    resolution: {integrity: sha512-nOttamHUR3YzdEqdM/XXDyCSdxMA9VizUKoroLX6yTyRtggzQMHXcmwh8a7ZErcJttIBIc9s68a1B8GZ+Dmvsw==}
+    dev: true
+
+  /@vue/babel-plugin-jsx@1.2.2(@babel/core@7.24.4):
+    resolution: {integrity: sha512-nYTkZUVTu4nhP199UoORePsql0l+wj7v/oyQjtThUVhJl1U+6qHuoVhIvR3bf7eVKjbCK+Cs2AWd7mi9Mpz9rA==}
     peerDependencies:
-      typescript: '*'
+      '@babel/core': ^7.0.0-0
     peerDependenciesMeta:
-      typescript:
+      '@babel/core':
         optional: true
     dependencies:
-      '@typescript-eslint/types': 7.1.0
-      '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4
-      globby: 11.1.0
-      is-glob: 4.0.3
-      minimatch: 9.0.3
-      semver: 7.6.0
-      ts-api-utils: 1.2.1(typescript@5.3.3)
-      typescript: 5.3.3
+      '@babel/core': 7.24.4
+      '@babel/helper-module-imports': 7.22.15
+      '@babel/helper-plugin-utils': 7.24.0
+      '@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.4)
+      '@babel/template': 7.24.0
+      '@babel/traverse': 7.24.1
+      '@babel/types': 7.24.0
+      '@vue/babel-helper-vue-transform-on': 1.2.2
+      '@vue/babel-plugin-resolve-type': 1.2.2(@babel/core@7.24.4)
+      camelcase: 6.3.0
+      html-tags: 3.3.1
+      svg-tags: 1.0.0
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/utils@7.1.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  /@vue/babel-plugin-resolve-type@1.2.2(@babel/core@7.24.4):
+    resolution: {integrity: sha512-EntyroPwNg5IPVdUJupqs0CFzuf6lUrVvCspmv2J1FITLeGnUCuoGNNk78dgCusxEiYj6RMkTJflGSxk5aIC4A==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/code-frame': 7.24.2
+      '@babel/core': 7.24.4
+      '@babel/helper-module-imports': 7.22.15
+      '@babel/helper-plugin-utils': 7.24.0
+      '@babel/parser': 7.24.4
+      '@vue/compiler-sfc': 3.4.21
+    dev: true
+
+  /@vue/compiler-core@3.4.21:
+    resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
+    dependencies:
+      '@babel/parser': 7.24.4
+      '@vue/shared': 3.4.21
+      entities: 4.5.0
+      estree-walker: 2.0.2
+      source-map-js: 1.2.0
+
+  /@vue/compiler-dom@3.4.21:
+    resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==}
+    dependencies:
+      '@vue/compiler-core': 3.4.21
+      '@vue/shared': 3.4.21
+
+  /@vue/compiler-sfc@3.4.21:
+    resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==}
+    dependencies:
+      '@babel/parser': 7.24.4
+      '@vue/compiler-core': 3.4.21
+      '@vue/compiler-dom': 3.4.21
+      '@vue/compiler-ssr': 3.4.21
+      '@vue/shared': 3.4.21
+      estree-walker: 2.0.2
+      magic-string: 0.30.8
+      postcss: 8.4.38
+      source-map-js: 1.2.0
+
+  /@vue/compiler-ssr@3.4.21:
+    resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==}
+    dependencies:
+      '@vue/compiler-dom': 3.4.21
+      '@vue/shared': 3.4.21
+
+  /@vue/devtools-api@6.6.1:
+    resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
+    dev: false
+
+  /@vue/eslint-config-prettier@9.0.0(eslint@8.57.0)(prettier@3.2.5):
+    resolution: {integrity: sha512-z1ZIAAUS9pKzo/ANEfd2sO+v2IUalz7cM/cTLOZ7vRFOPk5/xuRKQteOu1DErFLAh/lYGXMVZ0IfYKlyInuDVg==}
+    peerDependencies:
+      eslint: '>= 8.0.0'
+      prettier: '>= 3.0.0'
+    dependencies:
+      eslint: 8.57.0
+      eslint-config-prettier: 9.1.0(eslint@8.57.0)
+      eslint-plugin-prettier: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5)
+      prettier: 3.2.5
+    transitivePeerDependencies:
+      - '@types/eslint'
+    dev: true
+
+  /@vue/eslint-config-typescript@13.0.0(eslint-plugin-vue@9.24.0)(eslint@8.57.0)(typescript@5.4.3):
+    resolution: {integrity: sha512-MHh9SncG/sfqjVqjcuFLOLD6Ed4dRAis4HNt0dXASeAuLqIAx4YMB1/m2o4pUKK1vCt8fUvYG8KKX2Ot3BVZTg==}
+    engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       eslint: ^8.56.0
+      eslint-plugin-vue: ^9.0.0
+      typescript: '>=4.7.4'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
-      '@types/json-schema': 7.0.15
-      '@types/semver': 7.5.8
-      '@typescript-eslint/scope-manager': 7.1.0
-      '@typescript-eslint/types': 7.1.0
-      '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
+      '@typescript-eslint/eslint-plugin': 7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.57.0)(typescript@5.4.3)
+      '@typescript-eslint/parser': 7.5.0(eslint@8.57.0)(typescript@5.4.3)
       eslint: 8.57.0
-      semver: 7.6.0
+      eslint-plugin-vue: 9.24.0(eslint@8.57.0)
+      typescript: 5.4.3
+      vue-eslint-parser: 9.4.2(eslint@8.57.0)
     transitivePeerDependencies:
       - supports-color
-      - typescript
     dev: true
 
-  /@typescript-eslint/visitor-keys@6.21.0:
-    resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  /@vue/reactivity@3.4.21:
+    resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
     dependencies:
-      '@typescript-eslint/types': 6.21.0
-      eslint-visitor-keys: 3.4.3
-    dev: true
+      '@vue/shared': 3.4.21
 
-  /@typescript-eslint/visitor-keys@7.1.0:
-    resolution: {integrity: sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  /@vue/runtime-core@3.4.21:
+    resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==}
     dependencies:
-      '@typescript-eslint/types': 7.1.0
-      eslint-visitor-keys: 3.4.3
+      '@vue/reactivity': 3.4.21
+      '@vue/shared': 3.4.21
+
+  /@vue/runtime-dom@3.4.21:
+    resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==}
+    dependencies:
+      '@vue/runtime-core': 3.4.21
+      '@vue/shared': 3.4.21
+      csstype: 3.1.3
+
+  /@vue/server-renderer@3.4.21(vue@3.4.21):
+    resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==}
+    peerDependencies:
+      vue: 3.4.21
+    dependencies:
+      '@vue/compiler-ssr': 3.4.21
+      '@vue/shared': 3.4.21
+      vue: 3.4.21(typescript@5.4.3)
+
+  /@vue/shared@3.4.21:
+    resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==}
+
+  /@vue/test-utils@2.4.5:
+    resolution: {integrity: sha512-oo2u7vktOyKUked36R93NB7mg2B+N7Plr8lxp2JBGwr18ch6EggFjixSCdIVVLkT6Qr0z359Xvnafc9dcKyDUg==}
+    dependencies:
+      js-beautify: 1.15.1
+      vue-component-type-helpers: 2.0.7
     dev: true
 
-  /@ungap/structured-clone@1.2.0:
-    resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+  /@vue/tsconfig@0.5.1:
+    resolution: {integrity: sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==}
     dev: true
 
   /@webgpu/types@0.1.16:
@@ -1886,6 +2612,11 @@ packages:
     requiresBuild: true
     optional: true
 
+  /abbrev@2.0.0:
+    resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+    dev: true
+
   /abort-controller@3.0.0:
     resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
     engines: {node: '>=6.5'}
@@ -1941,8 +2672,8 @@ packages:
       - supports-color
     optional: true
 
-  /agent-base@7.1.0:
-    resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
+  /agent-base@7.1.1:
+    resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==}
     engines: {node: '>= 14'}
     dependencies:
       debug: 4.3.4
@@ -1975,6 +2706,18 @@ packages:
         optional: true
     dependencies:
       ajv: 8.12.0
+    dev: true
+
+  /ajv-formats@3.0.1(ajv@8.12.0):
+    resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
+    peerDependencies:
+      ajv: ^8.0.0
+    peerDependenciesMeta:
+      ajv:
+        optional: true
+    dependencies:
+      ajv: 8.12.0
+    dev: false
 
   /ajv@6.12.6:
     resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
@@ -2011,11 +2754,9 @@ packages:
       type-fest: 0.21.3
     dev: true
 
-  /ansi-escapes@6.2.0:
-    resolution: {integrity: sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==}
+  /ansi-escapes@6.2.1:
+    resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==}
     engines: {node: '>=14.16'}
-    dependencies:
-      type-fest: 3.13.1
     dev: true
 
   /ansi-regex@2.1.1:
@@ -2131,13 +2872,14 @@ packages:
     resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==}
     dev: true
 
-  /array-includes@3.1.7:
-    resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==}
+  /array-includes@3.1.8:
+    resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
     engines: {node: '>= 0.4'}
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-abstract: 1.23.3
+      es-object-atoms: 1.0.0
       get-intrinsic: 1.2.4
       is-string: 1.0.7
     dev: true
@@ -2146,25 +2888,15 @@ packages:
     resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
     engines: {node: '>=8'}
 
-  /array.prototype.filter@1.0.3:
-    resolution: {integrity: sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==}
+  /array.prototype.findlastindex@1.2.5:
+    resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==}
     engines: {node: '>= 0.4'}
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
-      es-array-method-boxes-properly: 1.0.0
-      is-string: 1.0.7
-    dev: true
-
-  /array.prototype.findlastindex@1.2.4:
-    resolution: {integrity: sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-abstract: 1.23.3
       es-errors: 1.3.0
+      es-object-atoms: 1.0.0
       es-shim-unscopables: 1.0.2
     dev: true
 
@@ -2174,7 +2906,7 @@ packages:
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-abstract: 1.23.3
       es-shim-unscopables: 1.0.2
     dev: true
 
@@ -2184,18 +2916,19 @@ packages:
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-abstract: 1.23.3
       es-shim-unscopables: 1.0.2
     dev: true
 
-  /array.prototype.map@1.0.6:
-    resolution: {integrity: sha512-nK1psgF2cXqP3wSyCSq0Hc7zwNq3sfljQqaG27r/7a7ooNUnn5nGq6yYWyks9jMO5EoFQ0ax80hSg6oXSRNXaw==}
+  /array.prototype.map@1.0.7:
+    resolution: {integrity: sha512-XpcFfLoBEAhezrrNw1V+yLXkE7M6uR7xJEsxbG6c/V9v043qurwVJB9r9UTnoSioFDoz1i1VOydpWGmJpfVZbg==}
     engines: {node: '>= 0.4'}
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-abstract: 1.23.3
       es-array-method-boxes-properly: 1.0.0
+      es-object-atoms: 1.0.0
       is-string: 1.0.7
     dev: true
 
@@ -2206,20 +2939,19 @@ packages:
       array-buffer-byte-length: 1.0.1
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-abstract: 1.23.3
       es-errors: 1.3.0
       get-intrinsic: 1.2.4
       is-array-buffer: 3.0.4
       is-shared-array-buffer: 1.0.3
     dev: true
 
-  /asn1.js@5.4.1:
-    resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==}
+  /asn1.js@4.10.1:
+    resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==}
     dependencies:
       bn.js: 4.12.0
       inherits: 2.0.4
       minimalistic-assert: 1.0.1
-      safer-buffer: 2.1.2
     dev: true
 
   /asn1@0.2.6:
@@ -2240,6 +2972,10 @@ packages:
       util: 0.10.4
     dev: true
 
+  /assertion-error@1.1.0:
+    resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
+    dev: true
+
   /ast-types@0.13.4:
     resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
     engines: {node: '>=4'}
@@ -2296,7 +3032,7 @@ packages:
     dependencies:
       chalk: 4.1.2
       char-spinner: 1.0.1
-      cli-table3: 0.6.3
+      cli-table3: 0.6.4
       color-support: 1.1.3
       cross-argv: 2.0.0
       form-data: 4.0.0
@@ -2358,8 +3094,8 @@ packages:
     resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
     dev: true
 
-  /binary-extensions@2.2.0:
-    resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
+  /binary-extensions@2.3.0:
+    resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
     engines: {node: '>=8'}
     dev: true
 
@@ -2387,6 +3123,10 @@ packages:
     resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==}
     dev: true
 
+  /boolbase@1.0.0:
+    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+    dev: true
+
   /boxen@5.1.2:
     resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==}
     engines: {node: '>=10'}
@@ -2504,18 +3244,19 @@ packages:
       randombytes: 2.1.0
     dev: true
 
-  /browserify-sign@4.2.2:
-    resolution: {integrity: sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==}
-    engines: {node: '>= 4'}
+  /browserify-sign@4.2.3:
+    resolution: {integrity: sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==}
+    engines: {node: '>= 0.12'}
     dependencies:
       bn.js: 5.2.1
       browserify-rsa: 4.1.0
       create-hash: 1.2.0
       create-hmac: 1.1.7
-      elliptic: 6.5.4
+      elliptic: 6.5.5
+      hash-base: 3.0.4
       inherits: 2.0.4
-      parse-asn1: 5.1.6
-      readable-stream: 3.6.2
+      parse-asn1: 5.1.7
+      readable-stream: 2.3.8
       safe-buffer: 5.2.1
     dev: true
 
@@ -2580,8 +3321,19 @@ packages:
       xtend: 4.0.2
     dev: true
 
-  /bson@6.3.0:
-    resolution: {integrity: sha512-balJfqwwTBddxfnidJZagCBPP/f48zj9Sdp3OJswREOgsJzHiQSaOIAtApSgDQFYgHqAvFkp53AFSqjMDZoTFw==}
+  /browserslist@4.23.0:
+    resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+    dependencies:
+      caniuse-lite: 1.0.30001605
+      electron-to-chromium: 1.4.724
+      node-releases: 2.0.14
+      update-browserslist-db: 1.0.13(browserslist@4.23.0)
+    dev: true
+
+  /bson@6.6.0:
+    resolution: {integrity: sha512-BVINv2SgcMjL4oYbBuCQTpE3/VKOSxrOA8Cj/wQP7izSzlBGVomdm+TcUd0Pzy0ytLSSDweCKQ6X3f5veM5LQA==}
     engines: {node: '>=16.20.1'}
     dev: false
 
@@ -2617,7 +3369,6 @@ packages:
     requiresBuild: true
     dependencies:
       node-gyp-build: 4.8.0
-    dev: false
 
   /builtin-modules@3.3.0:
     resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
@@ -2659,6 +3410,11 @@ packages:
       yargs-parser: 21.1.1
     dev: true
 
+  /cac@6.7.14:
+    resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+    engines: {node: '>=8'}
+    dev: true
+
   /cacache@15.3.0:
     resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==}
     engines: {node: '>= 10'}
@@ -2680,7 +3436,7 @@ packages:
       promise-inflight: 1.0.1
       rimraf: 3.0.2
       ssri: 8.0.1
-      tar: 6.2.0
+      tar: 6.2.1
       unique-filename: 1.1.1
     transitivePeerDependencies:
       - bluebird
@@ -2700,7 +3456,7 @@ packages:
       http-cache-semantics: 4.1.1
       keyv: 4.5.4
       mimic-response: 4.0.0
-      normalize-url: 8.0.0
+      normalize-url: 8.0.1
       responselike: 3.0.0
     dev: true
 
@@ -2716,7 +3472,7 @@ packages:
       es-errors: 1.3.0
       function-bind: 1.1.2
       get-intrinsic: 1.2.4
-      set-function-length: 1.2.1
+      set-function-length: 1.2.2
     dev: true
 
   /callsites@3.1.0:
@@ -2746,6 +3502,10 @@ packages:
     engines: {node: '>=14.16'}
     dev: true
 
+  /caniuse-lite@1.0.30001605:
+    resolution: {integrity: sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==}
+    dev: true
+
   /caseless@0.12.0:
     resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
     dev: true
@@ -2754,6 +3514,19 @@ packages:
     resolution: {integrity: sha512-twuUuJRrIrsELHz6foJtZlqrz6FC36zoHZJvvThsrM1UWPKxyoilw1Rka6Hk0AmPFKHKUoGwGfAtvNZNtNZu0g==}
     dev: true
 
+  /chai@4.4.1:
+    resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==}
+    engines: {node: '>=4'}
+    dependencies:
+      assertion-error: 1.1.0
+      check-error: 1.0.3
+      deep-eql: 4.1.3
+      get-func-name: 2.0.2
+      loupe: 2.3.7
+      pathval: 1.1.1
+      type-detect: 4.0.8
+    dev: true
+
   /chalk@1.1.3:
     resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==}
     engines: {node: '>=0.10.0'}
@@ -2794,6 +3567,12 @@ packages:
     resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
     dev: true
 
+  /check-error@1.0.3:
+    resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
+    dependencies:
+      get-func-name: 2.0.2
+    dev: true
+
   /chokidar@3.6.0:
     resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
     engines: {node: '>= 8.10.0'}
@@ -2873,8 +3652,8 @@ packages:
     engines: {node: '>=6'}
     dev: true
 
-  /cli-table3@0.6.3:
-    resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==}
+  /cli-table3@0.6.4:
+    resolution: {integrity: sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==}
     engines: {node: 10.* || >= 12.*}
     dependencies:
       string-width: 4.2.3
@@ -2954,8 +3733,8 @@ packages:
     engines: {node: '>=0.8'}
     dev: true
 
-  /code-block-writer@12.0.0:
-    resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==}
+  /code-block-writer@13.0.1:
+    resolution: {integrity: sha512-c5or4P6erEA69TxaxTNcHUNcIn+oyxSRTOWV+pSYF+z4epXqNvwvJ70XPGjPNgue83oAFAPBRQYwpAJ/Hpe/Sg==}
     dev: false
 
   /code-point-at@1.1.0:
@@ -3200,7 +3979,7 @@ packages:
     resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
     dev: true
 
-  /cosmiconfig-typescript-loader@5.0.0(@types/node@20.11.24)(cosmiconfig@8.3.6)(typescript@5.3.3):
+  /cosmiconfig-typescript-loader@5.0.0(@types/node@20.12.3)(cosmiconfig@9.0.0)(typescript@5.4.3):
     resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==}
     engines: {node: '>=v16'}
     peerDependencies:
@@ -3208,29 +3987,13 @@ packages:
       cosmiconfig: '>=8.2'
       typescript: '>=4'
     dependencies:
-      '@types/node': 20.11.24
-      cosmiconfig: 8.3.6(typescript@5.3.3)
+      '@types/node': 20.12.3
+      cosmiconfig: 9.0.0(typescript@5.4.3)
       jiti: 1.21.0
-      typescript: 5.3.3
-    dev: true
-
-  /cosmiconfig@8.3.6(typescript@5.3.3):
-    resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==}
-    engines: {node: '>=14'}
-    peerDependencies:
-      typescript: '>=4.9.5'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      import-fresh: 3.3.0
-      js-yaml: 4.1.0
-      parse-json: 5.2.0
-      path-type: 4.0.0
-      typescript: 5.3.3
+      typescript: 5.4.3
     dev: true
 
-  /cosmiconfig@9.0.0(typescript@5.3.3):
+  /cosmiconfig@9.0.0(typescript@5.4.3):
     resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==}
     engines: {node: '>=14'}
     peerDependencies:
@@ -3243,14 +4006,14 @@ packages:
       import-fresh: 3.3.0
       js-yaml: 4.1.0
       parse-json: 5.2.0
-      typescript: 5.3.3
+      typescript: 5.4.3
     dev: true
 
   /create-ecdh@4.0.4:
     resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==}
     dependencies:
       bn.js: 4.12.0
-      elliptic: 6.5.4
+      elliptic: 6.5.5
     dev: true
 
   /create-hash@1.2.0:
@@ -3307,7 +4070,7 @@ packages:
     resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==}
     dependencies:
       browserify-cipher: 1.0.1
-      browserify-sign: 4.2.2
+      browserify-sign: 4.2.3
       create-ecdh: 4.0.4
       create-hash: 1.2.0
       create-hmac: 1.1.7
@@ -3331,6 +4094,22 @@ packages:
       type-fest: 1.4.0
     dev: true
 
+  /cssesc@3.0.0:
+    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+    engines: {node: '>=4'}
+    hasBin: true
+    dev: true
+
+  /cssstyle@4.0.1:
+    resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==}
+    engines: {node: '>=18'}
+    dependencies:
+      rrweb-cssom: 0.6.0
+    dev: true
+
+  /csstype@3.1.3:
+    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
   /cwise-compiler@1.1.3:
     resolution: {integrity: sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==}
     dependencies:
@@ -3460,11 +4239,12 @@ packages:
       d3-transition: 1.3.2
     dev: true
 
-  /d@1.0.1:
-    resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==}
+  /d@1.0.2:
+    resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
+    engines: {node: '>=0.12'}
     dependencies:
       es5-ext: 0.10.64
-      type: 1.2.0
+      type: 2.7.2
     dev: true
 
   /dargs@7.0.0:
@@ -3502,11 +4282,46 @@ packages:
     engines: {node: '>= 14'}
     dev: true
 
+  /data-urls@5.0.0:
+    resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
+    engines: {node: '>=18'}
+    dependencies:
+      whatwg-mimetype: 4.0.0
+      whatwg-url: 14.0.0
+    dev: true
+
+  /data-view-buffer@1.0.1:
+    resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.7
+      es-errors: 1.3.0
+      is-data-view: 1.0.1
+    dev: true
+
+  /data-view-byte-length@1.0.1:
+    resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.7
+      es-errors: 1.3.0
+      is-data-view: 1.0.1
+    dev: true
+
+  /data-view-byte-offset@1.0.0:
+    resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind: 1.0.7
+      es-errors: 1.3.0
+      is-data-view: 1.0.1
+    dev: true
+
   /dataloader@2.2.2:
     resolution: {integrity: sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==}
 
-  /date-fns@3.3.1:
-    resolution: {integrity: sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==}
+  /date-fns@3.6.0:
+    resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
     dev: false
 
   /debounce-fn@4.0.0:
@@ -3520,6 +4335,17 @@ packages:
     resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
     dev: true
 
+  /debug@2.6.9:
+    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.0.0
+    dev: false
+
   /debug@3.2.7:
     resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
     peerDependencies:
@@ -3547,12 +4373,23 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /decimal.js@10.4.3:
+    resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
+    dev: true
+
   /decompress-response@6.0.0:
     resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
     engines: {node: '>=10'}
     dependencies:
       mimic-response: 3.1.0
 
+  /deep-eql@4.1.3:
+    resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
+    engines: {node: '>=6'}
+    dependencies:
+      type-detect: 4.0.8
+    dev: true
+
   /deep-extend@0.6.0:
     resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
     engines: {node: '>=4.0.0'}
@@ -3650,6 +4487,11 @@ packages:
     engines: {node: '>=0.10'}
     dev: false
 
+  /depd@2.0.0:
+    resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+    engines: {node: '>= 0.8'}
+    dev: false
+
   /deprecation@2.3.1:
     resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==}
     dev: true
@@ -3671,13 +4513,18 @@ packages:
       minimalistic-assert: 1.0.1
     dev: true
 
+  /destroy@1.2.0:
+    resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
+    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+    dev: false
+
   /detect-indent@7.0.1:
     resolution: {integrity: sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==}
     engines: {node: '>=12.20'}
     dev: true
 
-  /detect-libc@2.0.2:
-    resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
+  /detect-libc@2.0.3:
+    resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
     engines: {node: '>=8'}
 
   /detective@5.2.1:
@@ -3753,8 +4600,8 @@ packages:
       is-obj: 2.0.0
     dev: true
 
-  /dotenv@16.4.4:
-    resolution: {integrity: sha512-XvPXc8XAQThSjAbY6cQ/9PcBXmFoWuw1sQ3b8HqUCR6ziGXjkTi//kB9SWa2UwqlgdAIuRqAa/9hVljzPehbYg==}
+  /dotenv@16.4.5:
+    resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
     engines: {node: '>=12'}
 
   /dup@1.0.0:
@@ -3767,8 +4614,8 @@ packages:
       readable-stream: 2.3.8
     dev: true
 
-  /duplexify@4.1.2:
-    resolution: {integrity: sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==}
+  /duplexify@4.1.3:
+    resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==}
     dependencies:
       end-of-stream: 1.4.4
       inherits: 2.0.4
@@ -3787,8 +4634,27 @@ packages:
       safer-buffer: 2.1.2
     dev: true
 
-  /elliptic@6.5.4:
-    resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==}
+  /editorconfig@1.0.4:
+    resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
+    engines: {node: '>=14'}
+    hasBin: true
+    dependencies:
+      '@one-ini/wasm': 0.1.1
+      commander: 10.0.1
+      minimatch: 9.0.1
+      semver: 7.6.0
+    dev: true
+
+  /ee-first@1.1.1:
+    resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+    dev: false
+
+  /electron-to-chromium@1.4.724:
+    resolution: {integrity: sha512-RTRvkmRkGhNBPPpdrgtDKvmOEYTrPlXDfc0J/Nfq5s29tEahAwhiX4mmhNzj6febWMleulxVYPh7QwCSL/EldA==}
+    dev: true
+
+  /elliptic@6.5.5:
+    resolution: {integrity: sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==}
     dependencies:
       bn.js: 4.12.0
       brorand: 1.1.0
@@ -3819,6 +4685,11 @@ packages:
     resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
     dev: false
 
+  /encodeurl@1.0.2:
+    resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
+    engines: {node: '>= 0.8'}
+    dev: false
+
   /encoding@0.1.13:
     resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
     requiresBuild: true
@@ -3837,14 +4708,18 @@ packages:
       inherits: 2.0.4
     dev: true
 
-  /enhanced-resolve@5.15.1:
-    resolution: {integrity: sha512-3d3JRbwsCLJsYgvb6NuWEG44jjPSOMuS73L/6+7BZuoKm3W+qXnSoIYVHi8dG7Qcg4inAY4jbzkZ7MnskePeDg==}
+  /enhanced-resolve@5.16.0:
+    resolution: {integrity: sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==}
     engines: {node: '>=10.13.0'}
     dependencies:
       graceful-fs: 4.2.11
       tapable: 2.2.1
     dev: true
 
+  /entities@4.5.0:
+    resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+    engines: {node: '>=0.12'}
+
   /env-paths@2.2.1:
     resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
     engines: {node: '>=6'}
@@ -3864,16 +4739,20 @@ packages:
       is-arrayish: 0.2.1
     dev: true
 
-  /es-abstract@1.22.5:
-    resolution: {integrity: sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==}
+  /es-abstract@1.23.3:
+    resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==}
     engines: {node: '>= 0.4'}
     dependencies:
       array-buffer-byte-length: 1.0.1
       arraybuffer.prototype.slice: 1.0.3
       available-typed-arrays: 1.0.7
       call-bind: 1.0.7
+      data-view-buffer: 1.0.1
+      data-view-byte-length: 1.0.1
+      data-view-byte-offset: 1.0.0
       es-define-property: 1.0.0
       es-errors: 1.3.0
+      es-object-atoms: 1.0.0
       es-set-tostringtag: 2.0.3
       es-to-primitive: 1.2.1
       function.prototype.name: 1.1.6
@@ -3884,10 +4763,11 @@ packages:
       has-property-descriptors: 1.0.2
       has-proto: 1.0.3
       has-symbols: 1.0.3
-      hasown: 2.0.1
+      hasown: 2.0.2
       internal-slot: 1.0.7
       is-array-buffer: 3.0.4
       is-callable: 1.2.7
+      is-data-view: 1.0.1
       is-negative-zero: 2.0.3
       is-regex: 1.1.4
       is-shared-array-buffer: 1.0.3
@@ -3898,17 +4778,17 @@ packages:
       object-keys: 1.1.1
       object.assign: 4.1.5
       regexp.prototype.flags: 1.5.2
-      safe-array-concat: 1.1.0
+      safe-array-concat: 1.1.2
       safe-regex-test: 1.0.3
-      string.prototype.trim: 1.2.8
-      string.prototype.trimend: 1.0.7
-      string.prototype.trimstart: 1.0.7
+      string.prototype.trim: 1.2.9
+      string.prototype.trimend: 1.0.8
+      string.prototype.trimstart: 1.0.8
       typed-array-buffer: 1.0.2
       typed-array-byte-length: 1.0.1
       typed-array-byte-offset: 1.0.2
-      typed-array-length: 1.0.5
+      typed-array-length: 1.0.6
       unbox-primitive: 1.0.2
-      which-typed-array: 1.1.14
+      which-typed-array: 1.1.15
     dev: true
 
   /es-array-method-boxes-properly@1.0.0:
@@ -3934,26 +4814,33 @@ packages:
       get-intrinsic: 1.2.4
       has-symbols: 1.0.3
       is-arguments: 1.1.1
-      is-map: 2.0.2
-      is-set: 2.0.2
+      is-map: 2.0.3
+      is-set: 2.0.3
       is-string: 1.0.7
       isarray: 2.0.5
       stop-iteration-iterator: 1.0.0
     dev: true
 
+  /es-object-atoms@1.0.0:
+    resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      es-errors: 1.3.0
+    dev: true
+
   /es-set-tostringtag@2.0.3:
     resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
     engines: {node: '>= 0.4'}
     dependencies:
       get-intrinsic: 1.2.4
       has-tostringtag: 1.0.2
-      hasown: 2.0.1
+      hasown: 2.0.2
     dev: true
 
   /es-shim-unscopables@1.0.2:
     resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
     dependencies:
-      hasown: 2.0.1
+      hasown: 2.0.2
     dev: true
 
   /es-to-primitive@1.2.1:
@@ -3971,7 +4858,7 @@ packages:
     requiresBuild: true
     dependencies:
       es6-iterator: 2.0.3
-      es6-symbol: 3.1.3
+      es6-symbol: 3.1.4
       esniff: 2.0.1
       next-tick: 1.1.0
     dev: true
@@ -3979,19 +4866,19 @@ packages:
   /es6-iterator@2.0.3:
     resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==}
     dependencies:
-      d: 1.0.1
+      d: 1.0.2
       es5-ext: 0.10.64
-      es6-symbol: 3.1.3
+      es6-symbol: 3.1.4
     dev: true
 
   /es6-map@0.1.5:
     resolution: {integrity: sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A==}
     dependencies:
-      d: 1.0.1
+      d: 1.0.2
       es5-ext: 0.10.64
       es6-iterator: 2.0.3
       es6-set: 0.1.6
-      es6-symbol: 3.1.3
+      es6-symbol: 3.1.4
       event-emitter: 0.3.5
     dev: true
 
@@ -3999,39 +4886,40 @@ packages:
     resolution: {integrity: sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw==}
     engines: {node: '>=0.12'}
     dependencies:
-      d: 1.0.1
+      d: 1.0.2
       es5-ext: 0.10.64
       es6-iterator: 2.0.3
-      es6-symbol: 3.1.3
+      es6-symbol: 3.1.4
       event-emitter: 0.3.5
       type: 2.7.2
     dev: true
 
-  /es6-symbol@3.1.3:
-    resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==}
+  /es6-symbol@3.1.4:
+    resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==}
+    engines: {node: '>=0.12'}
     dependencies:
-      d: 1.0.1
+      d: 1.0.2
       ext: 1.7.0
     dev: true
 
-  /esbuild-plugin-clean@1.0.1(esbuild@0.20.1):
+  /esbuild-plugin-clean@1.0.1(esbuild@0.20.2):
     resolution: {integrity: sha512-ul606g0wX6oeobBgi3EqpZtCBCwNwCDivvnshsNS5pUsRylKoxUnDqK0ZIyPinlMbP6s8Opc9y2zOeY1Plhe8Q==}
     peerDependencies:
       esbuild: '>= 0.14.0'
     dependencies:
       chalk: 4.1.2
       del: 6.1.1
-      esbuild: 0.20.1
+      esbuild: 0.20.2
     dev: true
 
-  /esbuild-plugin-copy@2.1.1(esbuild@0.20.1):
+  /esbuild-plugin-copy@2.1.1(esbuild@0.20.2):
     resolution: {integrity: sha512-Bk66jpevTcV8KMFzZI1P7MZKZ+uDcrZm2G2egZ2jNIvVnivDpodZI+/KnpL3Jnap0PBdIHU7HwFGB8r+vV5CVw==}
     peerDependencies:
       esbuild: '>= 0.14.0'
     dependencies:
       chalk: 4.1.2
       chokidar: 3.6.0
-      esbuild: 0.20.1
+      esbuild: 0.20.2
       fs-extra: 10.1.0
       globby: 11.1.0
     dev: true
@@ -4067,35 +4955,35 @@ packages:
       '@esbuild/win32-x64': 0.19.12
     dev: true
 
-  /esbuild@0.20.1:
-    resolution: {integrity: sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==}
+  /esbuild@0.20.2:
+    resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
     engines: {node: '>=12'}
     hasBin: true
     requiresBuild: true
     optionalDependencies:
-      '@esbuild/aix-ppc64': 0.20.1
-      '@esbuild/android-arm': 0.20.1
-      '@esbuild/android-arm64': 0.20.1
-      '@esbuild/android-x64': 0.20.1
-      '@esbuild/darwin-arm64': 0.20.1
-      '@esbuild/darwin-x64': 0.20.1
-      '@esbuild/freebsd-arm64': 0.20.1
-      '@esbuild/freebsd-x64': 0.20.1
-      '@esbuild/linux-arm': 0.20.1
-      '@esbuild/linux-arm64': 0.20.1
-      '@esbuild/linux-ia32': 0.20.1
-      '@esbuild/linux-loong64': 0.20.1
-      '@esbuild/linux-mips64el': 0.20.1
-      '@esbuild/linux-ppc64': 0.20.1
-      '@esbuild/linux-riscv64': 0.20.1
-      '@esbuild/linux-s390x': 0.20.1
-      '@esbuild/linux-x64': 0.20.1
-      '@esbuild/netbsd-x64': 0.20.1
-      '@esbuild/openbsd-x64': 0.20.1
-      '@esbuild/sunos-x64': 0.20.1
-      '@esbuild/win32-arm64': 0.20.1
-      '@esbuild/win32-ia32': 0.20.1
-      '@esbuild/win32-x64': 0.20.1
+      '@esbuild/aix-ppc64': 0.20.2
+      '@esbuild/android-arm': 0.20.2
+      '@esbuild/android-arm64': 0.20.2
+      '@esbuild/android-x64': 0.20.2
+      '@esbuild/darwin-arm64': 0.20.2
+      '@esbuild/darwin-x64': 0.20.2
+      '@esbuild/freebsd-arm64': 0.20.2
+      '@esbuild/freebsd-x64': 0.20.2
+      '@esbuild/linux-arm': 0.20.2
+      '@esbuild/linux-arm64': 0.20.2
+      '@esbuild/linux-ia32': 0.20.2
+      '@esbuild/linux-loong64': 0.20.2
+      '@esbuild/linux-mips64el': 0.20.2
+      '@esbuild/linux-ppc64': 0.20.2
+      '@esbuild/linux-riscv64': 0.20.2
+      '@esbuild/linux-s390x': 0.20.2
+      '@esbuild/linux-x64': 0.20.2
+      '@esbuild/netbsd-x64': 0.20.2
+      '@esbuild/openbsd-x64': 0.20.2
+      '@esbuild/sunos-x64': 0.20.2
+      '@esbuild/win32-arm64': 0.20.2
+      '@esbuild/win32-ia32': 0.20.2
+      '@esbuild/win32-x64': 0.20.2
     dev: true
 
   /escalade@3.1.2:
@@ -4112,6 +5000,10 @@ packages:
     engines: {node: '>=12'}
     dev: true
 
+  /escape-html@1.0.3:
+    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+    dev: false
+
   /escape-string-regexp@1.0.5:
     resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
     engines: {node: '>=0.8.0'}
@@ -4152,37 +5044,47 @@ packages:
       source-map: 0.6.1
     dev: true
 
-  /eslint-compat-utils@0.1.2(eslint@8.57.0):
-    resolution: {integrity: sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==}
+  /eslint-compat-utils@0.5.0(eslint@8.57.0):
+    resolution: {integrity: sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==}
     engines: {node: '>=12'}
     peerDependencies:
       eslint: '>=6.0.0'
     dependencies:
       eslint: 8.57.0
+      semver: 7.6.0
     dev: true
 
-  /eslint-config-standard-with-typescript@43.0.1(@typescript-eslint/eslint-plugin@7.1.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-WfZ986+qzIzX6dcr4yGUyVb/l9N3Z8wPXCc5z/70fljs3UbWhhV+WxrfgsqMToRzuuyX9MqZ974pq2UPhDTOcA==}
+  /eslint-config-love@44.0.0(@typescript-eslint/eslint-plugin@7.5.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.0)(typescript@5.4.3):
+    resolution: {integrity: sha512-SKEMIUUrKUs/SGgfMqnTFOhkJ2UN+kxzDgXRKLM+2SADP2UxMjPHGhmZnKSsXtYKhP/4J+L5hvsV1oEyxx2e4A==}
     peerDependencies:
-      '@typescript-eslint/eslint-plugin': ^6.4.0
+      '@typescript-eslint/eslint-plugin': ^7.0.1
       eslint: ^8.0.1
       eslint-plugin-import: ^2.25.2
       eslint-plugin-n: '^15.0.0 || ^16.0.0 '
       eslint-plugin-promise: ^6.0.0
       typescript: '*'
     dependencies:
-      '@typescript-eslint/eslint-plugin': 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/eslint-plugin': 7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.57.0)(typescript@5.4.3)
+      '@typescript-eslint/parser': 7.5.0(eslint@8.57.0)(typescript@5.4.3)
       eslint: 8.57.0
       eslint-config-standard: 17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.0)
-      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.5.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
       eslint-plugin-n: 16.6.2(eslint@8.57.0)
       eslint-plugin-promise: 6.1.1(eslint@8.57.0)
-      typescript: 5.3.3
+      typescript: 5.4.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
+  /eslint-config-prettier@9.1.0(eslint@8.57.0):
+    resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==}
+    hasBin: true
+    peerDependencies:
+      eslint: '>=7.0.0'
+    dependencies:
+      eslint: 8.57.0
+    dev: true
+
   /eslint-config-standard@17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.0):
     resolution: {integrity: sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==}
     engines: {node: '>=12.0.0'}
@@ -4193,7 +5095,7 @@ packages:
       eslint-plugin-promise: ^6.0.0
     dependencies:
       eslint: 8.57.0
-      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.5.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
       eslint-plugin-n: 16.6.2(eslint@8.57.0)
       eslint-plugin-promise: 6.1.1(eslint@8.57.0)
     dev: true
@@ -4213,7 +5115,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
+  /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.5.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
     resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
@@ -4221,12 +5123,12 @@ packages:
       eslint-plugin-import: '*'
     dependencies:
       debug: 4.3.4
-      enhanced-resolve: 5.15.1
+      enhanced-resolve: 5.16.0
       eslint: 8.57.0
-      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
-      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.5.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.5.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
       fast-glob: 3.3.2
-      get-tsconfig: 4.7.2
+      get-tsconfig: 4.7.3
       is-core-module: 2.13.1
       is-glob: 4.0.3
     transitivePeerDependencies:
@@ -4236,7 +5138,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-module-utils@2.8.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
+  /eslint-module-utils@2.8.1(@typescript-eslint/parser@7.5.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
     resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -4257,17 +5159,17 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 7.5.0(eslint@8.57.0)(typescript@5.4.3)
       debug: 3.2.7
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+      eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.5.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /eslint-plugin-es-x@7.5.0(eslint@8.57.0):
-    resolution: {integrity: sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==}
+  /eslint-plugin-es-x@7.6.0(eslint@8.57.0):
+    resolution: {integrity: sha512-I0AmeNgevgaTR7y2lrVCJmGYF0rjoznpDvqV/kIkZSZbZ8Rw3eu4cGlvBBULScfkSOCzqKbff5LR4CNrV7mZHA==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
       eslint: '>=8'
@@ -4275,10 +5177,10 @@ packages:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
       '@eslint-community/regexpp': 4.10.0
       eslint: 8.57.0
-      eslint-compat-utils: 0.1.2(eslint@8.57.0)
+      eslint-compat-utils: 0.5.0(eslint@8.57.0)
     dev: true
 
-  /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
+  /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.5.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
     resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -4288,23 +5190,23 @@ packages:
       '@typescript-eslint/parser':
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      array-includes: 3.1.7
-      array.prototype.findlastindex: 1.2.4
+      '@typescript-eslint/parser': 7.5.0(eslint@8.57.0)(typescript@5.4.3)
+      array-includes: 3.1.8
+      array.prototype.findlastindex: 1.2.5
       array.prototype.flat: 1.3.2
       array.prototype.flatmap: 1.3.2
       debug: 3.2.7
       doctrine: 2.1.0
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
-      hasown: 2.0.1
+      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.5.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+      hasown: 2.0.2
       is-core-module: 2.13.1
       is-glob: 4.0.3
       minimatch: 3.1.2
-      object.fromentries: 2.0.7
-      object.groupby: 1.0.2
-      object.values: 1.1.7
+      object.fromentries: 2.0.8
+      object.groupby: 1.0.3
+      object.values: 1.2.0
       semver: 7.6.0
       tsconfig-paths: 3.15.0
     transitivePeerDependencies:
@@ -4313,8 +5215,8 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-plugin-jsdoc@48.2.0(eslint@8.57.0):
-    resolution: {integrity: sha512-O2B1XLBJnUCRkggFzUQ+PBYJDit8iAgXdlu8ucolqGrbmOWPvttZQZX8d1sC0MbqDMSLs8SHSQxaNPRY1RQREg==}
+  /eslint-plugin-jsdoc@48.2.2(eslint@8.57.0):
+    resolution: {integrity: sha512-S0Gk+rpT5w/ephKCncUY7kUsix9uE4B9XI8D/fS1/26d8okE+vZsuG1IvIt4B6sJUdQqsnzi+YXfmh+HJG11CA==}
     engines: {node: '>=18'}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
@@ -4342,8 +5244,8 @@ packages:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
       builtins: 5.0.1
       eslint: 8.57.0
-      eslint-plugin-es-x: 7.5.0(eslint@8.57.0)
-      get-tsconfig: 4.7.2
+      eslint-plugin-es-x: 7.6.0(eslint@8.57.0)
+      get-tsconfig: 4.7.3
       globals: 13.24.0
       ignore: 5.3.1
       is-builtin-module: 3.2.1
@@ -4353,6 +5255,27 @@ packages:
       semver: 7.6.0
     dev: true
 
+  /eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5):
+    resolution: {integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      '@types/eslint': '>=8.0.0'
+      eslint: '>=8.0.0'
+      eslint-config-prettier: '*'
+      prettier: '>=3.0.0'
+    peerDependenciesMeta:
+      '@types/eslint':
+        optional: true
+      eslint-config-prettier:
+        optional: true
+    dependencies:
+      eslint: 8.57.0
+      eslint-config-prettier: 9.1.0(eslint@8.57.0)
+      prettier: 3.2.5
+      prettier-linter-helpers: 1.0.0
+      synckit: 0.8.8
+    dev: true
+
   /eslint-plugin-promise@6.1.1(eslint@8.57.0):
     resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -4362,6 +5285,14 @@ packages:
       eslint: 8.57.0
     dev: true
 
+  /eslint-plugin-simple-import-sort@12.0.0(eslint@8.57.0):
+    resolution: {integrity: sha512-8o0dVEdAkYap0Cn5kNeklaKcT1nUsa3LITWEuFk3nJifOoD+5JQGoyDUW2W/iPWwBsNBJpyJS9y4je/BgxLcyQ==}
+    peerDependencies:
+      eslint: '>=5.0.0'
+    dependencies:
+      eslint: 8.57.0
+    dev: true
+
   /eslint-plugin-tsdoc@0.2.17:
     resolution: {integrity: sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==}
     dependencies:
@@ -4369,6 +5300,25 @@ packages:
       '@microsoft/tsdoc-config': 0.16.2
     dev: true
 
+  /eslint-plugin-vue@9.24.0(eslint@8.57.0):
+    resolution: {integrity: sha512-9SkJMvF8NGMT9aQCwFc5rj8Wo1XWSMSHk36i7ZwdI614BU7sIOR28ZjuFPKp8YGymZN12BSEbiSwa7qikp+PBw==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
+      eslint: 8.57.0
+      globals: 13.24.0
+      natural-compare: 1.4.0
+      nth-check: 2.1.1
+      postcss-selector-parser: 6.0.16
+      semver: 7.6.0
+      vue-eslint-parser: 9.4.2(eslint@8.57.0)
+      xml-name-validator: 4.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /eslint-scope@7.2.2:
     resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -4437,7 +5387,7 @@ packages:
     resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==}
     engines: {node: '>=0.10'}
     dependencies:
-      d: 1.0.1
+      d: 1.0.2
       es5-ext: 0.10.64
       event-emitter: 0.3.5
       type: 2.7.2
@@ -4489,15 +5439,29 @@ packages:
     resolution: {integrity: sha512-Ec+X44CapIGExvSZN+pGkmr5p7HwUVQoPQSd458Lqwvaf4/61k/invHSh4BYK8OXnCkfEhWuIoG5hayKLQStIg==}
     dev: true
 
+  /estree-walker@2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+  /estree-walker@3.0.3:
+    resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+    dependencies:
+      '@types/estree': 1.0.5
+    dev: true
+
   /esutils@2.0.3:
     resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /etag@1.8.1:
+    resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
+    engines: {node: '>= 0.6'}
+    dev: false
+
   /event-emitter@0.3.5:
     resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
     dependencies:
-      d: 1.0.1
+      d: 1.0.2
       es5-ext: 0.10.64
     dev: true
 
@@ -4615,6 +5579,10 @@ packages:
   /fast-deep-equal@3.1.3:
     resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
 
+  /fast-diff@1.3.0:
+    resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
+    dev: true
+
   /fast-glob@3.3.2:
     resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
     engines: {node: '>=8.6.0'}
@@ -4696,6 +5664,21 @@ packages:
     dependencies:
       to-regex-range: 5.0.1
 
+  /finalhandler@1.2.0:
+    resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      debug: 2.6.9
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      on-finished: 2.4.1
+      parseurl: 1.3.3
+      statuses: 2.0.1
+      unpipe: 1.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: false
+
   /find-up@3.0.0:
     resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
     engines: {node: '>=6'}
@@ -4795,6 +5778,11 @@ packages:
       fetch-blob: 3.2.0
     dev: true
 
+  /fresh@0.5.2:
+    resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
+    engines: {node: '>= 0.6'}
+    dev: false
+
   /from2-string@1.1.0:
     resolution: {integrity: sha512-m8vCh+KnXXXBtfF2VUbiYlQ+nczLcntB0BrtNgpmLkHylhObe9WF1b2LZjBBzrZzA6P4mkEla6ZYQoOUTG8cYA==}
     dependencies:
@@ -4855,7 +5843,7 @@ packages:
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-abstract: 1.23.3
       functions-have-names: 1.2.3
     dev: true
 
@@ -4890,6 +5878,11 @@ packages:
       is-property: 1.0.2
     dev: true
 
+  /gensync@1.0.0-beta.2:
+    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
   /get-assigned-identifiers@1.2.0:
     resolution: {integrity: sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==}
     dev: true
@@ -4904,6 +5897,10 @@ packages:
     engines: {node: '>=18'}
     dev: true
 
+  /get-func-name@2.0.2:
+    resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
+    dev: true
+
   /get-intrinsic@1.2.4:
     resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
     engines: {node: '>= 0.4'}
@@ -4912,7 +5909,7 @@ packages:
       function-bind: 1.1.2
       has-proto: 1.0.3
       has-symbols: 1.0.3
-      hasown: 2.0.1
+      hasown: 2.0.2
     dev: true
 
   /get-package-type@0.1.0:
@@ -4945,8 +5942,8 @@ packages:
       get-intrinsic: 1.2.4
     dev: true
 
-  /get-tsconfig@4.7.2:
-    resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==}
+  /get-tsconfig@4.7.3:
+    resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==}
     dependencies:
       resolve-pkg-maps: 1.0.0
     dev: true
@@ -5011,16 +6008,16 @@ packages:
       is-glob: 4.0.3
     dev: true
 
-  /glob@10.3.10:
-    resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
+  /glob@10.3.12:
+    resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==}
     engines: {node: '>=16 || 14 >=14.17'}
     hasBin: true
     dependencies:
       foreground-child: 3.1.1
       jackspeak: 2.3.6
-      minimatch: 9.0.3
+      minimatch: 9.0.4
       minipass: 7.0.4
-      path-scurry: 1.10.1
+      path-scurry: 1.10.2
     dev: true
 
   /glob@7.2.3:
@@ -5047,6 +6044,11 @@ packages:
       ini: 2.0.0
     dev: true
 
+  /globals@11.12.0:
+    resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+    engines: {node: '>=4'}
+    dev: true
+
   /globals@13.24.0:
     resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
     engines: {node: '>=8'}
@@ -5206,6 +6208,14 @@ packages:
     engines: {node: '>= 0.4.0'}
     dev: true
 
+  /hash-base@3.0.4:
+    resolution: {integrity: sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==}
+    engines: {node: '>=4'}
+    dependencies:
+      inherits: 2.0.4
+      safe-buffer: 5.2.1
+    dev: true
+
   /hash-base@3.1.0:
     resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==}
     engines: {node: '>=4'}
@@ -5222,8 +6232,8 @@ packages:
       minimalistic-assert: 1.0.1
     dev: true
 
-  /hasown@2.0.1:
-    resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==}
+  /hasown@2.0.2:
+    resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
     engines: {node: '>= 0.4'}
     dependencies:
       function-bind: 1.1.2
@@ -5268,10 +6278,22 @@ packages:
     resolution: {integrity: sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==}
     dev: true
 
+  /html-encoding-sniffer@4.0.0:
+    resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+    engines: {node: '>=18'}
+    dependencies:
+      whatwg-encoding: 3.1.1
+    dev: true
+
   /html-escaper@2.0.2:
     resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
     dev: true
 
+  /html-tags@3.3.1:
+    resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
+    engines: {node: '>=8'}
+    dev: true
+
   /htmlescape@1.1.1:
     resolution: {integrity: sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==}
     engines: {node: '>=0.10'}
@@ -5280,6 +6302,17 @@ packages:
   /http-cache-semantics@4.1.1:
     resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
 
+  /http-errors@2.0.0:
+    resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      depd: 2.0.0
+      inherits: 2.0.4
+      setprototypeof: 1.2.0
+      statuses: 2.0.1
+      toidentifier: 1.0.1
+    dev: false
+
   /http-parser-js@0.5.8:
     resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==}
     dev: true
@@ -5300,7 +6333,7 @@ packages:
     resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
     engines: {node: '>= 14'}
     dependencies:
-      agent-base: 7.1.0
+      agent-base: 7.1.1
       debug: 4.3.4
     transitivePeerDependencies:
       - supports-color
@@ -5346,7 +6379,7 @@ packages:
     resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
     engines: {node: '>= 14'}
     dependencies:
-      agent-base: 7.1.0
+      agent-base: 7.1.1
       debug: 4.3.4
     transitivePeerDependencies:
       - supports-color
@@ -5480,6 +6513,11 @@ packages:
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dev: true
 
+  /ini@4.1.2:
+    resolution: {integrity: sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+    dev: true
+
   /inline-source-map@0.6.3:
     resolution: {integrity: sha512-1aVsPEsJWMJq/pdMU61CDlm1URcW702MTB4w9/zUjMus6H/Py8o7g68Pr9D4I6QluWGt/KdmswuRhaA05xVR1w==}
     dependencies:
@@ -5509,7 +6547,7 @@ packages:
     resolution: {integrity: sha512-4ByIMt677Iz5AvjyKrDpzaepIyMewNvDcvwpVVRZNmy9dLakVoVgdCHZXbK1SlVJra1db0JZ6XkJyHsanpdrdQ==}
     engines: {node: '>=18'}
     dependencies:
-      '@ljharb/through': 2.3.12
+      '@ljharb/through': 2.3.13
       ansi-escapes: 4.3.2
       chalk: 5.3.0
       cli-cursor: 3.1.0
@@ -5562,8 +6600,8 @@ packages:
     engines: {node: '>= 0.4'}
     dependencies:
       es-errors: 1.3.0
-      hasown: 2.0.1
-      side-channel: 1.0.5
+      hasown: 2.0.2
+      side-channel: 1.0.6
     dev: true
 
   /internmap@1.0.1:
@@ -5628,7 +6666,7 @@ packages:
     resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
     engines: {node: '>=8'}
     dependencies:
-      binary-extensions: 2.2.0
+      binary-extensions: 2.3.0
     dev: true
 
   /is-boolean-attribute@0.0.1:
@@ -5681,7 +6719,14 @@ packages:
   /is-core-module@2.13.1:
     resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
     dependencies:
-      hasown: 2.0.1
+      hasown: 2.0.2
+
+  /is-data-view@1.0.1:
+    resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      is-typed-array: 1.1.13
+    dev: true
 
   /is-date-object@1.0.5:
     resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
@@ -5785,8 +6830,9 @@ packages:
     requiresBuild: true
     optional: true
 
-  /is-map@2.0.2:
-    resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
+  /is-map@2.0.3:
+    resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
+    engines: {node: '>= 0.4'}
     dev: true
 
   /is-negative-zero@2.0.3:
@@ -5830,6 +6876,10 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
+  /is-potential-custom-element-name@1.0.1:
+    resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+    dev: true
+
   /is-property@1.0.2:
     resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==}
     dev: true
@@ -5842,8 +6892,9 @@ packages:
       has-tostringtag: 1.0.2
     dev: true
 
-  /is-set@2.0.2:
-    resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
+  /is-set@2.0.3:
+    resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
+    engines: {node: '>= 0.4'}
     dev: true
 
   /is-shared-array-buffer@1.0.3:
@@ -5893,7 +6944,7 @@ packages:
     resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
     engines: {node: '>= 0.4'}
     dependencies:
-      which-typed-array: 1.1.14
+      which-typed-array: 1.1.15
     dev: true
 
   /is-typedarray@1.0.0:
@@ -5985,6 +7036,17 @@ packages:
       supports-color: 7.2.0
     dev: true
 
+  /istanbul-lib-source-maps@5.0.4:
+    resolution: {integrity: sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==}
+    engines: {node: '>=10'}
+    dependencies:
+      '@jridgewell/trace-mapping': 0.3.25
+      debug: 4.3.4
+      istanbul-lib-coverage: 3.2.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /istanbul-reports@3.1.7:
     resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==}
     engines: {node: '>=8'}
@@ -6042,7 +7104,7 @@ packages:
     resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
-      '@babel/code-frame': 7.23.5
+      '@babel/code-frame': 7.24.2
       '@jest/types': 29.6.3
       '@types/stack-utils': 2.0.3
       chalk: 4.1.2
@@ -6058,7 +7120,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.24
+      '@types/node': 20.12.3
       chalk: 4.1.2
       ci-info: 3.9.0
       graceful-fs: 4.2.11
@@ -6074,10 +7136,31 @@ packages:
     resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==}
     dev: true
 
+  /js-beautify@1.15.1:
+    resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==}
+    engines: {node: '>=14'}
+    hasBin: true
+    dependencies:
+      config-chain: 1.1.13
+      editorconfig: 1.0.4
+      glob: 10.3.12
+      js-cookie: 3.0.5
+      nopt: 7.2.0
+    dev: true
+
+  /js-cookie@3.0.5:
+    resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
+    engines: {node: '>=14'}
+    dev: true
+
   /js-tokens@4.0.0:
     resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
     dev: true
 
+  /js-tokens@9.0.0:
+    resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==}
+    dev: true
+
   /js-yaml@4.1.0:
     resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
     hasBin: true
@@ -6097,6 +7180,48 @@ packages:
     engines: {node: '>=12.0.0'}
     dev: true
 
+  /jsdom@24.0.0(bufferutil@4.0.8)(utf-8-validate@6.0.3):
+    resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==}
+    engines: {node: '>=18'}
+    peerDependencies:
+      canvas: ^2.11.2
+    peerDependenciesMeta:
+      canvas:
+        optional: true
+    dependencies:
+      cssstyle: 4.0.1
+      data-urls: 5.0.0
+      decimal.js: 10.4.3
+      form-data: 4.0.0
+      html-encoding-sniffer: 4.0.0
+      http-proxy-agent: 7.0.2
+      https-proxy-agent: 7.0.4
+      is-potential-custom-element-name: 1.0.1
+      nwsapi: 2.2.7
+      parse5: 7.1.2
+      rrweb-cssom: 0.6.0
+      saxes: 6.0.0
+      symbol-tree: 3.2.4
+      tough-cookie: 4.1.3
+      w3c-xmlserializer: 5.0.0
+      webidl-conversions: 7.0.0
+      whatwg-encoding: 3.1.1
+      whatwg-mimetype: 4.0.0
+      whatwg-url: 14.0.0
+      ws: 8.16.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)
+      xml-name-validator: 5.0.0
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+    dev: true
+
+  /jsesc@2.5.2:
+    resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
+    engines: {node: '>=4'}
+    hasBin: true
+    dev: true
+
   /json-buffer@3.0.1:
     resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
     dev: true
@@ -6141,6 +7266,10 @@ packages:
     hasBin: true
     dev: true
 
+  /jsonc-parser@3.2.1:
+    resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==}
+    dev: true
+
   /jsonfile@6.1.0:
     resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
     dependencies:
@@ -6311,6 +7440,14 @@ packages:
       wrap-ansi: 9.0.0
     dev: true
 
+  /local-pkg@0.5.0:
+    resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
+    engines: {node: '>=14'}
+    dependencies:
+      mlly: 1.6.1
+      pkg-types: 1.0.3
+    dev: true
+
   /locate-path@3.0.0:
     resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
     engines: {node: '>=6'}
@@ -6432,7 +7569,7 @@ packages:
     resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==}
     engines: {node: '>=18'}
     dependencies:
-      ansi-escapes: 6.2.0
+      ansi-escapes: 6.2.1
       cli-cursor: 4.0.0
       slice-ansi: 7.1.0
       strip-ansi: 7.1.0
@@ -6466,6 +7603,12 @@ packages:
       js-tokens: 4.0.0
     dev: true
 
+  /loupe@2.3.7:
+    resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
+    dependencies:
+      get-func-name: 2.0.2
+    dev: true
+
   /lower-case@1.1.4:
     resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==}
     dev: true
@@ -6480,6 +7623,12 @@ packages:
     engines: {node: 14 || >=16.14}
     dev: true
 
+  /lru-cache@5.1.1:
+    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+    dependencies:
+      yallist: 3.1.1
+    dev: true
+
   /lru-cache@6.0.0:
     resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
     engines: {node: '>=10'}
@@ -6513,6 +7662,20 @@ packages:
       sourcemap-codec: 1.4.8
     dev: true
 
+  /magic-string@0.30.8:
+    resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.4.15
+
+  /magicast@0.3.3:
+    resolution: {integrity: sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==}
+    dependencies:
+      '@babel/parser': 7.24.4
+      '@babel/types': 7.24.0
+      source-map-js: 1.2.0
+    dev: true
+
   /make-dir@3.1.0:
     resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
     engines: {node: '>=8'}
@@ -6613,8 +7776,8 @@ packages:
       braces: 3.0.2
       picomatch: 2.3.1
 
-  /mikro-orm@6.1.6:
-    resolution: {integrity: sha512-Ky3+edAOT54qBODbTMJNcRu7mYZsh8Y2EwT2WpcT+IRpUZBjqb1kg86XuYLqp0pqd9MqykNtOW+Q9PglNW/6UQ==}
+  /mikro-orm@6.1.12:
+    resolution: {integrity: sha512-pXpZ5dGMM0BBqYouU5EPuWkjWX/xnNzRVxsOTrKyyrm1ICTN7pawHn3UCVAggi+z1qVhpwxThGfZj+ZWD54duw==}
     engines: {node: '>= 18.12.0'}
 
   /miller-rabin@4.0.1:
@@ -6637,6 +7800,12 @@ packages:
       mime-db: 1.52.0
     dev: true
 
+  /mime@1.6.0:
+    resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
+    engines: {node: '>=4'}
+    hasBin: true
+    dev: false
+
   /mimic-fn@1.2.0:
     resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==}
     engines: {node: '>=4'}
@@ -6672,7 +7841,7 @@ packages:
     dependencies:
       concat-stream: 2.0.0
       convert-source-map: 1.9.0
-      duplexify: 4.1.2
+      duplexify: 4.1.3
       from2-string: 1.1.0
       terser: 4.8.1
       xtend: 4.0.2
@@ -6691,8 +7860,22 @@ packages:
     dependencies:
       brace-expansion: 1.1.11
 
-  /minimatch@9.0.3:
-    resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
+  /minimatch@9.0.1:
+    resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    dependencies:
+      brace-expansion: 2.0.1
+    dev: true
+
+  /minimatch@9.0.3:
+    resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    dependencies:
+      brace-expansion: 2.0.1
+    dev: true
+
+  /minimatch@9.0.4:
+    resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
     engines: {node: '>=16 || 14 >=14.17'}
     dependencies:
       brace-expansion: 2.0.1
@@ -6842,6 +8025,15 @@ packages:
     resolution: {integrity: sha512-VoAYUqmPRmzKbbqRejjqceGFp3VF81Qe8XXFGU0UXLxB7Mf4GGvyGq5Qn3k4AiQgDEV6WzobqlPOd+j0+m6IrA==}
     dev: true
 
+  /mlly@1.6.1:
+    resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==}
+    dependencies:
+      acorn: 8.11.3
+      pathe: 1.1.2
+      pkg-types: 1.0.3
+      ufo: 1.5.3
+    dev: true
+
   /mnemonist@0.40.0-rc1:
     resolution: {integrity: sha512-38L0xGDezsPweee5i7duiaCRzlkkCJorozW6Rta60iel7ZkT4vF6jDIfr101NxE3rsAsumuOvc8yiTGZD5gG4w==}
     dependencies:
@@ -6887,8 +8079,8 @@ packages:
       whatwg-url: 13.0.0
     dev: false
 
-  /mongodb@6.3.0:
-    resolution: {integrity: sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==}
+  /mongodb@6.5.0:
+    resolution: {integrity: sha512-Fozq68InT+JKABGLqctgtb8P56pRrJFkbhW0ux+x1mdHeyinor8oNzJqwLjV/t5X5nJGfTlluxfyMnOXNggIUA==}
     engines: {node: '>=16.20.1'}
     peerDependencies:
       '@aws-sdk/credential-providers': ^3.188.0
@@ -6914,8 +8106,8 @@ packages:
       socks:
         optional: true
     dependencies:
-      '@mongodb-js/saslprep': 1.1.4
-      bson: 6.3.0
+      '@mongodb-js/saslprep': 1.1.5
+      bson: 6.6.0
       mongodb-connection-string-url: 3.0.0
     dev: false
 
@@ -6923,6 +8115,10 @@ packages:
     resolution: {integrity: sha512-Dqb/lHFyTi7SZpY0a5R4I/0Edo+iPMbaUexsHHsLAByyixCDiLHPHyVoKVmrpL0THcT7V9Cgev9y21TQYq6wQg==}
     dev: true
 
+  /ms@2.0.0:
+    resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+    dev: false
+
   /ms@2.1.2:
     resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
 
@@ -6985,7 +8181,6 @@ packages:
     resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
     hasBin: true
-    dev: true
 
   /napi-build-utils@1.0.2:
     resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
@@ -7085,8 +8280,8 @@ packages:
       lower-case: 1.1.4
     dev: true
 
-  /node-abi@3.56.0:
-    resolution: {integrity: sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==}
+  /node-abi@3.57.0:
+    resolution: {integrity: sha512-Dp+A9JWxRaKuHP35H77I4kCKesDy5HUDEmScia2FyncMTOXASMyg251F5PhFoDA5uqBrDDffiLpbqnrZmNXW+g==}
     engines: {node: '>=10'}
     dependencies:
       semver: 7.6.0
@@ -7137,7 +8332,6 @@ packages:
     resolution: {integrity: sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==}
     hasBin: true
     requiresBuild: true
-    dev: false
 
   /node-gyp@8.4.1:
     resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==}
@@ -7153,13 +8347,17 @@ packages:
       npmlog: 6.0.2
       rimraf: 3.0.2
       semver: 7.6.0
-      tar: 6.2.0
+      tar: 6.2.1
       which: 2.0.2
     transitivePeerDependencies:
       - bluebird
       - supports-color
     optional: true
 
+  /node-releases@2.0.14:
+    resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
+    dev: true
+
   /nopt@5.0.0:
     resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
     engines: {node: '>=6'}
@@ -7169,6 +8367,14 @@ packages:
       abbrev: 1.1.1
     optional: true
 
+  /nopt@7.2.0:
+    resolution: {integrity: sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+    hasBin: true
+    dependencies:
+      abbrev: 2.0.0
+    dev: true
+
   /normalize-html-whitespace@0.2.0:
     resolution: {integrity: sha512-5CZAEQ4bQi8Msqw0GAT6rrkrjNN4ZKqAG3+jJMwms4O6XoMvh6ekwOueG4mRS1LbPUR1r9EdnhxxfpzMTOdzKw==}
     engines: {node: '>= 0.10'}
@@ -7179,8 +8385,8 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
-  /normalize-url@8.0.0:
-    resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==}
+  /normalize-url@8.0.1:
+    resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
     engines: {node: '>=14.16'}
     dev: true
 
@@ -7209,11 +8415,21 @@ packages:
       set-blocking: 2.0.0
     optional: true
 
+  /nth-check@2.1.1:
+    resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+    dependencies:
+      boolbase: 1.0.0
+    dev: true
+
   /number-is-nan@1.0.1:
     resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==}
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /nwsapi@2.2.7:
+    resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==}
+    dev: true
+
   /oauth-sign@0.9.0:
     resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==}
     dev: true
@@ -7242,38 +8458,45 @@ packages:
       object-keys: 1.1.1
     dev: true
 
-  /object.fromentries@2.0.7:
-    resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==}
+  /object.fromentries@2.0.8:
+    resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
     engines: {node: '>= 0.4'}
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-abstract: 1.23.3
+      es-object-atoms: 1.0.0
     dev: true
 
-  /object.groupby@1.0.2:
-    resolution: {integrity: sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==}
+  /object.groupby@1.0.3:
+    resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==}
+    engines: {node: '>= 0.4'}
     dependencies:
-      array.prototype.filter: 1.0.3
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
-      es-errors: 1.3.0
+      es-abstract: 1.23.3
     dev: true
 
-  /object.values@1.1.7:
-    resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==}
+  /object.values@1.2.0:
+    resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==}
     engines: {node: '>= 0.4'}
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-object-atoms: 1.0.0
     dev: true
 
   /obliterator@2.0.4:
     resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==}
     dev: false
 
+  /on-finished@2.4.1:
+    resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      ee-first: 1.1.1
+    dev: false
+
   /on-net-listen@1.1.2:
     resolution: {integrity: sha512-y1HRYy8s/RlcBvDUwKXSmkODMdx4KSuIvloCnQYJ2LdBBC1asY4HtfhXwe3UWknLakATZDnbzht2Ijw3M1EqFg==}
     engines: {node: '>=9.4.0 || ^8.9.4'}
@@ -7441,6 +8664,13 @@ packages:
       yocto-queue: 1.0.0
     dev: true
 
+  /p-limit@5.0.0:
+    resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==}
+    engines: {node: '>=18'}
+    dependencies:
+      yocto-queue: 1.0.0
+    dev: true
+
   /p-locate@3.0.0:
     resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
     engines: {node: '>=6'}
@@ -7479,13 +8709,13 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       '@tootallnate/quickjs-emscripten': 0.23.0
-      agent-base: 7.1.0
+      agent-base: 7.1.1
       debug: 4.3.4
       get-uri: 6.0.3
       http-proxy-agent: 7.0.2
       https-proxy-agent: 7.0.4
       pac-resolver: 7.0.1
-      socks-proxy-agent: 8.0.2
+      socks-proxy-agent: 8.0.3
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -7540,12 +8770,14 @@ packages:
       path-platform: 0.11.15
     dev: true
 
-  /parse-asn1@5.1.6:
-    resolution: {integrity: sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==}
+  /parse-asn1@5.1.7:
+    resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==}
+    engines: {node: '>= 0.10'}
     dependencies:
-      asn1.js: 5.4.1
+      asn1.js: 4.10.1
       browserify-aes: 1.2.0
       evp_bytestokey: 1.0.3
+      hash-base: 3.0.4
       pbkdf2: 3.1.2
       safe-buffer: 5.2.1
     dev: true
@@ -7560,7 +8792,7 @@ packages:
     resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
     engines: {node: '>=8'}
     dependencies:
-      '@babel/code-frame': 7.23.5
+      '@babel/code-frame': 7.24.2
       error-ex: 1.3.2
       json-parse-even-better-errors: 2.3.1
       lines-and-columns: 1.2.4
@@ -7578,6 +8810,17 @@ packages:
       parse-path: 7.0.0
     dev: true
 
+  /parse5@7.1.2:
+    resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
+    dependencies:
+      entities: 4.5.0
+    dev: true
+
+  /parseurl@1.3.3:
+    resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+    engines: {node: '>= 0.8'}
+    dev: false
+
   /path-browserify@1.0.1:
     resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
 
@@ -7618,8 +8861,8 @@ packages:
     engines: {node: '>= 0.8.0'}
     dev: true
 
-  /path-scurry@1.10.1:
-    resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
+  /path-scurry@1.10.2:
+    resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==}
     engines: {node: '>=16 || 14 >=14.17'}
     dependencies:
       lru-cache: 10.2.0
@@ -7635,6 +8878,14 @@ packages:
     engines: {node: '>=12'}
     dev: true
 
+  /pathe@1.1.2:
+    resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+    dev: true
+
+  /pathval@1.1.1:
+    resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
+    dev: true
+
   /pbkdf2@3.1.2:
     resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==}
     engines: {node: '>=0.12'}
@@ -7655,7 +8906,6 @@ packages:
 
   /picocolors@1.0.0:
     resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
-    dev: true
 
   /picomatch@2.3.1:
     resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
@@ -7672,6 +8922,14 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /pkg-types@1.0.3:
+    resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
+    dependencies:
+      jsonc-parser: 3.2.1
+      mlly: 1.6.1
+      pathe: 1.1.2
+    dev: true
+
   /pkg-up@3.1.0:
     resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==}
     engines: {node: '>=8'}
@@ -7685,8 +8943,8 @@ packages:
       semver-compare: 1.0.0
     dev: false
 
-  /poolifier@3.1.21:
-    resolution: {integrity: sha512-6bWowXA6RedrnEypsc+5g1qbv+m7KgiY2H/Jf+iGIjbDr6/46voj7Ap6NepzknOGCB2JmU13bvAbyQkD2fvFGw==}
+  /poolifier@3.1.29:
+    resolution: {integrity: sha512-7yUcXh8Gvis13F9vwIbDdjPmY7d5MNNXVt+ccBwfCrWSUdpa7cHuc4rVXASYM7V4Hq7VAEEKbdmv/DO+Ry9Y3Q==}
     engines: {node: '>=18.0.0', pnpm: '>=8.6.0'}
     requiresBuild: true
     dev: false
@@ -7696,43 +8954,50 @@ packages:
     engines: {node: '>= 0.4'}
     dev: true
 
-  /postcss-import@13.0.0(postcss@8.4.35):
+  /postcss-import@13.0.0(postcss@8.4.38):
     resolution: {integrity: sha512-LPUbm3ytpYopwQQjqgUH4S3EM/Gb9QsaSPP/5vnoi+oKVy3/mIk2sc0Paqw7RL57GpScm9MdIMUypw2znWiBpg==}
     engines: {node: '>=10.0.0'}
     peerDependencies:
       postcss: ^8.0.0
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
       read-cache: 1.0.0
       resolve: 1.22.8
     dev: true
 
+  /postcss-selector-parser@6.0.16:
+    resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==}
+    engines: {node: '>=4'}
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+    dev: true
+
   /postcss-value-parser@4.2.0:
     resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
     dev: true
 
-  /postcss@8.4.35:
-    resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
+  /postcss@8.4.38:
+    resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
     engines: {node: ^10 || ^12 || >=14}
     dependencies:
       nanoid: 3.3.7
       picocolors: 1.0.0
-      source-map-js: 1.0.2
-    dev: true
+      source-map-js: 1.2.0
 
-  /prebuild-install@7.1.1:
-    resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
+  /prebuild-install@7.1.2:
+    resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==}
     engines: {node: '>=10'}
     hasBin: true
     dependencies:
-      detect-libc: 2.0.2
+      detect-libc: 2.0.3
       expand-template: 2.0.3
       github-from-package: 0.0.0
       minimist: 1.2.8
       mkdirp-classic: 0.5.3
       napi-build-utils: 1.0.2
-      node-abi: 3.56.0
+      node-abi: 3.57.0
       pump: 3.0.0
       rc: 1.2.8
       simple-get: 4.0.1
@@ -7749,6 +9014,13 @@ packages:
     engines: {node: '>= 0.8.0'}
     dev: true
 
+  /prettier-linter-helpers@1.0.0:
+    resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      fast-diff: 1.3.0
+    dev: true
+
   /prettier@3.2.5:
     resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
     engines: {node: '>=14'}
@@ -7811,10 +9083,10 @@ packages:
     resolution: {integrity: sha512-hezvKvQQmsFkOdrZfYxUxkyxl8mgFQeT259Ajj9PXdbg9VzBCWrItOev72JyWxkCD5VSSqAeHmlN3tWx4DlmsA==}
     engines: {node: '>= 0.4'}
     dependencies:
-      array.prototype.map: 1.0.6
+      array.prototype.map: 1.0.7
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-abstract: 1.23.3
       get-intrinsic: 1.2.4
       iterate-value: 1.0.2
     dev: true
@@ -7855,14 +9127,14 @@ packages:
     resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==}
     engines: {node: '>= 14'}
     dependencies:
-      agent-base: 7.1.0
+      agent-base: 7.1.1
       debug: 4.3.4
       http-proxy-agent: 7.0.2
       https-proxy-agent: 7.0.4
       lru-cache: 7.18.3
       pac-proxy-agent: 7.0.1
       proxy-from-env: 1.1.0
-      socks-proxy-agent: 8.0.2
+      socks-proxy-agent: 8.0.3
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -7881,7 +9153,7 @@ packages:
       bn.js: 4.12.0
       browserify-rsa: 4.1.0
       create-hash: 1.2.0
-      parse-asn1: 5.1.6
+      parse-asn1: 5.1.7
       randombytes: 2.1.0
       safe-buffer: 5.2.1
     dev: true
@@ -7895,7 +9167,7 @@ packages:
   /pumpify@2.0.1:
     resolution: {integrity: sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==}
     dependencies:
-      duplexify: 4.1.2
+      duplexify: 4.1.3
       inherits: 2.0.4
       pump: 3.0.0
     dev: true
@@ -7922,11 +9194,11 @@ packages:
       escape-goat: 4.0.0
     dev: true
 
-  /qs@6.11.2:
-    resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==}
+  /qs@6.12.0:
+    resolution: {integrity: sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==}
     engines: {node: '>=0.6'}
     dependencies:
-      side-channel: 1.0.5
+      side-channel: 1.0.6
     dev: true
 
   /qs@6.5.3:
@@ -7964,8 +9236,8 @@ packages:
       through2: 2.0.5
     dev: true
 
-  /rambda@9.1.0:
-    resolution: {integrity: sha512-fCRAq8Of+bAuqjAA0MSb/umbNgiYwy9N5camM2T++Qu/mRImLT3a31hf0REvnqaWCrgrdC3ewf/ucAILsYeBgQ==}
+  /rambda@9.2.0:
+    resolution: {integrity: sha512-RjM8TBNPR+iSvWLqbBpFveDfEf2RPRKHuwBHjQdXsYFDwn3MIvgmJiqVVC1CIQKnOwzeDQd44zqDFgSKQ7RT1Q==}
     dev: false
 
   /randombytes@2.1.0:
@@ -7981,6 +9253,11 @@ packages:
       safe-buffer: 5.2.1
     dev: true
 
+  /range-parser@1.2.1:
+    resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+    engines: {node: '>= 0.6'}
+    dev: false
+
   /rc@1.2.8:
     resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
     hasBin: true
@@ -8091,7 +9368,7 @@ packages:
     resolution: {integrity: sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==}
     dev: true
 
-  /release-it@17.1.1(typescript@5.3.3):
+  /release-it@17.1.1(typescript@5.4.3):
     resolution: {integrity: sha512-b+4Tu2eb5f2wIdIe5E9hre0evbMQrXp/kRq0natHsHYJVqu1Bd4/h2a+swFi0faGmC3cJdB16uYR6LscG9SchQ==}
     engines: {node: '>=18'}
     hasBin: true
@@ -8100,7 +9377,7 @@ packages:
       '@octokit/rest': 20.0.2
       async-retry: 1.3.3
       chalk: 5.3.0
-      cosmiconfig: 9.0.0(typescript@5.3.3)
+      cosmiconfig: 9.0.0(typescript@5.4.3)
       execa: 8.0.1
       git-url-parse: 14.0.0
       globby: 14.0.1
@@ -8269,7 +9546,7 @@ packages:
     engines: {node: '>=14'}
     hasBin: true
     dependencies:
-      glob: 10.3.10
+      glob: 10.3.12
     dev: true
 
   /ripemd160@2.0.2:
@@ -8279,6 +9556,35 @@ packages:
       inherits: 2.0.4
     dev: true
 
+  /rollup@4.14.0:
+    resolution: {integrity: sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==}
+    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+    hasBin: true
+    dependencies:
+      '@types/estree': 1.0.5
+    optionalDependencies:
+      '@rollup/rollup-android-arm-eabi': 4.14.0
+      '@rollup/rollup-android-arm64': 4.14.0
+      '@rollup/rollup-darwin-arm64': 4.14.0
+      '@rollup/rollup-darwin-x64': 4.14.0
+      '@rollup/rollup-linux-arm-gnueabihf': 4.14.0
+      '@rollup/rollup-linux-arm64-gnu': 4.14.0
+      '@rollup/rollup-linux-arm64-musl': 4.14.0
+      '@rollup/rollup-linux-powerpc64le-gnu': 4.14.0
+      '@rollup/rollup-linux-riscv64-gnu': 4.14.0
+      '@rollup/rollup-linux-s390x-gnu': 4.14.0
+      '@rollup/rollup-linux-x64-gnu': 4.14.0
+      '@rollup/rollup-linux-x64-musl': 4.14.0
+      '@rollup/rollup-win32-arm64-msvc': 4.14.0
+      '@rollup/rollup-win32-ia32-msvc': 4.14.0
+      '@rollup/rollup-win32-x64-msvc': 4.14.0
+      fsevents: 2.3.3
+    dev: true
+
+  /rrweb-cssom@0.6.0:
+    resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
+    dev: true
+
   /run-applescript@7.0.0:
     resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
     engines: {node: '>=18'}
@@ -8312,8 +9618,8 @@ packages:
       tslib: 2.6.2
     dev: true
 
-  /safe-array-concat@1.1.0:
-    resolution: {integrity: sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==}
+  /safe-array-concat@1.1.2:
+    resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
     engines: {node: '>=0.4'}
     dependencies:
       call-bind: 1.0.7
@@ -8346,6 +9652,13 @@ packages:
   /safer-buffer@2.1.2:
     resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
 
+  /saxes@6.0.0:
+    resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+    engines: {node: '>=v12.22.7'}
+    dependencies:
+      xmlchars: 2.2.0
+    dev: true
+
   /scope-analyzer@2.1.2:
     resolution: {integrity: sha512-5cfCmsTYV/wPaRIItNxatw02ua/MThdIUNnUOCYp+3LSEJvnG804ANw2VLaavNILIfWXF1D1G2KNANkBBvInwQ==}
     dependencies:
@@ -8353,7 +9666,7 @@ packages:
       dash-ast: 2.0.1
       es6-map: 0.1.5
       es6-set: 0.1.6
-      es6-symbol: 3.1.3
+      es6-symbol: 3.1.4
       estree-is-function: 1.0.0
       get-assigned-identifiers: 1.2.0
     dev: true
@@ -8387,11 +9700,44 @@ packages:
     dependencies:
       lru-cache: 6.0.0
 
+  /send@0.18.0:
+    resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      debug: 2.6.9
+      depd: 2.0.0
+      destroy: 1.2.0
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      etag: 1.8.1
+      fresh: 0.5.2
+      http-errors: 2.0.0
+      mime: 1.6.0
+      ms: 2.1.3
+      on-finished: 2.4.1
+      range-parser: 1.2.1
+      statuses: 2.0.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: false
+
+  /serve-static@1.15.0:
+    resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      parseurl: 1.3.3
+      send: 0.18.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: false
+
   /set-blocking@2.0.0:
     resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
 
-  /set-function-length@1.2.1:
-    resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==}
+  /set-function-length@1.2.2:
+    resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
     engines: {node: '>= 0.4'}
     dependencies:
       define-data-property: 1.1.4
@@ -8412,6 +9758,10 @@ packages:
       has-property-descriptors: 1.0.2
     dev: true
 
+  /setprototypeof@1.2.0:
+    resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+    dev: false
+
   /sha.js@2.4.11:
     resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==}
     hasBin: true
@@ -8463,8 +9813,8 @@ packages:
       yargs: 14.2.3
     dev: true
 
-  /side-channel@1.0.5:
-    resolution: {integrity: sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==}
+  /side-channel@1.0.6:
+    resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
     engines: {node: '>= 0.4'}
     dependencies:
       call-bind: 1.0.7
@@ -8473,6 +9823,10 @@ packages:
       object-inspect: 1.13.1
     dev: true
 
+  /siginfo@2.0.0:
+    resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+    dev: true
+
   /signal-exit@3.0.7:
     resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
 
@@ -8555,11 +9909,11 @@ packages:
       - supports-color
     optional: true
 
-  /socks-proxy-agent@8.0.2:
-    resolution: {integrity: sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==}
+  /socks-proxy-agent@8.0.3:
+    resolution: {integrity: sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==}
     engines: {node: '>= 14'}
     dependencies:
-      agent-base: 7.1.0
+      agent-base: 7.1.1
       debug: 4.3.4
       socks: 2.8.1
     transitivePeerDependencies:
@@ -8580,10 +9934,9 @@ packages:
       flatstr: 1.0.12
     dev: true
 
-  /source-map-js@1.0.2:
-    resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+  /source-map-js@1.2.0:
+    resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
     engines: {node: '>=0.10.0'}
-    dev: true
 
   /source-map-support@0.5.21:
     resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
@@ -8643,8 +9996,8 @@ packages:
     dependencies:
       bindings: 1.5.0
       node-addon-api: 7.1.0
-      prebuild-install: 7.1.1
-      tar: 6.2.0
+      prebuild-install: 7.1.2
+      tar: 6.2.1
     optionalDependencies:
       node-gyp: 8.4.1
     transitivePeerDependencies:
@@ -8695,6 +10048,10 @@ packages:
       escape-string-regexp: 2.0.0
     dev: true
 
+  /stackback@0.0.2:
+    resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+    dev: true
+
   /static-eval@2.1.1:
     resolution: {integrity: sha512-MgWpQ/ZjGieSVB3eOJVs4OA2LT/q1vx98KPCTTQPzq/aLr0YUXTsgryTXr4SLfR0ZfUUCiedM9n/ABeDIyy4mA==}
     dependencies:
@@ -8720,6 +10077,15 @@ packages:
       through2: 2.0.5
     dev: true
 
+  /statuses@2.0.1:
+    resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
+    engines: {node: '>= 0.8'}
+    dev: false
+
+  /std-env@3.7.0:
+    resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==}
+    dev: true
+
   /stdin-discarder@0.2.2:
     resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==}
     engines: {node: '>=18'}
@@ -8844,29 +10210,31 @@ packages:
       strip-ansi: 7.1.0
     dev: true
 
-  /string.prototype.trim@1.2.8:
-    resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==}
+  /string.prototype.trim@1.2.9:
+    resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==}
     engines: {node: '>= 0.4'}
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-abstract: 1.23.3
+      es-object-atoms: 1.0.0
     dev: true
 
-  /string.prototype.trimend@1.0.7:
-    resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==}
+  /string.prototype.trimend@1.0.8:
+    resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==}
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-object-atoms: 1.0.0
     dev: true
 
-  /string.prototype.trimstart@1.0.7:
-    resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==}
+  /string.prototype.trimstart@1.0.8:
+    resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
+    engines: {node: '>= 0.4'}
     dependencies:
       call-bind: 1.0.7
       define-properties: 1.2.1
-      es-abstract: 1.22.5
+      es-object-atoms: 1.0.0
     dev: true
 
   /string_decoder@1.1.1:
@@ -8938,6 +10306,12 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
+  /strip-literal@2.1.0:
+    resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==}
+    dependencies:
+      js-tokens: 9.0.0
+    dev: true
+
   /subarg@1.0.0:
     resolution: {integrity: sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==}
     dependencies:
@@ -8971,6 +10345,22 @@ packages:
     resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
     engines: {node: '>= 0.4'}
 
+  /svg-tags@1.0.0:
+    resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
+    dev: true
+
+  /symbol-tree@3.2.4:
+    resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+    dev: true
+
+  /synckit@0.8.8:
+    resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    dependencies:
+      '@pkgr/core': 0.1.1
+      tslib: 2.6.2
+    dev: true
+
   /syntax-error@1.4.0:
     resolution: {integrity: sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==}
     dependencies:
@@ -9004,8 +10394,8 @@ packages:
       inherits: 2.0.4
       readable-stream: 3.6.2
 
-  /tar@6.2.0:
-    resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==}
+  /tar@6.2.1:
+    resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
     engines: {node: '>=10'}
     dependencies:
       chownr: 2.0.0
@@ -9092,6 +10482,20 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
+  /tinybench@2.6.0:
+    resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==}
+    dev: true
+
+  /tinypool@0.8.3:
+    resolution: {integrity: sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==}
+    engines: {node: '>=14.0.0'}
+    dev: true
+
+  /tinyspy@2.2.1:
+    resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==}
+    engines: {node: '>=14.0.0'}
+    dev: true
+
   /tmp@0.0.33:
     resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
     engines: {node: '>=0.6.0'}
@@ -9099,6 +10503,10 @@ packages:
       os-tmpdir: 1.0.2
     dev: true
 
+  /to-fast-properties@2.0.0:
+    resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+    engines: {node: '>=4'}
+
   /to-regex-range@5.0.1:
     resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
     engines: {node: '>=8.0'}
@@ -9109,6 +10517,11 @@ packages:
     resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
     dev: true
 
+  /toidentifier@1.0.1:
+    resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+    engines: {node: '>=0.6'}
+    dev: false
+
   /tough-cookie@4.1.3:
     resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
     engines: {node: '>=6'}
@@ -9130,6 +10543,13 @@ packages:
       punycode: 2.3.1
     dev: false
 
+  /tr46@5.0.0:
+    resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
+    engines: {node: '>=18'}
+    dependencies:
+      punycode: 2.3.1
+    dev: true
+
   /transform-ast@2.4.4:
     resolution: {integrity: sha512-AxjeZAcIOUO2lev2GDe3/xZ1Q0cVGjIMk5IsriTy8zbWlsEnjeB025AhkhBJHoy997mXpLd4R+kRbvnnQVuQHQ==}
     dependencies:
@@ -9147,23 +10567,23 @@ packages:
     engines: {node: '>= 14.0.0'}
     dev: false
 
-  /ts-api-utils@1.2.1(typescript@5.3.3):
-    resolution: {integrity: sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==}
+  /ts-api-utils@1.3.0(typescript@5.4.3):
+    resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
     engines: {node: '>=16'}
     peerDependencies:
       typescript: '>=4.2.0'
     dependencies:
-      typescript: 5.3.3
+      typescript: 5.4.3
     dev: true
 
-  /ts-morph@21.0.1:
-    resolution: {integrity: sha512-dbDtVdEAncKctzrVZ+Nr7kHpHkv+0JDJb2MjjpBaj8bFeCkePU9rHfMklmhuLFnpeq/EJZk2IhStY6NzqgjOkg==}
+  /ts-morph@22.0.0:
+    resolution: {integrity: sha512-M9MqFGZREyeb5fTl6gNHKZLqBQA0TjA1lea+CR48R8EBTDuWrNqW6ccC5QvjNR4s6wDumD3LTCjOFSp9iwlzaw==}
     dependencies:
-      '@ts-morph/common': 0.22.0
-      code-block-writer: 12.0.0
+      '@ts-morph/common': 0.23.0
+      code-block-writer: 13.0.1
     dev: false
 
-  /ts-node@10.9.2(@types/node@20.11.24)(typescript@5.3.3):
+  /ts-node@10.9.2(@types/node@20.12.3)(typescript@5.4.3):
     resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
     hasBin: true
     peerDependencies:
@@ -9178,18 +10598,18 @@ packages:
         optional: true
     dependencies:
       '@cspotcode/source-map-support': 0.8.1
-      '@tsconfig/node10': 1.0.9
+      '@tsconfig/node10': 1.0.11
       '@tsconfig/node12': 1.0.11
       '@tsconfig/node14': 1.0.3
       '@tsconfig/node16': 1.0.4
-      '@types/node': 20.11.24
+      '@types/node': 20.12.3
       acorn: 8.11.3
       acorn-walk: 8.3.2
       arg: 4.1.3
       create-require: 1.1.1
       diff: 4.0.2
       make-error: 1.3.6
-      typescript: 5.3.3
+      typescript: 5.4.3
       v8-compile-cache-lib: 3.0.1
       yn: 3.1.1
     dev: true
@@ -9226,7 +10646,7 @@ packages:
     hasBin: true
     dependencies:
       esbuild: 0.19.12
-      get-tsconfig: 4.7.2
+      get-tsconfig: 4.7.3
     optionalDependencies:
       fsevents: 2.3.3
     dev: true
@@ -9275,6 +10695,11 @@ packages:
     resolution: {integrity: sha512-mDZRBQS2yZkwRQKfjJvQ8UIYJeBNNWCq+HBNstl9N5s9jZ4dkVYXEGkVPsSCEh5Ld4JM1kmrZTzjnrqSAIQ7dw==}
     dev: true
 
+  /type-detect@4.0.8:
+    resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
+    engines: {node: '>=4'}
+    dev: true
+
   /type-fest@0.20.2:
     resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
     engines: {node: '>=10'}
@@ -9295,15 +10720,6 @@ packages:
     engines: {node: '>=12.20'}
     dev: true
 
-  /type-fest@3.13.1:
-    resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==}
-    engines: {node: '>=14.16'}
-    dev: true
-
-  /type@1.2.0:
-    resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==}
-    dev: true
-
   /type@2.7.2:
     resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==}
     dev: true
@@ -9340,8 +10756,8 @@ packages:
       is-typed-array: 1.1.13
     dev: true
 
-  /typed-array-length@1.0.5:
-    resolution: {integrity: sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==}
+  /typed-array-length@1.0.6:
+    resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
     engines: {node: '>= 0.4'}
     dependencies:
       call-bind: 1.0.7
@@ -9369,10 +10785,13 @@ packages:
     resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
     dev: true
 
-  /typescript@5.3.3:
-    resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
+  /typescript@5.4.3:
+    resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==}
     engines: {node: '>=14.17'}
     hasBin: true
+
+  /ufo@1.5.3:
+    resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==}
     dev: true
 
   /uglify-js@3.17.4:
@@ -9462,6 +10881,22 @@ packages:
     resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
     engines: {node: '>= 10.0.0'}
 
+  /unpipe@1.0.0:
+    resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+    engines: {node: '>= 0.8'}
+    dev: false
+
+  /update-browserslist-db@1.0.13(browserslist@4.23.0):
+    resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+    dependencies:
+      browserslist: 4.23.0
+      escalade: 3.1.2
+      picocolors: 1.0.0
+    dev: true
+
   /update-notifier@5.1.0:
     resolution: {integrity: sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==}
     engines: {node: '>=10'}
@@ -9525,7 +10960,7 @@ packages:
     resolution: {integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==}
     dependencies:
       punycode: 1.4.1
-      qs: 6.11.2
+      qs: 6.12.0
     dev: true
 
   /utf-8-validate@6.0.3:
@@ -9534,7 +10969,6 @@ packages:
     requiresBuild: true
     dependencies:
       node-gyp-build: 4.8.0
-    dev: false
 
   /util-deprecate@1.0.2:
     resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@@ -9556,7 +10990,7 @@ packages:
       is-arguments: 1.1.1
       is-generator-function: 1.0.10
       is-typed-array: 1.1.13
-      which-typed-array: 1.1.14
+      which-typed-array: 1.1.15
     dev: true
 
   /uuid-parse@1.1.0:
@@ -9576,7 +11010,7 @@ packages:
     resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==}
     engines: {node: '>=10.12.0'}
     dependencies:
-      '@jridgewell/trace-mapping': 0.3.23
+      '@jridgewell/trace-mapping': 0.3.25
       '@types/istanbul-lib-coverage': 2.0.6
       convert-source-map: 2.0.0
     dev: true
@@ -9598,10 +11032,186 @@ packages:
       extsprintf: 1.3.0
     dev: true
 
+  /vite-node@1.4.0(@types/node@20.12.3):
+    resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    hasBin: true
+    dependencies:
+      cac: 6.7.14
+      debug: 4.3.4
+      pathe: 1.1.2
+      picocolors: 1.0.0
+      vite: 5.2.8(@types/node@20.12.3)
+    transitivePeerDependencies:
+      - '@types/node'
+      - less
+      - lightningcss
+      - sass
+      - stylus
+      - sugarss
+      - supports-color
+      - terser
+    dev: true
+
+  /vite@5.2.8(@types/node@20.12.3):
+    resolution: {integrity: sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': ^18.0.0 || >=20.0.0
+      less: '*'
+      lightningcss: ^1.21.0
+      sass: '*'
+      stylus: '*'
+      sugarss: '*'
+      terser: ^5.4.0
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+    dependencies:
+      '@types/node': 20.12.3
+      esbuild: 0.20.2
+      postcss: 8.4.38
+      rollup: 4.14.0
+    optionalDependencies:
+      fsevents: 2.3.3
+    dev: true
+
+  /vitest@1.4.0(@types/node@20.12.3)(jsdom@24.0.0):
+    resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    hasBin: true
+    peerDependencies:
+      '@edge-runtime/vm': '*'
+      '@types/node': ^18.0.0 || >=20.0.0
+      '@vitest/browser': 1.4.0
+      '@vitest/ui': 1.4.0
+      happy-dom: '*'
+      jsdom: '*'
+    peerDependenciesMeta:
+      '@edge-runtime/vm':
+        optional: true
+      '@types/node':
+        optional: true
+      '@vitest/browser':
+        optional: true
+      '@vitest/ui':
+        optional: true
+      happy-dom:
+        optional: true
+      jsdom:
+        optional: true
+    dependencies:
+      '@types/node': 20.12.3
+      '@vitest/expect': 1.4.0
+      '@vitest/runner': 1.4.0
+      '@vitest/snapshot': 1.4.0
+      '@vitest/spy': 1.4.0
+      '@vitest/utils': 1.4.0
+      acorn-walk: 8.3.2
+      chai: 4.4.1
+      debug: 4.3.4
+      execa: 8.0.1
+      jsdom: 24.0.0(bufferutil@4.0.8)(utf-8-validate@6.0.3)
+      local-pkg: 0.5.0
+      magic-string: 0.30.8
+      pathe: 1.1.2
+      picocolors: 1.0.0
+      std-env: 3.7.0
+      strip-literal: 2.1.0
+      tinybench: 2.6.0
+      tinypool: 0.8.3
+      vite: 5.2.8(@types/node@20.12.3)
+      vite-node: 1.4.0(@types/node@20.12.3)
+      why-is-node-running: 2.2.2
+    transitivePeerDependencies:
+      - less
+      - lightningcss
+      - sass
+      - stylus
+      - sugarss
+      - supports-color
+      - terser
+    dev: true
+
   /vm-browserify@1.1.2:
     resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==}
     dev: true
 
+  /vue-component-type-helpers@2.0.7:
+    resolution: {integrity: sha512-7e12Evdll7JcTIocojgnCgwocX4WzIYStGClBQ+QuWPinZo/vQolv2EMq4a3lg16TKfwWafLimG77bxb56UauA==}
+    dev: true
+
+  /vue-eslint-parser@9.4.2(eslint@8.57.0):
+    resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: '>=6.0.0'
+    dependencies:
+      debug: 4.3.4
+      eslint: 8.57.0
+      eslint-scope: 7.2.2
+      eslint-visitor-keys: 3.4.3
+      espree: 9.6.1
+      esquery: 1.5.0
+      lodash: 4.17.21
+      semver: 7.6.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /vue-router@4.3.0(vue@3.4.21):
+    resolution: {integrity: sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==}
+    peerDependencies:
+      vue: ^3.2.0
+    dependencies:
+      '@vue/devtools-api': 6.6.1
+      vue: 3.4.21(typescript@5.4.3)
+    dev: false
+
+  /vue-toast-notification@3.1.2(vue@3.4.21):
+    resolution: {integrity: sha512-oNRL/W9aaHoeScp+iTIW7k09vM16/+8aptp2maa+7qTB43JuxmAgKdXKFYtf+uvSNOYYq2BIWgLCeJ61pwom/A==}
+    engines: {node: '>=12.15.0'}
+    peerDependencies:
+      vue: ^3.0
+    dependencies:
+      vue: 3.4.21(typescript@5.4.3)
+    dev: false
+
+  /vue@3.4.21(typescript@5.4.3):
+    resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@vue/compiler-dom': 3.4.21
+      '@vue/compiler-sfc': 3.4.21
+      '@vue/runtime-dom': 3.4.21
+      '@vue/server-renderer': 3.4.21(vue@3.4.21)
+      '@vue/shared': 3.4.21
+      typescript: 5.4.3
+
+  /w3c-xmlserializer@5.0.0:
+    resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+    engines: {node: '>=18'}
+    dependencies:
+      xml-name-validator: 5.0.0
+    dev: true
+
   /wcwidth@1.0.1:
     resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
     dependencies:
@@ -9624,7 +11234,18 @@ packages:
   /webidl-conversions@7.0.0:
     resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
     engines: {node: '>=12'}
-    dev: false
+
+  /whatwg-encoding@3.1.1:
+    resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+    engines: {node: '>=18'}
+    dependencies:
+      iconv-lite: 0.6.3
+    dev: true
+
+  /whatwg-mimetype@4.0.0:
+    resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+    engines: {node: '>=18'}
+    dev: true
 
   /whatwg-url@13.0.0:
     resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==}
@@ -9634,6 +11255,14 @@ packages:
       webidl-conversions: 7.0.0
     dev: false
 
+  /whatwg-url@14.0.0:
+    resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==}
+    engines: {node: '>=18'}
+    dependencies:
+      tr46: 5.0.0
+      webidl-conversions: 7.0.0
+    dev: true
+
   /whatwg-url@5.0.0:
     resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
     dependencies:
@@ -9655,8 +11284,8 @@ packages:
     resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
     dev: true
 
-  /which-typed-array@1.1.14:
-    resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==}
+  /which-typed-array@1.1.15:
+    resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==}
     engines: {node: '>= 0.4'}
     dependencies:
       available-typed-arrays: 1.0.7
@@ -9673,6 +11302,15 @@ packages:
     dependencies:
       isexe: 2.0.0
 
+  /why-is-node-running@2.2.2:
+    resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==}
+    engines: {node: '>=8'}
+    hasBin: true
+    dependencies:
+      siginfo: 2.0.0
+      stackback: 0.0.2
+    dev: true
+
   /wide-align@1.1.5:
     resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
     requiresBuild: true
@@ -9712,7 +11350,7 @@ packages:
       execa: 5.1.1
     dev: true
 
-  /winston-daily-rotate-file@5.0.0(winston@3.11.0):
+  /winston-daily-rotate-file@5.0.0(winston@3.13.0):
     resolution: {integrity: sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==}
     engines: {node: '>=8'}
     peerDependencies:
@@ -9721,7 +11359,7 @@ packages:
       file-stream-rotator: 0.6.1
       object-hash: 3.0.0
       triple-beam: 1.4.1
-      winston: 3.11.0
+      winston: 3.13.0
       winston-transport: 4.7.0
     dev: false
 
@@ -9734,8 +11372,8 @@ packages:
       triple-beam: 1.4.1
     dev: false
 
-  /winston@3.11.0:
-    resolution: {integrity: sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==}
+  /winston@3.13.0:
+    resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==}
     engines: {node: '>= 12.0.0'}
     dependencies:
       '@colors/colors': 1.6.0
@@ -9832,7 +11470,6 @@ packages:
     dependencies:
       bufferutil: 4.0.8
       utf-8-validate: 6.0.3
-    dev: false
 
   /xdg-basedir@4.0.0:
     resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==}
@@ -9844,6 +11481,20 @@ packages:
     engines: {node: '>=12'}
     dev: true
 
+  /xml-name-validator@4.0.0:
+    resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /xml-name-validator@5.0.0:
+    resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+    engines: {node: '>=18'}
+    dev: true
+
+  /xmlchars@2.2.0:
+    resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+    dev: true
+
   /xtend@4.0.2:
     resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
     engines: {node: '>=0.4'}
@@ -9858,6 +11509,10 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
+  /yallist@3.1.1:
+    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+    dev: true
+
   /yallist@4.0.0:
     resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
 
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644 (file)
index 0000000..5a8abdc
--- /dev/null
@@ -0,0 +1,3 @@
+packages:
+  - ./
+  - ./ui/web
index 16824fa71ef62f2ba40b94730ae82077ba5df091..d3cd9046fc8846dbed45d65fd32c989b02567f4c 100644 (file)
@@ -3,7 +3,7 @@ sonar.organization=sap-1
 
 # This is the name and version displayed in the SonarCloud UI.
 sonar.projectName=e-mobility-charging-stations-simulator
-sonar.projectVersion=1.2.37
+sonar.projectVersion=1.3.1
 
 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
 sonar.sources=src
index 0946258fc1b483a4adedfa4b98d70484eae7f703..943e9c5b2c4998b556c614eb80714666e5afbf4c 100644 (file)
@@ -31,7 +31,8 @@
     },
     {
       "file": "keba.station-template.json",
-      "numberOfStations": 2
+      "numberOfStations": 2,
+      "provisionedNumberOfStations": 2
     },
     {
       "file": "abb.station-template.json",
index 95483b8ec54dd80072f9fe48171e45fe08761de0..a6143a8c0a3bb7de88d668a15bf0ac28cf5b323b 100644 (file)
@@ -1,11 +1,9 @@
 // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
 
+import { randomInt } from 'node:crypto'
+
 import { hoursToMilliseconds, secondsToMilliseconds } from 'date-fns'
 
-import type { ChargingStation } from './ChargingStation.js'
-import { checkChargingStation } from './Helpers.js'
-import { IdTagsCache } from './IdTagsCache.js'
-import { isIdTagAuthorized } from './ocpp/index.js'
 import { BaseError } from '../exception/index.js'
 import { PerformanceStatistics } from '../performance/index.js'
 import {
@@ -18,17 +16,20 @@ import {
   type StopTransactionResponse
 } from '../types/index.js'
 import {
-  Constants,
   clone,
+  Constants,
   convertToDate,
   formatDurationMilliSeconds,
-  getRandomInteger,
   isValidDate,
-  logPrefix,
   logger,
+  logPrefix,
   secureRandom,
   sleep
 } from '../utils/index.js'
+import type { ChargingStation } from './ChargingStation.js'
+import { checkChargingStation } from './Helpers.js'
+import { IdTagsCache } from './IdTagsCache.js'
+import { isIdTagAuthorized } from './ocpp/index.js'
 
 export class AutomaticTransactionGenerator {
   private static readonly instances: Map<string, AutomaticTransactionGenerator> = new Map<
@@ -199,11 +200,11 @@ export class AutomaticTransactionGenerator {
         break
       }
       const wait = secondsToMilliseconds(
-        getRandomInteger(
+        randomInt(
           this.chargingStation.getAutomaticTransactionGeneratorConfiguration()
-            ?.maxDelayBetweenTwoTransactions,
+            ?.minDelayBetweenTwoTransactions,
           this.chargingStation.getAutomaticTransactionGeneratorConfiguration()
-            ?.minDelayBetweenTwoTransactions
+            ?.maxDelayBetweenTwoTransactions
         )
       )
       logger.info(`${this.logPrefix(connectorId)} waiting for ${formatDurationMilliSeconds(wait)}`)
@@ -221,9 +222,9 @@ export class AutomaticTransactionGenerator {
         if (startResponse?.idTagInfo.status === AuthorizationStatus.ACCEPTED) {
           // Wait until end of transaction
           const waitTrxEnd = secondsToMilliseconds(
-            getRandomInteger(
-              this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.maxDuration,
-              this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.minDuration
+            randomInt(
+              this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.minDuration,
+              this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.maxDuration
             )
           )
           logger.info(
index 047b321b55c36c910211e381482f09439422a63b..5293a9a83c28a9a1791f166606d3ca275f2cbb67 100644 (file)
@@ -1,33 +1,32 @@
 // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
 
 import { EventEmitter } from 'node:events'
-import { dirname, extname, join, parse } from 'node:path'
+import { dirname, extname, join } from 'node:path'
 import process, { exit } from 'node:process'
 import { fileURLToPath } from 'node:url'
 import { isMainThread } from 'node:worker_threads'
-import type { Worker } from 'worker_threads'
 
 import chalk from 'chalk'
-import { type MessageHandler, availableParallelism } from 'poolifier'
+import { availableParallelism, type MessageHandler } from 'poolifier'
+import type { Worker } from 'worker_threads'
 
-import { waitChargingStationEvents } from './Helpers.js'
-import type { AbstractUIServer } from './ui-server/AbstractUIServer.js'
-import { UIServerFactory } from './ui-server/UIServerFactory.js'
 import { version } from '../../package.json'
 import { BaseError } from '../exception/index.js'
 import { type Storage, StorageFactory } from '../performance/index.js'
 import {
   type ChargingStationData,
+  type ChargingStationInfo,
   type ChargingStationOptions,
   type ChargingStationWorkerData,
-  type ChargingStationWorkerEventError,
   type ChargingStationWorkerMessage,
   type ChargingStationWorkerMessageData,
   ChargingStationWorkerMessageEvents,
   ConfigurationSection,
   ProcedureName,
+  type SimulatorState,
   type Statistics,
   type StorageConfiguration,
+  type TemplateStatistics,
   type UIServerConfiguration,
   type WorkerConfiguration
 } from '../types/index.js'
@@ -40,10 +39,13 @@ import {
   handleUnhandledRejection,
   isAsyncFunction,
   isNotEmptyArray,
-  logPrefix,
-  logger
+  logger,
+  logPrefix
 } from '../utils/index.js'
-import { type WorkerAbstract, WorkerFactory } from '../worker/index.js'
+import { DEFAULT_ELEMENTS_PER_WORKER, type WorkerAbstract, WorkerFactory } from '../worker/index.js'
+import { buildTemplateName, waitChargingStationEvents } from './Helpers.js'
+import type { AbstractUIServer } from './ui-server/AbstractUIServer.js'
+import { UIServerFactory } from './ui-server/UIServerFactory.js'
 
 const moduleName = 'Bootstrap'
 
@@ -55,21 +57,13 @@ enum exitCodes {
   gracefulShutdownError = 4
 }
 
-interface TemplateChargingStations {
-  configured: number
-  added: number
-  started: number
-  indexes: Set<number>
-}
-
 export class Bootstrap extends EventEmitter {
   private static instance: Bootstrap | null = null
-  private workerImplementation?: WorkerAbstract<ChargingStationWorkerData>
+  private workerImplementation?: WorkerAbstract<ChargingStationWorkerData, ChargingStationInfo>
   private readonly uiServer: AbstractUIServer
   private storage?: Storage
-  private readonly templatesChargingStations: Map<string, TemplateChargingStations>
+  private readonly templateStatistics: Map<string, TemplateStatistics>
   private readonly version: string = version
-  private initializedCounters: boolean
   private started: boolean
   private starting: boolean
   private stopping: boolean
@@ -87,12 +81,14 @@ export class Bootstrap extends EventEmitter {
     this.starting = false
     this.stopping = false
     this.uiServerStarted = false
+    this.templateStatistics = new Map<string, TemplateStatistics>()
     this.uiServer = UIServerFactory.getUIServerImplementation(
       Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer)
     )
-    this.templatesChargingStations = new Map<string, TemplateChargingStations>()
-    this.initializedCounters = false
     this.initializeCounters()
+    this.initializeWorkerImplementation(
+      Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
+    )
     Configuration.configurationChangeCallback = async () => {
       if (isMainThread) {
         await Bootstrap.getInstance().restart()
@@ -108,19 +104,35 @@ export class Bootstrap extends EventEmitter {
   }
 
   public get numberOfChargingStationTemplates (): number {
-    return this.templatesChargingStations.size
+    return this.templateStatistics.size
   }
 
   public get numberOfConfiguredChargingStations (): number {
-    return [...this.templatesChargingStations.values()].reduce(
+    return [...this.templateStatistics.values()].reduce(
       (accumulator, value) => accumulator + value.configured,
       0
     )
   }
 
+  public get numberOfProvisionedChargingStations (): number {
+    return [...this.templateStatistics.values()].reduce(
+      (accumulator, value) => accumulator + value.provisioned,
+      0
+    )
+  }
+
+  public getState (): SimulatorState {
+    return {
+      version: this.version,
+      configuration: Configuration.getConfigurationData(),
+      started: this.started,
+      templateStatistics: this.templateStatistics
+    }
+  }
+
   public getLastIndex (templateName: string): number {
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    const indexes = [...this.templatesChargingStations.get(templateName)!.indexes]
+    const indexes = [...this.templateStatistics.get(templateName)!.indexes]
       .concat(0)
       .sort((a, b) => a - b)
     for (let i = 0; i < indexes.length - 1; i++) {
@@ -136,14 +148,14 @@ export class Bootstrap extends EventEmitter {
   }
 
   private get numberOfAddedChargingStations (): number {
-    return [...this.templatesChargingStations.values()].reduce(
+    return [...this.templateStatistics.values()].reduce(
       (accumulator, value) => accumulator + value.added,
       0
     )
   }
 
   private get numberOfStartedChargingStations (): number {
-    return [...this.templatesChargingStations.values()].reduce(
+    return [...this.templateStatistics.values()].reduce(
       (accumulator, value) => accumulator + value.started,
       0
     )
@@ -162,21 +174,12 @@ export class Bootstrap extends EventEmitter {
           ChargingStationWorkerMessageEvents.performanceStatistics,
           this.workerEventPerformanceStatistics
         )
-        this.on(
-          ChargingStationWorkerMessageEvents.workerElementError,
-          (eventError: ChargingStationWorkerEventError) => {
-            logger.error(
-              `${this.logPrefix()} ${moduleName}.start: Error occurred while handling '${eventError.event}' event on worker:`,
-              eventError
-            )
-          }
-        )
-        this.initializeCounters()
-        const workerConfiguration = Configuration.getConfigurationSection<WorkerConfiguration>(
-          ConfigurationSection.worker
-        )
-        this.initializeWorkerImplementation(workerConfiguration)
-        await this.workerImplementation?.start()
+        // eslint-disable-next-line @typescript-eslint/unbound-method
+        if (isAsyncFunction(this.workerImplementation?.start)) {
+          await this.workerImplementation.start()
+        } else {
+          (this.workerImplementation?.start as () => void)()
+        }
         const performanceStorageConfiguration =
           Configuration.getConfigurationSection<StorageConfiguration>(
             ConfigurationSection.performanceStorage
@@ -204,9 +207,7 @@ export class Bootstrap extends EventEmitter {
         // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
         for (const stationTemplateUrl of Configuration.getStationTemplateUrls()!) {
           try {
-            const nbStations =
-              this.templatesChargingStations.get(parse(stationTemplateUrl.file).name)?.configured ??
-              stationTemplateUrl.numberOfStations
+            const nbStations = stationTemplateUrl.numberOfStations
             for (let index = 1; index <= nbStations; index++) {
               await this.addChargingStation(index, stationTemplateUrl.file)
             }
@@ -219,11 +220,14 @@ export class Bootstrap extends EventEmitter {
             )
           }
         }
+        const workerConfiguration = Configuration.getConfigurationSection<WorkerConfiguration>(
+          ConfigurationSection.worker
+        )
         console.info(
           chalk.green(
             `Charging stations simulator ${
               this.version
-            } started with ${this.numberOfConfiguredChargingStations} configured charging station(s) from ${this.numberOfChargingStationTemplates} charging station template(s) and ${
+            } started with ${this.numberOfConfiguredChargingStations} configured and ${this.numberOfProvisionedChargingStations} provisioned charging station(s) from ${this.numberOfChargingStationTemplates} charging station template(s) and ${
               Configuration.workerDynamicPoolInUse() ? `${workerConfiguration.poolMinSize}/` : ''
             }${this.workerImplementation?.size}${
               Configuration.workerPoolInUse() ? `/${workerConfiguration.poolMaxSize}` : ''
@@ -268,10 +272,8 @@ export class Bootstrap extends EventEmitter {
           console.error(chalk.red('Error while waiting for charging stations to stop: '), error)
         }
         await this.workerImplementation?.stop()
-        delete this.workerImplementation
         this.removeAllListeners()
         this.uiServer.clearCaches()
-        this.initializedCounters = false
         await this.storage?.close()
         delete this.storage
         this.started = false
@@ -294,11 +296,16 @@ export class Bootstrap extends EventEmitter {
       this.uiServer.stop()
       this.uiServerStarted = false
     }
+    this.initializeCounters()
+    // FIXME: initialize worker implementation only if the worker section has changed
+    this.initializeWorkerImplementation(
+      Configuration.getConfigurationSection<WorkerConfiguration>(ConfigurationSection.worker)
+    )
     await this.start()
   }
 
   private async waitChargingStationsStopped (): Promise<string> {
-    return await new Promise<string>((resolve, reject) => {
+    return await new Promise<string>((resolve, reject: (reason?: unknown) => void) => {
       const waitTimeout = setTimeout(() => {
         const timeoutMessage = `Timeout ${formatDurationMilliSeconds(
           Constants.STOP_CHARGING_STATIONS_TIMEOUT
@@ -328,17 +335,27 @@ export class Bootstrap extends EventEmitter {
     let elementsPerWorker: number
     switch (workerConfiguration.elementsPerWorker) {
       case 'all':
-        elementsPerWorker = this.numberOfConfiguredChargingStations
+        elementsPerWorker =
+          this.numberOfConfiguredChargingStations + this.numberOfProvisionedChargingStations
         break
       case 'auto':
-      default:
         elementsPerWorker =
-          this.numberOfConfiguredChargingStations > availableParallelism()
-            ? Math.round(this.numberOfConfiguredChargingStations / (availableParallelism() * 1.5))
+          this.numberOfConfiguredChargingStations + this.numberOfProvisionedChargingStations >
+          availableParallelism()
+            ? Math.round(
+              (this.numberOfConfiguredChargingStations +
+                  this.numberOfProvisionedChargingStations) /
+                  (availableParallelism() * 1.5)
+            )
             : 1
         break
+      default:
+        elementsPerWorker = workerConfiguration.elementsPerWorker ?? DEFAULT_ELEMENTS_PER_WORKER
     }
-    this.workerImplementation = WorkerFactory.getWorkerImplementation<ChargingStationWorkerData>(
+    this.workerImplementation = WorkerFactory.getWorkerImplementation<
+    ChargingStationWorkerData,
+    ChargingStationInfo
+    >(
       join(
         dirname(fileURLToPath(import.meta.url)),
         `ChargingStationWorker${extname(fileURLToPath(import.meta.url))}`
@@ -347,7 +364,7 @@ export class Bootstrap extends EventEmitter {
       workerConfiguration.processType!,
       {
         workerStartDelay: workerConfiguration.startDelay,
-        elementStartDelay: workerConfiguration.elementStartDelay,
+        elementAddDelay: workerConfiguration.elementAddDelay,
         // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
         poolMaxSize: workerConfiguration.poolMaxSize!,
         // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -355,7 +372,9 @@ export class Bootstrap extends EventEmitter {
         elementsPerWorker,
         poolOptions: {
           messageHandler: this.messageHandler.bind(this) as MessageHandler<Worker>,
-          workerOptions: { resourceLimits: workerConfiguration.resourceLimits }
+          ...(workerConfiguration.resourceLimits != null && {
+            workerOptions: { resourceLimits: workerConfiguration.resourceLimits }
+          })
         }
       }
     )
@@ -365,50 +384,46 @@ export class Bootstrap extends EventEmitter {
     msg: ChargingStationWorkerMessage<ChargingStationWorkerMessageData>
   ): void {
     // logger.debug(
-    //   `${this.logPrefix()} ${moduleName}.messageHandler: Worker channel message received: ${JSON.stringify(
+    //   `${this.logPrefix()} ${moduleName}.messageHandler: Charging station worker message received: ${JSON.stringify(
     //     msg,
     //     undefined,
     //     2
     //   )}`
     // )
+    // Skip worker message events processing
+    // eslint-disable-next-line @typescript-eslint/dot-notation
+    if (msg['uuid'] != null) {
+      return
+    }
+    const { event, data } = msg
     try {
-      switch (msg.event) {
+      switch (event) {
         case ChargingStationWorkerMessageEvents.added:
-          this.emit(ChargingStationWorkerMessageEvents.added, msg.data)
+          this.emit(ChargingStationWorkerMessageEvents.added, data)
           break
         case ChargingStationWorkerMessageEvents.deleted:
-          this.emit(ChargingStationWorkerMessageEvents.deleted, msg.data)
+          this.emit(ChargingStationWorkerMessageEvents.deleted, data)
           break
         case ChargingStationWorkerMessageEvents.started:
-          this.emit(ChargingStationWorkerMessageEvents.started, msg.data)
+          this.emit(ChargingStationWorkerMessageEvents.started, data)
           break
         case ChargingStationWorkerMessageEvents.stopped:
-          this.emit(ChargingStationWorkerMessageEvents.stopped, msg.data)
+          this.emit(ChargingStationWorkerMessageEvents.stopped, data)
           break
         case ChargingStationWorkerMessageEvents.updated:
-          this.emit(ChargingStationWorkerMessageEvents.updated, msg.data)
+          this.emit(ChargingStationWorkerMessageEvents.updated, data)
           break
         case ChargingStationWorkerMessageEvents.performanceStatistics:
-          this.emit(ChargingStationWorkerMessageEvents.performanceStatistics, msg.data)
-          break
-        case ChargingStationWorkerMessageEvents.addedWorkerElement:
-          this.emit(ChargingStationWorkerMessageEvents.addWorkerElement, msg.data)
-          break
-        case ChargingStationWorkerMessageEvents.workerElementError:
-          this.emit(ChargingStationWorkerMessageEvents.workerElementError, msg.data)
+          this.emit(ChargingStationWorkerMessageEvents.performanceStatistics, data)
           break
         default:
           throw new BaseError(
-            `Unknown charging station worker event: '${
-              msg.event
-            }' received with data: ${JSON.stringify(msg.data, undefined, 2)}`
+            `Unknown charging station worker message event: '${event}' received with data: ${JSON.stringify(data, undefined, 2)}`
           )
       }
     } catch (error) {
       logger.error(
-        `${this.logPrefix()} ${moduleName}.messageHandler: Error occurred while handling '${
-          msg.event
-        }' event:`,
+        `${this.logPrefix()} ${moduleName}.messageHandler: Error occurred while handling charging station worker message event '${event}':`,
         error
       )
     }
@@ -421,31 +436,29 @@ export class Bootstrap extends EventEmitter {
         data.stationInfo.chargingStationId
       } (hashId: ${data.stationInfo.hashId}) added (${
         this.numberOfAddedChargingStations
-      } added from ${this.numberOfConfiguredChargingStations} configured charging station(s))`
+      } added from ${this.numberOfConfiguredChargingStations} configured and ${this.numberOfProvisionedChargingStations} provisioned charging station(s))`
     )
   }
 
   private readonly workerEventDeleted = (data: ChargingStationData): void => {
     this.uiServer.chargingStations.delete(data.stationInfo.hashId)
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    const templateChargingStations = this.templatesChargingStations.get(
-      data.stationInfo.templateName
-    )!
-    --templateChargingStations.added
-    templateChargingStations.indexes.delete(data.stationInfo.templateIndex)
+    const templateStatistics = this.templateStatistics.get(data.stationInfo.templateName)!
+    --templateStatistics.added
+    templateStatistics.indexes.delete(data.stationInfo.templateIndex)
     logger.info(
       `${this.logPrefix()} ${moduleName}.workerEventDeleted: Charging station ${
         data.stationInfo.chargingStationId
       } (hashId: ${data.stationInfo.hashId}) deleted (${
         this.numberOfAddedChargingStations
-      } added from ${this.numberOfConfiguredChargingStations} configured charging station(s))`
+      } added from ${this.numberOfConfiguredChargingStations} configured and ${this.numberOfProvisionedChargingStations} provisioned charging station(s))`
     )
   }
 
   private readonly workerEventStarted = (data: ChargingStationData): void => {
     this.uiServer.chargingStations.set(data.stationInfo.hashId, data)
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    ++this.templatesChargingStations.get(data.stationInfo.templateName)!.started
+    ++this.templateStatistics.get(data.stationInfo.templateName)!.started
     logger.info(
       `${this.logPrefix()} ${moduleName}.workerEventStarted: Charging station ${
         data.stationInfo.chargingStationId
@@ -458,7 +471,7 @@ export class Bootstrap extends EventEmitter {
   private readonly workerEventStopped = (data: ChargingStationData): void => {
     this.uiServer.chargingStations.set(data.stationInfo.hashId, data)
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    --this.templatesChargingStations.get(data.stationInfo.templateName)!.started
+    --this.templateStatistics.get(data.stationInfo.templateName)!.started
     logger.info(
       `${this.logPrefix()} ${moduleName}.workerEventStopped: Charging station ${
         data.stationInfo.chargingStationId
@@ -488,71 +501,73 @@ export class Bootstrap extends EventEmitter {
   }
 
   private initializeCounters (): void {
-    if (!this.initializedCounters) {
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      const stationTemplateUrls = Configuration.getStationTemplateUrls()!
-      if (isNotEmptyArray(stationTemplateUrls)) {
-        for (const stationTemplateUrl of stationTemplateUrls) {
-          const templateName = parse(stationTemplateUrl.file).name
-          this.templatesChargingStations.set(templateName, {
-            configured: stationTemplateUrl.numberOfStations,
-            added: 0,
-            started: 0,
-            indexes: new Set<number>()
-          })
-          this.uiServer.chargingStationTemplates.add(templateName)
-        }
-        if (this.templatesChargingStations.size !== stationTemplateUrls.length) {
-          console.error(
-            chalk.red(
-              "'stationTemplateUrls' contains duplicate entries, please check your configuration"
-            )
-          )
-          exit(exitCodes.duplicateChargingStationTemplateUrls)
-        }
-      } else {
-        console.error(
-          chalk.red("'stationTemplateUrls' not defined or empty, please check your configuration")
-        )
-        exit(exitCodes.missingChargingStationsConfiguration)
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const stationTemplateUrls = Configuration.getStationTemplateUrls()!
+    if (isNotEmptyArray(stationTemplateUrls)) {
+      for (const stationTemplateUrl of stationTemplateUrls) {
+        const templateName = buildTemplateName(stationTemplateUrl.file)
+        this.templateStatistics.set(templateName, {
+          configured: stationTemplateUrl.numberOfStations,
+          provisioned: stationTemplateUrl.provisionedNumberOfStations ?? 0,
+          added: 0,
+          started: 0,
+          indexes: new Set<number>()
+        })
+        this.uiServer.chargingStationTemplates.add(templateName)
       }
-      if (
-        this.numberOfConfiguredChargingStations === 0 &&
-        Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer)
-          .enabled !== true
-      ) {
+      if (this.templateStatistics.size !== stationTemplateUrls.length) {
         console.error(
           chalk.red(
-            "'stationTemplateUrls' has no charging station enabled and UI server is disabled, please check your configuration"
+            "'stationTemplateUrls' contains duplicate entries, please check your configuration"
           )
         )
-        exit(exitCodes.noChargingStationTemplates)
+        exit(exitCodes.duplicateChargingStationTemplateUrls)
       }
-      this.initializedCounters = true
+    } else {
+      console.error(
+        chalk.red("'stationTemplateUrls' not defined or empty, please check your configuration")
+      )
+      exit(exitCodes.missingChargingStationsConfiguration)
+    }
+    if (
+      this.numberOfConfiguredChargingStations === 0 &&
+      Configuration.getConfigurationSection<UIServerConfiguration>(ConfigurationSection.uiServer)
+        .enabled !== true
+    ) {
+      console.error(
+        chalk.red(
+          "'stationTemplateUrls' has no charging station enabled and UI server is disabled, please check your configuration"
+        )
+      )
+      exit(exitCodes.noChargingStationTemplates)
     }
   }
 
   public async addChargingStation (
     index: number,
-    stationTemplateFile: string,
+    templateFile: string,
     options?: ChargingStationOptions
-  ): Promise<void> {
-    await this.workerImplementation?.addElement({
+  ): Promise<ChargingStationInfo | undefined> {
+    if (!this.started && !this.starting) {
+      throw new BaseError(
+        'Cannot add charging station while the charging stations simulator is not started'
+      )
+    }
+    const stationInfo = await this.workerImplementation?.addElement({
       index,
       templateFile: join(
         dirname(fileURLToPath(import.meta.url)),
         'assets',
         'station-templates',
-        stationTemplateFile
+        templateFile
       ),
       options
     })
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    const templateChargingStations = this.templatesChargingStations.get(
-      parse(stationTemplateFile).name
-    )!
-    ++templateChargingStations.added
-    templateChargingStations.indexes.add(index)
+    const templateStatistics = this.templateStatistics.get(buildTemplateName(templateFile))!
+    ++templateStatistics.added
+    templateStatistics.indexes.add(index)
+    return stationInfo
   }
 
   private gracefulShutdown (): void {
@@ -569,7 +584,7 @@ export class Bootstrap extends EventEmitter {
             exit(exitCodes.gracefulShutdownError)
           })
       })
-      .catch(error => {
+      .catch((error: unknown) => {
         console.error(chalk.red('Error while shutdowning charging stations simulator: '), error)
         exit(exitCodes.gracefulShutdownError)
       })
index 82fba5adad98c6629a6579b8e7c8516df38bb7bb..926915fb016a5497fd519667dff1199052b666e2 100644 (file)
@@ -1,67 +1,16 @@
 // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
 
-import { createHash } from 'node:crypto'
+import { createHash, randomInt } from 'node:crypto'
 import { EventEmitter } from 'node:events'
-import { type FSWatcher, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
-import { dirname, join, parse } from 'node:path'
+import { existsSync, type FSWatcher, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
+import { dirname, join } from 'node:path'
 import { URL } from 'node:url'
 import { parentPort } from 'node:worker_threads'
 
 import { millisecondsToSeconds, secondsToMilliseconds } from 'date-fns'
-import { mergeDeepRight } from 'rambda'
+import { mergeDeepRight, once } from 'rambda'
 import { type RawData, WebSocket } from 'ws'
 
-import { AutomaticTransactionGenerator } from './AutomaticTransactionGenerator.js'
-import { ChargingStationWorkerBroadcastChannel } from './broadcast-channel/ChargingStationWorkerBroadcastChannel.js'
-import {
-  addConfigurationKey,
-  deleteConfigurationKey,
-  getConfigurationKey,
-  setConfigurationKeyValue
-} from './ConfigurationKeyUtils.js'
-import {
-  buildConnectorsMap,
-  checkChargingStation,
-  checkConfiguration,
-  checkConnectorsConfiguration,
-  checkStationInfoConnectorStatus,
-  checkTemplate,
-  createBootNotificationRequest,
-  createSerialNumber,
-  getAmperageLimitationUnitDivider,
-  getBootConnectorStatus,
-  getChargingStationConnectorChargingProfilesPowerLimit,
-  getChargingStationId,
-  getDefaultVoltageOut,
-  getHashId,
-  getIdTagsFile,
-  getMaxNumberOfEvses,
-  getNumberOfReservableConnectors,
-  getPhaseRotationValue,
-  hasFeatureProfile,
-  hasReservationExpired,
-  initializeConnectorsMapStatus,
-  propagateSerialNumber,
-  setChargingStationOptions,
-  stationTemplateToStationInfo,
-  warnTemplateKeysDeprecation
-} from './Helpers.js'
-import { IdTagsCache } from './IdTagsCache.js'
-import {
-  OCPP16IncomingRequestService,
-  OCPP16RequestService,
-  OCPP16ResponseService,
-  OCPP20IncomingRequestService,
-  OCPP20RequestService,
-  OCPP20ResponseService,
-  type OCPPIncomingRequestService,
-  type OCPPRequestService,
-  buildMeterValue,
-  buildTransactionEndMeterValue,
-  getMessageTypeString,
-  sendAndSetConnectorStatus
-} from './ocpp/index.js'
-import { SharedLRUCache } from './SharedLRUCache.js'
 import { BaseError, OCPPError } from '../exception/index.js'
 import { PerformanceStatistics } from '../performance/index.js'
 import {
@@ -113,17 +62,14 @@ import {
   SupervisionUrlDistribution,
   SupportedFeatureProfiles,
   type Voltage,
-  type WSError,
   WebSocketCloseEventStatusCode,
+  type WSError,
   type WsOptions
 } from '../types/index.js'
 import {
   ACElectricUtils,
   AsyncLock,
   AsyncLockType,
-  Configuration,
-  Constants,
-  DCElectricUtils,
   buildAddedMessage,
   buildChargingStationAutomaticTransactionGeneratorConfiguration,
   buildConnectorsStatus,
@@ -133,26 +79,79 @@ import {
   buildStoppedMessage,
   buildUpdatedMessage,
   clone,
+  Configuration,
+  Constants,
   convertToBoolean,
   convertToDate,
   convertToInt,
+  DCElectricUtils,
   exponentialDelay,
   formatDurationMilliSeconds,
   formatDurationSeconds,
-  getRandomInteger,
   getWebSocketCloseEventStatusString,
   handleFileException,
   isNotEmptyArray,
   isNotEmptyString,
-  logPrefix,
   logger,
+  logPrefix,
   min,
-  once,
   roundTo,
   secureRandom,
   sleep,
   watchJsonFile
 } from '../utils/index.js'
+import { AutomaticTransactionGenerator } from './AutomaticTransactionGenerator.js'
+import { ChargingStationWorkerBroadcastChannel } from './broadcast-channel/ChargingStationWorkerBroadcastChannel.js'
+import {
+  addConfigurationKey,
+  deleteConfigurationKey,
+  getConfigurationKey,
+  setConfigurationKeyValue
+} from './ConfigurationKeyUtils.js'
+import {
+  buildConnectorsMap,
+  buildTemplateName,
+  checkChargingStation,
+  checkConfiguration,
+  checkConnectorsConfiguration,
+  checkStationInfoConnectorStatus,
+  checkTemplate,
+  createBootNotificationRequest,
+  createSerialNumber,
+  getAmperageLimitationUnitDivider,
+  getBootConnectorStatus,
+  getChargingStationConnectorChargingProfilesPowerLimit,
+  getChargingStationId,
+  getDefaultVoltageOut,
+  getHashId,
+  getIdTagsFile,
+  getMaxNumberOfEvses,
+  getNumberOfReservableConnectors,
+  getPhaseRotationValue,
+  hasFeatureProfile,
+  hasReservationExpired,
+  initializeConnectorsMapStatus,
+  propagateSerialNumber,
+  setChargingStationOptions,
+  stationTemplateToStationInfo,
+  warnTemplateKeysDeprecation
+} from './Helpers.js'
+import { IdTagsCache } from './IdTagsCache.js'
+import {
+  buildMeterValue,
+  buildTransactionEndMeterValue,
+  getMessageTypeString,
+  OCPP16IncomingRequestService,
+  OCPP16RequestService,
+  OCPP16ResponseService,
+  OCPP20IncomingRequestService,
+  OCPP20RequestService,
+  OCPP20ResponseService,
+  type OCPPIncomingRequestService,
+  type OCPPRequestService,
+  sendAndSetConnectorStatus
+} from './ocpp/index.js'
+import { SharedLRUCache } from './SharedLRUCache.js'
 
 export class ChargingStation extends EventEmitter {
   public readonly index: number
@@ -229,7 +228,7 @@ export class ChargingStation extends EventEmitter {
         this.wsConnectionRetried
           ? true
           : this.getAutomaticTransactionGeneratorConfiguration()?.stopAbsoluteDuration
-      ).catch(error => {
+      ).catch((error: unknown) => {
         logger.error(`${this.logPrefix()} Error while starting the message sequence:`, error)
       })
       this.wsConnectionRetried = false
@@ -287,7 +286,7 @@ export class ChargingStation extends EventEmitter {
         readFileSync(this.templateFile, 'utf8')
       ) as ChargingStationTemplate
     } catch {
-      stationTemplate = undefined
+      // Ignore
     }
     return logPrefix(` ${getChargingStationId(this.index, stationTemplate)} |`)
   }
@@ -553,7 +552,7 @@ export class ChargingStation extends EventEmitter {
       this.heartbeatSetInterval = setInterval(() => {
         this.ocppRequestService
           .requestHandler<HeartbeatRequest, HeartbeatResponse>(this, RequestCommand.HEARTBEAT)
-          .catch(error => {
+          .catch((error: unknown) => {
             logger.error(
               `${this.logPrefix()} Error while sending '${RequestCommand.HEARTBEAT}':`,
               error
@@ -638,7 +637,7 @@ export class ChargingStation extends EventEmitter {
             meterValue: [meterValue]
           }
         )
-          .catch(error => {
+          .catch((error: unknown) => {
             logger.error(
               `${this.logPrefix()} Error while sending '${RequestCommand.METER_VALUES}':`,
               error
@@ -842,7 +841,7 @@ export class ChargingStation extends EventEmitter {
     this.wsConnection.on('close', this.onClose.bind(this))
     // Handle WebSocket open
     this.wsConnection.on('open', () => {
-      this.onOpen().catch(error =>
+      this.onOpen().catch((error: unknown) =>
         logger.error(`${this.logPrefix()} Error while opening WebSocket connection:`, error)
       )
     })
@@ -1155,7 +1154,7 @@ export class ChargingStation extends EventEmitter {
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     const stationTemplate = this.getTemplateFromFile()!
     checkTemplate(stationTemplate, this.logPrefix(), this.templateFile)
-    const warnTemplateKeysDeprecationOnce = once(warnTemplateKeysDeprecation, this)
+    const warnTemplateKeysDeprecationOnce = once(warnTemplateKeysDeprecation)
     warnTemplateKeysDeprecationOnce(stationTemplate, this.logPrefix(), this.templateFile)
     if (stationTemplate.Connectors != null) {
       checkConnectorsConfiguration(stationTemplate, this.logPrefix(), this.templateFile)
@@ -1163,7 +1162,7 @@ export class ChargingStation extends EventEmitter {
     const stationInfo = stationTemplateToStationInfo(stationTemplate)
     stationInfo.hashId = getHashId(this.index, stationTemplate)
     stationInfo.templateIndex = this.index
-    stationInfo.templateName = parse(this.templateFile).name
+    stationInfo.templateName = buildTemplateName(this.templateFile)
     stationInfo.chargingStationId = getChargingStationId(this.index, stationTemplate)
     createSerialNumber(stationTemplate, stationInfo)
     stationInfo.voltageOut = this.getVoltageOut(stationInfo)
@@ -1192,15 +1191,6 @@ export class ChargingStation extends EventEmitter {
         } does not match firmware version pattern '${stationInfo.firmwareVersionPattern}'`
       )
     }
-    stationInfo.firmwareUpgrade = mergeDeepRight(
-      {
-        versionUpgrade: {
-          step: 1
-        },
-        reset: true
-      },
-      stationTemplate.firmwareUpgrade ?? {}
-    )
     if (stationTemplate.resetTime != null) {
       stationInfo.resetTime = secondsToMilliseconds(stationTemplate.resetTime)
     }
@@ -1223,7 +1213,7 @@ export class ChargingStation extends EventEmitter {
         }
         // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
         if (stationInfo.templateName == null) {
-          stationInfo.templateName = parse(this.templateFile).name
+          stationInfo.templateName = buildTemplateName(this.templateFile)
         }
       }
     }
@@ -1237,6 +1227,7 @@ export class ChargingStation extends EventEmitter {
     const stationInfoFromFile = this.getStationInfoFromFile(
       stationInfoFromTemplate.stationInfoPersistentConfiguration
     )
+    let stationInfo: ChargingStationInfo
     // Priority:
     // 1. charging station info from template
     // 2. charging station info from configuration file
@@ -1244,19 +1235,14 @@ export class ChargingStation extends EventEmitter {
       stationInfoFromFile != null &&
       stationInfoFromFile.templateHash === stationInfoFromTemplate.templateHash
     ) {
-      return setChargingStationOptions(
-        { ...Constants.DEFAULT_STATION_INFO, ...stationInfoFromFile },
-        options
-      )
+      stationInfo = stationInfoFromFile
+    } else {
+      stationInfo = stationInfoFromTemplate
+      stationInfoFromFile != null &&
+        propagateSerialNumber(this.getTemplateFromFile(), stationInfoFromFile, stationInfo)
     }
-    stationInfoFromFile != null &&
-      propagateSerialNumber(
-        this.getTemplateFromFile(),
-        stationInfoFromFile,
-        stationInfoFromTemplate
-      )
     return setChargingStationOptions(
-      { ...Constants.DEFAULT_STATION_INFO, ...stationInfoFromTemplate },
+      mergeDeepRight(Constants.DEFAULT_STATION_INFO, stationInfo),
       options
     )
   }
@@ -1554,7 +1540,7 @@ export class ChargingStation extends EventEmitter {
             }
             const templateConnectorId =
               connectorId > 0 && stationTemplate.randomConnectors === true
-                ? getRandomInteger(templateMaxAvailableConnectors, 1)
+                ? randomInt(1, templateMaxAvailableConnectors)
                 : connectorId
             const connectorStatus = stationTemplate.Connectors[templateConnectorId]
             checkStationInfoConnectorStatus(
@@ -1767,7 +1753,7 @@ export class ChargingStation extends EventEmitter {
             this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash)
             this.sharedLRUCache.setChargingStationConfiguration(configurationData)
             this.configurationFileHash = configurationHash
-          }).catch(error => {
+          }).catch((error: unknown) => {
             handleFileException(
               this.configurationFile,
               FileType.ChargingStationConfiguration,
@@ -1911,7 +1897,9 @@ export class ChargingStation extends EventEmitter {
             .then(() => {
               this.emit(ChargingStationEvents.updated)
             })
-            .catch(error => logger.error(`${this.logPrefix()} Error while reconnecting:`, error))
+            .catch((error: unknown) =>
+              logger.error(`${this.logPrefix()} Error while reconnecting:`, error)
+            )
         break
     }
   }
@@ -2364,11 +2352,11 @@ export class ChargingStation extends EventEmitter {
             // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
             Configuration.getSupervisionUrlDistribution()!
           ) &&
-            logger.error(
+            logger.warn(
               // eslint-disable-next-line @typescript-eslint/no-base-to-string
-              `${this.logPrefix()} Unknown supervision url distribution '${Configuration.getSupervisionUrlDistribution()}' from values '${SupervisionUrlDistribution.toString()}', defaulting to ${
+              `${this.logPrefix()} Unknown supervision url distribution '${Configuration.getSupervisionUrlDistribution()}' in configuration from values '${SupervisionUrlDistribution.toString()}', defaulting to '${
                 SupervisionUrlDistribution.CHARGING_STATION_AFFINITY
-              }`
+              }'`
             )
           configuredSupervisionUrlIndex = (this.index - 1) % supervisionUrls.length
           break
index 2623f1272d2f19142715f12bfaadaff469294ddb..ab32a464fabbcf3ca3ee2e20a7c48d685945f327 100644 (file)
@@ -4,62 +4,66 @@ import { parentPort } from 'node:worker_threads'
 
 import { ThreadWorker } from 'poolifier'
 
-import { ChargingStation } from './ChargingStation.js'
 import { BaseError } from '../exception/index.js'
-import type {
-  ChargingStationData,
-  ChargingStationWorkerData,
-  ChargingStationWorkerEventError,
-  ChargingStationWorkerMessage
-} from '../types/index.js'
-import { Configuration, buildChargingStationDataPayload } from '../utils/index.js'
-import { type WorkerMessage, WorkerMessageEvents } from '../worker/index.js'
+import type { ChargingStationInfo, ChargingStationWorkerData } from '../types/index.js'
+import { Configuration } from '../utils/index.js'
+import { type WorkerDataError, type WorkerMessage, WorkerMessageEvents } from '../worker/index.js'
+import { ChargingStation } from './ChargingStation.js'
 
 export let chargingStationWorker: object
 if (Configuration.workerPoolInUse()) {
-  chargingStationWorker = new ThreadWorker<ChargingStationWorkerData>(
-    (data?: ChargingStationWorkerData): void => {
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, no-new
-      new ChargingStation(data!.index, data!.templateFile, data!.options)
-    }
-  )
+  chargingStationWorker = new ThreadWorker<
+  ChargingStationWorkerData,
+  ChargingStationInfo | undefined
+  >((data?: ChargingStationWorkerData): ChargingStationInfo | undefined => {
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const { index, templateFile, options } = data!
+    return new ChargingStation(index, templateFile, options).stationInfo
+  })
 } else {
   // eslint-disable-next-line @typescript-eslint/no-extraneous-class
   class ChargingStationWorker<Data extends ChargingStationWorkerData> {
     constructor () {
       parentPort?.on('message', (message: WorkerMessage<Data>) => {
-        switch (message.event) {
-          case WorkerMessageEvents.addWorkerElement:
-            try {
-              const chargingStation = new ChargingStation(
-                message.data.index,
-                message.data.templateFile,
-                message.data.options
+        const { uuid, event, data } = message
+        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+        if (uuid != null) {
+          switch (event) {
+            case WorkerMessageEvents.addWorkerElement:
+              try {
+                const chargingStation = new ChargingStation(
+                  data.index,
+                  data.templateFile,
+                  data.options
+                )
+                parentPort?.postMessage({
+                  uuid,
+                  event: WorkerMessageEvents.addedWorkerElement,
+                  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+                  data: chargingStation.stationInfo!
+                } satisfies WorkerMessage<ChargingStationInfo>)
+              } catch (error) {
+                parentPort?.postMessage({
+                  uuid,
+                  event: WorkerMessageEvents.workerElementError,
+                  data: {
+                    event,
+                    name: (error as Error).name,
+                    message: (error as Error).message,
+                    stack: (error as Error).stack
+                  }
+                } satisfies WorkerMessage<WorkerDataError>)
+              }
+              break
+            default:
+              throw new BaseError(
+                `Unknown worker message event: '${event}' received with data: '${JSON.stringify(
+                  data,
+                  undefined,
+                  2
+                )}'`
               )
-              parentPort?.postMessage({
-                event: WorkerMessageEvents.addedWorkerElement,
-                data: buildChargingStationDataPayload(chargingStation)
-              } satisfies ChargingStationWorkerMessage<ChargingStationData>)
-            } catch (error) {
-              parentPort?.postMessage({
-                event: WorkerMessageEvents.workerElementError,
-                data: {
-                  event: WorkerMessageEvents.addWorkerElement,
-                  name: (error as Error).name,
-                  message: (error as Error).message,
-                  stack: (error as Error).stack
-                }
-              } satisfies ChargingStationWorkerMessage<ChargingStationWorkerEventError>)
-            }
-            break
-          default:
-            throw new BaseError(
-              `Unknown worker event: '${message.event}' received with data: '${JSON.stringify(
-                message.data,
-                undefined,
-                2
-              )}'`
-            )
+          }
         }
       })
     }
index 075a497988ae2f5375a665568be67bd7fe8dae82..4b9eb40aa211f9c5d5c651aae0203bc8f330fb74 100644 (file)
@@ -1,6 +1,6 @@
-import type { ChargingStation } from './ChargingStation.js'
 import type { ConfigurationKey, ConfigurationKeyType } from '../types/index.js'
 import { logger } from '../utils/index.js'
+import type { ChargingStation } from './ChargingStation.js'
 
 interface ConfigurationKeyOptions {
   readonly?: boolean
index 22ed5041e0515cc6b0769b3acb85df2d54525287..735a0b39c9bcf967f69297aa1a66618d2b4f9752 100644 (file)
@@ -1,18 +1,18 @@
 import { createHash, randomBytes } from 'node:crypto'
 import type { EventEmitter } from 'node:events'
-import { basename, dirname, join } from 'node:path'
+import { basename, dirname, isAbsolute, join, parse, relative, resolve } from 'node:path'
 import { env } from 'node:process'
 import { fileURLToPath } from 'node:url'
 
 import chalk from 'chalk'
 import {
-  type Interval,
   addDays,
   addSeconds,
   addWeeks,
   differenceInDays,
   differenceInSeconds,
   differenceInWeeks,
+  type Interval,
   isAfter,
   isBefore,
   isDate,
@@ -21,9 +21,8 @@ import {
   toDate
 } from 'date-fns'
 import { maxTime } from 'date-fns/constants'
+import { isEmpty } from 'rambda'
 
-import type { ChargingStation } from './ChargingStation.js'
-import { getConfigurationKey } from './ConfigurationKeyUtils.js'
 import { BaseError } from '../exception/index.js'
 import {
   AmpereUnits,
@@ -56,23 +55,34 @@ import {
 } from '../types/index.js'
 import {
   ACElectricUtils,
-  Constants,
-  DCElectricUtils,
   clone,
+  Constants,
   convertToDate,
   convertToInt,
+  DCElectricUtils,
   isArraySorted,
-  isEmptyObject,
-  isEmptyString,
   isNotEmptyArray,
   isNotEmptyString,
   isValidDate,
   logger,
   secureRandom
 } from '../utils/index.js'
+import type { ChargingStation } from './ChargingStation.js'
+import { getConfigurationKey } from './ConfigurationKeyUtils.js'
 
 const moduleName = 'Helpers'
 
+export const buildTemplateName = (templateFile: string): string => {
+  if (isAbsolute(templateFile)) {
+    templateFile = relative(
+      resolve(join(dirname(fileURLToPath(import.meta.url)), 'assets', 'station-templates')),
+      templateFile
+    )
+  }
+  const templateFileParsedPath = parse(templateFile)
+  return join(templateFileParsedPath.dir, templateFileParsedPath.name)
+}
+
 export const getChargingStationId = (
   index: number,
   stationTemplate: ChargingStationTemplate | undefined
@@ -243,12 +253,12 @@ export const checkTemplate = (
     logger.error(`${logPrefix} ${errorMsg}`)
     throw new BaseError(errorMsg)
   }
-  if (isEmptyObject(stationTemplate)) {
+  if (isEmpty(stationTemplate)) {
     const errorMsg = `Empty charging station information from template file ${templateFile}`
     logger.error(`${logPrefix} ${errorMsg}`)
     throw new BaseError(errorMsg)
   }
-  if (stationTemplate.idTagsFile == null || isEmptyString(stationTemplate.idTagsFile)) {
+  if (stationTemplate.idTagsFile == null || isEmpty(stationTemplate.idTagsFile)) {
     logger.warn(
       `${logPrefix} Missing id tags file in template file ${templateFile}. That can lead to issues with the Automatic Transaction Generator`
     )
@@ -265,7 +275,7 @@ export const checkConfiguration = (
     logger.error(`${logPrefix} ${errorMsg}`)
     throw new BaseError(errorMsg)
   }
-  if (isEmptyObject(stationConfiguration)) {
+  if (isEmpty(stationConfiguration)) {
     const errorMsg = `Empty charging station configuration from file ${configurationFile}`
     logger.error(`${logPrefix} ${errorMsg}`)
     throw new BaseError(errorMsg)
@@ -964,7 +974,7 @@ export const prepareChargingProfileKind = (
       if (connectorStatus?.transactionStarted === true) {
         chargingProfile.chargingSchedule.startSchedule = connectorStatus.transactionStart
       }
-      // FIXME: Handle relative charging profile duration
+      // FIXME: handle relative charging profile duration
       break
   }
   return true
index e1bc8b843eb124e822eea882e900210112710cdd..da4b38bfce79de421343e16f90dd2b6a17e85c6b 100644 (file)
@@ -1,16 +1,16 @@
 import { type FSWatcher, readFileSync } from 'node:fs'
 
-import type { ChargingStation } from './ChargingStation.js'
-import { getIdTagsFile } from './Helpers.js'
 import { FileType, IdTagDistribution } from '../types/index.js'
 import {
   handleFileException,
   isNotEmptyString,
-  logPrefix,
   logger,
+  logPrefix,
   secureRandom,
   watchJsonFile
 } from '../utils/index.js'
+import type { ChargingStation } from './ChargingStation.js'
+import { getIdTagsFile } from './Helpers.js'
 
 interface IdTagsCacheValueType {
   idTags: string[]
index eaa8c912672d0ce74a06a5a797a18ece1c67747b..0da4558d4d46f4c4bd3d98f92eb2a7677856517d 100644 (file)
@@ -1,8 +1,9 @@
 import { LRUMapWithDelete as LRUCache } from 'mnemonist'
+import { isEmpty } from 'rambda'
 
-import { Bootstrap } from './Bootstrap.js'
 import type { ChargingStationConfiguration, ChargingStationTemplate } from '../types/index.js'
-import { isEmptyObject, isNotEmptyArray, isNotEmptyString } from '../utils/index.js'
+import { isNotEmptyArray, isNotEmptyString } from '../utils/index.js'
+import { Bootstrap } from './Bootstrap.js'
 
 enum CacheType {
   chargingStationTemplate = 'chargingStationTemplate',
@@ -18,7 +19,8 @@ export class SharedLRUCache {
   private constructor () {
     this.lruCache = new LRUCache<string, CacheValueType>(
       Bootstrap.getInstance().numberOfChargingStationTemplates +
-        Bootstrap.getInstance().numberOfConfiguredChargingStations
+        Bootstrap.getInstance().numberOfConfiguredChargingStations +
+        Bootstrap.getInstance().numberOfProvisionedChargingStations
     )
   }
 
@@ -116,8 +118,8 @@ export class SharedLRUCache {
       chargingStationConfiguration.automaticTransactionGenerator != null &&
       chargingStationConfiguration.configurationHash != null &&
       isNotEmptyArray(chargingStationConfiguration.configurationKey) &&
-      !isEmptyObject(chargingStationConfiguration.stationInfo) &&
-      !isEmptyObject(chargingStationConfiguration.automaticTransactionGenerator) &&
+      !isEmpty(chargingStationConfiguration.stationInfo) &&
+      !isEmpty(chargingStationConfiguration.automaticTransactionGenerator) &&
       isNotEmptyString(chargingStationConfiguration.configurationHash)
     )
   }
index 26aa61d38e7c97149877fcb13584715afc430d34..b5fc1cef13cc76b3f8e5250227485594c55e4210 100644 (file)
@@ -1,6 +1,6 @@
 import { secondsToMilliseconds } from 'date-fns'
+import { isEmpty } from 'rambda'
 
-import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js'
 import { BaseError, type OCPPError } from '../../exception/index.js'
 import {
   AuthorizationStatus,
@@ -37,16 +37,11 @@ import {
   type StopTransactionRequest,
   type StopTransactionResponse
 } from '../../types/index.js'
-import {
-  Constants,
-  convertToInt,
-  isAsyncFunction,
-  isEmptyObject,
-  logger
-} from '../../utils/index.js'
+import { Constants, convertToInt, isAsyncFunction, logger } from '../../utils/index.js'
 import type { ChargingStation } from '../ChargingStation.js'
 import { getConfigurationKey } from '../ConfigurationKeyUtils.js'
 import { buildMeterValue } from '../ocpp/index.js'
+import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js'
 
 const moduleName = 'ChargingStationWorkerBroadcastChannel'
 
@@ -293,7 +288,7 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne
     let responsePayload: BroadcastChannelResponsePayload | undefined
     this.commandHandler(command, requestPayload)
       .then(commandResponse => {
-        if (commandResponse == null || isEmptyObject(commandResponse)) {
+        if (commandResponse == null || isEmpty(commandResponse)) {
           responsePayload = {
             hashId: this.chargingStation.stationInfo?.hashId,
             status: ResponseStatus.SUCCESS
@@ -306,7 +301,7 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne
           )
         }
       })
-      .catch(error => {
+      .catch((error: unknown) => {
         logger.error(
           `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
           error
@@ -420,7 +415,7 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne
         return ResponseStatus.FAILURE
       case BroadcastChannelProcedureName.STATUS_NOTIFICATION:
       case BroadcastChannelProcedureName.METER_VALUES:
-        if (isEmptyObject(commandResponse)) {
+        if (isEmpty(commandResponse)) {
           return ResponseStatus.SUCCESS
         }
         return ResponseStatus.FAILURE
index 3a0c75b7d7e7ac19bce1cb255e320b7b4d35c5b0..ef07b5dac21abda443378f0ad0470b14f0bb4403 100644 (file)
@@ -1,4 +1,3 @@
-import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js'
 import {
   type BroadcastChannelResponse,
   type BroadcastChannelResponsePayload,
@@ -8,6 +7,7 @@ import {
 } from '../../types/index.js'
 import { logger } from '../../utils/index.js'
 import type { AbstractUIService } from '../ui-server/ui-services/AbstractUIService.js'
+import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js'
 
 const moduleName = 'UIServiceWorkerBroadcastChannel'
 
index dfd0c3e54f7804dbb4d1178ea82132b9988664ff..91c92ac2b41ede0d234b184d127ec6329f1b9427 100644 (file)
@@ -6,7 +6,7 @@ import type {
   JsonType,
   MessageEvent
 } from '../../types/index.js'
-import { logPrefix, logger, validateUUID } from '../../utils/index.js'
+import { logger, logPrefix, validateUUID } from '../../utils/index.js'
 
 const moduleName = 'WorkerBroadcastChannel'
 
index f77dfa7f8b0efa1b0ddde1c51601b80740ab3ced..f4a890248c89b40948cc7986e66c9340eae3cc35 100644 (file)
@@ -1,26 +1,26 @@
 // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
 
+import { randomInt } from 'node:crypto'
 import { createWriteStream, readdirSync } from 'node:fs'
-import { dirname, join, resolve } from 'node:path'
-import { URL, fileURLToPath } from 'node:url'
+import { dirname, extname, join, resolve } from 'node:path'
+import { fileURLToPath, URL } from 'node:url'
 
 import type { ValidateFunction } from 'ajv'
 import { Client, type FTPResponse } from 'basic-ftp'
 import {
-  type Interval,
   addSeconds,
   differenceInSeconds,
+  type Interval,
   isDate,
   secondsToMilliseconds
 } from 'date-fns'
 import { maxTime } from 'date-fns/constants'
+import { isEmpty } from 'rambda'
 import { create } from 'tar'
 
-import { OCPP16Constants } from './OCPP16Constants.js'
-import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
 import {
-  type ChargingStation,
   canProceedChargingProfile,
+  type ChargingStation,
   checkChargingStation,
   getConfigurationKey,
   getConnectorChargingProfiles,
@@ -32,6 +32,7 @@ import { OCPPError } from '../../../exception/index.js'
 import {
   type ChangeConfigurationRequest,
   type ChangeConfigurationResponse,
+  ConfigurationSection,
   ErrorType,
   type GenericResponse,
   GenericStatus,
@@ -41,6 +42,7 @@ import {
   type GetDiagnosticsResponse,
   type IncomingRequestHandler,
   type JsonType,
+  type LogConfiguration,
   OCPP16AuthorizationStatus,
   OCPP16AvailabilityType,
   type OCPP16BootNotificationRequest,
@@ -98,19 +100,20 @@ import {
   type UnlockConnectorResponse
 } from '../../../types/index.js'
 import {
+  Configuration,
   Constants,
   convertToDate,
   convertToInt,
   formatDurationMilliSeconds,
-  getRandomInteger,
   isAsyncFunction,
-  isEmptyArray,
   isNotEmptyArray,
   isNotEmptyString,
   logger,
   sleep
 } from '../../../utils/index.js'
 import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js'
+import { OCPP16Constants } from './OCPP16Constants.js'
+import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
 
 const moduleName = 'OCPP16IncomingRequestService'
 
@@ -438,7 +441,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
                 )
               }
             })
-            .catch(error => {
+            .catch((error: unknown) => {
               logger.error(
                 `${chargingStation.logPrefix()} ${moduleName}.constructor: Remote start transaction error:`,
                 error
@@ -470,7 +473,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
                 )
               }
             })
-            .catch(error => {
+            .catch((error: unknown) => {
               logger.error(
                 `${chargingStation.logPrefix()} ${moduleName}.constructor: Remote stop transaction error:`,
                 error
@@ -490,7 +493,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
           return
         }
         const { requestedMessage, connectorId } = request
-        const errorHandler = (error: Error): void => {
+        const errorHandler = (error: unknown): void => {
           logger.error(
             `${chargingStation.logPrefix()} ${moduleName}.constructor: Trigger ${requestedMessage} error:`,
             error
@@ -643,7 +646,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         // Throw exception
         throw new OCPPError(
           ErrorType.NOT_IMPLEMENTED,
-          `${commandName} is not implemented to handle request PDU ${JSON.stringify(
+          `'${commandName}' is not implemented to handle request PDU ${JSON.stringify(
             commandPayload,
             undefined,
             2
@@ -940,8 +943,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     }
     const connectorStatus = chargingStation.getConnectorStatus(connectorId)
     if (
-      isEmptyArray(connectorStatus?.chargingProfiles) &&
-      isEmptyArray(chargingStation.getConnectorStatus(0)?.chargingProfiles)
+      isEmpty(connectorStatus?.chargingProfiles) &&
+      isEmpty(chargingStation.getConnectorStatus(0)?.chargingProfiles)
     ) {
       return OCPP16Constants.OCPP_RESPONSE_REJECTED
     }
@@ -1343,7 +1346,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
       OCPP16FirmwareStatus.DownloadFailed
     ) {
-      await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay)))
+      await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay)))
       await chargingStation.ocppRequestService.requestHandler<
       OCPP16FirmwareStatusNotificationRequest,
       OCPP16FirmwareStatusNotificationResponse
@@ -1354,7 +1357,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         chargingStation.stationInfo.firmwareUpgrade.failureStatus
       return
     }
-    await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay)))
+    await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay)))
     await chargingStation.ocppRequestService.requestHandler<
     OCPP16FirmwareStatusNotificationRequest,
     OCPP16FirmwareStatusNotificationResponse
@@ -1410,8 +1413,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         transactionsStarted = false
       }
     } while (transactionsStarted)
-    !wasTransactionsStarted &&
-      (await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))))
+    !wasTransactionsStarted && (await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay))))
     if (!checkChargingStation(chargingStation, chargingStation.logPrefix())) {
       return
     }
@@ -1427,7 +1429,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
       OCPP16FirmwareStatus.InstallationFailed
     ) {
-      await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay)))
+      await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay)))
       await chargingStation.ocppRequestService.requestHandler<
       OCPP16FirmwareStatusNotificationRequest,
       OCPP16FirmwareStatusNotificationResponse
@@ -1439,7 +1441,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
       return
     }
     if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) {
-      await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay)))
+      await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay)))
       await chargingStation.reset(OCPP16StopTransactionReason.REBOOT)
     }
   }
@@ -1465,14 +1467,19 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     if (uri.protocol.startsWith('ftp:')) {
       let ftpClient: Client | undefined
       try {
+        const logConfiguration = Configuration.getConfigurationSection<LogConfiguration>(
+          ConfigurationSection.log
+        )
         const logFiles = readdirSync(resolve(dirname(fileURLToPath(import.meta.url)), '../'))
-          .filter(file => file.endsWith('.log'))
-          .map(file => join('./', file))
+          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+          .filter(file => file.endsWith(extname(logConfiguration.file!)))
+          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+          .map(file => join(dirname(logConfiguration.file!), file))
         const diagnosticsArchive = `${chargingStation.stationInfo?.chargingStationId}_logs.tar.gz`
         create({ gzip: true }, logFiles).pipe(createWriteStream(diagnosticsArchive))
         ftpClient = new Client()
         const accessResponse = await ftpClient.access({
-          host: uri.host,
+          host: uri.hostname,
           ...(isNotEmptyString(uri.port) && { port: convertToInt(uri.port) }),
           ...(isNotEmptyString(uri.username) && { user: uri.username }),
           ...(isNotEmptyString(uri.password) && { password: uri.password })
@@ -1492,7 +1499,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
             >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
               status: OCPP16DiagnosticsStatus.Uploading
             })
-              .catch(error => {
+              .catch((error: unknown) => {
                 logger.error(
                   `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
                     OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
index 74c00daa8f42335383b7d5d7f603cdd40fd90303..d673e8a31597cf893691991a50f901b874468677 100644 (file)
@@ -2,8 +2,6 @@
 
 import type { ValidateFunction } from 'ajv'
 
-import { OCPP16Constants } from './OCPP16Constants.js'
-import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
 import type { ChargingStation } from '../../../charging-station/index.js'
 import { OCPPError } from '../../../exception/index.js'
 import {
@@ -28,6 +26,8 @@ import {
 import { Constants, generateUUID } from '../../../utils/index.js'
 import { OCPPRequestService } from '../OCPPRequestService.js'
 import type { OCPPResponseService } from '../OCPPResponseService.js'
+import { OCPP16Constants } from './OCPP16Constants.js'
+import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
 
 const moduleName = 'OCPP16RequestService'
 
index 691bf0f6c6b243465255c2c4f322c1c65917ffb8..0fa38728420de1d8e329f1671a2ab9cb77ae00fa 100644 (file)
@@ -3,10 +3,9 @@
 import type { ValidateFunction } from 'ajv'
 import { secondsToMilliseconds } from 'date-fns'
 
-import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
 import {
-  type ChargingStation,
   addConfigurationKey,
+  type ChargingStation,
   getConfigurationKey,
   hasReservationExpired,
   resetConnectorStatus
@@ -53,6 +52,7 @@ import {
 } from '../../../types/index.js'
 import { Constants, convertToInt, isAsyncFunction, logger } from '../../../utils/index.js'
 import { OCPPResponseService } from '../OCPPResponseService.js'
+import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
 
 const moduleName = 'OCPP16ResponseService'
 
@@ -468,7 +468,7 @@ export class OCPP16ResponseService extends OCPPResponseService {
         // Throw exception
         throw new OCPPError(
           ErrorType.NOT_IMPLEMENTED,
-          `${commandName} is not implemented to handle response PDU ${JSON.stringify(
+          `'${commandName}' is not implemented to handle response PDU ${JSON.stringify(
             payload,
             undefined,
             2
index a959844ac497f46b1876acd068ac56f61c87045d..d9144064003fb5bf07e0443720e17ac2af084501 100644 (file)
@@ -2,16 +2,15 @@
 
 import type { JSONSchemaType } from 'ajv'
 import {
-  type Interval,
   addSeconds,
   areIntervalsOverlapping,
   differenceInSeconds,
+  type Interval,
   isAfter,
   isBefore,
   isWithinInterval
 } from 'date-fns'
 
-import { OCPP16Constants } from './OCPP16Constants.js'
 import {
   type ChargingStation,
   hasFeatureProfile,
@@ -40,6 +39,7 @@ import {
 } from '../../../types/index.js'
 import { convertToDate, isNotEmptyArray, logger, roundTo } from '../../../utils/index.js'
 import { OCPPServiceUtils } from '../OCPPServiceUtils.js'
+import { OCPP16Constants } from './OCPP16Constants.js'
 
 export class OCPP16ServiceUtils extends OCPPServiceUtils {
   public static checkFeatureProfile (
index 32843895142ea43e136ae226d6e61cbe91eb68bd..b1cd51fc8e935a7cebfe05a935a623ca4e41dc6c 100644 (file)
@@ -2,7 +2,6 @@
 
 import type { ValidateFunction } from 'ajv'
 
-import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
 import type { ChargingStation } from '../../../charging-station/index.js'
 import { OCPPError } from '../../../exception/index.js'
 import {
@@ -15,6 +14,7 @@ import {
 } from '../../../types/index.js'
 import { isAsyncFunction, logger } from '../../../utils/index.js'
 import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js'
+import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
 
 const moduleName = 'OCPP20IncomingRequestService'
 
@@ -109,7 +109,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
         // Throw exception
         throw new OCPPError(
           ErrorType.NOT_IMPLEMENTED,
-          `${commandName} is not implemented to handle request PDU ${JSON.stringify(
+          `'${commandName}' is not implemented to handle request PDU ${JSON.stringify(
             commandPayload,
             undefined,
             2
index 03921891105e745103479904b0b87dbd1bd03c7a..6673b9b43813d8cbf0985bed82001ef085d6b5d1 100644 (file)
@@ -2,8 +2,6 @@
 
 import type { ValidateFunction } from 'ajv'
 
-import { OCPP20Constants } from './OCPP20Constants.js'
-import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
 import type { ChargingStation } from '../../../charging-station/index.js'
 import { OCPPError } from '../../../exception/index.js'
 import {
@@ -20,6 +18,8 @@ import {
 import { generateUUID } from '../../../utils/index.js'
 import { OCPPRequestService } from '../OCPPRequestService.js'
 import type { OCPPResponseService } from '../OCPPResponseService.js'
+import { OCPP20Constants } from './OCPP20Constants.js'
+import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
 
 const moduleName = 'OCPP20RequestService'
 
index 09b319c9a653b9375920703f3a57cb4b073f8a13..6ab91c074d01ec4f24b01a4c5fbdffb374ad1fa9 100644 (file)
@@ -2,8 +2,7 @@
 
 import type { ValidateFunction } from 'ajv'
 
-import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
-import { type ChargingStation, addConfigurationKey } from '../../../charging-station/index.js'
+import { addConfigurationKey, type ChargingStation } from '../../../charging-station/index.js'
 import { OCPPError } from '../../../exception/index.js'
 import {
   ErrorType,
@@ -21,6 +20,7 @@ import {
 } from '../../../types/index.js'
 import { isAsyncFunction, logger } from '../../../utils/index.js'
 import { OCPPResponseService } from '../OCPPResponseService.js'
+import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
 
 const moduleName = 'OCPP20ResponseService'
 
@@ -141,7 +141,7 @@ export class OCPP20ResponseService extends OCPPResponseService {
         // Throw exception
         throw new OCPPError(
           ErrorType.NOT_IMPLEMENTED,
-          `${commandName} is not implemented to handle response PDU ${JSON.stringify(
+          `'${commandName}' is not implemented to handle response PDU ${JSON.stringify(
             payload,
             undefined,
             2
index 49128ccbd1b74a9440d76ac998bd849f539ac723..7c88547a6c63ce285e2009e6561b9d813fc99b53 100644 (file)
@@ -3,8 +3,6 @@ import { EventEmitter } from 'node:events'
 import _Ajv, { type ValidateFunction } from 'ajv'
 import _ajvFormats from 'ajv-formats'
 
-import { OCPPConstants } from './OCPPConstants.js'
-import { OCPPServiceUtils } from './OCPPServiceUtils.js'
 import { type ChargingStation, getIdTagsFile } from '../../charging-station/index.js'
 import { OCPPError } from '../../exception/index.js'
 import type {
@@ -15,6 +13,8 @@ import type {
   OCPPVersion
 } from '../../types/index.js'
 import { logger, setDefaultErrorParams } from '../../utils/index.js'
+import { OCPPConstants } from './OCPPConstants.js'
+import { OCPPServiceUtils } from './OCPPServiceUtils.js'
 type Ajv = _Ajv.default
 // eslint-disable-next-line @typescript-eslint/no-redeclare
 const Ajv = _Ajv.default
index 57f6a06191081d328bc8069be96b1133699a2611..d7757eaa1af230ddb847e20d1298dbf88267c51f 100644 (file)
@@ -1,9 +1,6 @@
 import _Ajv, { type ValidateFunction } from 'ajv'
 import _ajvFormats from 'ajv-formats'
 
-import { OCPPConstants } from './OCPPConstants.js'
-import type { OCPPResponseService } from './OCPPResponseService.js'
-import { OCPPServiceUtils } from './OCPPServiceUtils.js'
 import type { ChargingStation } from '../../charging-station/index.js'
 import { OCPPError } from '../../exception/index.js'
 import { PerformanceStatistics } from '../../performance/index.js'
@@ -29,6 +26,9 @@ import {
   handleSendMessageError,
   logger
 } from '../../utils/index.js'
+import { OCPPConstants } from './OCPPConstants.js'
+import type { OCPPResponseService } from './OCPPResponseService.js'
+import { OCPPServiceUtils } from './OCPPServiceUtils.js'
 type Ajv = _Ajv.default
 // eslint-disable-next-line @typescript-eslint/no-redeclare
 const Ajv = _Ajv.default
@@ -245,7 +245,7 @@ export abstract class OCPPRequestService {
       // eslint-disable-next-line @typescript-eslint/no-this-alias
       const self = this
       // Send a message through wsConnection
-      return await new Promise<ResponseType>((resolve, reject) => {
+      return await new Promise<ResponseType>((resolve, reject: (reason?: unknown) => void) => {
         /**
          * Function that will receive the request's response
          *
@@ -303,7 +303,7 @@ export abstract class OCPPRequestService {
         }
 
         const handleSendError = (ocppError: OCPPError): void => {
-          if (params?.skipBufferingOnError === false) {
+          if (params.skipBufferingOnError === false) {
             // Buffer
             chargingStation.bufferMessage(messageToSend)
             if (messageType === MessageType.CALL_MESSAGE) {
@@ -317,7 +317,7 @@ export abstract class OCPPRequestService {
               )
             }
           } else if (
-            params?.skipBufferingOnError === true &&
+            params.skipBufferingOnError === true &&
             messageType === MessageType.CALL_MESSAGE
           ) {
             // Remove request from the cache
@@ -346,7 +346,7 @@ export abstract class OCPPRequestService {
                 `Timeout ${formatDurationMilliSeconds(
                   OCPPConstants.OCPP_WEBSOCKET_TIMEOUT
                 )} reached for ${
-                  params?.skipBufferingOnError === false ? '' : 'non '
+                  params.skipBufferingOnError === false ? '' : 'non '
                 }buffered message id '${messageId}' with content '${messageToSend}'`,
                 commandName,
                 (messagePayload as OCPPError).details
@@ -380,7 +380,7 @@ export abstract class OCPPRequestService {
                 new OCPPError(
                   ErrorType.GENERIC_ERROR,
                   `WebSocket errored for ${
-                    params?.skipBufferingOnError === false ? '' : 'non '
+                    params.skipBufferingOnError === false ? '' : 'non '
                   }buffered message id '${messageId}' with content '${messageToSend}'`,
                   commandName,
                   { name: error.name, message: error.message, stack: error.stack }
@@ -393,7 +393,7 @@ export abstract class OCPPRequestService {
             new OCPPError(
               ErrorType.GENERIC_ERROR,
               `WebSocket closed for ${
-                params?.skipBufferingOnError === false ? '' : 'non '
+                params.skipBufferingOnError === false ? '' : 'non '
               }buffered message id '${messageId}' with content '${messageToSend}'`,
               commandName,
               (messagePayload as OCPPError).details
index a526c28c6812fea7b27108d1b46daa9c3fe731ec..0e9c6164d299a927dd8adf3f58a15d7247e6ced6 100644 (file)
@@ -1,7 +1,6 @@
 import _Ajv, { type ValidateFunction } from 'ajv'
 import _ajvFormats from 'ajv-formats'
 
-import { OCPPServiceUtils } from './OCPPServiceUtils.js'
 import type { ChargingStation } from '../../charging-station/index.js'
 import { OCPPError } from '../../exception/index.js'
 import type {
@@ -11,6 +10,7 @@ import type {
   RequestCommand
 } from '../../types/index.js'
 import { Constants, logger } from '../../utils/index.js'
+import { OCPPServiceUtils } from './OCPPServiceUtils.js'
 type Ajv = _Ajv.default
 // eslint-disable-next-line @typescript-eslint/no-redeclare
 const Ajv = _Ajv.default
index 35b15d9401051f19848660cdb78c4654a3eceb90..8a42bf67a7b05f361374a1d04bf996ab5d6b304f 100644 (file)
@@ -1,3 +1,4 @@
+import { randomInt } from 'node:crypto'
 import { readFileSync } from 'node:fs'
 import { dirname, join } from 'node:path'
 import { fileURLToPath } from 'node:url'
@@ -5,9 +6,6 @@ import { fileURLToPath } from 'node:url'
 import type { DefinedError, ErrorObject, JSONSchemaType } from 'ajv'
 import { isDate } from 'date-fns'
 
-import { OCPP16Constants } from './1.6/OCPP16Constants.js'
-import { OCPP20Constants } from './2.0/OCPP20Constants.js'
-import { OCPPConstants } from './OCPPConstants.js'
 import {
   type ChargingStation,
   getConfigurationKey,
@@ -51,21 +49,23 @@ import {
 import {
   ACElectricUtils,
   Constants,
-  DCElectricUtils,
   convertToFloat,
   convertToInt,
+  DCElectricUtils,
   getRandomFloatFluctuatedRounded,
   getRandomFloatRounded,
-  getRandomInteger,
   handleFileException,
   isNotEmptyArray,
   isNotEmptyString,
-  logPrefix,
   logger,
+  logPrefix,
   max,
   min,
   roundTo
 } from '../../utils/index.js'
+import { OCPP16Constants } from './1.6/OCPP16Constants.js'
+import { OCPP20Constants } from './2.0/OCPP20Constants.js'
+import { OCPPConstants } from './OCPPConstants.js'
 
 export const getMessageTypeString = (messageType: MessageType | undefined): string => {
   switch (messageType) {
@@ -284,7 +284,7 @@ export const buildMeterValue = (
             parseInt(socSampledValueTemplate.value),
             socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
           )
-          : getRandomInteger(socMaximumValue, socMinimumValue)
+          : randomInt(socMinimumValue, socMaximumValue)
         meterValue.sampledValue.push(
           buildSampledValue(socSampledValueTemplate, socSampledValueTemplateValue)
         )
index 566d496167d288d0861ad694cd81ee19c61b90f6..9d2705f28f677f37ffcc869c60587ade090745f5 100644 (file)
@@ -1,16 +1,14 @@
 import { type IncomingMessage, Server, type ServerResponse } from 'node:http'
-import { type Http2Server, createServer } from 'node:http2'
+import { createServer, type Http2Server } from 'node:http2'
 
 import type { WebSocket } from 'ws'
 
-import type { AbstractUIService } from './ui-services/AbstractUIService.js'
-import { UIServiceFactory } from './ui-services/UIServiceFactory.js'
-import { getUsernameAndPasswordFromAuthorizationToken } from './UIServerUtils.js'
 import { BaseError } from '../../exception/index.js'
 import {
   ApplicationProtocolVersion,
   AuthenticationType,
   type ChargingStationData,
+  ConfigurationSection,
   type ProcedureName,
   type ProtocolRequest,
   type ProtocolResponse,
@@ -20,6 +18,9 @@ import {
   type UIServerConfiguration
 } from '../../types/index.js'
 import { logger } from '../../utils/index.js'
+import type { AbstractUIService } from './ui-services/AbstractUIService.js'
+import { UIServiceFactory } from './ui-services/UIServiceFactory.js'
+import { getUsernameAndPasswordFromAuthorizationToken } from './UIServerUtils.js'
 
 const moduleName = 'AbstractUIServer'
 
@@ -27,7 +28,11 @@ export abstract class AbstractUIServer {
   public readonly chargingStations: Map<string, ChargingStationData>
   public readonly chargingStationTemplates: Set<string>
   protected readonly httpServer: Server | Http2Server
-  protected readonly responseHandlers: Map<string, ServerResponse | WebSocket>
+  protected readonly responseHandlers: Map<
+    `${string}-${string}-${string}-${string}-${string}`,
+  ServerResponse | WebSocket
+  >
+
   protected readonly uiServices: Map<ProtocolVersion, AbstractUIService>
 
   public constructor (protected readonly uiServerConfiguration: UIServerConfiguration) {
@@ -42,23 +47,29 @@ export abstract class AbstractUIServer {
         break
       default:
         throw new BaseError(
-          `Unsupported application protocol version ${this.uiServerConfiguration.version}`
+          `Unsupported application protocol version ${this.uiServerConfiguration.version} in '${ConfigurationSection.uiServer}' configuration section`
         )
     }
-    this.responseHandlers = new Map<string, ServerResponse | WebSocket>()
+    this.responseHandlers = new Map<
+      `${string}-${string}-${string}-${string}-${string}`,
+    ServerResponse | WebSocket
+    >()
     this.uiServices = new Map<ProtocolVersion, AbstractUIService>()
   }
 
   public buildProtocolRequest (
-    id: string,
+    uuid: `${string}-${string}-${string}-${string}-${string}`,
     procedureName: ProcedureName,
     requestPayload: RequestPayload
   ): ProtocolRequest {
-    return [id, procedureName, requestPayload]
+    return [uuid, procedureName, requestPayload]
   }
 
-  public buildProtocolResponse (id: string, responsePayload: ResponsePayload): ProtocolResponse {
-    return [id, responsePayload]
+  public buildProtocolResponse (
+    uuid: `${string}-${string}-${string}-${string}-${string}`,
+    responsePayload: ResponsePayload
+  ): ProtocolResponse {
+    return [uuid, responsePayload]
   }
 
   public stop (): void {
@@ -82,8 +93,8 @@ export abstract class AbstractUIServer {
       ?.requestHandler(request) as Promise<ProtocolResponse>)
   }
 
-  public hasResponseHandler (id: string): boolean {
-    return this.responseHandlers.has(id)
+  public hasResponseHandler (uuid: `${string}-${string}-${string}-${string}-${string}`): boolean {
+    return this.responseHandlers.has(uuid)
   }
 
   protected startHttpServer (): void {
index fb967ef4ecb0403d54b074738e11dcfb8446af05..5bcae7beff6ed84fc6d0e202437361bc7f1c3725 100644 (file)
@@ -2,11 +2,10 @@ import type { IncomingMessage, ServerResponse } from 'node:http'
 
 import { StatusCodes } from 'http-status-codes'
 
-import { AbstractUIServer } from './AbstractUIServer.js'
-import { isProtocolAndVersionSupported } from './UIServerUtils.js'
 import { BaseError } from '../../exception/index.js'
 import {
   ApplicationProtocolVersion,
+  MapStringifyFormat,
   type ProcedureName,
   type Protocol,
   type ProtocolRequest,
@@ -18,12 +17,14 @@ import {
 } from '../../types/index.js'
 import {
   Constants,
-  JSONStringifyWithMapSupport,
   generateUUID,
   isNotEmptyString,
-  logPrefix,
-  logger
+  JSONStringify,
+  logger,
+  logPrefix
 } from '../../utils/index.js'
+import { AbstractUIServer } from './AbstractUIServer.js'
+import { isProtocolAndVersionSupported } from './UIServerUtils.js'
 
 const moduleName = 'UIHttpServer'
 
@@ -61,7 +62,7 @@ export class UIHttpServer extends AbstractUIServer {
           .writeHead(this.responseStatusToStatusCode(payload.status), {
             'Content-Type': 'application/json'
           })
-          .end(JSONStringifyWithMapSupport(payload))
+          .end(JSONStringify(payload, undefined, MapStringifyFormat.object))
       } else {
         logger.error(
           `${this.logPrefix(moduleName, 'sendResponse')} Response for unknown request id: ${uuid}`
index d7de7ddd93b152a6bd26b75007dd72b1d31f05e9..34138e02b0204636b79d5ab884fe9f6328514d8f 100644 (file)
@@ -1,16 +1,18 @@
 import chalk from 'chalk'
 
-import type { AbstractUIServer } from './AbstractUIServer.js'
-import { UIHttpServer } from './UIHttpServer.js'
-import { isLoopback } from './UIServerUtils.js'
-import { UIWebSocketServer } from './UIWebSocketServer.js'
 import { BaseError } from '../../exception/index.js'
 import {
   ApplicationProtocol,
   ApplicationProtocolVersion,
   AuthenticationType,
+  ConfigurationSection,
   type UIServerConfiguration
 } from '../../types/index.js'
+import { logger, logPrefix } from '../../utils/index.js'
+import type { AbstractUIServer } from './AbstractUIServer.js'
+import { UIHttpServer } from './UIHttpServer.js'
+import { isLoopback } from './UIServerUtils.js'
+import { UIWebSocketServer } from './UIWebSocketServer.js'
 
 // eslint-disable-next-line @typescript-eslint/no-extraneous-class
 export class UIServerFactory {
@@ -26,7 +28,7 @@ export class UIServerFactory {
       !Object.values(AuthenticationType).includes(uiServerConfiguration.authentication.type)
     ) {
       throw new BaseError(
-        `Unknown authentication type '${uiServerConfiguration.authentication.type}' for UI server`
+        `Unknown authentication type '${uiServerConfiguration.authentication.type}' in '${ConfigurationSection.uiServer}' configuration section`
       )
     }
     if (
@@ -34,28 +36,26 @@ export class UIServerFactory {
       uiServerConfiguration.authentication?.enabled === true &&
       uiServerConfiguration.authentication.type === AuthenticationType.PROTOCOL_BASIC_AUTH
     ) {
-      throw new BaseError('Protocol basic authentication is not supported for HTTP UI server')
+      throw new BaseError(
+        `'${uiServerConfiguration.authentication.type}' authentication type with application protocol type '${uiServerConfiguration.type}' is not supported in '${ConfigurationSection.uiServer}' configuration section`
+      )
     }
     if (
       uiServerConfiguration.authentication?.enabled !== true &&
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
       !isLoopback(uiServerConfiguration.options!.host!)
     ) {
-      console.warn(
-        chalk.yellow(
-          'Non loopback address in UI server configuration without authentication enabled. This is not recommended'
-        )
-      )
+      const logMsg = `Non loopback address in '${ConfigurationSection.uiServer}' configuration section without authentication enabled. This is not recommended`
+      logger.warn(`${UIServerFactory.logPrefix()} ${logMsg}`)
+      console.warn(chalk.yellow(logMsg))
     }
     if (
       uiServerConfiguration.type === ApplicationProtocol.WS &&
       uiServerConfiguration.version !== ApplicationProtocolVersion.VERSION_11
     ) {
-      console.warn(
-        chalk.yellow(
-          `Only version ${ApplicationProtocolVersion.VERSION_11} is supported for WebSocket UI server. Falling back to version ${ApplicationProtocolVersion.VERSION_11}`
-        )
-      )
+      const logMsg = `Only version ${ApplicationProtocolVersion.VERSION_11} with application protocol type '${uiServerConfiguration.type}' is supported in '${ConfigurationSection.uiServer}' configuration section. Falling back to version ${ApplicationProtocolVersion.VERSION_11}`
+      logger.warn(`${UIServerFactory.logPrefix()} ${logMsg}`)
+      console.warn(chalk.yellow(logMsg))
       uiServerConfiguration.version = ApplicationProtocolVersion.VERSION_11
     }
     switch (uiServerConfiguration.type) {
@@ -63,7 +63,28 @@ export class UIServerFactory {
         return new UIHttpServer(uiServerConfiguration)
       case ApplicationProtocol.WS:
       default:
+        if (
+          !Object.values(ApplicationProtocol).includes(
+            uiServerConfiguration.type as ApplicationProtocol
+          )
+        ) {
+          // eslint-disable-next-line @typescript-eslint/no-base-to-string
+          const logMsg = `Unknown application protocol type '${uiServerConfiguration.type}' in '${ConfigurationSection.uiServer}' configuration section from values '${ApplicationProtocol.toString()}', defaulting to '${
+            ApplicationProtocol.WS
+          }'`
+          logger.warn(`${UIServerFactory.logPrefix()} ${logMsg}`)
+          console.warn(logMsg)
+        }
         return new UIWebSocketServer(uiServerConfiguration)
     }
   }
+
+  private static readonly logPrefix = (modName?: string, methodName?: string): string => {
+    const logMsgPrefix = 'UI Server'
+    const logMsg =
+      modName != null && methodName != null
+        ? ` ${logMsgPrefix} | ${modName}.${methodName}:`
+        : ` ${logMsgPrefix} |`
+    return logPrefix(logMsg)
+  }
 }
index 2aa2e92b0727a11c4796745142ed06e6c00e7371..cfe4fd2f6198ee6ae544d0b46b786e9f496f69af 100644 (file)
@@ -2,7 +2,7 @@ import type { IncomingMessage } from 'node:http'
 
 import { BaseError } from '../../exception/index.js'
 import { Protocol, ProtocolVersion } from '../../types/index.js'
-import { logPrefix, logger } from '../../utils/index.js'
+import { logger, logPrefix } from '../../utils/index.js'
 
 export const getUsernameAndPasswordFromAuthorizationToken = (
   authorizationToken: string,
index 1e1a372c698a941365df864aac280aa3bb32778c..bf0a6c444749201fe06d2cdcc6f24b10fa09582a 100644 (file)
@@ -4,13 +4,8 @@ import type { Duplex } from 'node:stream'
 import { StatusCodes } from 'http-status-codes'
 import { type RawData, WebSocket, WebSocketServer } from 'ws'
 
-import { AbstractUIServer } from './AbstractUIServer.js'
-import {
-  getProtocolAndVersion,
-  handleProtocols,
-  isProtocolAndVersionSupported
-} from './UIServerUtils.js'
 import {
+  MapStringifyFormat,
   type ProtocolRequest,
   type ProtocolResponse,
   type UIServerConfiguration,
@@ -18,13 +13,19 @@ import {
 } from '../../types/index.js'
 import {
   Constants,
-  JSONStringifyWithMapSupport,
   getWebSocketCloseEventStatusString,
   isNotEmptyString,
-  logPrefix,
+  JSONStringify,
   logger,
+  logPrefix,
   validateUUID
 } from '../../utils/index.js'
+import { AbstractUIServer } from './AbstractUIServer.js'
+import {
+  getProtocolAndVersion,
+  handleProtocols,
+  isProtocolAndVersionSupported
+} from './UIServerUtils.js'
 
 const moduleName = 'UIWebSocketServer'
 
@@ -136,7 +137,7 @@ export class UIWebSocketServer extends AbstractUIServer {
       if (this.hasResponseHandler(responseId)) {
         const ws = this.responseHandlers.get(responseId) as WebSocket
         if (ws.readyState === WebSocket.OPEN) {
-          ws.send(JSONStringifyWithMapSupport(response))
+          ws.send(JSONStringify(response, undefined, MapStringifyFormat.object))
         } else {
           logger.error(
             `${this.logPrefix(
index fbca016bed65fd3cf0d4a4ac3563fafa7105e514..ffdc256583adccd581d72c99496e3bfe726333e2 100644 (file)
@@ -2,8 +2,10 @@ import { BaseError, type OCPPError } from '../../../exception/index.js'
 import {
   BroadcastChannelProcedureName,
   type BroadcastChannelRequestPayload,
+  type ChargingStationInfo,
   type ChargingStationOptions,
   ConfigurationSection,
+  type JsonObject,
   type JsonType,
   ProcedureName,
   type ProtocolRequest,
@@ -22,6 +24,12 @@ import type { AbstractUIServer } from '../AbstractUIServer.js'
 
 const moduleName = 'AbstractUIService'
 
+interface AddChargingStationsRequestPayload extends RequestPayload {
+  template: string
+  numberOfStations: number
+  options?: ChargingStationOptions
+}
+
 export abstract class AbstractUIService {
   protected static readonly ProcedureNameToBroadCastChannelProcedureNameMapping = new Map<
   ProcedureName,
@@ -66,7 +74,10 @@ export abstract class AbstractUIService {
   private readonly version: ProtocolVersion
   private readonly uiServer: AbstractUIServer
   private readonly uiServiceWorkerBroadcastChannel: UIServiceWorkerBroadcastChannel
-  private readonly broadcastChannelRequests: Map<string, number>
+  private readonly broadcastChannelRequests: Map<
+    `${string}-${string}-${string}-${string}-${string}`,
+  number
+  >
 
   constructor (uiServer: AbstractUIServer, version: ProtocolVersion) {
     this.uiServer = uiServer
@@ -76,11 +87,15 @@ export abstract class AbstractUIService {
       [ProcedureName.LIST_CHARGING_STATIONS, this.handleListChargingStations.bind(this)],
       [ProcedureName.ADD_CHARGING_STATIONS, this.handleAddChargingStations.bind(this)],
       [ProcedureName.PERFORMANCE_STATISTICS, this.handlePerformanceStatistics.bind(this)],
+      [ProcedureName.SIMULATOR_STATE, this.handleSimulatorState.bind(this)],
       [ProcedureName.START_SIMULATOR, this.handleStartSimulator.bind(this)],
       [ProcedureName.STOP_SIMULATOR, this.handleStopSimulator.bind(this)]
     ])
     this.uiServiceWorkerBroadcastChannel = new UIServiceWorkerBroadcastChannel(this)
-    this.broadcastChannelRequests = new Map<string, number>()
+    this.broadcastChannelRequests = new Map<
+      `${string}-${string}-${string}-${string}-${string}`,
+    number
+    >()
   }
 
   public stop (): void {
@@ -89,16 +104,16 @@ export abstract class AbstractUIService {
   }
 
   public async requestHandler (request: ProtocolRequest): Promise<ProtocolResponse | undefined> {
-    let messageId: string | undefined
+    let uuid: `${string}-${string}-${string}-${string}-${string}` | undefined
     let command: ProcedureName | undefined
     let requestPayload: RequestPayload | undefined
     let responsePayload: ResponsePayload | undefined
     try {
-      [messageId, command, requestPayload] = request
+      [uuid, command, requestPayload] = request
 
       if (!this.requestHandlers.has(command)) {
         throw new BaseError(
-          `${command} is not implemented to handle message payload ${JSON.stringify(
+          `'${command}' is not implemented to handle message payload ${JSON.stringify(
             requestPayload,
             undefined,
             2
@@ -110,7 +125,7 @@ export abstract class AbstractUIService {
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
       const requestHandler = this.requestHandlers.get(command)!
       if (isAsyncFunction(requestHandler)) {
-        responsePayload = await requestHandler(messageId, command, requestPayload)
+        responsePayload = await requestHandler(uuid, command, requestPayload)
       } else {
         responsePayload = (
           requestHandler as (
@@ -118,7 +133,7 @@ export abstract class AbstractUIService {
             procedureName?: ProcedureName,
             payload?: RequestPayload
           ) => undefined | ResponsePayload
-        )(messageId, command, requestPayload)
+        )(uuid, command, requestPayload)
       }
     } catch (error) {
       // Log
@@ -136,23 +151,26 @@ export abstract class AbstractUIService {
     }
     if (responsePayload != null) {
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      return this.uiServer.buildProtocolResponse(messageId!, responsePayload)
+      return this.uiServer.buildProtocolResponse(uuid!, responsePayload)
     }
   }
 
   // public sendRequest (
-  //   messageId: string,
+  //   uuid: `${string}-${string}-${string}-${string}-${string}`,
   //   procedureName: ProcedureName,
   //   requestPayload: RequestPayload
   // ): void {
   //   this.uiServer.sendRequest(
-  //     this.uiServer.buildProtocolRequest(messageId, procedureName, requestPayload)
+  //     this.uiServer.buildProtocolRequest(uuid, procedureName, requestPayload)
   //   )
   // }
 
-  public sendResponse (messageId: string, responsePayload: ResponsePayload): void {
-    if (this.uiServer.hasResponseHandler(messageId)) {
-      this.uiServer.sendResponse(this.uiServer.buildProtocolResponse(messageId, responsePayload))
+  public sendResponse (
+    uuid: `${string}-${string}-${string}-${string}-${string}`,
+    responsePayload: ResponsePayload
+  ): void {
+    if (this.uiServer.hasResponseHandler(uuid)) {
+      this.uiServer.sendResponse(this.uiServer.buildProtocolResponse(uuid, responsePayload))
     }
   }
 
@@ -160,17 +178,21 @@ export abstract class AbstractUIService {
     return this.uiServer.logPrefix(modName, methodName, this.version)
   }
 
-  public deleteBroadcastChannelRequest (uuid: string): void {
+  public deleteBroadcastChannelRequest (
+    uuid: `${string}-${string}-${string}-${string}-${string}`
+  ): void {
     this.broadcastChannelRequests.delete(uuid)
   }
 
-  public getBroadcastChannelExpectedResponses (uuid: string): number {
+  public getBroadcastChannelExpectedResponses (
+    uuid: `${string}-${string}-${string}-${string}-${string}`
+  ): number {
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     return this.broadcastChannelRequests.get(uuid)!
   }
 
   protected handleProtocolRequest (
-    uuid: string,
+    uuid: `${string}-${string}-${string}-${string}-${string}`,
     procedureName: ProcedureName,
     payload: RequestPayload
   ): void {
@@ -183,7 +205,7 @@ export abstract class AbstractUIService {
   }
 
   private sendBroadcastChannelRequest (
-    uuid: string,
+    uuid: `${string}-${string}-${string}-${string}-${string}`,
     procedureName: BroadcastChannelProcedureName,
     payload: BroadcastChannelRequestPayload
   ): void {
@@ -232,14 +254,24 @@ export abstract class AbstractUIService {
   }
 
   private async handleAddChargingStations (
-    _messageId?: string,
+    _uuid?: `${string}-${string}-${string}-${string}-${string}`,
     _procedureName?: ProcedureName,
     requestPayload?: RequestPayload
   ): Promise<ResponsePayload> {
-    const { template, numberOfStations, options } = requestPayload as {
-      template: string
-      numberOfStations: number
-      options?: ChargingStationOptions
+    const { template, numberOfStations, options } =
+      requestPayload as AddChargingStationsRequestPayload
+    if (!Bootstrap.getInstance().getState().started) {
+      return {
+        status: ResponseStatus.FAILURE,
+        errorMessage:
+          'Cannot add charging station(s) while the charging stations simulator is not started'
+      } satisfies ResponsePayload
+    }
+    if (typeof template !== 'string' || typeof numberOfStations !== 'number') {
+      return {
+        status: ResponseStatus.FAILURE,
+        errorMessage: 'Invalid request payload'
+      } satisfies ResponsePayload
     }
     if (!this.uiServer.chargingStationTemplates.has(template)) {
       return {
@@ -247,24 +279,37 @@ export abstract class AbstractUIService {
         errorMessage: `Template '${template}' not found`
       } satisfies ResponsePayload
     }
+    const succeededStationInfos: ChargingStationInfo[] = []
+    const failedStationInfos: ChargingStationInfo[] = []
+    let err: Error | undefined
     for (let i = 0; i < numberOfStations; i++) {
+      let stationInfo: ChargingStationInfo | undefined
       try {
-        await Bootstrap.getInstance().addChargingStation(
+        stationInfo = await Bootstrap.getInstance().addChargingStation(
           Bootstrap.getInstance().getLastIndex(template) + 1,
           `${template}.json`,
           options
         )
+        if (stationInfo != null) {
+          succeededStationInfos.push(stationInfo)
+        }
       } catch (error) {
-        return {
-          status: ResponseStatus.FAILURE,
-          errorMessage: (error as Error).message,
-          errorStack: (error as Error).stack
-        } satisfies ResponsePayload
+        err = error as Error
+        if (stationInfo != null) {
+          failedStationInfos.push(stationInfo)
+        }
       }
     }
     return {
-      status: ResponseStatus.SUCCESS
-    }
+      status: err != null ? ResponseStatus.FAILURE : ResponseStatus.SUCCESS,
+      ...(succeededStationInfos.length > 0 && {
+        hashIdsSucceeded: succeededStationInfos.map(stationInfo => stationInfo.hashId)
+      }),
+      ...(failedStationInfos.length > 0 && {
+        hashIdsFailed: failedStationInfos.map(stationInfo => stationInfo.hashId)
+      }),
+      ...(err != null && { errorMessage: err.message, errorStack: err.stack })
+    } satisfies ResponsePayload
   }
 
   private handlePerformanceStatistics (): ResponsePayload {
@@ -285,7 +330,22 @@ export abstract class AbstractUIService {
           // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
           ...Bootstrap.getInstance().getPerformanceStatistics()!
         ] as JsonType[]
-      }
+      } satisfies ResponsePayload
+    } catch (error) {
+      return {
+        status: ResponseStatus.FAILURE,
+        errorMessage: (error as Error).message,
+        errorStack: (error as Error).stack
+      } satisfies ResponsePayload
+    }
+  }
+
+  private handleSimulatorState (): ResponsePayload {
+    try {
+      return {
+        status: ResponseStatus.SUCCESS,
+        state: Bootstrap.getInstance().getState() as unknown as JsonObject
+      } satisfies ResponsePayload
     } catch (error) {
       return {
         status: ResponseStatus.FAILURE,
index 674f89857404d1c34eb29de90e449d8f983382ea..511bfa7a3de64383c7302584a5c4c6f42d155157 100644 (file)
@@ -1,6 +1,6 @@
-import { AbstractUIService } from './AbstractUIService.js'
 import { type ProtocolRequestHandler, ProtocolVersion } from '../../../types/index.js'
 import type { AbstractUIServer } from '../AbstractUIServer.js'
+import { AbstractUIService } from './AbstractUIService.js'
 
 export class UIService001 extends AbstractUIService {
   constructor (uiServer: AbstractUIServer) {
index 81fc2201f294f89f14c7de9479921feb0141525e..08d81a360bdc669dac2d6fc2fd009d09a8e306bd 100644 (file)
@@ -1,7 +1,7 @@
-import type { AbstractUIService } from './AbstractUIService.js'
-import { UIService001 } from './UIService001.js'
 import { ProtocolVersion } from '../../../types/index.js'
 import type { AbstractUIServer } from '../AbstractUIServer.js'
+import type { AbstractUIService } from './AbstractUIService.js'
+import { UIService001 } from './UIService001.js'
 
 // eslint-disable-next-line @typescript-eslint/no-extraneous-class
 export class UIServiceFactory {
index 27876dadfef495d674f6cae7422fa793129a09b5..1e716ae4d1d8f7c530e489a7fb37151f5fba8644 100644 (file)
@@ -1,8 +1,8 @@
 // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
 
-import { BaseError } from './BaseError.js'
 import type { ErrorType, IncomingRequestCommand, JsonType, RequestCommand } from '../types/index.js'
 import { Constants } from '../utils/index.js'
+import { BaseError } from './BaseError.js'
 
 export class OCPPError extends BaseError {
   code: ErrorType
index e90595225278d3a926059a2ca3ab7821b800d644..265cb6c963918b1a3970a95e5822742f3202c0d4 100644 (file)
@@ -1,16 +1,18 @@
 // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
 
-import { type PerformanceEntry, PerformanceObserver, performance } from 'node:perf_hooks'
+import { performance, type PerformanceEntry, PerformanceObserver } from 'node:perf_hooks'
 import type { URL } from 'node:url'
 import { parentPort } from 'node:worker_threads'
 
 import { secondsToMilliseconds } from 'date-fns'
+import { is, mean, median } from 'rambda'
 
 import { BaseError } from '../exception/index.js'
 import {
   ConfigurationSection,
   type IncomingRequestCommand,
   type LogConfiguration,
+  MapStringifyFormat,
   MessageType,
   type RequestCommand,
   type Statistics,
@@ -19,19 +21,17 @@ import {
   type TimestampedData
 } from '../types/index.js'
 import {
+  buildPerformanceStatisticsMessage,
   CircularArray,
   Configuration,
   Constants,
-  JSONStringifyWithMapSupport,
-  average,
-  buildPerformanceStatisticsMessage,
   extractTimeSeriesValues,
   formatDurationSeconds,
   generateUUID,
-  logPrefix,
+  JSONStringify,
   logger,
+  logPrefix,
   max,
-  median,
   min,
   nthPercentile,
   stdDeviation
@@ -107,7 +107,7 @@ export class PerformanceStatistics {
     try {
       performance.measure(name, markId)
     } catch (error) {
-      if (error instanceof Error && error.message.includes('performance mark has not been set')) {
+      if (is(Error, error) && error.message.includes('performance mark has not been set')) {
         /* Ignore */
       } else {
         throw error
@@ -214,7 +214,7 @@ export class PerformanceStatistics {
     logger.info(this.logPrefix(), {
       ...this.statistics,
       statisticsData: JSON.parse(
-        JSONStringifyWithMapSupport(this.statistics.statisticsData)
+        JSONStringify(this.statistics.statisticsData, undefined, MapStringifyFormat.object)
       ) as Map<string | RequestCommand | IncomingRequestCommand, StatisticsData>
     })
   }
@@ -292,8 +292,7 @@ export class PerformanceStatistics {
       this.statistics.statisticsData.get(entry.name)!.measurementTimeSeries!
     )
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    this.statistics.statisticsData.get(entry.name)!.avgTimeMeasurement =
-      average(timeMeasurementValues)
+    this.statistics.statisticsData.get(entry.name)!.avgTimeMeasurement = mean(timeMeasurementValues)
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     this.statistics.statisticsData.get(entry.name)!.medTimeMeasurement =
       median(timeMeasurementValues)
index 1cbed3782f18202307b662ee92398d1d6a2f511e..851999c96aff134f8e6a2474127bbc70840ac580 100644 (file)
@@ -1,3 +1,3 @@
 export { PerformanceStatistics } from './PerformanceStatistics.js'
-export { type Storage } from './storage/Storage.js'
+export type { Storage } from './storage/Storage.js'
 export { StorageFactory } from './storage/StorageFactory.js'
index a69edd69ace56acf62fbd3992cb401e901fc5c3b..97ab104ccfb73a11fb52aafd8ca408eeaf1ff84f 100644 (file)
@@ -3,15 +3,10 @@
 import { closeSync, existsSync, mkdirSync, openSync, writeSync } from 'node:fs'
 import { dirname } from 'node:path'
 
-import { Storage } from './Storage.js'
 import { BaseError } from '../../exception/index.js'
-import { FileType, type Statistics } from '../../types/index.js'
-import {
-  AsyncLock,
-  AsyncLockType,
-  JSONStringifyWithMapSupport,
-  handleFileException
-} from '../../utils/index.js'
+import { FileType, MapStringifyFormat, type Statistics } from '../../types/index.js'
+import { AsyncLock, AsyncLockType, handleFileException, JSONStringify } from '../../utils/index.js'
+import { Storage } from './Storage.js'
 
 export class JsonFileStorage extends Storage {
   private fd?: number
@@ -28,11 +23,11 @@ export class JsonFileStorage extends Storage {
       writeSync(
         // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
         this.fd!,
-        JSONStringifyWithMapSupport([...this.getPerformanceStatistics()], 2),
+        JSONStringify([...this.getPerformanceStatistics()], 2, MapStringifyFormat.object),
         0,
         'utf8'
       )
-    }).catch(error => {
+    }).catch((error: unknown) => {
       handleFileException(
         this.dbName,
         FileType.PerformanceRecords,
index e91693060aea213908d1df126e99aed81b930421..7b4545bdff36ba824597c22549889641c6f60538 100644 (file)
@@ -3,9 +3,9 @@
 import { MikroORM as MariaDbORM, type Options as MariaDbOptions } from '@mikro-orm/mariadb'
 import { MikroORM as SqliteORM, type Options as SqliteOptions } from '@mikro-orm/sqlite'
 
-import { Storage } from './Storage.js'
 import { type PerformanceRecord, type Statistics, StorageType } from '../../types/index.js'
 import { Constants } from '../../utils/index.js'
+import { Storage } from './Storage.js'
 
 export class MikroOrmStorage extends Storage {
   private readonly storageType: StorageType
index b3d535e2906d33d6b13b1184e8b8458256f294a3..c3c98022d4204c63aa6d940f7cac887d2424ed58 100644 (file)
@@ -2,10 +2,10 @@
 
 import { MongoClient } from 'mongodb'
 
-import { Storage } from './Storage.js'
 import { BaseError } from '../../exception/index.js'
 import { type Statistics, StorageType } from '../../types/index.js'
 import { Constants } from '../../utils/index.js'
+import { Storage } from './Storage.js'
 
 export class MongoDBStorage extends Storage {
   private readonly client?: MongoClient
index 6506d36b6ad3fdf9efb524899551de725ae98d56..aae7547f5b32afb656c023106c01babd846205b4 100644 (file)
@@ -1,7 +1,7 @@
 // Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
 
-import { Storage } from './Storage.js'
 import type { Statistics } from '../../types/index.js'
+import { Storage } from './Storage.js'
 
 export class None extends Storage {
   constructor () {
@@ -13,7 +13,7 @@ export class None extends Storage {
   }
 
   public open (): void {
-    /** Intentionally empty   */
+    /** Intentionally empty */
   }
 
   public close (): void {
index 9da6b54e98fa1d181b9a8c982e0b9d365535fa9f..2b6b84ffe5b8ad75da53fc7c5fa9687b0e0089cd 100644 (file)
@@ -1,12 +1,12 @@
 // Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
 
+import { BaseError } from '../../exception/index.js'
+import { StorageType } from '../../types/index.js'
 import { JsonFileStorage } from './JsonFileStorage.js'
 import { MikroOrmStorage } from './MikroOrmStorage.js'
 import { MongoDBStorage } from './MongoDBStorage.js'
 import { None } from './None.js'
 import type { Storage } from './Storage.js'
-import { BaseError } from '../../exception/index.js'
-import { StorageType } from '../../types/index.js'
 
 // eslint-disable-next-line @typescript-eslint/no-extraneous-class
 export class StorageFactory {
index 1592c5507fb4d8b7629dd51d1e22cba31971fd88..2ff04c09096a42fcc8e33b2a8bb937fa84fd4cc8 100644 (file)
@@ -1,10 +1,12 @@
+import type { JsonObject } from './JsonType.js'
+
 export enum IdTagDistribution {
   RANDOM = 'random',
   ROUND_ROBIN = 'round-robin',
   CONNECTOR_AFFINITY = 'connector-affinity'
 }
 
-export interface AutomaticTransactionGeneratorConfiguration {
+export interface AutomaticTransactionGeneratorConfiguration extends JsonObject {
   enable: boolean
   minDuration: number
   maxDuration: number
index 4ebdc150720488c5d37837c4161055d5f52979d6..12327cd891b918cee810e3843f180503f0f41547 100644 (file)
@@ -17,7 +17,7 @@ ChargingStationTemplate,
   hashId: string
   templateIndex: number
   templateName: string
-  /** @deprecated Use hashId instead */
+  /** @deprecated Use `hashId` instead. */
   infoHash?: string
   chargingStationId?: string
   chargeBoxSerialNumber?: string
index 2ea2c53e5e12677132557991a0cd4d0f890c91e1..fdf03a40574765aa9fd7247f13edb18305c23554 100644 (file)
@@ -1,10 +1,11 @@
+import type { JsonObject } from './JsonType.js'
 import type { OCPPConfigurationKey } from './ocpp/Configuration.js'
 
-export type ConfigurationKey = OCPPConfigurationKey & {
+export interface ConfigurationKey extends OCPPConfigurationKey {
   visible?: boolean
   reboot?: boolean
 }
 
-export interface ChargingStationOcppConfiguration {
+export interface ChargingStationOcppConfiguration extends JsonObject {
   configurationKey?: ConfigurationKey[]
 }
index d17fd58f3a3f2ad998dbb5583b808d0289458061..b3cc773489d0f384feb8bf93cb5934039628eeae 100644 (file)
@@ -6,6 +6,7 @@ import type { AutomaticTransactionGeneratorConfiguration } from './AutomaticTran
 import type { ChargingStationOcppConfiguration } from './ChargingStationOcppConfiguration.js'
 import type { ConnectorStatus } from './ConnectorStatus.js'
 import type { EvseTemplate } from './Evse.js'
+import type { JsonObject } from './JsonType.js'
 import type { OCPPProtocol } from './ocpp/OCPPProtocol.js'
 import type { OCPPVersion } from './ocpp/OCPPVersion.js'
 import type {
@@ -41,7 +42,7 @@ export enum Voltage {
 
 export type WsOptions = ClientOptions & ClientRequestArgs
 
-export interface FirmwareUpgrade {
+export interface FirmwareUpgrade extends JsonObject {
   versionUpgrade?: {
     patternGroup?: number
     step?: number
@@ -50,7 +51,7 @@ export interface FirmwareUpgrade {
   failureStatus?: FirmwareStatus
 }
 
-interface CommandsSupport {
+interface CommandsSupport extends JsonObject {
   incomingCommands: Record<IncomingRequestCommand, boolean>
   outgoingCommands?: Record<RequestCommand, boolean>
 }
@@ -110,9 +111,9 @@ export interface ChargingStationTemplate {
   registrationMaxRetries?: number
   enableStatistics?: boolean
   remoteAuthorization?: boolean
-  /** @deprecated Replaced by remoteAuthorization */
+  /** @deprecated Replaced by remoteAuthorization. */
   mustAuthorizeAtRemoteStart?: boolean
-  /** @deprecated Replaced by ocppStrictCompliance */
+  /** @deprecated Replaced by ocppStrictCompliance. */
   payloadSchemaValidation?: boolean
   amperageLimitationOcppKey?: string
   amperageLimitationUnit?: AmpereUnits
index ff3c5ef0fe56ed6b29d11745279dd334741a3b92..4d47a5e3698bb96a5bad1e9f3093af396993be32 100644 (file)
@@ -1,5 +1,6 @@
 import type { WebSocket } from 'ws'
 
+import type { WorkerData } from '../worker/index.js'
 import type { ChargingStationAutomaticTransactionGeneratorConfiguration } from './AutomaticTransactionGenerator.js'
 import { ChargingStationEvents } from './ChargingStationEvents.js'
 import type { ChargingStationInfo } from './ChargingStationInfo.js'
@@ -9,7 +10,6 @@ import type { EvseStatus } from './Evse.js'
 import type { JsonObject } from './JsonType.js'
 import type { BootNotificationResponse } from './ocpp/Responses.js'
 import type { Statistics } from './Statistics.js'
-import { type WorkerData, type WorkerMessage, WorkerMessageEvents } from '../worker/index.js'
 
 export interface ChargingStationOptions extends JsonObject {
   supervisionUrls?: string | string[]
@@ -52,31 +52,17 @@ enum ChargingStationMessageEvents {
 }
 
 export const ChargingStationWorkerMessageEvents = {
-  ...WorkerMessageEvents,
   ...ChargingStationEvents,
   ...ChargingStationMessageEvents
 } as const
 // eslint-disable-next-line @typescript-eslint/no-redeclare
 export type ChargingStationWorkerMessageEvents =
-  | WorkerMessageEvents
   | ChargingStationEvents
   | ChargingStationMessageEvents
 
-export interface ChargingStationWorkerEventError extends WorkerData {
-  event: WorkerMessageEvents
-  name: string
-  message: string
-  stack?: string
-}
-
-export type ChargingStationWorkerMessageData =
-  | ChargingStationData
-  | Statistics
-  | ChargingStationWorkerEventError
+export type ChargingStationWorkerMessageData = ChargingStationData | Statistics
 
-export type ChargingStationWorkerMessage<T extends ChargingStationWorkerMessageData> = Omit<
-WorkerMessage<T>,
-'event'
-> & {
+export interface ChargingStationWorkerMessage<T extends ChargingStationWorkerMessageData> {
   event: ChargingStationWorkerMessageEvents
+  data: T
 }
index fe8b84819452354572011d23388ffc679637d701..ffbb7deca5994abb61e9b5fc4736e4fb14b1cf61 100644 (file)
@@ -3,9 +3,9 @@ import type { ResourceLimits } from 'node:worker_threads'
 
 import type { WorkerChoiceStrategy } from 'poolifier'
 
+import type { WorkerProcessType } from '../worker/index.js'
 import type { StorageType } from './Storage.js'
 import type { ApplicationProtocol, AuthenticationType } from './UIProtocol.js'
-import type { WorkerProcessType } from '../worker/index.js'
 
 type ServerOptions = ListenOptions
 
@@ -25,6 +25,7 @@ export enum SupervisionUrlDistribution {
 export interface StationTemplateUrl {
   file: string
   numberOfStations: number
+  provisionedNumberOfStations?: number
 }
 
 export interface LogConfiguration {
@@ -70,7 +71,9 @@ export interface WorkerConfiguration {
   processType?: WorkerProcessType
   startDelay?: number
   elementsPerWorker?: ElementsPerWorkerType
+  /** @deprecated Use `elementAddDelay` instead. */
   elementStartDelay?: number
+  elementAddDelay?: number
   poolMinSize?: number
   poolMaxSize?: number
   resourceLimits?: ResourceLimits
@@ -84,14 +87,14 @@ export interface ConfigurationData {
   worker?: WorkerConfiguration
   uiServer?: UIServerConfiguration
   performanceStorage?: StorageConfiguration
-  /** @deprecated Moved to charging station template */
+  /** @deprecated Moved to charging station template. */
   autoReconnectMaxRetries?: number
   /** @deprecated Moved to worker configuration section. */
   workerProcess?: WorkerProcessType
   /** @deprecated Moved to worker configuration section. */
   workerStartDelay?: number
   /** @deprecated Moved to worker configuration section. */
-  elementStartDelay?: number
+  elementAddDelay?: number
   /** @deprecated Moved to worker configuration section. */
   workerPoolMinSize?: number
   /** @deprecated Moved to worker configuration section. */
diff --git a/src/types/MapStringifyFormat.ts b/src/types/MapStringifyFormat.ts
new file mode 100644 (file)
index 0000000..8543ef2
--- /dev/null
@@ -0,0 +1,4 @@
+export enum MapStringifyFormat {
+  array = 'array',
+  object = 'object'
+}
index 5da56b48ba4ca5dced29af98bc19cd27d5a0de4e..99572673edbfaa7d36450b860b8f190d13522d4c 100644 (file)
@@ -1,6 +1,6 @@
 import type { SampledValue } from './ocpp/MeterValues.js'
 
-export type SampledValueTemplate = SampledValue & {
+export interface SampledValueTemplate extends SampledValue {
   fluctuationPercent?: number
   minimumValue?: number
 }
diff --git a/src/types/SimulatorState.ts b/src/types/SimulatorState.ts
new file mode 100644 (file)
index 0000000..0ba5307
--- /dev/null
@@ -0,0 +1,9 @@
+import type { ConfigurationData } from './ConfigurationData.js'
+import type { TemplateStatistics } from './Statistics.js'
+
+export interface SimulatorState {
+  version: string
+  configuration: ConfigurationData | undefined
+  started: boolean
+  templateStatistics: Map<string, TemplateStatistics>
+}
index 9bb614ee2654509daec9b1a90551a5e52650b9bd..4c03dd9d73c62c06f35d432bbc18cf9be62ec082 100644 (file)
@@ -1,6 +1,6 @@
-import type { IncomingRequestCommand, RequestCommand } from './ocpp/Requests.js'
 import type { CircularArray } from '../utils/index.js'
 import type { WorkerData } from '../worker/index.js'
+import type { IncomingRequestCommand, RequestCommand } from './ocpp/Requests.js'
 
 export interface TimestampedData {
   timestamp: number
@@ -23,11 +23,19 @@ export type StatisticsData = Partial<{
   stdDevTimeMeasurement: number
 }>
 
-export type Statistics = {
+export interface Statistics extends WorkerData {
   id: string
   name: string
   uri: string
   createdAt: Date
   updatedAt?: Date
   statisticsData: Map<string | RequestCommand | IncomingRequestCommand, StatisticsData>
-} & WorkerData
+}
+
+export interface TemplateStatistics {
+  configured: number
+  provisioned: number
+  added: number
+  started: number
+  indexes: Set<number>
+}
index 6e120337197d3ea39b65a48517b6e9f554e9eaa2..b5cf30fff02cbca47089e577d75a0a8a8e7f0bd2 100644 (file)
@@ -19,16 +19,24 @@ export enum ProtocolVersion {
   '0.0.1' = '0.0.1'
 }
 
-export type ProtocolRequest = [string, ProcedureName, RequestPayload]
-export type ProtocolResponse = [string, ResponsePayload]
+export type ProtocolRequest = [
+  `${string}-${string}-${string}-${string}-${string}`,
+  ProcedureName,
+  RequestPayload
+]
+export type ProtocolResponse = [
+  `${string}-${string}-${string}-${string}-${string}`,
+  ResponsePayload
+]
 
 export type ProtocolRequestHandler = (
-  uuid?: string,
+  uuid?: `${string}-${string}-${string}-${string}-${string}`,
   procedureName?: ProcedureName,
   payload?: RequestPayload
 ) => undefined | Promise<undefined> | ResponsePayload | Promise<ResponsePayload>
 
 export enum ProcedureName {
+  SIMULATOR_STATE = 'simulatorState',
   START_SIMULATOR = 'startSimulator',
   STOP_SIMULATOR = 'stopSimulator',
   LIST_TEMPLATES = 'listTemplates',
index caa56e9ff9ae5e674c069bd98c828ced97495cd5..077c2f3750acce4fe2e6ece971e8aff3ba5d5d09 100644 (file)
@@ -1,11 +1,14 @@
 import type { RequestPayload, ResponsePayload } from './UIProtocol.js'
 
 export type BroadcastChannelRequest = [
-  string,
+  `${string}-${string}-${string}-${string}-${string}`,
   BroadcastChannelProcedureName,
   BroadcastChannelRequestPayload
 ]
-export type BroadcastChannelResponse = [string, BroadcastChannelResponsePayload]
+export type BroadcastChannelResponse = [
+  `${string}-${string}-${string}-${string}-${string}`,
+  BroadcastChannelResponsePayload
+]
 
 export enum BroadcastChannelProcedureName {
   START_CHARGING_STATION = 'startChargingStation',
index d5f887f81c3014c4a874063969608f433965166a..868dd73df07ea4fa37b95a333357c8bf4436a381 100644 (file)
@@ -1,85 +1,86 @@
-export {
-  ApplicationProtocol,
-  AuthenticationType,
-  ProcedureName,
-  Protocol,
-  type ProtocolRequest,
-  type ProtocolRequestHandler,
-  type ProtocolResponse,
-  ProtocolVersion,
-  type RequestPayload,
-  type ResponsePayload,
-  ResponseStatus
-} from './UIProtocol.js'
 export {
   type AutomaticTransactionGeneratorConfiguration,
   type ChargingStationAutomaticTransactionGeneratorConfiguration,
   IdTagDistribution,
   type Status
 } from './AutomaticTransactionGenerator.js'
-export { type GenericResponse, GenericStatus, RegistrationStatusEnumType } from './ocpp/Common.js'
+export type {
+  ChargingStationConfiguration,
+  EvseStatusConfiguration
+} from './ChargingStationConfiguration.js'
+export { ChargingStationEvents } from './ChargingStationEvents.js'
+export type { ChargingStationInfo } from './ChargingStationInfo.js'
+export type {
+  ChargingStationOcppConfiguration,
+  ConfigurationKey
+} from './ChargingStationOcppConfiguration.js'
 export {
-  AvailabilityType,
-  type BootNotificationRequest,
-  type CachedRequest,
-  type DataTransferRequest,
-  type DiagnosticsStatusNotificationRequest,
-  type ErrorCallback,
-  FirmwareStatus,
-  type FirmwareStatusNotificationRequest,
-  type HeartbeatRequest,
-  type IncomingRequest,
-  IncomingRequestCommand,
-  type IncomingRequestHandler,
-  MessageTrigger,
-  type MeterValuesRequest,
-  type OutgoingRequest,
-  RequestCommand,
-  type RequestParams,
-  type ResponseCallback,
-  type ResponseType,
-  type StatusNotificationRequest
-} from './ocpp/Requests.js'
+  AmpereUnits,
+  type ChargingStationTemplate,
+  CurrentType,
+  type FirmwareUpgrade,
+  PowerUnits,
+  Voltage,
+  type WsOptions
+} from './ChargingStationTemplate.js'
 export {
-  AvailabilityStatus,
-  type BootNotificationResponse,
-  ChargingProfileStatus,
-  type ClearCacheResponse,
-  ClearChargingProfileStatus,
-  ConfigurationStatus,
-  type DataTransferResponse,
-  DataTransferStatus,
-  type DiagnosticsStatusNotificationResponse,
-  type ErrorResponse,
-  type FirmwareStatusNotificationResponse,
-  type HeartbeatResponse,
-  type MeterValuesResponse,
-  ReservationStatus,
-  type Response,
-  type ResponseHandler,
-  type StatusNotificationResponse,
-  TriggerMessageStatus,
-  UnlockStatus
-} from './ocpp/Responses.js'
+  type ChargingStationData,
+  type ChargingStationOptions,
+  type ChargingStationWorkerData,
+  type ChargingStationWorkerMessage,
+  type ChargingStationWorkerMessageData,
+  ChargingStationWorkerMessageEvents,
+  type EvseStatusWorkerType
+} from './ChargingStationWorker.js'
 export {
-  AuthorizationStatus,
-  type AuthorizeRequest,
-  type AuthorizeResponse,
-  type StartTransactionRequest,
-  type StartTransactionResponse,
-  StopTransactionReason,
-  type StopTransactionRequest,
-  type StopTransactionResponse
-} from './ocpp/Transaction.js'
-export { BootReasonEnumType, OCPP20ConnectorStatusEnumType } from './ocpp/2.0/Common.js'
+  ApplicationProtocolVersion,
+  type ConfigurationData,
+  ConfigurationSection,
+  type ElementsPerWorkerType,
+  type LogConfiguration,
+  type StationTemplateUrl,
+  type StorageConfiguration,
+  SupervisionUrlDistribution,
+  type UIServerConfiguration,
+  type WorkerConfiguration
+} from './ConfigurationData.js'
+export type { ConnectorStatus } from './ConnectorStatus.js'
+export type { EmptyObject } from './EmptyObject.js'
+export type { HandleErrorParams } from './Error.js'
+export type { EvseStatus, EvseTemplate } from './Evse.js'
+export { FileType } from './FileType.js'
+export type { JsonObject, JsonType } from './JsonType.js'
+export { MapStringifyFormat } from './MapStringifyFormat.js'
+export type {
+  MeasurandPerPhaseSampledValueTemplates,
+  SampledValueTemplate
+} from './MeasurandPerPhaseSampledValueTemplates.js'
+export type { MeasurandValues } from './MeasurandValues.js'
+export { OCPP16ChargePointErrorCode } from './ocpp/1.6/ChargePointErrorCode.js'
+export { OCPP16ChargePointStatus } from './ocpp/1.6/ChargePointStatus.js'
 export {
-  BroadcastChannelProcedureName,
-  type BroadcastChannelRequest,
-  type BroadcastChannelRequestPayload,
-  type BroadcastChannelResponse,
-  type BroadcastChannelResponsePayload,
-  type MessageEvent
-} from './WorkerBroadcastChannel.js'
+  type OCPP16ChargingProfile,
+  OCPP16ChargingProfilePurposeType,
+  OCPP16ChargingRateUnitType,
+  type OCPP16ChargingSchedule,
+  type OCPP16ChargingSchedulePeriod
+} from './ocpp/1.6/ChargingProfile.js'
+export {
+  OCPP16StandardParametersKey,
+  OCPP16SupportedFeatureProfiles
+} from './ocpp/1.6/Configuration.js'
+export { OCPP16DiagnosticsStatus } from './ocpp/1.6/DiagnosticsStatus.js'
+export {
+  type OCPP16MeterValue,
+  OCPP16MeterValueContext,
+  OCPP16MeterValueLocation,
+  OCPP16MeterValueMeasurand,
+  OCPP16MeterValuePhase,
+  type OCPP16MeterValuesRequest,
+  type OCPP16MeterValuesResponse,
+  OCPP16MeterValueUnit,
+  type OCPP16SampledValue
+} from './ocpp/1.6/MeterValues.js'
 export {
   type ChangeConfigurationRequest,
   type GetConfigurationRequest,
@@ -131,6 +132,32 @@ export {
   type SetChargingProfileResponse,
   type UnlockConnectorResponse
 } from './ocpp/1.6/Responses.js'
+export {
+  OCPP16AuthorizationStatus,
+  type OCPP16AuthorizeRequest,
+  type OCPP16AuthorizeResponse,
+  type OCPP16StartTransactionRequest,
+  type OCPP16StartTransactionResponse,
+  OCPP16StopTransactionReason,
+  type OCPP16StopTransactionRequest,
+  type OCPP16StopTransactionResponse
+} from './ocpp/1.6/Transaction.js'
+export { BootReasonEnumType, OCPP20ConnectorStatusEnumType } from './ocpp/2.0/Common.js'
+export {
+  type OCPP20BootNotificationRequest,
+  type OCPP20ClearCacheRequest,
+  type OCPP20HeartbeatRequest,
+  OCPP20IncomingRequestCommand,
+  OCPP20RequestCommand,
+  type OCPP20StatusNotificationRequest
+} from './ocpp/2.0/Requests.js'
+export type {
+  OCPP20BootNotificationResponse,
+  OCPP20ClearCacheResponse,
+  OCPP20HeartbeatResponse,
+  OCPP20StatusNotificationResponse
+} from './ocpp/2.0/Responses.js'
+export { OCPP20OptionalVariableName } from './ocpp/2.0/Variables.js'
 export { ChargePointErrorCode } from './ocpp/ChargePointErrorCode.js'
 export {
   type ChargingProfile,
@@ -139,46 +166,7 @@ export {
   type ChargingSchedulePeriod,
   RecurrencyKindType
 } from './ocpp/ChargingProfile.js'
-export type {
-  ChargingStationConfiguration,
-  EvseStatusConfiguration
-} from './ChargingStationConfiguration.js'
-export {
-  type ChargingStationData,
-  type ChargingStationWorkerData,
-  type ChargingStationWorkerEventError,
-  type ChargingStationWorkerMessage,
-  type ChargingStationWorkerMessageData,
-  ChargingStationWorkerMessageEvents,
-  type ChargingStationOptions,
-  type EvseStatusWorkerType
-} from './ChargingStationWorker.js'
-export type { ChargingStationInfo } from './ChargingStationInfo.js'
-export type {
-  ChargingStationOcppConfiguration,
-  ConfigurationKey
-} from './ChargingStationOcppConfiguration.js'
-export {
-  AmpereUnits,
-  type ChargingStationTemplate,
-  CurrentType,
-  type FirmwareUpgrade,
-  PowerUnits,
-  Voltage,
-  type WsOptions
-} from './ChargingStationTemplate.js'
-export {
-  ApplicationProtocolVersion,
-  type ConfigurationData,
-  ConfigurationSection,
-  type ElementsPerWorkerType,
-  type LogConfiguration,
-  type StationTemplateUrl,
-  type StorageConfiguration,
-  SupervisionUrlDistribution,
-  type UIServerConfiguration,
-  type WorkerConfiguration
-} from './ConfigurationData.js'
+export { type GenericResponse, GenericStatus, RegistrationStatusEnumType } from './ocpp/Common.js'
 export {
   type ConfigurationKeyType,
   ConnectorPhaseRotation,
@@ -187,20 +175,8 @@ export {
   SupportedFeatureProfiles,
   VendorParametersKey
 } from './ocpp/Configuration.js'
-export type { ConnectorStatus } from './ConnectorStatus.js'
 export { ConnectorStatusEnum, type ConnectorStatusTransition } from './ocpp/ConnectorStatusEnum.js'
-export { DBName, StorageType } from './Storage.js'
-export type { EmptyObject } from './EmptyObject.js'
 export { ErrorType } from './ocpp/ErrorType.js'
-export type { EvseTemplate, EvseStatus } from './Evse.js'
-export { FileType } from './FileType.js'
-export type { HandleErrorParams } from './Error.js'
-export type { JsonObject, JsonType } from './JsonType.js'
-export type {
-  MeasurandPerPhaseSampledValueTemplates,
-  SampledValueTemplate
-} from './MeasurandPerPhaseSampledValueTemplates.js'
-export type { MeasurandValues } from './MeasurandValues.js'
 export { MessageType } from './ocpp/MessageType.js'
 export {
   type MeterValue,
@@ -211,67 +187,97 @@ export {
   MeterValueUnit,
   type SampledValue
 } from './ocpp/MeterValues.js'
+export { OCPPVersion } from './ocpp/OCPPVersion.js'
 export {
-  type OCPP16MeterValue,
-  OCPP16MeterValueContext,
-  OCPP16MeterValueLocation,
-  OCPP16MeterValueMeasurand,
-  OCPP16MeterValuePhase,
-  OCPP16MeterValueUnit,
-  type OCPP16MeterValuesRequest,
-  type OCPP16MeterValuesResponse,
-  type OCPP16SampledValue
-} from './ocpp/1.6/MeterValues.js'
-export {
-  OCPP16AuthorizationStatus,
-  type OCPP16AuthorizeRequest,
-  type OCPP16AuthorizeResponse,
-  type OCPP16StartTransactionRequest,
-  type OCPP16StartTransactionResponse,
-  OCPP16StopTransactionReason,
-  type OCPP16StopTransactionRequest,
-  type OCPP16StopTransactionResponse
-} from './ocpp/1.6/Transaction.js'
-export { OCPP16ChargePointErrorCode } from './ocpp/1.6/ChargePointErrorCode.js'
-export { OCPP16ChargePointStatus } from './ocpp/1.6/ChargePointStatus.js'
+  AvailabilityType,
+  type BootNotificationRequest,
+  type CachedRequest,
+  type DataTransferRequest,
+  type DiagnosticsStatusNotificationRequest,
+  type ErrorCallback,
+  FirmwareStatus,
+  type FirmwareStatusNotificationRequest,
+  type HeartbeatRequest,
+  type IncomingRequest,
+  IncomingRequestCommand,
+  type IncomingRequestHandler,
+  MessageTrigger,
+  type MeterValuesRequest,
+  type OutgoingRequest,
+  RequestCommand,
+  type RequestParams,
+  type ResponseCallback,
+  type ResponseType,
+  type StatusNotificationRequest
+} from './ocpp/Requests.js'
 export {
-  type OCPP16ChargingProfile,
-  OCPP16ChargingProfilePurposeType,
-  OCPP16ChargingRateUnitType,
-  type OCPP16ChargingSchedule,
-  type OCPP16ChargingSchedulePeriod
-} from './ocpp/1.6/ChargingProfile.js'
+  type Reservation,
+  type ReservationKey,
+  ReservationTerminationReason
+} from './ocpp/Reservation.js'
 export {
-  OCPP16StandardParametersKey,
-  OCPP16SupportedFeatureProfiles
-} from './ocpp/1.6/Configuration.js'
-export { OCPP16DiagnosticsStatus } from './ocpp/1.6/DiagnosticsStatus.js'
+  AvailabilityStatus,
+  type BootNotificationResponse,
+  ChargingProfileStatus,
+  type ClearCacheResponse,
+  ClearChargingProfileStatus,
+  ConfigurationStatus,
+  type DataTransferResponse,
+  DataTransferStatus,
+  type DiagnosticsStatusNotificationResponse,
+  type ErrorResponse,
+  type FirmwareStatusNotificationResponse,
+  type HeartbeatResponse,
+  type MeterValuesResponse,
+  ReservationStatus,
+  type Response,
+  type ResponseHandler,
+  type StatusNotificationResponse,
+  TriggerMessageStatus,
+  UnlockStatus
+} from './ocpp/Responses.js'
 export {
-  type OCPP20BootNotificationRequest,
-  type OCPP20ClearCacheRequest,
-  type OCPP20HeartbeatRequest,
-  OCPP20IncomingRequestCommand,
-  OCPP20RequestCommand,
-  type OCPP20StatusNotificationRequest
-} from './ocpp/2.0/Requests.js'
-export type {
-  OCPP20BootNotificationResponse,
-  OCPP20ClearCacheResponse,
-  OCPP20HeartbeatResponse,
-  OCPP20StatusNotificationResponse
-} from './ocpp/2.0/Responses.js'
-export { OCPP20OptionalVariableName } from './ocpp/2.0/Variables.js'
-export { OCPPVersion } from './ocpp/OCPPVersion.js'
+  AuthorizationStatus,
+  type AuthorizeRequest,
+  type AuthorizeResponse,
+  type StartTransactionRequest,
+  type StartTransactionResponse,
+  StopTransactionReason,
+  type StopTransactionRequest,
+  type StopTransactionResponse
+} from './ocpp/Transaction.js'
 export { PerformanceRecord } from './orm/entities/PerformanceRecord.js'
-export type { Statistics, StatisticsData, TimestampedData } from './Statistics.js'
+export type { SimulatorState } from './SimulatorState.js'
+export type {
+  Statistics,
+  StatisticsData,
+  TemplateStatistics,
+  TimestampedData
+} from './Statistics.js'
+export { DBName, StorageType } from './Storage.js'
+export {
+  ApplicationProtocol,
+  AuthenticationType,
+  ProcedureName,
+  Protocol,
+  type ProtocolRequest,
+  type ProtocolRequestHandler,
+  type ProtocolResponse,
+  ProtocolVersion,
+  type RequestPayload,
+  type ResponsePayload,
+  ResponseStatus
+} from './UIProtocol.js'
 export {
-  type WSError,
   WebSocketCloseEventStatusCode,
-  WebSocketCloseEventStatusString
+  WebSocketCloseEventStatusString,
+  type WSError
 } from './WebSocket.js'
 export {
-  type Reservation,
-  type ReservationKey,
-  ReservationTerminationReason
-} from './ocpp/Reservation.js'
-export { ChargingStationEvents } from './ChargingStationEvents.js'
+  BroadcastChannelProcedureName,
+  type BroadcastChannelRequest,
+  type BroadcastChannelRequestPayload,
+  type BroadcastChannelResponse,
+  type BroadcastChannelResponsePayload,
+  type MessageEvent
+} from './WorkerBroadcastChannel.js'
index 32d9757f4cea58be69ca349976cbc760709933c2..c57be2032751277115f298947575d7877457d4cd 100644 (file)
@@ -1,3 +1,5 @@
+import type { EmptyObject } from '../../EmptyObject.js'
+import type { JsonObject } from '../../JsonType.js'
 import type { OCPP16ChargePointErrorCode } from './ChargePointErrorCode.js'
 import type { OCPP16ChargePointStatus } from './ChargePointStatus.js'
 import type {
@@ -7,8 +9,6 @@ import type {
 } from './ChargingProfile.js'
 import type { OCPP16StandardParametersKey, OCPP16VendorParametersKey } from './Configuration.js'
 import type { OCPP16DiagnosticsStatus } from './DiagnosticsStatus.js'
-import type { EmptyObject } from '../../EmptyObject.js'
-import type { JsonObject } from '../../JsonType.js'
 
 export enum OCPP16RequestCommand {
   BOOT_NOTIFICATION = 'BootNotification',
@@ -148,9 +148,9 @@ export enum OCPP16FirmwareStatus {
   Installed = 'Installed'
 }
 
-export type OCPP16FirmwareStatusNotificationRequest = {
+export interface OCPP16FirmwareStatusNotificationRequest extends JsonObject {
   status: OCPP16FirmwareStatus
-} & JsonObject
+}
 
 export interface GetDiagnosticsRequest extends JsonObject {
   location: string
index b8c36ed24f471f6d23734b74c569e5eedd6ddfa0..8b67a9267d5a3a9da03632a0bd7b20a381434102 100644 (file)
@@ -1,8 +1,8 @@
-import type { OCPP16ChargingSchedule } from './ChargingProfile.js'
 import type { EmptyObject } from '../../EmptyObject.js'
 import type { JsonObject } from '../../JsonType.js'
 import type { GenericStatus, RegistrationStatusEnumType } from '../Common.js'
 import type { OCPPConfigurationKey } from '../Configuration.js'
+import type { OCPP16ChargingSchedule } from './ChargingProfile.js'
 
 export interface OCPP16HeartbeatResponse extends JsonObject {
   currentTime: Date
index 2e6c7289cde3ac8940d605b74bdff41c86b061df..f9659f695398822e3932197137b8ee581626d6d2 100644 (file)
@@ -1,5 +1,5 @@
-import type { OCPP16MeterValue } from './MeterValues.js'
 import type { JsonObject } from '../../JsonType.js'
+import type { OCPP16MeterValue } from './MeterValues.js'
 
 export enum OCPP16StopTransactionReason {
   EMERGENCY_STOP = 'EmergencyStop',
index 9d13b2a829aa295cb6cffd86b0409d5cb1b419f9..785c604646654b509a803ff4ec57140a397a7a38 100644 (file)
@@ -94,33 +94,33 @@ export enum CertificateSigningUseEnumType {
 
 export type CertificateSignedStatusEnumType = GenericStatusEnumType
 
-export type CertificateHashDataType = {
+export interface CertificateHashDataType extends JsonObject {
   hashAlgorithm: HashAlgorithmEnumType
   issuerNameHash: string
   issuerKeyHash: string
   serialNumber: string
-} & JsonObject
+}
 
-export type CertificateHashDataChainType = {
+export interface CertificateHashDataChainType extends JsonObject {
   certificateType: GetCertificateIdUseEnumType
   certificateHashData: CertificateHashDataType
   childCertificateHashData?: CertificateHashDataType
-} & JsonObject
+}
 
-export type OCSPRequestDataType = {
+export interface OCSPRequestDataType extends JsonObject {
   hashAlgorithm: HashAlgorithmEnumType
   issuerNameHash: string
   issuerKeyHash: string
   serialNumber: string
   responderURL: string
-} & JsonObject
+}
 
-export type StatusInfoType = {
+export interface StatusInfoType extends JsonObject {
   reasonCode: string
   additionalInfo?: string
-} & JsonObject
+}
 
-export type EVSEType = {
+export interface EVSEType extends JsonObject {
   id: number
   connectorId?: string
-} & JsonObject
+}
index ef7a5161b3797282425947b30d5e0c30c85bddaa..f075e642f7284f657398f5e3a7a46b0ab7f565db 100644 (file)
@@ -1,11 +1,11 @@
+import type { EmptyObject } from '../../EmptyObject.js'
+import type { JsonObject } from '../../JsonType.js'
 import type {
   BootReasonEnumType,
   InstallCertificateUseEnumType,
   OCPP20ConnectorStatusEnumType
 } from './Common.js'
 import type { OCPP20SetVariableDataType } from './Variables.js'
-import type { EmptyObject } from '../../EmptyObject.js'
-import type { JsonObject } from '../../JsonType.js'
 
 export enum OCPP20RequestCommand {
   BOOT_NOTIFICATION = 'BootNotification',
@@ -19,40 +19,40 @@ export enum OCPP20IncomingRequestCommand {
   REQUEST_STOP_TRANSACTION = 'RequestStopTransaction'
 }
 
-type ModemType = {
+interface ModemType extends JsonObject {
   iccid?: string
   imsi?: string
-} & JsonObject
+}
 
-type ChargingStationType = {
+interface ChargingStationType extends JsonObject {
   serialNumber?: string
   model: string
   vendorName: string
   firmwareVersion?: string
   modem?: ModemType
-} & JsonObject
+}
 
-export type OCPP20BootNotificationRequest = {
+export interface OCPP20BootNotificationRequest extends JsonObject {
   reason: BootReasonEnumType
   chargingStation: ChargingStationType
-} & JsonObject
+}
 
 export type OCPP20HeartbeatRequest = EmptyObject
 
 export type OCPP20ClearCacheRequest = EmptyObject
 
-export type OCPP20StatusNotificationRequest = {
+export interface OCPP20StatusNotificationRequest extends JsonObject {
   timestamp: Date
   connectorStatus: OCPP20ConnectorStatusEnumType
   evseId: number
   connectorId: number
-} & JsonObject
+}
 
-export type OCPP20SetVariablesRequest = {
+export interface OCPP20SetVariablesRequest extends JsonObject {
   setVariableData: OCPP20SetVariableDataType[]
-} & JsonObject
+}
 
-export type OCPP20InstallCertificateRequest = {
+export interface OCPP20InstallCertificateRequest extends JsonObject {
   certificateType: InstallCertificateUseEnumType
   certificate: string
-} & JsonObject
+}
index dbeb1e3ffd556d00e8cf9d3614ece82c00f765bc..4248c5d8f6baf74bc7b5874371c6026a781e9442 100644 (file)
@@ -1,36 +1,36 @@
+import type { EmptyObject } from '../../EmptyObject.js'
+import type { JsonObject } from '../../JsonType.js'
+import type { RegistrationStatusEnumType } from '../Common.js'
 import type {
   GenericStatusEnumType,
   InstallCertificateStatusEnumType,
   StatusInfoType
 } from './Common.js'
 import type { OCPP20SetVariableResultType } from './Variables.js'
-import type { EmptyObject } from '../../EmptyObject.js'
-import type { JsonObject } from '../../JsonType.js'
-import type { RegistrationStatusEnumType } from '../Common.js'
 
-export type OCPP20BootNotificationResponse = {
+export interface OCPP20BootNotificationResponse extends JsonObject {
   currentTime: Date
   status: RegistrationStatusEnumType
   interval: number
   statusInfo?: StatusInfoType
-} & JsonObject
+}
 
-export type OCPP20HeartbeatResponse = {
+export interface OCPP20HeartbeatResponse extends JsonObject {
   currentTime: Date
-} & JsonObject
+}
 
-export type OCPP20ClearCacheResponse = {
+export interface OCPP20ClearCacheResponse extends JsonObject {
   status: GenericStatusEnumType
   statusInfo?: StatusInfoType
-} & JsonObject
+}
 
 export type OCPP20StatusNotificationResponse = EmptyObject
 
-export type OCPP20SetVariablesResponse = {
+export interface OCPP20SetVariablesResponse extends JsonObject {
   setVariableResult: OCPP20SetVariableResultType[]
-} & JsonObject
+}
 
-export type OCPP20InstallCertificateResponse = {
+export interface OCPP20InstallCertificateResponse extends JsonObject {
   status: InstallCertificateStatusEnumType
   statusInfo?: StatusInfoType
-} & JsonObject
+}
index b5cd53e5620d001b9a1e39c71185b064b7fe9309..04a344564bcd202e3a6ab5dcef35798545b09620 100644 (file)
@@ -1,5 +1,5 @@
-import type { EVSEType, StatusInfoType } from './Common.js'
 import type { JsonObject } from '../../JsonType.js'
+import type { EVSEType, StatusInfoType } from './Common.js'
 
 enum OCPP20ComponentName {
   AlignedDataCtrlr = 'AlignedDataCtrlr',
@@ -69,11 +69,11 @@ enum AttributeEnumType {
   MaxSet = 'MaxSet'
 }
 
-type ComponentType = {
+interface ComponentType extends JsonObject {
   name: string | OCPP20ComponentName
   instance?: string
   evse?: EVSEType
-} & JsonObject
+}
 
 type VariableName =
   | string
@@ -81,17 +81,17 @@ type VariableName =
   | OCPP20OptionalVariableName
   | OCPP20VendorVariableName
 
-type VariableType = {
+interface VariableType extends JsonObject {
   name: VariableName
   instance?: string
-} & JsonObject
+}
 
-export type OCPP20SetVariableDataType = {
+export interface OCPP20SetVariableDataType extends JsonObject {
   attributeType?: AttributeEnumType
   attributeValue: string
   component: ComponentType
   variable: VariableType
-} & JsonObject
+}
 
 enum SetVariableStatusEnumType {
   Accepted = 'Accepted',
@@ -102,15 +102,15 @@ enum SetVariableStatusEnumType {
   RebootRequired = 'RebootRequired'
 }
 
-export type OCPP20SetVariableResultType = {
+export interface OCPP20SetVariableResultType extends JsonObject {
   attributeType?: AttributeEnumType
   attributeStatus: SetVariableStatusEnumType
   component: ComponentType
   variable: VariableType
   attributeStatusInfo?: StatusInfoType
-} & JsonObject
+}
 
-export type OCPP20ComponentVariableType = {
+export interface OCPP20ComponentVariableType extends JsonObject {
   component: ComponentType
   variable?: VariableType
-} & JsonObject
+}
index ac8a094508bfa15fe57eac1a2a1bdda844c1f145..f1cd34ba3f8ec0cd79b70de1d8378fb409d7e511 100644 (file)
@@ -1,3 +1,4 @@
+import type { JsonObject } from '../JsonType.js'
 import {
   OCPP16StandardParametersKey,
   OCPP16SupportedFeatureProfiles,
@@ -8,7 +9,6 @@ import {
   OCPP20RequiredVariableName,
   OCPP20VendorVariableName
 } from './2.0/Variables.js'
-import type { JsonObject } from '../JsonType.js'
 
 export const StandardParametersKey = {
   ...OCPP16StandardParametersKey,
@@ -44,8 +44,8 @@ export enum ConnectorPhaseRotation {
 
 export type ConfigurationKeyType = string | StandardParametersKey | VendorParametersKey
 
-export type OCPPConfigurationKey = {
+export interface OCPPConfigurationKey extends JsonObject {
   key: ConfigurationKeyType
   readonly: boolean
   value?: string
-} & JsonObject
+}
index c718f1e03ab5b61c6fc8b92cdf49dd1154f179fd..12e1c68c3e6996cf9c3d1ddb48158662ea7ab0df 100644 (file)
@@ -1,3 +1,6 @@
+import type { ChargingStation } from '../../charging-station/index.js'
+import type { OCPPError } from '../../exception/index.js'
+import type { JsonType } from '../JsonType.js'
 import { OCPP16DiagnosticsStatus } from './1.6/DiagnosticsStatus.js'
 import type { OCPP16MeterValuesRequest } from './1.6/MeterValues.js'
 import {
@@ -23,9 +26,6 @@ import {
   type OCPP20StatusNotificationRequest
 } from './2.0/Requests.js'
 import type { MessageType } from './MessageType.js'
-import type { ChargingStation } from '../../charging-station/index.js'
-import type { OCPPError } from '../../exception/index.js'
-import type { JsonType } from '../JsonType.js'
 
 export const RequestCommand = {
   ...OCPP16RequestCommand,
index 9983ee8a81b0721fb306d55cfb88dca15055ac57..aac5a5fc3774b39d008b454d5e85faad021e2016 100644 (file)
@@ -1,3 +1,5 @@
+import type { ChargingStation } from '../../charging-station/index.js'
+import type { JsonType } from '../JsonType.js'
 import type { OCPP16MeterValuesResponse } from './1.6/MeterValues.js'
 import {
   OCPP16AvailabilityStatus,
@@ -19,8 +21,6 @@ import type { OCPP20BootNotificationResponse, OCPP20ClearCacheResponse } from '.
 import { type GenericResponse, GenericStatus } from './Common.js'
 import type { ErrorType } from './ErrorType.js'
 import type { MessageType } from './MessageType.js'
-import type { ChargingStation } from '../../charging-station/index.js'
-import type { JsonType } from '../JsonType.js'
 
 export type Response = [MessageType.CALL_RESULT_MESSAGE, string, JsonType]
 
index 0e32c7e629b28bfa3ade6eba052272130877a8ea..7b5346d52392deb1996673bc7210e1a3bf300708 100644 (file)
@@ -21,7 +21,7 @@ export const buildChargingStationAutomaticTransactionGeneratorConfiguration = (
 
 export const buildConnectorsStatus = (chargingStation: ChargingStation): ConnectorStatus[] => {
   return [...chargingStation.connectors.values()].map(
-    ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest
+    ({ transactionSetInterval, ...connectorStatus }) => connectorStatus
   )
 }
 
@@ -37,7 +37,7 @@ export const buildEvsesStatus = (
   // eslint-disable-next-line array-callback-return
   return [...chargingStation.evses.values()].map(evseStatus => {
     const connectorsStatus = [...evseStatus.connectors.values()].map(
-      ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest
+      ({ transactionSetInterval, ...connectorStatus }) => connectorStatus
     )
     let status: EvseStatusConfiguration
     switch (outputFormat) {
index d577ded4141858bbc08213615f116fd199bfc2c8..25a1475bc2a5f6ec9a6ea40df049048db9538ad8 100644 (file)
@@ -4,18 +4,8 @@ import { env } from 'node:process'
 import { fileURLToPath } from 'node:url'
 
 import chalk from 'chalk'
-import { mergeDeepRight } from 'rambda'
+import { mergeDeepRight, once } from 'rambda'
 
-import {
-  buildPerformanceUriFilePath,
-  checkWorkerElementsPerWorker,
-  checkWorkerProcessType,
-  getDefaultPerformanceStorageUri,
-  handleFileException,
-  logPrefix
-} from './ConfigurationUtils.js'
-import { Constants } from './Constants.js'
-import { hasOwnProp, isCFEnvironment, once } from './Utils.js'
 import {
   ApplicationProtocol,
   ApplicationProtocolVersion,
@@ -31,12 +21,22 @@ import {
   type WorkerConfiguration
 } from '../types/index.js'
 import {
-  DEFAULT_ELEMENT_START_DELAY,
+  DEFAULT_ELEMENT_ADD_DELAY,
   DEFAULT_POOL_MAX_SIZE,
   DEFAULT_POOL_MIN_SIZE,
   DEFAULT_WORKER_START_DELAY,
   WorkerProcessType
 } from '../worker/index.js'
+import {
+  buildPerformanceUriFilePath,
+  checkWorkerElementsPerWorker,
+  checkWorkerProcessType,
+  getDefaultPerformanceStorageUri,
+  handleFileException,
+  logPrefix
+} from './ConfigurationUtils.js'
+import { Constants } from './Constants.js'
+import { hasOwnProp, isCFEnvironment } from './Utils.js'
 
 type ConfigurationSectionType =
   | LogConfiguration
@@ -82,8 +82,7 @@ export class Configuration {
 
   public static getStationTemplateUrls (): StationTemplateUrl[] | undefined {
     const checkDeprecatedConfigurationKeysOnce = once(
-      Configuration.checkDeprecatedConfigurationKeys.bind(Configuration),
-      Configuration
+      Configuration.checkDeprecatedConfigurationKeys.bind(Configuration)
     )
     checkDeprecatedConfigurationKeysOnce()
     return Configuration.getConfigurationData()?.stationTemplateUrls
@@ -276,10 +275,11 @@ export class Configuration {
       processType: WorkerProcessType.workerSet,
       startDelay: DEFAULT_WORKER_START_DELAY,
       elementsPerWorker: 'auto',
-      elementStartDelay: DEFAULT_ELEMENT_START_DELAY,
+      elementAddDelay: DEFAULT_ELEMENT_ADD_DELAY,
       poolMinSize: DEFAULT_POOL_MIN_SIZE,
       poolMaxSize: DEFAULT_POOL_MAX_SIZE
     }
+
     const deprecatedWorkerConfiguration: WorkerConfiguration = {
       ...(hasOwnProp(Configuration.getConfigurationData(), 'workerProcess') && {
         processType: Configuration.getConfigurationData()?.workerProcess
@@ -290,8 +290,11 @@ export class Configuration {
       ...(hasOwnProp(Configuration.getConfigurationData(), 'chargingStationsPerWorker') && {
         elementsPerWorker: Configuration.getConfigurationData()?.chargingStationsPerWorker
       }),
-      ...(hasOwnProp(Configuration.getConfigurationData(), 'elementStartDelay') && {
-        elementStartDelay: Configuration.getConfigurationData()?.elementStartDelay
+      ...(hasOwnProp(Configuration.getConfigurationData(), 'elementAddDelay') && {
+        elementAddDelay: Configuration.getConfigurationData()?.elementAddDelay
+      }),
+      ...(hasOwnProp(Configuration.getConfigurationData()?.worker, 'elementStartDelay') && {
+        elementAddDelay: Configuration.getConfigurationData()?.worker?.elementStartDelay
       }),
       ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMinSize') && {
         poolMinSize: Configuration.getConfigurationData()?.workerPoolMinSize
@@ -397,9 +400,9 @@ export class Configuration {
       `Use '${ConfigurationSection.worker}' section to define the number of element(s) per worker instead`
     )
     Configuration.warnDeprecatedConfigurationKey(
-      'elementStartDelay',
+      'elementAddDelay',
       undefined,
-      `Use '${ConfigurationSection.worker}' section to define the worker's element start delay instead`
+      `Use '${ConfigurationSection.worker}' section to define the worker's element add delay instead`
     )
     Configuration.warnDeprecatedConfigurationKey(
       'workerPoolMinSize',
@@ -426,6 +429,11 @@ export class Configuration {
       ConfigurationSection.worker,
       'Not publicly exposed to end users'
     )
+    Configuration.warnDeprecatedConfigurationKey(
+      'elementStartDelay',
+      ConfigurationSection.worker,
+      "Use 'elementAddDelay' instead"
+    )
     if (
       Configuration.getConfigurationData()?.worker?.processType ===
       ('staticPool' as WorkerProcessType)
@@ -505,22 +513,22 @@ export class Configuration {
 
   private static warnDeprecatedConfigurationKey (
     key: string,
-    sectionName?: string,
+    configurationSection?: ConfigurationSection,
     logMsgToAppend = ''
   ): void {
     if (
-      sectionName != null &&
-      Configuration.getConfigurationData()?.[sectionName as keyof ConfigurationData] != null &&
+      configurationSection != null &&
+      Configuration.getConfigurationData()?.[configurationSection as keyof ConfigurationData] !=
+        null &&
       (
-        Configuration.getConfigurationData()?.[sectionName as keyof ConfigurationData] as Record<
-        string,
-        unknown
-        >
+        Configuration.getConfigurationData()?.[
+          configurationSection as keyof ConfigurationData
+        ] as Record<string, unknown>
       )[key] != null
     ) {
       console.error(
         `${chalk.green(logPrefix())} ${chalk.red(
-          `Deprecated configuration key '${key}' usage in section '${sectionName}'${
+          `Deprecated configuration key '${key}' usage in section '${configurationSection}'${
             logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : ''
           }`
         )}`
@@ -536,7 +544,7 @@ export class Configuration {
     }
   }
 
-  private static getConfigurationData (): ConfigurationData | undefined {
+  public static getConfigurationData (): ConfigurationData | undefined {
     if (Configuration.configurationData == null) {
       try {
         Configuration.configurationData = JSON.parse(
@@ -567,7 +575,7 @@ export class Configuration {
           event === 'change'
         ) {
           Configuration.configurationFileReloading = true
-          const consoleWarnOnce = once(console.warn, this)
+          const consoleWarnOnce = once(console.warn)
           consoleWarnOnce(
             `${chalk.green(logPrefix())} ${chalk.yellow(
               `${FileType.Configuration} ${this.configurationFile} file have changed, reload`
@@ -577,7 +585,7 @@ export class Configuration {
           Configuration.configurationSectionCache.clear()
           if (Configuration.configurationChangeCallback != null) {
             Configuration.configurationChangeCallback()
-              .catch(error => {
+              .catch((error: unknown) => {
                 throw typeof error === 'string' ? new Error(error) : error
               })
               .finally(() => {
index fd976859aeef04ff6f76f5cccd4395b8b75d1b5c..2a85bb0e2fd5686b603bf9a405181c3bc13f1b64 100644 (file)
@@ -3,10 +3,10 @@ import { fileURLToPath } from 'node:url'
 
 import chalk from 'chalk'
 
-import { Constants } from './Constants.js'
-import { isNotEmptyString, logPrefix as utilsLogPrefix } from './Utils.js'
 import { type ElementsPerWorkerType, type FileType, StorageType } from '../types/index.js'
 import { WorkerProcessType } from '../worker/index.js'
+import { Constants } from './Constants.js'
+import { isNotEmptyString, logPrefix as utilsLogPrefix } from './Utils.js'
 
 export const logPrefix = (): string => {
   return utilsLogPrefix(' Simulator configuration |')
index ea0ff8fca7dece1e9f88092664e698006724ae7a..3746c4fdd91e8277ebc5167c563cbd3cf88fb673 100644 (file)
@@ -34,6 +34,12 @@ export class Constants {
     useConnectorId0: true,
     ocppVersion: OCPPVersion.VERSION_16,
     firmwareVersionPattern: Constants.SEMVER_PATTERN,
+    firmwareUpgrade: {
+      versionUpgrade: {
+        step: 1
+      },
+      reset: true
+    },
     ocppPersistentConfiguration: true,
     stationInfoPersistentConfiguration: true,
     automaticTransactionGeneratorPersistentConfiguration: true,
@@ -85,7 +91,7 @@ export class Constants {
     | RequestCommand
     | IncomingRequestCommand
 
-  static readonly MAX_RANDOM_INTEGER = 281474976710654
+  static readonly MAX_RANDOM_INTEGER = 281474976710655
 
   static readonly STOP_CHARGING_STATIONS_TIMEOUT = 60000 // Ms
 
index bd4fcf375ca334e96c62afa5d09b88cc8fa7a21b..9c243a299e769f67c4e70859aae110651a3444cd 100644 (file)
@@ -2,8 +2,6 @@ import process from 'node:process'
 
 import chalk from 'chalk'
 
-import { logger } from './Logger.js'
-import { isNotEmptyString } from './Utils.js'
 import type { ChargingStation } from '../charging-station/index.js'
 import type {
   EmptyObject,
@@ -13,6 +11,8 @@ import type {
   JsonType,
   RequestCommand
 } from '../types/index.js'
+import { logger } from './Logger.js'
+import { isNotEmptyString } from './Utils.js'
 
 const defaultErrorParams = {
   throwError: true,
index bc8dd07c24725d990ae282056fc5ab8d4243b942..543a2b23c2eda50f157680f15377ee335ec697da 100644 (file)
@@ -1,9 +1,9 @@
-import { type FSWatcher, type WatchListener, readFileSync, watch } from 'node:fs'
+import { type FSWatcher, readFileSync, watch, type WatchListener } from 'node:fs'
 
+import type { FileType, JsonType } from '../types/index.js'
 import { handleFileException } from './ErrorUtils.js'
 import { logger } from './Logger.js'
 import { isNotEmptyString } from './Utils.js'
-import type { FileType, JsonType } from '../types/index.js'
 
 export const watchJsonFile = <T extends JsonType>(
   file: string,
index 7d80e1aece51349349abd0766f8af5ae2b196833..b13ec0477ab9314aca3859f264e62c40ae531929 100644 (file)
@@ -3,9 +3,9 @@ import { createLogger, format, type transport } from 'winston'
 import TransportType from 'winston/lib/winston/transports/index.js'
 import DailyRotateFile from 'winston-daily-rotate-file'
 
+import { ConfigurationSection, type LogConfiguration } from '../types/index.js'
 import { Configuration } from './Configuration.js'
 import { insertAt } from './Utils.js'
-import { ConfigurationSection, type LogConfiguration } from '../types/index.js'
 
 const logConfiguration = Configuration.getConfigurationSection<LogConfiguration>(
   ConfigurationSection.log
index 36f797f5eb88feb3a9f5e0a4b6ea978b660a9f9b..4164c36bf11c96f18b8250cfd47be98bdeb897d5 100644 (file)
@@ -1,9 +1,3 @@
-import {
-  OutputFormat,
-  buildChargingStationAutomaticTransactionGeneratorConfiguration,
-  buildConnectorsStatus,
-  buildEvsesStatus
-} from './ChargingStationConfigurationUtils.js'
 import type { ChargingStation } from '../charging-station/index.js'
 import {
   type ChargingStationData,
@@ -11,6 +5,12 @@ import {
   ChargingStationWorkerMessageEvents,
   type Statistics
 } from '../types/index.js'
+import {
+  buildChargingStationAutomaticTransactionGeneratorConfiguration,
+  buildConnectorsStatus,
+  buildEvsesStatus,
+  OutputFormat
+} from './ChargingStationConfigurationUtils.js'
 
 export const buildAddedMessage = (
   chargingStation: ChargingStation
@@ -66,9 +66,7 @@ export const buildPerformanceStatisticsMessage = (
   }
 }
 
-export const buildChargingStationDataPayload = (
-  chargingStation: ChargingStation
-): ChargingStationData => {
+const buildChargingStationDataPayload = (chargingStation: ChargingStation): ChargingStationData => {
   return {
     started: chargingStation.started,
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
index 9ca4398ca76590250de3b1c5a935f42ca373cd6f..4125ab06a9598dd7d6dd7812dbb0539ecc20ba69 100644 (file)
@@ -1,37 +1,10 @@
-/**
- * Computes the average of the given data set.
- *
- * @param dataSet - Data set.
- * @returns The average of the given data set.
- * @internal
- */
-export const average = (dataSet: number[]): number => {
-  if (Array.isArray(dataSet) && dataSet.length === 0) {
-    return 0
-  } else if (Array.isArray(dataSet) && dataSet.length === 1) {
-    return dataSet[0]
-  }
-  return dataSet.reduce((accumulator, nb) => accumulator + nb, 0) / dataSet.length
-}
+import { mean } from 'rambda'
 
-/**
- * Computes the median of the given data set.
- *
- * @param dataSet - Data set.
- * @returns The median of the given data set.
- * @internal
- */
-export const median = (dataSet: number[]): number => {
-  if (Array.isArray(dataSet) && dataSet.length === 0) {
-    return 0
-  } else if (Array.isArray(dataSet) && dataSet.length === 1) {
-    return dataSet[0]
-  }
-  const sortedDataSet = dataSet.slice().sort((a, b) => a - b)
-  return (
-    (sortedDataSet[(sortedDataSet.length - 1) >> 1] + sortedDataSet[sortedDataSet.length >> 1]) / 2
-  )
-}
+export const min = (...args: number[]): number =>
+  args.reduce((minimum, num) => (minimum < num ? minimum : num), Infinity)
+
+export const max = (...args: number[]): number =>
+  args.reduce((maximum, num) => (maximum > num ? maximum : num), -Infinity)
 
 // TODO: use order statistics tree https://en.wikipedia.org/wiki/Order_statistic_tree
 export const nthPercentile = (dataSet: number[], percentile: number): number => {
@@ -70,10 +43,7 @@ export const nthPercentile = (dataSet: number[], percentile: number): number =>
  * @see https://en.wikipedia.org/wiki/Unbiased_estimation_of_standard_deviation
  * @internal
  */
-export const stdDeviation = (
-  dataSet: number[],
-  dataSetAverage: number = average(dataSet)
-): number => {
+export const stdDeviation = (dataSet: number[], dataSetAverage: number = mean(dataSet)): number => {
   if (Array.isArray(dataSet) && (dataSet.length === 0 || dataSet.length === 1)) {
     return 0
   }
index abfac5e4873c98af8a4afa225ed1568fa4c9cec8..aeed9b0fa44b4a073bff682583c95695cbb954ac 100644 (file)
@@ -1,4 +1,4 @@
-import { getRandomValues, randomBytes, randomInt, randomUUID } from 'node:crypto'
+import { getRandomValues, randomBytes, randomUUID } from 'node:crypto'
 import { env, nextTick } from 'node:process'
 
 import {
@@ -12,11 +12,11 @@ import {
   minutesToSeconds,
   secondsToMilliseconds
 } from 'date-fns'
+import { is } from 'rambda'
 
-import { Constants } from './Constants.js'
 import {
-  type EmptyObject,
-  type ProtocolResponse,
+  type JsonType,
+  MapStringifyFormat,
   type TimestampedData,
   WebSocketCloseEventStatusString
 } from '../types/index.js'
@@ -25,11 +25,13 @@ export const logPrefix = (prefixString = ''): string => {
   return `${new Date().toLocaleString()}${prefixString}`
 }
 
-export const generateUUID = (): string => {
+export const generateUUID = (): `${string}-${string}-${string}-${string}-${string}` => {
   return randomUUID()
 }
 
-export const validateUUID = (uuid: string): boolean => {
+export const validateUUID = (
+  uuid: `${string}-${string}-${string}-${string}-${string}`
+): uuid is `${string}-${string}-${string}-${string}-${string}` => {
   return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(uuid)
 }
 
@@ -89,7 +91,7 @@ export const convertToDate = (
   if (isDate(value)) {
     return value
   }
-  if (isString(value) || typeof value === 'number') {
+  if (typeof value === 'string' || typeof value === 'number') {
     const valueToDate = new Date(value)
     if (isNaN(valueToDate.getTime())) {
       throw new Error(`Cannot convert to date: '${value}'`)
@@ -109,7 +111,7 @@ export const convertToInt = (value: unknown): number => {
     return Math.trunc(value)
   }
   let changedValue: number = value as number
-  if (isString(value)) {
+  if (typeof value === 'string') {
     changedValue = parseInt(value)
   }
   if (isNaN(changedValue)) {
@@ -123,7 +125,7 @@ export const convertToFloat = (value: unknown): number => {
     return 0
   }
   let changedValue: number = value as number
-  if (isString(value)) {
+  if (typeof value === 'string') {
     changedValue = parseFloat(value)
   }
   if (isNaN(changedValue)) {
@@ -138,7 +140,7 @@ export const convertToBoolean = (value: unknown): boolean => {
     // Check the type
     if (typeof value === 'boolean') {
       return value
-    } else if (isString(value) && (value.toLowerCase() === 'true' || value === '1')) {
+    } else if (typeof value === 'string' && (value.toLowerCase() === 'true' || value === '1')) {
       result = true
     } else if (typeof value === 'number' && value === 1) {
       result = true
@@ -157,15 +159,6 @@ export const getRandomFloat = (max = Number.MAX_VALUE, min = 0): number => {
   return (randomBytes(4).readUInt32LE() / 0xffffffff) * (max - min) + min
 }
 
-export const getRandomInteger = (max = Constants.MAX_RANDOM_INTEGER, min = 0): number => {
-  max = Math.floor(max)
-  if (min !== 0) {
-    min = Math.ceil(min)
-    return Math.floor(randomInt(min, max + 1))
-  }
-  return Math.floor(randomInt(max + 1))
-}
-
 /**
  * Rounds the given number to the given scale.
  * The rounding is done using the "round half away from zero" method.
@@ -223,24 +216,11 @@ export const clone = <T>(object: T): T => {
  * @internal
  */
 export const isAsyncFunction = (fn: unknown): fn is (...args: unknown[]) => Promise<unknown> => {
-  return typeof fn === 'function' && fn.constructor.name === 'AsyncFunction'
+  return is(Function, fn) && fn.constructor.name === 'AsyncFunction'
 }
 
 export const isObject = (value: unknown): value is object => {
-  return value != null && typeof value === 'object' && !Array.isArray(value)
-}
-
-export const isEmptyObject = (object: object): object is EmptyObject => {
-  if (object.constructor !== Object) {
-    return false
-  }
-  // Iterates over the keys of an object, if
-  // any exist, return false.
-  // eslint-disable-next-line no-unreachable-loop
-  for (const _ in object) {
-    return false
-  }
-  return true
+  return value != null && !Array.isArray(value) && is(Object, value)
 }
 
 export const hasOwnProp = (value: unknown, property: PropertyKey): boolean => {
@@ -251,20 +231,8 @@ export const isCFEnvironment = (): boolean => {
   return env.VCAP_APPLICATION != null
 }
 
-const isString = (value: unknown): value is string => {
-  return typeof value === 'string'
-}
-
-export const isEmptyString = (value: unknown): value is '' | undefined | null => {
-  return value == null || (isString(value) && value.trim().length === 0)
-}
-
 export const isNotEmptyString = (value: unknown): value is string => {
-  return isString(value) && value.trim().length > 0
-}
-
-export const isEmptyArray = (value: unknown): value is never[] => {
-  return Array.isArray(value) && value.length === 0
+  return typeof value === 'string' && value.trim().length > 0
 }
 
 export const isNotEmptyArray = (value: unknown): value is unknown[] => {
@@ -296,22 +264,30 @@ export const secureRandom = (): number => {
   return getRandomValues(new Uint32Array(1))[0] / 0x100000000
 }
 
-export const JSONStringifyWithMapSupport = (
-  object:
-  | Record<string, unknown>
+export const JSONStringify = <
+  T extends
+  | JsonType
   | Array<Record<string, unknown>>
-  | Map<unknown, unknown>
-  | ProtocolResponse,
-  space?: string | number
-): string => {
+  | Set<Record<string, unknown>>
+  | Map<string, Record<string, unknown>>
+>(
+    object: T,
+    space?: string | number,
+    mapFormat?: MapStringifyFormat
+  ): string => {
   return JSON.stringify(
     object,
     (_, value: Record<string, unknown>) => {
-      if (value instanceof Map) {
-        return {
-          dataType: 'Map',
-          value: [...value]
+      if (is(Map, value)) {
+        switch (mapFormat) {
+          case MapStringifyFormat.object:
+            return { ...Object.fromEntries<Map<string, Record<string, unknown>>>(value.entries()) }
+          case MapStringifyFormat.array:
+          default:
+            return [...value]
         }
+      } else if (is(Set, value)) {
+        return [...value] as JsonType[]
       }
       return value
     },
@@ -357,28 +333,6 @@ export const isArraySorted = <T>(array: T[], compareFn: (a: T, b: T) => number):
   return true
 }
 
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export const once = <T, A extends any[], R>(
-  fn: (...args: A) => R,
-  context: T
-): ((...args: A) => R) => {
-  let result: R
-  return (...args: A) => {
-    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
-    if (fn != null) {
-      result = fn.apply<T, A, R>(context, args)
-      ;(fn as unknown as undefined) = (context as unknown as undefined) = undefined
-    }
-    return result
-  }
-}
-
-export const min = (...args: number[]): number =>
-  args.reduce((minimum, num) => (minimum < num ? minimum : num), Infinity)
-
-export const max = (...args: number[]): number =>
-  args.reduce((maximum, num) => (maximum > num ? maximum : num), -Infinity)
-
 export const throwErrorInNextTick = (error: Error): void => {
   nextTick(() => {
     throw error
index 21a650ef42d92d1d69a6b67bc1b85cfe083ce811..e1d6734c9c1fba31a6bf7eaeaad68e3e44abd913 100644 (file)
@@ -1,14 +1,14 @@
-export { ACElectricUtils, DCElectricUtils } from './ElectricUtils.js'
 export { AsyncLock, AsyncLockType } from './AsyncLock.js'
 export {
-  OutputFormat,
   buildChargingStationAutomaticTransactionGeneratorConfiguration,
   buildConnectorsStatus,
-  buildEvsesStatus
+  buildEvsesStatus,
+  OutputFormat
 } from './ChargingStationConfigurationUtils.js'
 export { CircularArray } from './CircularArray.js'
 export { Configuration } from './Configuration.js'
 export { Constants } from './Constants.js'
+export { ACElectricUtils, DCElectricUtils } from './ElectricUtils.js'
 export {
   handleFileException,
   handleSendMessageError,
@@ -17,17 +17,17 @@ export {
   setDefaultErrorParams
 } from './ErrorUtils.js'
 export { watchJsonFile } from './FileUtils.js'
+export { logger } from './Logger.js'
 export {
   buildAddedMessage,
-  buildChargingStationDataPayload,
   buildDeletedMessage,
   buildPerformanceStatisticsMessage,
   buildStartedMessage,
   buildStoppedMessage,
   buildUpdatedMessage
 } from './MessageChannelUtils.js'
+export { max, min, nthPercentile, stdDeviation } from './StatisticUtils.js'
 export {
-  JSONStringifyWithMapSupport,
   clone,
   convertToBoolean,
   convertToDate,
@@ -40,24 +40,16 @@ export {
   generateUUID,
   getRandomFloatFluctuatedRounded,
   getRandomFloatRounded,
-  getRandomInteger,
   getWebSocketCloseEventStatusString,
   isArraySorted,
   isAsyncFunction,
-  isEmptyArray,
-  isEmptyObject,
-  isEmptyString,
   isNotEmptyArray,
   isNotEmptyString,
   isValidDate,
+  JSONStringify,
   logPrefix,
-  max,
-  min,
-  once,
   roundTo,
   secureRandom,
   sleep,
   validateUUID
 } from './Utils.js'
-export { average, median, nthPercentile, stdDeviation } from './StatisticUtils.js'
-export { logger } from './Logger.js'
index 9a10e6f2afa1de4c463dd8a2803917b61f5b76b5..f6713f8976d2cdda47d74f5951880f8d1fdfd48d 100644 (file)
@@ -5,7 +5,7 @@ import type { PoolInfo } from 'poolifier'
 
 import type { SetInfo, WorkerData, WorkerOptions } from './WorkerTypes.js'
 
-export abstract class WorkerAbstract<T extends WorkerData> {
+export abstract class WorkerAbstract<D extends WorkerData, R extends WorkerData> {
   protected readonly workerScript: string
   protected readonly workerOptions: WorkerOptions
   public abstract readonly info: PoolInfo | SetInfo
@@ -39,7 +39,7 @@ export abstract class WorkerAbstract<T extends WorkerData> {
   /**
    * Starts the worker pool/set.
    */
-  public abstract start (): Promise<void>
+  public abstract start (): void | Promise<void>
   /**
    * Stops the worker pool/set.
    */
@@ -49,5 +49,5 @@ export abstract class WorkerAbstract<T extends WorkerData> {
    *
    * @param elementData -
    */
-  public abstract addElement (elementData: T): Promise<void>
+  public abstract addElement (elementData: D): Promise<R>
 }
index 404840954ddd702c32207853695764fe5379404a..ded5c89142f8e2aa9f485ed9df6ea476a7832100 100644 (file)
@@ -9,7 +9,7 @@ export const EMPTY_FUNCTION = Object.freeze(() => {
 
 export const workerSetVersion = '1.0.1'
 
-export const DEFAULT_ELEMENT_START_DELAY = 0
+export const DEFAULT_ELEMENT_ADD_DELAY = 0
 export const DEFAULT_WORKER_START_DELAY = 500
 export const DEFAULT_POOL_MIN_SIZE = Math.floor(availableParallelism() / 2)
 export const DEFAULT_POOL_MAX_SIZE = Math.round(availableParallelism() * 1.5)
@@ -17,11 +17,12 @@ export const DEFAULT_ELEMENTS_PER_WORKER = 1
 
 export const DEFAULT_WORKER_OPTIONS: WorkerOptions = Object.freeze({
   workerStartDelay: DEFAULT_WORKER_START_DELAY,
-  elementStartDelay: DEFAULT_ELEMENT_START_DELAY,
+  elementAddDelay: DEFAULT_ELEMENT_ADD_DELAY,
   poolMinSize: DEFAULT_POOL_MIN_SIZE,
   poolMaxSize: DEFAULT_POOL_MAX_SIZE,
   elementsPerWorker: DEFAULT_ELEMENTS_PER_WORKER,
   poolOptions: {
+    startWorkers: false,
     enableEvents: true,
     restartWorkerOnError: true,
     errorHandler: defaultErrorHandler,
index 817536f0237251b88af5c242fb0800914e4574d5..5217d7fe16a6772a81b946969702fe1189f0d5dc 100644 (file)
@@ -6,8 +6,11 @@ import { WorkerAbstract } from './WorkerAbstract.js'
 import type { WorkerData, WorkerOptions } from './WorkerTypes.js'
 import { randomizeDelay, sleep } from './WorkerUtils.js'
 
-export class WorkerDynamicPool extends WorkerAbstract<WorkerData> {
-  private readonly pool: DynamicThreadPool<WorkerData>
+export class WorkerDynamicPool<D extends WorkerData, R extends WorkerData> extends WorkerAbstract<
+D,
+R
+> {
+  private readonly pool: DynamicThreadPool<D, R>
 
   /**
    * Creates a new `WorkerDynamicPool`.
@@ -17,7 +20,7 @@ export class WorkerDynamicPool extends WorkerAbstract<WorkerData> {
    */
   constructor (workerScript: string, workerOptions: WorkerOptions) {
     super(workerScript, workerOptions)
-    this.pool = new DynamicThreadPool<WorkerData>(
+    this.pool = new DynamicThreadPool<D, R>(
       this.workerOptions.poolMinSize,
       this.workerOptions.poolMaxSize,
       this.workerScript,
@@ -42,8 +45,8 @@ export class WorkerDynamicPool extends WorkerAbstract<WorkerData> {
   }
 
   /** @inheritDoc */
-  public async start (): Promise<void> {
-    // This is intentional
+  public start (): void {
+    this.pool.start()
   }
 
   /** @inheritDoc */
@@ -52,12 +55,13 @@ export class WorkerDynamicPool extends WorkerAbstract<WorkerData> {
   }
 
   /** @inheritDoc */
-  public async addElement (elementData: WorkerData): Promise<void> {
-    await this.pool.execute(elementData)
+  public async addElement (elementData: D): Promise<R> {
+    const response = await this.pool.execute(elementData)
     // Start element sequentially to optimize memory at startup
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    this.workerOptions.elementStartDelay! > 0 &&
+    this.workerOptions.elementAddDelay! > 0 &&
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      (await sleep(randomizeDelay(this.workerOptions.elementStartDelay!)))
+      (await sleep(randomizeDelay(this.workerOptions.elementAddDelay!)))
+    return response
   }
 }
index 833c939f1767e832b021f152a5d2725caf221f72..73bcd56dc62467c7bb942157d88554014b8240af 100644 (file)
@@ -1,5 +1,7 @@
 import { isMainThread } from 'node:worker_threads'
 
+import { mergeDeepRight } from 'rambda'
+
 import type { WorkerAbstract } from './WorkerAbstract.js'
 import { DEFAULT_WORKER_OPTIONS } from './WorkerConstants.js'
 import { WorkerDynamicPool } from './WorkerDynamicPool.js'
@@ -13,30 +15,25 @@ export class WorkerFactory {
     // This is intentional
   }
 
-  public static getWorkerImplementation<T extends WorkerData>(
+  public static getWorkerImplementation<D extends WorkerData, R extends WorkerData>(
     workerScript: string,
     workerProcessType: WorkerProcessType,
     workerOptions?: WorkerOptions
-  ): WorkerAbstract<T> | undefined {
+  ): WorkerAbstract<D, R> {
     if (!isMainThread) {
       throw new Error('Cannot get a worker implementation outside the main thread')
     }
-    workerOptions = { ...DEFAULT_WORKER_OPTIONS, ...workerOptions }
-    let workerImplementation: WorkerAbstract<T>
+    workerOptions = mergeDeepRight<WorkerOptions>(DEFAULT_WORKER_OPTIONS, workerOptions ?? {})
     switch (workerProcessType) {
       case WorkerProcessType.workerSet:
-        workerImplementation = new WorkerSet(workerScript, workerOptions)
-        break
+        return new WorkerSet<D, R>(workerScript, workerOptions)
       case WorkerProcessType.fixedPool:
-        workerImplementation = new WorkerFixedPool(workerScript, workerOptions)
-        break
+        return new WorkerFixedPool<D, R>(workerScript, workerOptions)
       case WorkerProcessType.dynamicPool:
-        workerImplementation = new WorkerDynamicPool(workerScript, workerOptions)
-        break
+        return new WorkerDynamicPool<D, R>(workerScript, workerOptions)
       default:
         // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
         throw new Error(`Worker implementation type '${workerProcessType}' not found`)
     }
-    return workerImplementation
   }
 }
index 836690413516bf9c241cbbd7d1d352992c04eec8..96060854743112a438dc408dcc8285842e1bbd19 100644 (file)
@@ -6,8 +6,11 @@ import { WorkerAbstract } from './WorkerAbstract.js'
 import type { WorkerData, WorkerOptions } from './WorkerTypes.js'
 import { randomizeDelay, sleep } from './WorkerUtils.js'
 
-export class WorkerFixedPool extends WorkerAbstract<WorkerData> {
-  private readonly pool: FixedThreadPool<WorkerData>
+export class WorkerFixedPool<D extends WorkerData, R extends WorkerData> extends WorkerAbstract<
+D,
+R
+> {
+  private readonly pool: FixedThreadPool<D, R>
 
   /**
    * Creates a new `WorkerFixedPool`.
@@ -17,7 +20,7 @@ export class WorkerFixedPool extends WorkerAbstract<WorkerData> {
    */
   constructor (workerScript: string, workerOptions: WorkerOptions) {
     super(workerScript, workerOptions)
-    this.pool = new FixedThreadPool(
+    this.pool = new FixedThreadPool<D, R>(
       this.workerOptions.poolMaxSize,
       this.workerScript,
       this.workerOptions.poolOptions
@@ -41,8 +44,8 @@ export class WorkerFixedPool extends WorkerAbstract<WorkerData> {
   }
 
   /** @inheritDoc */
-  public async start (): Promise<void> {
-    // This is intentional
+  public start (): void {
+    this.pool.start()
   }
 
   /** @inheritDoc */
@@ -51,12 +54,13 @@ export class WorkerFixedPool extends WorkerAbstract<WorkerData> {
   }
 
   /** @inheritDoc */
-  public async addElement (elementData: WorkerData): Promise<void> {
-    await this.pool.execute(elementData)
+  public async addElement (elementData: D): Promise<R> {
+    const response = await this.pool.execute(elementData)
     // Start element sequentially to optimize memory at startup
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    this.workerOptions.elementStartDelay! > 0 &&
+    this.workerOptions.elementAddDelay! > 0 &&
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      (await sleep(randomizeDelay(this.workerOptions.elementStartDelay!)))
+      (await sleep(randomizeDelay(this.workerOptions.elementAddDelay!)))
+    return response
   }
 }
index 4a43e27b0601c44d3035c77411640cefd2fe798a..c45e6a617147edce428800d8a777178eea9b53ea 100644 (file)
@@ -1,5 +1,6 @@
 // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
 
+import { randomUUID } from 'node:crypto'
 import { EventEmitterAsyncResource } from 'node:events'
 import { SHARE_ENV, Worker } from 'node:worker_threads'
 
@@ -16,9 +17,20 @@ import {
 } from './WorkerTypes.js'
 import { randomizeDelay, sleep } from './WorkerUtils.js'
 
-export class WorkerSet extends WorkerAbstract<WorkerData> {
+interface ResponseWrapper<R extends WorkerData> {
+  resolve: (value: R | PromiseLike<R>) => void
+  reject: (reason?: unknown) => void
+  workerSetElement: WorkerSetElement
+}
+
+export class WorkerSet<D extends WorkerData, R extends WorkerData> extends WorkerAbstract<D, R> {
   public readonly emitter: EventEmitterAsyncResource | undefined
   private readonly workerSet: Set<WorkerSetElement>
+  private readonly promiseResponseMap: Map<
+    `${string}-${string}-${string}-${string}`,
+  ResponseWrapper<R>
+  >
+
   private started: boolean
   private workerStartup: boolean
 
@@ -40,6 +52,10 @@ export class WorkerSet extends WorkerAbstract<WorkerData> {
       throw new RangeError('Elements per worker must be greater than zero')
     }
     this.workerSet = new Set<WorkerSetElement>()
+    this.promiseResponseMap = new Map<
+      `${string}-${string}-${string}-${string}`,
+    ResponseWrapper<R>
+    >()
     if (this.workerOptions.poolOptions?.enableEvents === true) {
       this.emitter = new EventEmitterAsyncResource({ name: 'workerset' })
     }
@@ -99,26 +115,31 @@ export class WorkerSet extends WorkerAbstract<WorkerData> {
     this.emitter?.emit(WorkerSetEvents.stopped, this.info)
     this.started = false
     this.emitter?.emitDestroy()
-    this.emitter?.removeAllListeners()
   }
 
   /** @inheritDoc */
-  public async addElement (elementData: WorkerData): Promise<void> {
+  public async addElement (elementData: D): Promise<R> {
     if (!this.started) {
       throw new Error('Cannot add a WorkerSet element: not started')
     }
     const workerSetElement = await this.getWorkerSetElement()
-    workerSetElement.worker.postMessage({
-      event: WorkerMessageEvents.addWorkerElement,
-      data: elementData
+    const sendMessageToWorker = new Promise<R>((resolve, reject) => {
+      const message = {
+        uuid: randomUUID(),
+        event: WorkerMessageEvents.addWorkerElement,
+        data: elementData
+      } satisfies WorkerMessage<D>
+      workerSetElement.worker.postMessage(message)
+      this.promiseResponseMap.set(message.uuid, { resolve, reject, workerSetElement })
     })
-    ++workerSetElement.numberOfWorkerElements
+    const response = await sendMessageToWorker
     // Add element sequentially to optimize memory at startup
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    if (this.workerOptions.elementStartDelay! > 0) {
+    if (this.workerOptions.elementAddDelay! > 0) {
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      await sleep(randomizeDelay(this.workerOptions.elementStartDelay!))
+      await sleep(randomizeDelay(this.workerOptions.elementAddDelay!))
     }
+    return response
   }
 
   /**
@@ -131,15 +152,37 @@ export class WorkerSet extends WorkerAbstract<WorkerData> {
       ...this.workerOptions.poolOptions?.workerOptions
     })
     worker.on('message', this.workerOptions.poolOptions?.messageHandler ?? EMPTY_FUNCTION)
-    worker.on('message', (message: WorkerMessage<WorkerData>) => {
-      if (message.event === WorkerMessageEvents.addedWorkerElement) {
-        this.emitter?.emit(WorkerSetEvents.elementAdded, this.info)
-      } else if (message.event === WorkerMessageEvents.workerElementError) {
-        this.emitter?.emit(WorkerSetEvents.elementError, message.data)
+    worker.on('message', (message: WorkerMessage<R>) => {
+      const { uuid, event, data } = message
+      if (this.promiseResponseMap.has(uuid)) {
+        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+        const { resolve, reject, workerSetElement } = this.promiseResponseMap.get(uuid)!
+        switch (event) {
+          case WorkerMessageEvents.addedWorkerElement:
+            this.emitter?.emit(WorkerSetEvents.elementAdded, this.info)
+            ++workerSetElement.numberOfWorkerElements
+            resolve(data)
+            break
+          case WorkerMessageEvents.workerElementError:
+            this.emitter?.emit(WorkerSetEvents.elementError, data)
+            reject(data)
+            break
+          default:
+            reject(
+              new Error(
+                `Unknown worker message event: '${event}' received with data: '${JSON.stringify(
+                  data,
+                  undefined,
+                  2
+                )}'`
+              )
+            )
+        }
+        this.promiseResponseMap.delete(uuid)
       }
     })
     worker.on('error', this.workerOptions.poolOptions?.errorHandler ?? EMPTY_FUNCTION)
-    worker.on('error', error => {
+    worker.once('error', error => {
       this.emitter?.emit(WorkerSetEvents.error, error)
       if (
         this.workerOptions.poolOptions?.restartWorkerOnError === true &&
@@ -149,7 +192,7 @@ export class WorkerSet extends WorkerAbstract<WorkerData> {
         this.addWorkerSetElement()
       }
       worker.unref()
-      worker.terminate().catch(error => this.emitter?.emit(WorkerSetEvents.error, error))
+      worker.terminate().catch((error: unknown) => this.emitter?.emit(WorkerSetEvents.error, error))
     })
     worker.on('online', this.workerOptions.poolOptions?.onlineHandler ?? EMPTY_FUNCTION)
     worker.on('exit', this.workerOptions.poolOptions?.exitHandler ?? EMPTY_FUNCTION)
index 5c8c177fcfd4ee40e02b78ebb23818ef99ecbe9e..b180983e8a9594c3e1308fa12679a4fc2cfd4493 100644 (file)
@@ -36,7 +36,7 @@ export type WorkerEvents = PoolEvent | WorkerSetEvents
 
 export interface WorkerOptions {
   workerStartDelay?: number
-  elementStartDelay?: number
+  elementAddDelay?: number
   poolMaxSize: number
   poolMinSize: number
   elementsPerWorker?: number
@@ -45,12 +45,20 @@ export interface WorkerOptions {
 
 export type WorkerData = Record<string, unknown>
 
+export interface WorkerDataError extends WorkerData {
+  event: WorkerMessageEvents
+  name: string
+  message: string
+  stack?: string
+}
+
 export interface WorkerSetElement {
   worker: Worker
   numberOfWorkerElements: number
 }
 
 export interface WorkerMessage<T extends WorkerData> {
+  uuid: `${string}-${string}-${string}-${string}`
   event: WorkerMessageEvents
   data: T
 }
index 878ef686df5bbc3dfabc1dcbee402d54cdcb3824..190a6a41034d2738ef9b5e434cec82dc29917619 100644 (file)
@@ -1,6 +1,7 @@
 export type { WorkerAbstract } from './WorkerAbstract.js'
 export {
-  DEFAULT_ELEMENT_START_DELAY,
+  DEFAULT_ELEMENT_ADD_DELAY,
+  DEFAULT_ELEMENTS_PER_WORKER,
   DEFAULT_POOL_MAX_SIZE,
   DEFAULT_POOL_MIN_SIZE,
   DEFAULT_WORKER_START_DELAY
@@ -8,6 +9,7 @@ export {
 export { WorkerFactory } from './WorkerFactory.js'
 export {
   type WorkerData,
+  type WorkerDataError,
   WorkerEvents,
   type WorkerMessage,
   WorkerMessageEvents,
index 78af993ce08cc0b78f92903fe7080acfa551d504..b54cdee86a3d64325117dc59a4e3c6066a3f9c0c 100644 (file)
@@ -2,21 +2,23 @@ import { describe, it } from 'node:test'
 
 import { expect } from 'expect'
 
-import { average, median, nthPercentile, stdDeviation } from '../../src/utils/StatisticUtils.js'
+import { max, min, nthPercentile, stdDeviation } from '../../src/utils/StatisticUtils.js'
 
 await describe('StatisticUtils test suite', async () => {
-  await it('Verify average()', () => {
-    expect(average([])).toBe(0)
-    expect(average([0.08])).toBe(0.08)
-    expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(3.1642857142857146)
-    expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(2.8533333333333335)
+  await it('Verify min()', () => {
+    expect(min()).toBe(Infinity)
+    expect(min(0, 1)).toBe(0)
+    expect(min(1, 0)).toBe(0)
+    expect(min(0, -1)).toBe(-1)
+    expect(min(-1, 0)).toBe(-1)
   })
 
-  await it('Verify median()', () => {
-    expect(median([])).toBe(0)
-    expect(median([0.08])).toBe(0.08)
-    expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(3.05)
-    expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(2.535)
+  await it('Verify max()', () => {
+    expect(max()).toBe(-Infinity)
+    expect(max(0, 1)).toBe(1)
+    expect(max(1, 0)).toBe(1)
+    expect(max(0, -1)).toBe(0)
+    expect(max(-1, 0)).toBe(0)
   })
 
   await it('Verify nthPercentile()', () => {
index 0ed7d6b67a54eea7d96aff3ed2a40c2675a99413..5672b032187e336ba48e6a85c73a0d5b5f981a02 100644 (file)
@@ -1,3 +1,4 @@
+import { randomInt } from 'node:crypto'
 import { version } from 'node:process'
 import { describe, it } from 'node:test'
 
@@ -17,20 +18,13 @@ import {
   formatDurationSeconds,
   generateUUID,
   getRandomFloat,
-  getRandomInteger,
   hasOwnProp,
   isArraySorted,
   isAsyncFunction,
-  isEmptyArray,
-  isEmptyObject,
-  isEmptyString,
   isNotEmptyArray,
   isNotEmptyString,
   isObject,
   isValidDate,
-  max,
-  min,
-  once,
   roundTo,
   secureRandom,
   sleep,
@@ -101,7 +95,7 @@ await describe('Utils test suite', async () => {
     expect(convertToInt(undefined)).toBe(0)
     expect(convertToInt(null)).toBe(0)
     expect(convertToInt(0)).toBe(0)
-    const randomInteger = getRandomInteger()
+    const randomInteger = randomInt(Constants.MAX_RANDOM_INTEGER)
     expect(convertToInt(randomInteger)).toEqual(randomInteger)
     expect(convertToInt('-1')).toBe(-1)
     expect(convertToInt('1')).toBe(1)
@@ -163,36 +157,6 @@ await describe('Utils test suite', async () => {
     expect(random).toBeLessThan(1)
   })
 
-  await it('Verify getRandomInteger()', () => {
-    let randomInteger = getRandomInteger()
-    expect(Number.isSafeInteger(randomInteger)).toBe(true)
-    expect(randomInteger).toBeGreaterThanOrEqual(0)
-    expect(randomInteger).toBeLessThanOrEqual(Constants.MAX_RANDOM_INTEGER)
-    expect(randomInteger).not.toEqual(getRandomInteger())
-    randomInteger = getRandomInteger(0, -Constants.MAX_RANDOM_INTEGER)
-    expect(randomInteger).toBeGreaterThanOrEqual(-Constants.MAX_RANDOM_INTEGER)
-    expect(randomInteger).toBeLessThanOrEqual(0)
-    expect(() => getRandomInteger(0, 1)).toThrow(
-      'The value of "max" is out of range. It must be greater than the value of "min" (1). Received 1'
-    )
-    expect(() => getRandomInteger(-1)).toThrow(
-      'The value of "max" is out of range. It must be greater than the value of "min" (0). Received 0'
-    )
-    expect(() => getRandomInteger(Constants.MAX_RANDOM_INTEGER + 1)).toThrow(
-      `The value of "max" is out of range. It must be <= ${
-        Constants.MAX_RANDOM_INTEGER + 1
-      }. Received 281_474_976_710_656`
-    )
-    randomInteger = getRandomInteger(2, 1)
-    expect(randomInteger).toBeGreaterThanOrEqual(1)
-    expect(randomInteger).toBeLessThanOrEqual(2)
-    const maximum = 2.2
-    const minimum = 1.1
-    randomInteger = getRandomInteger(maximum, minimum)
-    expect(randomInteger).toBeLessThanOrEqual(Math.floor(maximum))
-    expect(randomInteger).toBeGreaterThanOrEqual(Math.ceil(minimum))
-  })
-
   await it('Verify roundTo()', () => {
     expect(roundTo(0, 2)).toBe(0)
     expect(roundTo(0.5, 0)).toBe(1)
@@ -375,24 +339,6 @@ await describe('Utils test suite', async () => {
     expect(hasOwnProp({ 1: '1' }, 2)).toBe(false)
   })
 
-  await it('Verify isEmptyString()', () => {
-    expect(isEmptyString('')).toBe(true)
-    expect(isEmptyString(' ')).toBe(true)
-    expect(isEmptyString('     ')).toBe(true)
-    expect(isEmptyString('test')).toBe(false)
-    expect(isEmptyString(' test')).toBe(false)
-    expect(isEmptyString('test ')).toBe(false)
-    expect(isEmptyString(undefined)).toBe(true)
-    expect(isEmptyString(null)).toBe(true)
-    expect(isEmptyString(0)).toBe(false)
-    expect(isEmptyString({})).toBe(false)
-    expect(isEmptyString([])).toBe(false)
-    expect(isEmptyString(new Map())).toBe(false)
-    expect(isEmptyString(new Set())).toBe(false)
-    expect(isEmptyString(new WeakMap())).toBe(false)
-    expect(isEmptyString(new WeakSet())).toBe(false)
-  })
-
   await it('Verify isNotEmptyString()', () => {
     expect(isNotEmptyString('')).toBe(false)
     expect(isNotEmptyString(' ')).toBe(false)
@@ -411,22 +357,6 @@ await describe('Utils test suite', async () => {
     expect(isNotEmptyString(new WeakSet())).toBe(false)
   })
 
-  await it('Verify isEmptyArray()', () => {
-    expect(isEmptyArray([])).toBe(true)
-    expect(isEmptyArray([1, 2])).toBe(false)
-    expect(isEmptyArray(['1', '2'])).toBe(false)
-    expect(isEmptyArray(undefined)).toBe(false)
-    expect(isEmptyArray(null)).toBe(false)
-    expect(isEmptyArray('')).toBe(false)
-    expect(isEmptyArray('test')).toBe(false)
-    expect(isEmptyArray(0)).toBe(false)
-    expect(isEmptyArray({})).toBe(false)
-    expect(isEmptyArray(new Map())).toBe(false)
-    expect(isEmptyArray(new Set())).toBe(false)
-    expect(isEmptyArray(new WeakMap())).toBe(false)
-    expect(isEmptyArray(new WeakSet())).toBe(false)
-  })
-
   await it('Verify isNotEmptyArray()', () => {
     expect(isNotEmptyArray([])).toBe(false)
     expect(isNotEmptyArray([1, 2])).toBe(true)
@@ -443,17 +373,6 @@ await describe('Utils test suite', async () => {
     expect(isNotEmptyArray(new WeakSet())).toBe(false)
   })
 
-  await it('Verify isEmptyObject()', () => {
-    expect(isEmptyObject({})).toBe(true)
-    expect(isEmptyObject({ 1: 1, 2: 2 })).toBe(false)
-    expect(isEmptyObject([])).toBe(false)
-    expect(isEmptyObject([1, 2])).toBe(false)
-    expect(isEmptyObject(new Map())).toBe(false)
-    expect(isEmptyObject(new Set())).toBe(false)
-    expect(isEmptyObject(new WeakMap())).toBe(false)
-    expect(isEmptyObject(new WeakSet())).toBe(false)
-  })
-
   await it('Verify isArraySorted()', () => {
     expect(
       isArraySorted([], (a, b) => {
@@ -469,35 +388,4 @@ await describe('Utils test suite', async () => {
     expect(isArraySorted<number>([1, 2, 3, 5, 4], (a, b) => a - b)).toBe(false)
     expect(isArraySorted<number>([2, 1, 3, 4, 5], (a, b) => a - b)).toBe(false)
   })
-
-  await it('Verify once()', () => {
-    let called = 0
-    const fn = (): number => ++called
-    const onceFn = once(fn, this)
-    const result1 = onceFn()
-    expect(called).toBe(1)
-    expect(result1).toBe(1)
-    const result2 = onceFn()
-    expect(called).toBe(1)
-    expect(result2).toBe(1)
-    const result3 = onceFn()
-    expect(called).toBe(1)
-    expect(result3).toBe(1)
-  })
-
-  await it('Verify min()', () => {
-    expect(min()).toBe(Infinity)
-    expect(min(0, 1)).toBe(0)
-    expect(min(1, 0)).toBe(0)
-    expect(min(0, -1)).toBe(-1)
-    expect(min(-1, 0)).toBe(-1)
-  })
-
-  await it('Verify max()', () => {
-    expect(max()).toBe(-Infinity)
-    expect(max(0, 1)).toBe(1)
-    expect(max(1, 0)).toBe(1)
-    expect(max(0, -1)).toBe(0)
-    expect(max(-1, 0)).toBe(0)
-  })
 })
index 0107ea53c898860570280bde6ad65d6e572ddd76..fc7955a36c773740f2f5213caa2acf3280a0587a 100644 (file)
@@ -1,21 +1,17 @@
 {
   "$schema": "https://json.schemastore.org/tsconfig",
   "compilerOptions": {
-    "target": "es2022",
+    "target": "ESNext",
     "module": "NodeNext",
-    "lib": ["es2022"],
+    "lib": ["ESNext"],
     "removeComments": true,
-
     "strict": true,
-
     "moduleResolution": "NodeNext",
     "resolveJsonModule": true,
     "allowSyntheticDefaultImports": true,
     "verbatimModuleSyntax": true,
-
     "experimentalDecorators": true,
     "emitDecoratorMetadata": true,
-
     "forceConsistentCasingInFileNames": true
   },
   "exclude": ["ui/web/**/*.ts"]
index 128bff5f0f6df12a76f1dc511d32b4f5b1b21b87..b6252bbc59024a4c4709eb4061f6ffbec0fdeae4 100644 (file)
@@ -9,11 +9,12 @@ module.exports = defineConfig({
     node: true
   },
 
-  plugins: ['import'],
+  plugins: ['simple-import-sort'],
 
   extends: [
     'eslint:recommended',
     'plugin:import/recommended',
+    'plugin:import/typescript',
     'plugin:vue/vue3-recommended',
     '@vue/eslint-config-typescript/recommended',
     '@vue/eslint-config-prettier'
@@ -28,20 +29,15 @@ module.exports = defineConfig({
   },
 
   parserOptions: {
+    sourceType: 'module',
     ecmaVersion: 'latest'
   },
 
   rules: {
     'no-console': env.NODE_ENV === 'production' ? 'warn' : 'off',
     'no-debugger': env.NODE_ENV === 'production' ? 'warn' : 'off',
-    'vue/require-v-for-key': 'off',
-    'vue/multi-word-component-names': 'off',
-    'sort-imports': [
-      'error',
-      {
-        ignoreDeclarationSort: true
-      }
-    ],
-    'import/order': 'error'
+    'simple-import-sort/imports': 'error',
+    'simple-import-sort/exports': 'error',
+    'vue/multi-word-component-names': 'off'
   }
 })
index 8dc9378925efa436bee08bae16efa12eb1aaf4e3..43bb9569139480ec1b1d3fd25f130666e4ec01c5 100644 (file)
@@ -1,16 +1,28 @@
+<div align="center">
+
 # Web UI
 
-The Web UI code and configuration is in the repository directory [ui/web](./../../ui/web/). Commands execution is relative to that directory.
+</div>
 
-## Project setup
+The Web UI code and configuration is in the repository directory [ui/web](./../../ui/web/). Commands execution is relative to that directory.
 
-### Dependencies
+## Table of contents
+
+- [Project setup](#project-setup)
+  - [Configuration](#configuration)
+    - [Simulator UI Server Configuration](#simulator-ui-server-configuration)
+    - [Web UI configuration](#web-ui-configuration)
+      - [Unique UI server](#unique-ui-server)
+      - [Multiple UI servers](#multiple-ui-servers)
+  - [Run](#run)
+    - [Compiles and run for production](#compiles-and-run-for-production)
+    - [Preview locally](#preview-locally)
+  - [Development](#development)
+    - [Compiles and run for development](#compiles-and-run-for-development)
+    - [Formats files](#formats-files)
+    - [Lints and fixes files](#lints-and-fixes-files)
 
-```shell
-corepack enable
-corepack prepare pnpm@latest --activate
-pnpm install
-```
+## Project setup
 
 ### Configuration
 
@@ -37,7 +49,7 @@ See [here](./../../README.md#charging-stations-simulator-configuration) for more
 
 Copy the configuration template [src/assets/config-template.json](./src/assets/config-template.json) to `public/config.json`.
 
-##### Unique UI server:
+##### Unique UI server
 
 ```json
 {
@@ -56,7 +68,7 @@ Copy the configuration template [src/assets/config-template.json](./src/assets/c
 }
 ```
 
-##### Multiple UI servers:
+##### Multiple UI servers
 
 ```json
 {
@@ -92,13 +104,13 @@ Copy the configuration template [src/assets/config-template.json](./src/assets/c
 
 ### Run
 
-#### Compiles for production and preview locally
+#### Compiles and run for production
 
 ```shell
 pnpm preview
 ```
 
-#### Try it out
+#### Preview locally
 
 You can now follow the link displayed in the terminal. The Web UI looks like the following:
 
index 94e75b397e54155d7428c119bea7719e64abf6f5..cb0a53119b0529464675b1bc4826b50fb8babe38 100644 (file)
@@ -1,24 +1,19 @@
 {
   "$schema": "https://json.schemastore.org/package",
   "name": "webui",
-  "version": "0.2.0",
+  "version": "0.3.0",
   "readme": "README.md",
   "engines": {
     "node": ">=18.0.0",
     "pnpm": ">=8.6.0"
   },
   "volta": {
-    "node": "20.11.1",
-    "pnpm": "8.15.4"
-  },
-  "pnpm": {
-    "overrides": {
-      "semver": "^7.5.3"
-    }
+    "node": "20.12.1",
+    "pnpm": "8.15.6"
   },
+  "packageManager": "pnpm@8.15.6",
   "type": "module",
   "scripts": {
-    "preinstall": "npx --yes only-allow pnpm",
     "build": "vite build",
     "preview": "pnpm build && vite preview",
     "start": "pnpm build && node start.js",
     "vue-toast-notification": "^3.1.2"
   },
   "devDependencies": {
-    "@rushstack/eslint-patch": "^1.7.2",
-    "@tsconfig/node20": "^20.1.2",
+    "@rushstack/eslint-patch": "^1.10.1",
+    "@tsconfig/node20": "^20.1.4",
     "@types/jsdom": "^21.1.6",
-    "@types/node": "^20.11.24",
-    "@typescript-eslint/eslint-plugin": "^7.1.0",
-    "@typescript-eslint/parser": "^7.1.0",
+    "@types/node": "^20.12.3",
+    "@typescript-eslint/eslint-plugin": "^7.5.0",
+    "@typescript-eslint/parser": "^7.5.0",
     "@vitejs/plugin-vue": "^5.0.4",
     "@vitejs/plugin-vue-jsx": "^3.1.0",
-    "@vitest/coverage-v8": "^1.3.1",
+    "@vitest/coverage-v8": "^1.4.0",
     "@vue/eslint-config-prettier": "^9.0.0",
-    "@vue/eslint-config-typescript": "^12.0.0",
-    "@vue/test-utils": "^2.4.4",
+    "@vue/eslint-config-typescript": "^13.0.0",
+    "@vue/test-utils": "^2.4.5",
     "@vue/tsconfig": "^0.5.1",
     "cross-env": "^7.0.3",
     "eslint": "^8.57.0",
     "eslint-define-config": "^2.1.0",
     "eslint-import-resolver-typescript": "^3.6.1",
     "eslint-plugin-import": "^2.29.1",
-    "eslint-plugin-vue": "^9.22.0",
+    "eslint-plugin-simple-import-sort": "^12.0.0",
+    "eslint-plugin-vue": "^9.24.0",
     "jsdom": "^24.0.0",
     "prettier": "^3.2.5",
     "rimraf": "^5.0.5",
-    "typescript": "~5.3.3",
-    "vite": "^5.1.4",
-    "vitest": "^1.3.1"
+    "typescript": "~5.4.3",
+    "vite": "^5.2.8",
+    "vitest": "^1.4.0"
   },
-  "_id": "webui@0.2.0"
+  "_id": "webui@0.3.0"
 }
diff --git a/ui/web/pnpm-lock.yaml b/ui/web/pnpm-lock.yaml
deleted file mode 100644 (file)
index 2304082..0000000
+++ /dev/null
@@ -1,4398 +0,0 @@
-lockfileVersion: '6.0'
-
-settings:
-  autoInstallPeers: true
-  excludeLinksFromLockfile: false
-
-overrides:
-  semver: ^7.5.3
-
-dependencies:
-  finalhandler:
-    specifier: ^1.2.0
-    version: 1.2.0
-  serve-static:
-    specifier: ^1.15.0
-    version: 1.15.0
-  vue:
-    specifier: ^3.4.21
-    version: 3.4.21(typescript@5.3.3)
-  vue-router:
-    specifier: ^4.3.0
-    version: 4.3.0(vue@3.4.21)
-  vue-toast-notification:
-    specifier: ^3.1.2
-    version: 3.1.2(vue@3.4.21)
-
-devDependencies:
-  '@rushstack/eslint-patch':
-    specifier: ^1.7.2
-    version: 1.7.2
-  '@tsconfig/node20':
-    specifier: ^20.1.2
-    version: 20.1.2
-  '@types/jsdom':
-    specifier: ^21.1.6
-    version: 21.1.6
-  '@types/node':
-    specifier: ^20.11.24
-    version: 20.11.24
-  '@typescript-eslint/eslint-plugin':
-    specifier: ^7.1.0
-    version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
-  '@typescript-eslint/parser':
-    specifier: ^7.1.0
-    version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-  '@vitejs/plugin-vue':
-    specifier: ^5.0.4
-    version: 5.0.4(vite@5.1.4)(vue@3.4.21)
-  '@vitejs/plugin-vue-jsx':
-    specifier: ^3.1.0
-    version: 3.1.0(vite@5.1.4)(vue@3.4.21)
-  '@vitest/coverage-v8':
-    specifier: ^1.3.1
-    version: 1.3.1(vitest@1.3.1)
-  '@vue/eslint-config-prettier':
-    specifier: ^9.0.0
-    version: 9.0.0(eslint@8.57.0)(prettier@3.2.5)
-  '@vue/eslint-config-typescript':
-    specifier: ^12.0.0
-    version: 12.0.0(eslint-plugin-vue@9.22.0)(eslint@8.57.0)(typescript@5.3.3)
-  '@vue/test-utils':
-    specifier: ^2.4.4
-    version: 2.4.4(vue@3.4.21)
-  '@vue/tsconfig':
-    specifier: ^0.5.1
-    version: 0.5.1
-  cross-env:
-    specifier: ^7.0.3
-    version: 7.0.3
-  eslint:
-    specifier: ^8.57.0
-    version: 8.57.0
-  eslint-define-config:
-    specifier: ^2.1.0
-    version: 2.1.0
-  eslint-import-resolver-typescript:
-    specifier: ^3.6.1
-    version: 3.6.1(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
-  eslint-plugin-import:
-    specifier: ^2.29.1
-    version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
-  eslint-plugin-vue:
-    specifier: ^9.22.0
-    version: 9.22.0(eslint@8.57.0)
-  jsdom:
-    specifier: ^24.0.0
-    version: 24.0.0
-  prettier:
-    specifier: ^3.2.5
-    version: 3.2.5
-  rimraf:
-    specifier: ^5.0.5
-    version: 5.0.5
-  typescript:
-    specifier: ~5.3.3
-    version: 5.3.3
-  vite:
-    specifier: ^5.1.4
-    version: 5.1.4(@types/node@20.11.24)
-  vitest:
-    specifier: ^1.3.1
-    version: 1.3.1(@types/node@20.11.24)(jsdom@24.0.0)
-
-packages:
-
-  /@aashutoshrathi/word-wrap@1.2.6:
-    resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /@ampproject/remapping@2.2.1:
-    resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
-    engines: {node: '>=6.0.0'}
-    dependencies:
-      '@jridgewell/gen-mapping': 0.3.4
-      '@jridgewell/trace-mapping': 0.3.23
-    dev: true
-
-  /@babel/code-frame@7.23.5:
-    resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/highlight': 7.23.4
-      chalk: 2.4.2
-    dev: true
-
-  /@babel/compat-data@7.23.5:
-    resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /@babel/core@7.24.0:
-    resolution: {integrity: sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@ampproject/remapping': 2.2.1
-      '@babel/code-frame': 7.23.5
-      '@babel/generator': 7.23.6
-      '@babel/helper-compilation-targets': 7.23.6
-      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
-      '@babel/helpers': 7.24.0
-      '@babel/parser': 7.24.0
-      '@babel/template': 7.24.0
-      '@babel/traverse': 7.24.0
-      '@babel/types': 7.24.0
-      convert-source-map: 2.0.0
-      debug: 4.3.4
-      gensync: 1.0.0-beta.2
-      json5: 2.2.3
-      semver: 7.6.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@babel/generator@7.23.6:
-    resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.24.0
-      '@jridgewell/gen-mapping': 0.3.4
-      '@jridgewell/trace-mapping': 0.3.23
-      jsesc: 2.5.2
-    dev: true
-
-  /@babel/helper-annotate-as-pure@7.22.5:
-    resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.24.0
-    dev: true
-
-  /@babel/helper-compilation-targets@7.23.6:
-    resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/compat-data': 7.23.5
-      '@babel/helper-validator-option': 7.23.5
-      browserslist: 4.23.0
-      lru-cache: 5.1.1
-      semver: 7.6.0
-    dev: true
-
-  /@babel/helper-create-class-features-plugin@7.24.0(@babel/core@7.24.0):
-    resolution: {integrity: sha512-QAH+vfvts51BCsNZ2PhY6HAggnlS6omLLFTsIpeqZk/MmJ6cW7tgz5yRv0fMJThcr6FmbMrENh1RgrWPTYA76g==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-    dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-environment-visitor': 7.22.20
-      '@babel/helper-function-name': 7.23.0
-      '@babel/helper-member-expression-to-functions': 7.23.0
-      '@babel/helper-optimise-call-expression': 7.22.5
-      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0)
-      '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
-      '@babel/helper-split-export-declaration': 7.22.6
-      semver: 7.6.0
-    dev: true
-
-  /@babel/helper-environment-visitor@7.22.20:
-    resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /@babel/helper-function-name@7.23.0:
-    resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/template': 7.24.0
-      '@babel/types': 7.24.0
-    dev: true
-
-  /@babel/helper-hoist-variables@7.22.5:
-    resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.24.0
-    dev: true
-
-  /@babel/helper-member-expression-to-functions@7.23.0:
-    resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.24.0
-    dev: true
-
-  /@babel/helper-module-imports@7.22.15:
-    resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.24.0
-    dev: true
-
-  /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-    dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-environment-visitor': 7.22.20
-      '@babel/helper-module-imports': 7.22.15
-      '@babel/helper-simple-access': 7.22.5
-      '@babel/helper-split-export-declaration': 7.22.6
-      '@babel/helper-validator-identifier': 7.22.20
-    dev: true
-
-  /@babel/helper-optimise-call-expression@7.22.5:
-    resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.24.0
-    dev: true
-
-  /@babel/helper-plugin-utils@7.24.0:
-    resolution: {integrity: sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /@babel/helper-replace-supers@7.22.20(@babel/core@7.24.0):
-    resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-    dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-environment-visitor': 7.22.20
-      '@babel/helper-member-expression-to-functions': 7.23.0
-      '@babel/helper-optimise-call-expression': 7.22.5
-    dev: true
-
-  /@babel/helper-simple-access@7.22.5:
-    resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.24.0
-    dev: true
-
-  /@babel/helper-skip-transparent-expression-wrappers@7.22.5:
-    resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.24.0
-    dev: true
-
-  /@babel/helper-split-export-declaration@7.22.6:
-    resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.24.0
-    dev: true
-
-  /@babel/helper-string-parser@7.23.4:
-    resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
-    engines: {node: '>=6.9.0'}
-
-  /@babel/helper-validator-identifier@7.22.20:
-    resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
-    engines: {node: '>=6.9.0'}
-
-  /@babel/helper-validator-option@7.23.5:
-    resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /@babel/helpers@7.24.0:
-    resolution: {integrity: sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/template': 7.24.0
-      '@babel/traverse': 7.24.0
-      '@babel/types': 7.24.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@babel/highlight@7.23.4:
-    resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/helper-validator-identifier': 7.22.20
-      chalk: 2.4.2
-      js-tokens: 4.0.0
-    dev: true
-
-  /@babel/parser@7.24.0:
-    resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
-    dependencies:
-      '@babel/types': 7.24.0
-
-  /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.24.0
-    dev: true
-
-  /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.24.0
-    dev: true
-
-  /@babel/plugin-transform-typescript@7.23.6(@babel/core@7.24.0):
-    resolution: {integrity: sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-create-class-features-plugin': 7.24.0(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.24.0
-      '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.24.0)
-    dev: true
-
-  /@babel/template@7.24.0:
-    resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/code-frame': 7.23.5
-      '@babel/parser': 7.24.0
-      '@babel/types': 7.24.0
-    dev: true
-
-  /@babel/traverse@7.24.0:
-    resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/code-frame': 7.23.5
-      '@babel/generator': 7.23.6
-      '@babel/helper-environment-visitor': 7.22.20
-      '@babel/helper-function-name': 7.23.0
-      '@babel/helper-hoist-variables': 7.22.5
-      '@babel/helper-split-export-declaration': 7.22.6
-      '@babel/parser': 7.24.0
-      '@babel/types': 7.24.0
-      debug: 4.3.4
-      globals: 11.12.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@babel/types@7.24.0:
-    resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/helper-string-parser': 7.23.4
-      '@babel/helper-validator-identifier': 7.22.20
-      to-fast-properties: 2.0.0
-
-  /@bcoe/v8-coverage@0.2.3:
-    resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
-    dev: true
-
-  /@esbuild/aix-ppc64@0.19.12:
-    resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
-    engines: {node: '>=12'}
-    cpu: [ppc64]
-    os: [aix]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/android-arm64@0.19.12:
-    resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/android-arm@0.19.12:
-    resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
-    engines: {node: '>=12'}
-    cpu: [arm]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/android-x64@0.19.12:
-    resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/darwin-arm64@0.19.12:
-    resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/darwin-x64@0.19.12:
-    resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/freebsd-arm64@0.19.12:
-    resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [freebsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/freebsd-x64@0.19.12:
-    resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [freebsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-arm64@0.19.12:
-    resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-arm@0.19.12:
-    resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
-    engines: {node: '>=12'}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-ia32@0.19.12:
-    resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
-    engines: {node: '>=12'}
-    cpu: [ia32]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-loong64@0.19.12:
-    resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
-    engines: {node: '>=12'}
-    cpu: [loong64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-mips64el@0.19.12:
-    resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
-    engines: {node: '>=12'}
-    cpu: [mips64el]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-ppc64@0.19.12:
-    resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
-    engines: {node: '>=12'}
-    cpu: [ppc64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-riscv64@0.19.12:
-    resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
-    engines: {node: '>=12'}
-    cpu: [riscv64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-s390x@0.19.12:
-    resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
-    engines: {node: '>=12'}
-    cpu: [s390x]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-x64@0.19.12:
-    resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/netbsd-x64@0.19.12:
-    resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [netbsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/openbsd-x64@0.19.12:
-    resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [openbsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/sunos-x64@0.19.12:
-    resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [sunos]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/win32-arm64@0.19.12:
-    resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/win32-ia32@0.19.12:
-    resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
-    engines: {node: '>=12'}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/win32-x64@0.19.12:
-    resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0):
-    resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    peerDependencies:
-      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
-    dependencies:
-      eslint: 8.57.0
-      eslint-visitor-keys: 3.4.3
-    dev: true
-
-  /@eslint-community/regexpp@4.10.0:
-    resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
-    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
-    dev: true
-
-  /@eslint/eslintrc@2.1.4:
-    resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    dependencies:
-      ajv: 6.12.6
-      debug: 4.3.4
-      espree: 9.6.1
-      globals: 13.24.0
-      ignore: 5.3.1
-      import-fresh: 3.3.0
-      js-yaml: 4.1.0
-      minimatch: 3.1.2
-      strip-json-comments: 3.1.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@eslint/js@8.57.0:
-    resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    dev: true
-
-  /@humanwhocodes/config-array@0.11.14:
-    resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
-    engines: {node: '>=10.10.0'}
-    dependencies:
-      '@humanwhocodes/object-schema': 2.0.2
-      debug: 4.3.4
-      minimatch: 3.1.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@humanwhocodes/module-importer@1.0.1:
-    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
-    engines: {node: '>=12.22'}
-    dev: true
-
-  /@humanwhocodes/object-schema@2.0.2:
-    resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
-    dev: true
-
-  /@isaacs/cliui@8.0.2:
-    resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
-    engines: {node: '>=12'}
-    dependencies:
-      string-width: 5.1.2
-      string-width-cjs: /string-width@4.2.3
-      strip-ansi: 7.1.0
-      strip-ansi-cjs: /strip-ansi@6.0.1
-      wrap-ansi: 8.1.0
-      wrap-ansi-cjs: /wrap-ansi@7.0.0
-    dev: true
-
-  /@istanbuljs/schema@0.1.3:
-    resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /@jest/schemas@29.6.3:
-    resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    dependencies:
-      '@sinclair/typebox': 0.27.8
-    dev: true
-
-  /@jridgewell/gen-mapping@0.3.4:
-    resolution: {integrity: sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==}
-    engines: {node: '>=6.0.0'}
-    dependencies:
-      '@jridgewell/set-array': 1.2.1
-      '@jridgewell/sourcemap-codec': 1.4.15
-      '@jridgewell/trace-mapping': 0.3.23
-    dev: true
-
-  /@jridgewell/resolve-uri@3.1.2:
-    resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
-    engines: {node: '>=6.0.0'}
-    dev: true
-
-  /@jridgewell/set-array@1.2.1:
-    resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
-    engines: {node: '>=6.0.0'}
-    dev: true
-
-  /@jridgewell/sourcemap-codec@1.4.15:
-    resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
-
-  /@jridgewell/trace-mapping@0.3.23:
-    resolution: {integrity: sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==}
-    dependencies:
-      '@jridgewell/resolve-uri': 3.1.2
-      '@jridgewell/sourcemap-codec': 1.4.15
-    dev: true
-
-  /@nodelib/fs.scandir@2.1.5:
-    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
-    engines: {node: '>= 8'}
-    dependencies:
-      '@nodelib/fs.stat': 2.0.5
-      run-parallel: 1.2.0
-    dev: true
-
-  /@nodelib/fs.stat@2.0.5:
-    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
-    engines: {node: '>= 8'}
-    dev: true
-
-  /@nodelib/fs.walk@1.2.8:
-    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
-    engines: {node: '>= 8'}
-    dependencies:
-      '@nodelib/fs.scandir': 2.1.5
-      fastq: 1.17.1
-    dev: true
-
-  /@one-ini/wasm@0.1.1:
-    resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
-    dev: true
-
-  /@pkgjs/parseargs@0.11.0:
-    resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
-    engines: {node: '>=14'}
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@pkgr/core@0.1.1:
-    resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
-    engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
-    dev: true
-
-  /@rollup/rollup-android-arm-eabi@4.12.0:
-    resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==}
-    cpu: [arm]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-android-arm64@4.12.0:
-    resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==}
-    cpu: [arm64]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-darwin-arm64@4.12.0:
-    resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-darwin-x64@4.12.0:
-    resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-linux-arm-gnueabihf@4.12.0:
-    resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-linux-arm64-gnu@4.12.0:
-    resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-linux-arm64-musl@4.12.0:
-    resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-linux-riscv64-gnu@4.12.0:
-    resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==}
-    cpu: [riscv64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-linux-x64-gnu@4.12.0:
-    resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-linux-x64-musl@4.12.0:
-    resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-win32-arm64-msvc@4.12.0:
-    resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-win32-ia32-msvc@4.12.0:
-    resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-win32-x64-msvc@4.12.0:
-    resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rushstack/eslint-patch@1.7.2:
-    resolution: {integrity: sha512-RbhOOTCNoCrbfkRyoXODZp75MlpiHMgbE5MEBZAnnnLyQNgrigEj4p0lzsMDyc1zVsJDLrivB58tgg3emX0eEA==}
-    dev: true
-
-  /@sinclair/typebox@0.27.8:
-    resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
-    dev: true
-
-  /@tsconfig/node20@20.1.2:
-    resolution: {integrity: sha512-madaWq2k+LYMEhmcp0fs+OGaLFk0OenpHa4gmI4VEmCKX4PJntQ6fnnGADVFrVkBj0wIdAlQnK/MrlYTHsa1gQ==}
-    dev: true
-
-  /@types/estree@1.0.5:
-    resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
-    dev: true
-
-  /@types/istanbul-lib-coverage@2.0.6:
-    resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
-    dev: true
-
-  /@types/jsdom@21.1.6:
-    resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==}
-    dependencies:
-      '@types/node': 20.11.24
-      '@types/tough-cookie': 4.0.5
-      parse5: 7.1.2
-    dev: true
-
-  /@types/json-schema@7.0.15:
-    resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
-    dev: true
-
-  /@types/json5@0.0.29:
-    resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
-    dev: true
-
-  /@types/node@20.11.24:
-    resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==}
-    dependencies:
-      undici-types: 5.26.5
-    dev: true
-
-  /@types/semver@7.5.8:
-    resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
-    dev: true
-
-  /@types/tough-cookie@4.0.5:
-    resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
-    dev: true
-
-  /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
-      eslint: ^7.0.0 || ^8.0.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@eslint-community/regexpp': 4.10.0
-      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/scope-manager': 6.21.0
-      '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/visitor-keys': 6.21.0
-      debug: 4.3.4
-      eslint: 8.57.0
-      graphemer: 1.4.0
-      ignore: 5.3.1
-      natural-compare: 1.4.0
-      semver: 7.6.0
-      ts-api-utils: 1.2.1(typescript@5.3.3)
-      typescript: 5.3.3
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      '@typescript-eslint/parser': ^7.0.0
-      eslint: ^8.56.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@eslint-community/regexpp': 4.10.0
-      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/scope-manager': 7.1.0
-      '@typescript-eslint/type-utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4
-      eslint: 8.57.0
-      graphemer: 1.4.0
-      ignore: 5.3.1
-      natural-compare: 1.4.0
-      semver: 7.6.0
-      ts-api-utils: 1.2.1(typescript@5.3.3)
-      typescript: 5.3.3
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      eslint: ^7.0.0 || ^8.0.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@typescript-eslint/scope-manager': 6.21.0
-      '@typescript-eslint/types': 6.21.0
-      '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3)
-      '@typescript-eslint/visitor-keys': 6.21.0
-      debug: 4.3.4
-      eslint: 8.57.0
-      typescript: 5.3.3
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      eslint: ^8.56.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@typescript-eslint/scope-manager': 7.1.0
-      '@typescript-eslint/types': 7.1.0
-      '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
-      '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4
-      eslint: 8.57.0
-      typescript: 5.3.3
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@typescript-eslint/scope-manager@6.21.0:
-    resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    dependencies:
-      '@typescript-eslint/types': 6.21.0
-      '@typescript-eslint/visitor-keys': 6.21.0
-    dev: true
-
-  /@typescript-eslint/scope-manager@7.1.0:
-    resolution: {integrity: sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    dependencies:
-      '@typescript-eslint/types': 7.1.0
-      '@typescript-eslint/visitor-keys': 7.1.0
-    dev: true
-
-  /@typescript-eslint/type-utils@6.21.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      eslint: ^7.0.0 || ^8.0.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
-      debug: 4.3.4
-      eslint: 8.57.0
-      ts-api-utils: 1.2.1(typescript@5.3.3)
-      typescript: 5.3.3
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@typescript-eslint/type-utils@7.1.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      eslint: ^8.56.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
-      '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      debug: 4.3.4
-      eslint: 8.57.0
-      ts-api-utils: 1.2.1(typescript@5.3.3)
-      typescript: 5.3.3
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@typescript-eslint/types@6.21.0:
-    resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    dev: true
-
-  /@typescript-eslint/types@7.1.0:
-    resolution: {integrity: sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    dev: true
-
-  /@typescript-eslint/typescript-estree@6.21.0(typescript@5.3.3):
-    resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@typescript-eslint/types': 6.21.0
-      '@typescript-eslint/visitor-keys': 6.21.0
-      debug: 4.3.4
-      globby: 11.1.0
-      is-glob: 4.0.3
-      minimatch: 9.0.3
-      semver: 7.6.0
-      ts-api-utils: 1.2.1(typescript@5.3.3)
-      typescript: 5.3.3
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@typescript-eslint/typescript-estree@7.1.0(typescript@5.3.3):
-    resolution: {integrity: sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@typescript-eslint/types': 7.1.0
-      '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4
-      globby: 11.1.0
-      is-glob: 4.0.3
-      minimatch: 9.0.3
-      semver: 7.6.0
-      ts-api-utils: 1.2.1(typescript@5.3.3)
-      typescript: 5.3.3
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      eslint: ^7.0.0 || ^8.0.0
-    dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
-      '@types/json-schema': 7.0.15
-      '@types/semver': 7.5.8
-      '@typescript-eslint/scope-manager': 6.21.0
-      '@typescript-eslint/types': 6.21.0
-      '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3)
-      eslint: 8.57.0
-      semver: 7.6.0
-    transitivePeerDependencies:
-      - supports-color
-      - typescript
-    dev: true
-
-  /@typescript-eslint/utils@7.1.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      eslint: ^8.56.0
-    dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
-      '@types/json-schema': 7.0.15
-      '@types/semver': 7.5.8
-      '@typescript-eslint/scope-manager': 7.1.0
-      '@typescript-eslint/types': 7.1.0
-      '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
-      eslint: 8.57.0
-      semver: 7.6.0
-    transitivePeerDependencies:
-      - supports-color
-      - typescript
-    dev: true
-
-  /@typescript-eslint/visitor-keys@6.21.0:
-    resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    dependencies:
-      '@typescript-eslint/types': 6.21.0
-      eslint-visitor-keys: 3.4.3
-    dev: true
-
-  /@typescript-eslint/visitor-keys@7.1.0:
-    resolution: {integrity: sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    dependencies:
-      '@typescript-eslint/types': 7.1.0
-      eslint-visitor-keys: 3.4.3
-    dev: true
-
-  /@ungap/structured-clone@1.2.0:
-    resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
-    dev: true
-
-  /@vitejs/plugin-vue-jsx@3.1.0(vite@5.1.4)(vue@3.4.21):
-    resolution: {integrity: sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==}
-    engines: {node: ^14.18.0 || >=16.0.0}
-    peerDependencies:
-      vite: ^4.0.0 || ^5.0.0
-      vue: ^3.0.0
-    dependencies:
-      '@babel/core': 7.24.0
-      '@babel/plugin-transform-typescript': 7.23.6(@babel/core@7.24.0)
-      '@vue/babel-plugin-jsx': 1.2.1(@babel/core@7.24.0)
-      vite: 5.1.4(@types/node@20.11.24)
-      vue: 3.4.21(typescript@5.3.3)
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@vitejs/plugin-vue@5.0.4(vite@5.1.4)(vue@3.4.21):
-    resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==}
-    engines: {node: ^18.0.0 || >=20.0.0}
-    peerDependencies:
-      vite: ^5.0.0
-      vue: ^3.2.25
-    dependencies:
-      vite: 5.1.4(@types/node@20.11.24)
-      vue: 3.4.21(typescript@5.3.3)
-    dev: true
-
-  /@vitest/coverage-v8@1.3.1(vitest@1.3.1):
-    resolution: {integrity: sha512-UuBnkSJUNE9rdHjDCPyJ4fYuMkoMtnghes1XohYa4At0MS3OQSAo97FrbwSLRshYsXThMZy1+ybD/byK5llyIg==}
-    peerDependencies:
-      vitest: 1.3.1
-    dependencies:
-      '@ampproject/remapping': 2.2.1
-      '@bcoe/v8-coverage': 0.2.3
-      debug: 4.3.4
-      istanbul-lib-coverage: 3.2.2
-      istanbul-lib-report: 3.0.1
-      istanbul-lib-source-maps: 4.0.1
-      istanbul-reports: 3.1.7
-      magic-string: 0.30.7
-      magicast: 0.3.3
-      picocolors: 1.0.0
-      std-env: 3.7.0
-      test-exclude: 6.0.0
-      v8-to-istanbul: 9.2.0
-      vitest: 1.3.1(@types/node@20.11.24)(jsdom@24.0.0)
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@vitest/expect@1.3.1:
-    resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==}
-    dependencies:
-      '@vitest/spy': 1.3.1
-      '@vitest/utils': 1.3.1
-      chai: 4.4.1
-    dev: true
-
-  /@vitest/runner@1.3.1:
-    resolution: {integrity: sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==}
-    dependencies:
-      '@vitest/utils': 1.3.1
-      p-limit: 5.0.0
-      pathe: 1.1.2
-    dev: true
-
-  /@vitest/snapshot@1.3.1:
-    resolution: {integrity: sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==}
-    dependencies:
-      magic-string: 0.30.7
-      pathe: 1.1.2
-      pretty-format: 29.7.0
-    dev: true
-
-  /@vitest/spy@1.3.1:
-    resolution: {integrity: sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==}
-    dependencies:
-      tinyspy: 2.2.1
-    dev: true
-
-  /@vitest/utils@1.3.1:
-    resolution: {integrity: sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==}
-    dependencies:
-      diff-sequences: 29.6.3
-      estree-walker: 3.0.3
-      loupe: 2.3.7
-      pretty-format: 29.7.0
-    dev: true
-
-  /@vue/babel-helper-vue-transform-on@1.2.1:
-    resolution: {integrity: sha512-jtEXim+pfyHWwvheYwUwSXm43KwQo8nhOBDyjrUITV6X2tB7lJm6n/+4sqR8137UVZZul5hBzWHdZ2uStYpyRQ==}
-    dev: true
-
-  /@vue/babel-plugin-jsx@1.2.1(@babel/core@7.24.0):
-    resolution: {integrity: sha512-Yy9qGktktXhB39QE99So/BO2Uwm/ZG+gpL9vMg51ijRRbINvgbuhyJEi4WYmGRMx/MSTfK0xjgZ3/MyY+iLCEg==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    peerDependenciesMeta:
-      '@babel/core':
-        optional: true
-    dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-module-imports': 7.22.15
-      '@babel/helper-plugin-utils': 7.24.0
-      '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0)
-      '@babel/template': 7.24.0
-      '@babel/traverse': 7.24.0
-      '@babel/types': 7.24.0
-      '@vue/babel-helper-vue-transform-on': 1.2.1
-      '@vue/babel-plugin-resolve-type': 1.2.1(@babel/core@7.24.0)
-      camelcase: 6.3.0
-      html-tags: 3.3.1
-      svg-tags: 1.0.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@vue/babel-plugin-resolve-type@1.2.1(@babel/core@7.24.0):
-    resolution: {integrity: sha512-IOtnI7pHunUzHS/y+EG/yPABIAp0VN8QhQ0UCS09jeMVxgAnI9qdOzO85RXdQGxq+aWCdv8/+k3W0aYO6j/8fQ==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/code-frame': 7.23.5
-      '@babel/core': 7.24.0
-      '@babel/helper-module-imports': 7.22.15
-      '@babel/helper-plugin-utils': 7.24.0
-      '@babel/parser': 7.24.0
-      '@vue/compiler-sfc': 3.4.21
-    dev: true
-
-  /@vue/compiler-core@3.4.21:
-    resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
-    dependencies:
-      '@babel/parser': 7.24.0
-      '@vue/shared': 3.4.21
-      entities: 4.5.0
-      estree-walker: 2.0.2
-      source-map-js: 1.0.2
-
-  /@vue/compiler-dom@3.4.21:
-    resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==}
-    dependencies:
-      '@vue/compiler-core': 3.4.21
-      '@vue/shared': 3.4.21
-
-  /@vue/compiler-sfc@3.4.21:
-    resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==}
-    dependencies:
-      '@babel/parser': 7.24.0
-      '@vue/compiler-core': 3.4.21
-      '@vue/compiler-dom': 3.4.21
-      '@vue/compiler-ssr': 3.4.21
-      '@vue/shared': 3.4.21
-      estree-walker: 2.0.2
-      magic-string: 0.30.7
-      postcss: 8.4.35
-      source-map-js: 1.0.2
-
-  /@vue/compiler-ssr@3.4.21:
-    resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==}
-    dependencies:
-      '@vue/compiler-dom': 3.4.21
-      '@vue/shared': 3.4.21
-
-  /@vue/devtools-api@6.6.1:
-    resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
-    dev: false
-
-  /@vue/eslint-config-prettier@9.0.0(eslint@8.57.0)(prettier@3.2.5):
-    resolution: {integrity: sha512-z1ZIAAUS9pKzo/ANEfd2sO+v2IUalz7cM/cTLOZ7vRFOPk5/xuRKQteOu1DErFLAh/lYGXMVZ0IfYKlyInuDVg==}
-    peerDependencies:
-      eslint: '>= 8.0.0'
-      prettier: '>= 3.0.0'
-    dependencies:
-      eslint: 8.57.0
-      eslint-config-prettier: 9.1.0(eslint@8.57.0)
-      eslint-plugin-prettier: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5)
-      prettier: 3.2.5
-    transitivePeerDependencies:
-      - '@types/eslint'
-    dev: true
-
-  /@vue/eslint-config-typescript@12.0.0(eslint-plugin-vue@9.22.0)(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-StxLFet2Qe97T8+7L8pGlhYBBr8Eg05LPuTDVopQV6il+SK6qqom59BA/rcFipUef2jD8P2X44Vd8tMFytfvlg==}
-    engines: {node: ^14.17.0 || >=16.0.0}
-    peerDependencies:
-      eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
-      eslint-plugin-vue: ^9.0.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.3.3)
-      eslint: 8.57.0
-      eslint-plugin-vue: 9.22.0(eslint@8.57.0)
-      typescript: 5.3.3
-      vue-eslint-parser: 9.4.2(eslint@8.57.0)
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@vue/reactivity@3.4.21:
-    resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
-    dependencies:
-      '@vue/shared': 3.4.21
-
-  /@vue/runtime-core@3.4.21:
-    resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==}
-    dependencies:
-      '@vue/reactivity': 3.4.21
-      '@vue/shared': 3.4.21
-
-  /@vue/runtime-dom@3.4.21:
-    resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==}
-    dependencies:
-      '@vue/runtime-core': 3.4.21
-      '@vue/shared': 3.4.21
-      csstype: 3.1.3
-
-  /@vue/server-renderer@3.4.21(vue@3.4.21):
-    resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==}
-    peerDependencies:
-      vue: 3.4.21
-    dependencies:
-      '@vue/compiler-ssr': 3.4.21
-      '@vue/shared': 3.4.21
-      vue: 3.4.21(typescript@5.3.3)
-
-  /@vue/shared@3.4.21:
-    resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==}
-
-  /@vue/test-utils@2.4.4(vue@3.4.21):
-    resolution: {integrity: sha512-8jkRxz8pNhClAf4Co4ZrpAoFISdvT3nuSkUlY6Ys6rmTpw3DMWG/X3mw3gQ7QJzgCZO9f+zuE2kW57fi09MW7Q==}
-    peerDependencies:
-      '@vue/server-renderer': ^3.0.1
-      vue: ^3.0.1
-    peerDependenciesMeta:
-      '@vue/server-renderer':
-        optional: true
-    dependencies:
-      js-beautify: 1.15.1
-      vue: 3.4.21(typescript@5.3.3)
-      vue-component-type-helpers: 1.8.27
-    dev: true
-
-  /@vue/tsconfig@0.5.1:
-    resolution: {integrity: sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==}
-    dev: true
-
-  /abbrev@2.0.0:
-    resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
-    dev: true
-
-  /acorn-jsx@5.3.2(acorn@8.11.3):
-    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
-    peerDependencies:
-      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
-    dependencies:
-      acorn: 8.11.3
-    dev: true
-
-  /acorn-walk@8.3.2:
-    resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
-    engines: {node: '>=0.4.0'}
-    dev: true
-
-  /acorn@8.11.3:
-    resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
-    engines: {node: '>=0.4.0'}
-    hasBin: true
-    dev: true
-
-  /agent-base@7.1.0:
-    resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
-    engines: {node: '>= 14'}
-    dependencies:
-      debug: 4.3.4
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /ajv@6.12.6:
-    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
-    dependencies:
-      fast-deep-equal: 3.1.3
-      fast-json-stable-stringify: 2.1.0
-      json-schema-traverse: 0.4.1
-      uri-js: 4.4.1
-    dev: true
-
-  /ansi-regex@5.0.1:
-    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /ansi-regex@6.0.1:
-    resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /ansi-styles@3.2.1:
-    resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
-    engines: {node: '>=4'}
-    dependencies:
-      color-convert: 1.9.3
-    dev: true
-
-  /ansi-styles@4.3.0:
-    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
-    engines: {node: '>=8'}
-    dependencies:
-      color-convert: 2.0.1
-    dev: true
-
-  /ansi-styles@5.2.0:
-    resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /ansi-styles@6.2.1:
-    resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /argparse@2.0.1:
-    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
-    dev: true
-
-  /array-buffer-byte-length@1.0.1:
-    resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      is-array-buffer: 3.0.4
-    dev: true
-
-  /array-includes@3.1.7:
-    resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-      get-intrinsic: 1.2.4
-      is-string: 1.0.7
-    dev: true
-
-  /array-union@2.1.0:
-    resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /array.prototype.filter@1.0.3:
-    resolution: {integrity: sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-      es-array-method-boxes-properly: 1.0.0
-      is-string: 1.0.7
-    dev: true
-
-  /array.prototype.findlastindex@1.2.4:
-    resolution: {integrity: sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-      es-errors: 1.3.0
-      es-shim-unscopables: 1.0.2
-    dev: true
-
-  /array.prototype.flat@1.3.2:
-    resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-      es-shim-unscopables: 1.0.2
-    dev: true
-
-  /array.prototype.flatmap@1.3.2:
-    resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-      es-shim-unscopables: 1.0.2
-    dev: true
-
-  /arraybuffer.prototype.slice@1.0.3:
-    resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      array-buffer-byte-length: 1.0.1
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-      es-errors: 1.3.0
-      get-intrinsic: 1.2.4
-      is-array-buffer: 3.0.4
-      is-shared-array-buffer: 1.0.3
-    dev: true
-
-  /assertion-error@1.1.0:
-    resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
-    dev: true
-
-  /asynckit@0.4.0:
-    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
-    dev: true
-
-  /available-typed-arrays@1.0.7:
-    resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      possible-typed-array-names: 1.0.0
-    dev: true
-
-  /balanced-match@1.0.2:
-    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
-    dev: true
-
-  /boolbase@1.0.0:
-    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
-    dev: true
-
-  /brace-expansion@1.1.11:
-    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
-    dependencies:
-      balanced-match: 1.0.2
-      concat-map: 0.0.1
-    dev: true
-
-  /brace-expansion@2.0.1:
-    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
-    dependencies:
-      balanced-match: 1.0.2
-    dev: true
-
-  /braces@3.0.2:
-    resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
-    engines: {node: '>=8'}
-    dependencies:
-      fill-range: 7.0.1
-    dev: true
-
-  /browserslist@4.23.0:
-    resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
-    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
-    hasBin: true
-    dependencies:
-      caniuse-lite: 1.0.30001591
-      electron-to-chromium: 1.4.687
-      node-releases: 2.0.14
-      update-browserslist-db: 1.0.13(browserslist@4.23.0)
-    dev: true
-
-  /cac@6.7.14:
-    resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /call-bind@1.0.7:
-    resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      es-define-property: 1.0.0
-      es-errors: 1.3.0
-      function-bind: 1.1.2
-      get-intrinsic: 1.2.4
-      set-function-length: 1.2.1
-    dev: true
-
-  /callsites@3.1.0:
-    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /camelcase@6.3.0:
-    resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /caniuse-lite@1.0.30001591:
-    resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==}
-    dev: true
-
-  /chai@4.4.1:
-    resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==}
-    engines: {node: '>=4'}
-    dependencies:
-      assertion-error: 1.1.0
-      check-error: 1.0.3
-      deep-eql: 4.1.3
-      get-func-name: 2.0.2
-      loupe: 2.3.7
-      pathval: 1.1.1
-      type-detect: 4.0.8
-    dev: true
-
-  /chalk@2.4.2:
-    resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
-    engines: {node: '>=4'}
-    dependencies:
-      ansi-styles: 3.2.1
-      escape-string-regexp: 1.0.5
-      supports-color: 5.5.0
-    dev: true
-
-  /chalk@4.1.2:
-    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
-    engines: {node: '>=10'}
-    dependencies:
-      ansi-styles: 4.3.0
-      supports-color: 7.2.0
-    dev: true
-
-  /check-error@1.0.3:
-    resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
-    dependencies:
-      get-func-name: 2.0.2
-    dev: true
-
-  /color-convert@1.9.3:
-    resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
-    dependencies:
-      color-name: 1.1.3
-    dev: true
-
-  /color-convert@2.0.1:
-    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
-    engines: {node: '>=7.0.0'}
-    dependencies:
-      color-name: 1.1.4
-    dev: true
-
-  /color-name@1.1.3:
-    resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
-    dev: true
-
-  /color-name@1.1.4:
-    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
-    dev: true
-
-  /combined-stream@1.0.8:
-    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
-    engines: {node: '>= 0.8'}
-    dependencies:
-      delayed-stream: 1.0.0
-    dev: true
-
-  /commander@10.0.1:
-    resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
-    engines: {node: '>=14'}
-    dev: true
-
-  /concat-map@0.0.1:
-    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
-    dev: true
-
-  /config-chain@1.1.13:
-    resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
-    dependencies:
-      ini: 1.3.8
-      proto-list: 1.2.4
-    dev: true
-
-  /convert-source-map@2.0.0:
-    resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
-    dev: true
-
-  /cross-env@7.0.3:
-    resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
-    engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
-    hasBin: true
-    dependencies:
-      cross-spawn: 7.0.3
-    dev: true
-
-  /cross-spawn@7.0.3:
-    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
-    engines: {node: '>= 8'}
-    dependencies:
-      path-key: 3.1.1
-      shebang-command: 2.0.0
-      which: 2.0.2
-    dev: true
-
-  /cssesc@3.0.0:
-    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
-    engines: {node: '>=4'}
-    hasBin: true
-    dev: true
-
-  /cssstyle@4.0.1:
-    resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==}
-    engines: {node: '>=18'}
-    dependencies:
-      rrweb-cssom: 0.6.0
-    dev: true
-
-  /csstype@3.1.3:
-    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
-
-  /data-urls@5.0.0:
-    resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
-    engines: {node: '>=18'}
-    dependencies:
-      whatwg-mimetype: 4.0.0
-      whatwg-url: 14.0.0
-    dev: true
-
-  /debug@2.6.9:
-    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-    dependencies:
-      ms: 2.0.0
-    dev: false
-
-  /debug@3.2.7:
-    resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-    dependencies:
-      ms: 2.1.3
-    dev: true
-
-  /debug@4.3.4:
-    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
-    engines: {node: '>=6.0'}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-    dependencies:
-      ms: 2.1.2
-    dev: true
-
-  /decimal.js@10.4.3:
-    resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
-    dev: true
-
-  /deep-eql@4.1.3:
-    resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
-    engines: {node: '>=6'}
-    dependencies:
-      type-detect: 4.0.8
-    dev: true
-
-  /deep-is@0.1.4:
-    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
-    dev: true
-
-  /define-data-property@1.1.4:
-    resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      es-define-property: 1.0.0
-      es-errors: 1.3.0
-      gopd: 1.0.1
-    dev: true
-
-  /define-properties@1.2.1:
-    resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      define-data-property: 1.1.4
-      has-property-descriptors: 1.0.2
-      object-keys: 1.1.1
-    dev: true
-
-  /delayed-stream@1.0.0:
-    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
-    engines: {node: '>=0.4.0'}
-    dev: true
-
-  /depd@2.0.0:
-    resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
-    engines: {node: '>= 0.8'}
-    dev: false
-
-  /destroy@1.2.0:
-    resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
-    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
-    dev: false
-
-  /diff-sequences@29.6.3:
-    resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    dev: true
-
-  /dir-glob@3.0.1:
-    resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
-    engines: {node: '>=8'}
-    dependencies:
-      path-type: 4.0.0
-    dev: true
-
-  /doctrine@2.1.0:
-    resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      esutils: 2.0.3
-    dev: true
-
-  /doctrine@3.0.0:
-    resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
-    engines: {node: '>=6.0.0'}
-    dependencies:
-      esutils: 2.0.3
-    dev: true
-
-  /eastasianwidth@0.2.0:
-    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
-    dev: true
-
-  /editorconfig@1.0.4:
-    resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
-    engines: {node: '>=14'}
-    hasBin: true
-    dependencies:
-      '@one-ini/wasm': 0.1.1
-      commander: 10.0.1
-      minimatch: 9.0.1
-      semver: 7.6.0
-    dev: true
-
-  /ee-first@1.1.1:
-    resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
-    dev: false
-
-  /electron-to-chromium@1.4.687:
-    resolution: {integrity: sha512-Ic85cOuXSP6h7KM0AIJ2hpJ98Bo4hyTUjc4yjMbkvD+8yTxEhfK9+8exT2KKYsSjnCn2tGsKVSZwE7ZgTORQCw==}
-    dev: true
-
-  /emoji-regex@8.0.0:
-    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
-    dev: true
-
-  /emoji-regex@9.2.2:
-    resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
-    dev: true
-
-  /encodeurl@1.0.2:
-    resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
-    engines: {node: '>= 0.8'}
-    dev: false
-
-  /enhanced-resolve@5.15.1:
-    resolution: {integrity: sha512-3d3JRbwsCLJsYgvb6NuWEG44jjPSOMuS73L/6+7BZuoKm3W+qXnSoIYVHi8dG7Qcg4inAY4jbzkZ7MnskePeDg==}
-    engines: {node: '>=10.13.0'}
-    dependencies:
-      graceful-fs: 4.2.11
-      tapable: 2.2.1
-    dev: true
-
-  /entities@4.5.0:
-    resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
-    engines: {node: '>=0.12'}
-
-  /es-abstract@1.22.5:
-    resolution: {integrity: sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      array-buffer-byte-length: 1.0.1
-      arraybuffer.prototype.slice: 1.0.3
-      available-typed-arrays: 1.0.7
-      call-bind: 1.0.7
-      es-define-property: 1.0.0
-      es-errors: 1.3.0
-      es-set-tostringtag: 2.0.3
-      es-to-primitive: 1.2.1
-      function.prototype.name: 1.1.6
-      get-intrinsic: 1.2.4
-      get-symbol-description: 1.0.2
-      globalthis: 1.0.3
-      gopd: 1.0.1
-      has-property-descriptors: 1.0.2
-      has-proto: 1.0.3
-      has-symbols: 1.0.3
-      hasown: 2.0.1
-      internal-slot: 1.0.7
-      is-array-buffer: 3.0.4
-      is-callable: 1.2.7
-      is-negative-zero: 2.0.3
-      is-regex: 1.1.4
-      is-shared-array-buffer: 1.0.3
-      is-string: 1.0.7
-      is-typed-array: 1.1.13
-      is-weakref: 1.0.2
-      object-inspect: 1.13.1
-      object-keys: 1.1.1
-      object.assign: 4.1.5
-      regexp.prototype.flags: 1.5.2
-      safe-array-concat: 1.1.0
-      safe-regex-test: 1.0.3
-      string.prototype.trim: 1.2.8
-      string.prototype.trimend: 1.0.7
-      string.prototype.trimstart: 1.0.7
-      typed-array-buffer: 1.0.2
-      typed-array-byte-length: 1.0.1
-      typed-array-byte-offset: 1.0.2
-      typed-array-length: 1.0.5
-      unbox-primitive: 1.0.2
-      which-typed-array: 1.1.14
-    dev: true
-
-  /es-array-method-boxes-properly@1.0.0:
-    resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==}
-    dev: true
-
-  /es-define-property@1.0.0:
-    resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      get-intrinsic: 1.2.4
-    dev: true
-
-  /es-errors@1.3.0:
-    resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
-    engines: {node: '>= 0.4'}
-    dev: true
-
-  /es-set-tostringtag@2.0.3:
-    resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      get-intrinsic: 1.2.4
-      has-tostringtag: 1.0.2
-      hasown: 2.0.1
-    dev: true
-
-  /es-shim-unscopables@1.0.2:
-    resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
-    dependencies:
-      hasown: 2.0.1
-    dev: true
-
-  /es-to-primitive@1.2.1:
-    resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      is-callable: 1.2.7
-      is-date-object: 1.0.5
-      is-symbol: 1.0.4
-    dev: true
-
-  /esbuild@0.19.12:
-    resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
-    engines: {node: '>=12'}
-    hasBin: true
-    requiresBuild: true
-    optionalDependencies:
-      '@esbuild/aix-ppc64': 0.19.12
-      '@esbuild/android-arm': 0.19.12
-      '@esbuild/android-arm64': 0.19.12
-      '@esbuild/android-x64': 0.19.12
-      '@esbuild/darwin-arm64': 0.19.12
-      '@esbuild/darwin-x64': 0.19.12
-      '@esbuild/freebsd-arm64': 0.19.12
-      '@esbuild/freebsd-x64': 0.19.12
-      '@esbuild/linux-arm': 0.19.12
-      '@esbuild/linux-arm64': 0.19.12
-      '@esbuild/linux-ia32': 0.19.12
-      '@esbuild/linux-loong64': 0.19.12
-      '@esbuild/linux-mips64el': 0.19.12
-      '@esbuild/linux-ppc64': 0.19.12
-      '@esbuild/linux-riscv64': 0.19.12
-      '@esbuild/linux-s390x': 0.19.12
-      '@esbuild/linux-x64': 0.19.12
-      '@esbuild/netbsd-x64': 0.19.12
-      '@esbuild/openbsd-x64': 0.19.12
-      '@esbuild/sunos-x64': 0.19.12
-      '@esbuild/win32-arm64': 0.19.12
-      '@esbuild/win32-ia32': 0.19.12
-      '@esbuild/win32-x64': 0.19.12
-    dev: true
-
-  /escalade@3.1.2:
-    resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /escape-html@1.0.3:
-    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
-    dev: false
-
-  /escape-string-regexp@1.0.5:
-    resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
-    engines: {node: '>=0.8.0'}
-    dev: true
-
-  /escape-string-regexp@4.0.0:
-    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /eslint-config-prettier@9.1.0(eslint@8.57.0):
-    resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==}
-    hasBin: true
-    peerDependencies:
-      eslint: '>=7.0.0'
-    dependencies:
-      eslint: 8.57.0
-    dev: true
-
-  /eslint-define-config@2.1.0:
-    resolution: {integrity: sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ==}
-    engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>=8.6.0'}
-    dev: true
-
-  /eslint-import-resolver-node@0.3.9:
-    resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
-    dependencies:
-      debug: 3.2.7
-      is-core-module: 2.13.1
-      resolve: 1.22.8
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
-    resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==}
-    engines: {node: ^14.18.0 || >=16.0.0}
-    peerDependencies:
-      eslint: '*'
-      eslint-plugin-import: '*'
-    dependencies:
-      debug: 4.3.4
-      enhanced-resolve: 5.15.1
-      eslint: 8.57.0
-      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
-      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
-      fast-glob: 3.3.2
-      get-tsconfig: 4.7.2
-      is-core-module: 2.13.1
-      is-glob: 4.0.3
-    transitivePeerDependencies:
-      - '@typescript-eslint/parser'
-      - eslint-import-resolver-node
-      - eslint-import-resolver-webpack
-      - supports-color
-    dev: true
-
-  /eslint-module-utils@2.8.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
-    resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==}
-    engines: {node: '>=4'}
-    peerDependencies:
-      '@typescript-eslint/parser': '*'
-      eslint: '*'
-      eslint-import-resolver-node: '*'
-      eslint-import-resolver-typescript: '*'
-      eslint-import-resolver-webpack: '*'
-    peerDependenciesMeta:
-      '@typescript-eslint/parser':
-        optional: true
-      eslint:
-        optional: true
-      eslint-import-resolver-node:
-        optional: true
-      eslint-import-resolver-typescript:
-        optional: true
-      eslint-import-resolver-webpack:
-        optional: true
-    dependencies:
-      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      debug: 3.2.7
-      eslint: 8.57.0
-      eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
-    resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
-    engines: {node: '>=4'}
-    peerDependencies:
-      '@typescript-eslint/parser': '*'
-      eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
-    peerDependenciesMeta:
-      '@typescript-eslint/parser':
-        optional: true
-    dependencies:
-      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      array-includes: 3.1.7
-      array.prototype.findlastindex: 1.2.4
-      array.prototype.flat: 1.3.2
-      array.prototype.flatmap: 1.3.2
-      debug: 3.2.7
-      doctrine: 2.1.0
-      eslint: 8.57.0
-      eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
-      hasown: 2.0.1
-      is-core-module: 2.13.1
-      is-glob: 4.0.3
-      minimatch: 3.1.2
-      object.fromentries: 2.0.7
-      object.groupby: 1.0.2
-      object.values: 1.1.7
-      semver: 7.6.0
-      tsconfig-paths: 3.15.0
-    transitivePeerDependencies:
-      - eslint-import-resolver-typescript
-      - eslint-import-resolver-webpack
-      - supports-color
-    dev: true
-
-  /eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5):
-    resolution: {integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==}
-    engines: {node: ^14.18.0 || >=16.0.0}
-    peerDependencies:
-      '@types/eslint': '>=8.0.0'
-      eslint: '>=8.0.0'
-      eslint-config-prettier: '*'
-      prettier: '>=3.0.0'
-    peerDependenciesMeta:
-      '@types/eslint':
-        optional: true
-      eslint-config-prettier:
-        optional: true
-    dependencies:
-      eslint: 8.57.0
-      eslint-config-prettier: 9.1.0(eslint@8.57.0)
-      prettier: 3.2.5
-      prettier-linter-helpers: 1.0.0
-      synckit: 0.8.8
-    dev: true
-
-  /eslint-plugin-vue@9.22.0(eslint@8.57.0):
-    resolution: {integrity: sha512-7wCXv5zuVnBtZE/74z4yZ0CM8AjH6bk4MQGm7hZjUC2DBppKU5ioeOk5LGSg/s9a1ZJnIsdPLJpXnu1Rc+cVHg==}
-    engines: {node: ^14.17.0 || >=16.0.0}
-    peerDependencies:
-      eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
-    dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
-      eslint: 8.57.0
-      natural-compare: 1.4.0
-      nth-check: 2.1.1
-      postcss-selector-parser: 6.0.15
-      semver: 7.6.0
-      vue-eslint-parser: 9.4.2(eslint@8.57.0)
-      xml-name-validator: 4.0.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /eslint-scope@7.2.2:
-    resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    dependencies:
-      esrecurse: 4.3.0
-      estraverse: 5.3.0
-    dev: true
-
-  /eslint-visitor-keys@3.4.3:
-    resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    dev: true
-
-  /eslint@8.57.0:
-    resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    hasBin: true
-    dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
-      '@eslint-community/regexpp': 4.10.0
-      '@eslint/eslintrc': 2.1.4
-      '@eslint/js': 8.57.0
-      '@humanwhocodes/config-array': 0.11.14
-      '@humanwhocodes/module-importer': 1.0.1
-      '@nodelib/fs.walk': 1.2.8
-      '@ungap/structured-clone': 1.2.0
-      ajv: 6.12.6
-      chalk: 4.1.2
-      cross-spawn: 7.0.3
-      debug: 4.3.4
-      doctrine: 3.0.0
-      escape-string-regexp: 4.0.0
-      eslint-scope: 7.2.2
-      eslint-visitor-keys: 3.4.3
-      espree: 9.6.1
-      esquery: 1.5.0
-      esutils: 2.0.3
-      fast-deep-equal: 3.1.3
-      file-entry-cache: 6.0.1
-      find-up: 5.0.0
-      glob-parent: 6.0.2
-      globals: 13.24.0
-      graphemer: 1.4.0
-      ignore: 5.3.1
-      imurmurhash: 0.1.4
-      is-glob: 4.0.3
-      is-path-inside: 3.0.3
-      js-yaml: 4.1.0
-      json-stable-stringify-without-jsonify: 1.0.1
-      levn: 0.4.1
-      lodash.merge: 4.6.2
-      minimatch: 3.1.2
-      natural-compare: 1.4.0
-      optionator: 0.9.3
-      strip-ansi: 6.0.1
-      text-table: 0.2.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /espree@9.6.1:
-    resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    dependencies:
-      acorn: 8.11.3
-      acorn-jsx: 5.3.2(acorn@8.11.3)
-      eslint-visitor-keys: 3.4.3
-    dev: true
-
-  /esquery@1.5.0:
-    resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
-    engines: {node: '>=0.10'}
-    dependencies:
-      estraverse: 5.3.0
-    dev: true
-
-  /esrecurse@4.3.0:
-    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
-    engines: {node: '>=4.0'}
-    dependencies:
-      estraverse: 5.3.0
-    dev: true
-
-  /estraverse@5.3.0:
-    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
-    engines: {node: '>=4.0'}
-    dev: true
-
-  /estree-walker@2.0.2:
-    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
-
-  /estree-walker@3.0.3:
-    resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
-    dependencies:
-      '@types/estree': 1.0.5
-    dev: true
-
-  /esutils@2.0.3:
-    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /etag@1.8.1:
-    resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
-    engines: {node: '>= 0.6'}
-    dev: false
-
-  /execa@8.0.1:
-    resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
-    engines: {node: '>=16.17'}
-    dependencies:
-      cross-spawn: 7.0.3
-      get-stream: 8.0.1
-      human-signals: 5.0.0
-      is-stream: 3.0.0
-      merge-stream: 2.0.0
-      npm-run-path: 5.3.0
-      onetime: 6.0.0
-      signal-exit: 4.1.0
-      strip-final-newline: 3.0.0
-    dev: true
-
-  /fast-deep-equal@3.1.3:
-    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
-    dev: true
-
-  /fast-diff@1.3.0:
-    resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
-    dev: true
-
-  /fast-glob@3.3.2:
-    resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
-    engines: {node: '>=8.6.0'}
-    dependencies:
-      '@nodelib/fs.stat': 2.0.5
-      '@nodelib/fs.walk': 1.2.8
-      glob-parent: 5.1.2
-      merge2: 1.4.1
-      micromatch: 4.0.5
-    dev: true
-
-  /fast-json-stable-stringify@2.1.0:
-    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
-    dev: true
-
-  /fast-levenshtein@2.0.6:
-    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
-    dev: true
-
-  /fastq@1.17.1:
-    resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
-    dependencies:
-      reusify: 1.0.4
-    dev: true
-
-  /file-entry-cache@6.0.1:
-    resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
-    engines: {node: ^10.12.0 || >=12.0.0}
-    dependencies:
-      flat-cache: 3.2.0
-    dev: true
-
-  /fill-range@7.0.1:
-    resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      to-regex-range: 5.0.1
-    dev: true
-
-  /finalhandler@1.2.0:
-    resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==}
-    engines: {node: '>= 0.8'}
-    dependencies:
-      debug: 2.6.9
-      encodeurl: 1.0.2
-      escape-html: 1.0.3
-      on-finished: 2.4.1
-      parseurl: 1.3.3
-      statuses: 2.0.1
-      unpipe: 1.0.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: false
-
-  /find-up@5.0.0:
-    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
-    engines: {node: '>=10'}
-    dependencies:
-      locate-path: 6.0.0
-      path-exists: 4.0.0
-    dev: true
-
-  /flat-cache@3.2.0:
-    resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
-    engines: {node: ^10.12.0 || >=12.0.0}
-    dependencies:
-      flatted: 3.3.1
-      keyv: 4.5.4
-      rimraf: 3.0.2
-    dev: true
-
-  /flatted@3.3.1:
-    resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
-    dev: true
-
-  /for-each@0.3.3:
-    resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
-    dependencies:
-      is-callable: 1.2.7
-    dev: true
-
-  /foreground-child@3.1.1:
-    resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
-    engines: {node: '>=14'}
-    dependencies:
-      cross-spawn: 7.0.3
-      signal-exit: 4.1.0
-    dev: true
-
-  /form-data@4.0.0:
-    resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
-    engines: {node: '>= 6'}
-    dependencies:
-      asynckit: 0.4.0
-      combined-stream: 1.0.8
-      mime-types: 2.1.35
-    dev: true
-
-  /fresh@0.5.2:
-    resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
-    engines: {node: '>= 0.6'}
-    dev: false
-
-  /fs.realpath@1.0.0:
-    resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
-    dev: true
-
-  /fsevents@2.3.3:
-    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
-    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /function-bind@1.1.2:
-    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
-    dev: true
-
-  /function.prototype.name@1.1.6:
-    resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-      functions-have-names: 1.2.3
-    dev: true
-
-  /functions-have-names@1.2.3:
-    resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
-    dev: true
-
-  /gensync@1.0.0-beta.2:
-    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /get-func-name@2.0.2:
-    resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
-    dev: true
-
-  /get-intrinsic@1.2.4:
-    resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      es-errors: 1.3.0
-      function-bind: 1.1.2
-      has-proto: 1.0.3
-      has-symbols: 1.0.3
-      hasown: 2.0.1
-    dev: true
-
-  /get-stream@8.0.1:
-    resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
-    engines: {node: '>=16'}
-    dev: true
-
-  /get-symbol-description@1.0.2:
-    resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      es-errors: 1.3.0
-      get-intrinsic: 1.2.4
-    dev: true
-
-  /get-tsconfig@4.7.2:
-    resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==}
-    dependencies:
-      resolve-pkg-maps: 1.0.0
-    dev: true
-
-  /glob-parent@5.1.2:
-    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
-    engines: {node: '>= 6'}
-    dependencies:
-      is-glob: 4.0.3
-    dev: true
-
-  /glob-parent@6.0.2:
-    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
-    engines: {node: '>=10.13.0'}
-    dependencies:
-      is-glob: 4.0.3
-    dev: true
-
-  /glob@10.3.10:
-    resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    hasBin: true
-    dependencies:
-      foreground-child: 3.1.1
-      jackspeak: 2.3.6
-      minimatch: 9.0.3
-      minipass: 7.0.4
-      path-scurry: 1.10.1
-    dev: true
-
-  /glob@7.2.3:
-    resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
-    dependencies:
-      fs.realpath: 1.0.0
-      inflight: 1.0.6
-      inherits: 2.0.4
-      minimatch: 3.1.2
-      once: 1.4.0
-      path-is-absolute: 1.0.1
-    dev: true
-
-  /globals@11.12.0:
-    resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /globals@13.24.0:
-    resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      type-fest: 0.20.2
-    dev: true
-
-  /globalthis@1.0.3:
-    resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      define-properties: 1.2.1
-    dev: true
-
-  /globby@11.1.0:
-    resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
-    engines: {node: '>=10'}
-    dependencies:
-      array-union: 2.1.0
-      dir-glob: 3.0.1
-      fast-glob: 3.3.2
-      ignore: 5.3.1
-      merge2: 1.4.1
-      slash: 3.0.0
-    dev: true
-
-  /gopd@1.0.1:
-    resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
-    dependencies:
-      get-intrinsic: 1.2.4
-    dev: true
-
-  /graceful-fs@4.2.11:
-    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
-    dev: true
-
-  /graphemer@1.4.0:
-    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
-    dev: true
-
-  /has-bigints@1.0.2:
-    resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
-    dev: true
-
-  /has-flag@3.0.0:
-    resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /has-flag@4.0.0:
-    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /has-property-descriptors@1.0.2:
-    resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
-    dependencies:
-      es-define-property: 1.0.0
-    dev: true
-
-  /has-proto@1.0.3:
-    resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
-    engines: {node: '>= 0.4'}
-    dev: true
-
-  /has-symbols@1.0.3:
-    resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
-    engines: {node: '>= 0.4'}
-    dev: true
-
-  /has-tostringtag@1.0.2:
-    resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      has-symbols: 1.0.3
-    dev: true
-
-  /hasown@2.0.1:
-    resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      function-bind: 1.1.2
-    dev: true
-
-  /html-encoding-sniffer@4.0.0:
-    resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
-    engines: {node: '>=18'}
-    dependencies:
-      whatwg-encoding: 3.1.1
-    dev: true
-
-  /html-escaper@2.0.2:
-    resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
-    dev: true
-
-  /html-tags@3.3.1:
-    resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /http-errors@2.0.0:
-    resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
-    engines: {node: '>= 0.8'}
-    dependencies:
-      depd: 2.0.0
-      inherits: 2.0.4
-      setprototypeof: 1.2.0
-      statuses: 2.0.1
-      toidentifier: 1.0.1
-    dev: false
-
-  /http-proxy-agent@7.0.2:
-    resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
-    engines: {node: '>= 14'}
-    dependencies:
-      agent-base: 7.1.0
-      debug: 4.3.4
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /https-proxy-agent@7.0.4:
-    resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
-    engines: {node: '>= 14'}
-    dependencies:
-      agent-base: 7.1.0
-      debug: 4.3.4
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /human-signals@5.0.0:
-    resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
-    engines: {node: '>=16.17.0'}
-    dev: true
-
-  /iconv-lite@0.6.3:
-    resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      safer-buffer: 2.1.2
-    dev: true
-
-  /ignore@5.3.1:
-    resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
-    engines: {node: '>= 4'}
-    dev: true
-
-  /import-fresh@3.3.0:
-    resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
-    engines: {node: '>=6'}
-    dependencies:
-      parent-module: 1.0.1
-      resolve-from: 4.0.0
-    dev: true
-
-  /imurmurhash@0.1.4:
-    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
-    engines: {node: '>=0.8.19'}
-    dev: true
-
-  /inflight@1.0.6:
-    resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
-    dependencies:
-      once: 1.4.0
-      wrappy: 1.0.2
-    dev: true
-
-  /inherits@2.0.4:
-    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
-
-  /ini@1.3.8:
-    resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
-    dev: true
-
-  /internal-slot@1.0.7:
-    resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      es-errors: 1.3.0
-      hasown: 2.0.1
-      side-channel: 1.0.5
-    dev: true
-
-  /is-array-buffer@3.0.4:
-    resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      get-intrinsic: 1.2.4
-    dev: true
-
-  /is-bigint@1.0.4:
-    resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
-    dependencies:
-      has-bigints: 1.0.2
-    dev: true
-
-  /is-boolean-object@1.1.2:
-    resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      has-tostringtag: 1.0.2
-    dev: true
-
-  /is-callable@1.2.7:
-    resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
-    engines: {node: '>= 0.4'}
-    dev: true
-
-  /is-core-module@2.13.1:
-    resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
-    dependencies:
-      hasown: 2.0.1
-    dev: true
-
-  /is-date-object@1.0.5:
-    resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      has-tostringtag: 1.0.2
-    dev: true
-
-  /is-extglob@2.1.1:
-    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /is-fullwidth-code-point@3.0.0:
-    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /is-glob@4.0.3:
-    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-extglob: 2.1.1
-    dev: true
-
-  /is-negative-zero@2.0.3:
-    resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
-    engines: {node: '>= 0.4'}
-    dev: true
-
-  /is-number-object@1.0.7:
-    resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      has-tostringtag: 1.0.2
-    dev: true
-
-  /is-number@7.0.0:
-    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
-    engines: {node: '>=0.12.0'}
-    dev: true
-
-  /is-path-inside@3.0.3:
-    resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /is-potential-custom-element-name@1.0.1:
-    resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
-    dev: true
-
-  /is-regex@1.1.4:
-    resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      has-tostringtag: 1.0.2
-    dev: true
-
-  /is-shared-array-buffer@1.0.3:
-    resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-    dev: true
-
-  /is-stream@3.0.0:
-    resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-    dev: true
-
-  /is-string@1.0.7:
-    resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      has-tostringtag: 1.0.2
-    dev: true
-
-  /is-symbol@1.0.4:
-    resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      has-symbols: 1.0.3
-    dev: true
-
-  /is-typed-array@1.1.13:
-    resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      which-typed-array: 1.1.14
-    dev: true
-
-  /is-weakref@1.0.2:
-    resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
-    dependencies:
-      call-bind: 1.0.7
-    dev: true
-
-  /isarray@2.0.5:
-    resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
-    dev: true
-
-  /isexe@2.0.0:
-    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
-    dev: true
-
-  /istanbul-lib-coverage@3.2.2:
-    resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /istanbul-lib-report@3.0.1:
-    resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
-    engines: {node: '>=10'}
-    dependencies:
-      istanbul-lib-coverage: 3.2.2
-      make-dir: 4.0.0
-      supports-color: 7.2.0
-    dev: true
-
-  /istanbul-lib-source-maps@4.0.1:
-    resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
-    engines: {node: '>=10'}
-    dependencies:
-      debug: 4.3.4
-      istanbul-lib-coverage: 3.2.2
-      source-map: 0.6.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /istanbul-reports@3.1.7:
-    resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==}
-    engines: {node: '>=8'}
-    dependencies:
-      html-escaper: 2.0.2
-      istanbul-lib-report: 3.0.1
-    dev: true
-
-  /jackspeak@2.3.6:
-    resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
-    engines: {node: '>=14'}
-    dependencies:
-      '@isaacs/cliui': 8.0.2
-    optionalDependencies:
-      '@pkgjs/parseargs': 0.11.0
-    dev: true
-
-  /js-beautify@1.15.1:
-    resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==}
-    engines: {node: '>=14'}
-    hasBin: true
-    dependencies:
-      config-chain: 1.1.13
-      editorconfig: 1.0.4
-      glob: 10.3.10
-      js-cookie: 3.0.5
-      nopt: 7.2.0
-    dev: true
-
-  /js-cookie@3.0.5:
-    resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
-    engines: {node: '>=14'}
-    dev: true
-
-  /js-tokens@4.0.0:
-    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
-    dev: true
-
-  /js-tokens@8.0.3:
-    resolution: {integrity: sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==}
-    dev: true
-
-  /js-yaml@4.1.0:
-    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
-    hasBin: true
-    dependencies:
-      argparse: 2.0.1
-    dev: true
-
-  /jsdom@24.0.0:
-    resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==}
-    engines: {node: '>=18'}
-    peerDependencies:
-      canvas: ^2.11.2
-    peerDependenciesMeta:
-      canvas:
-        optional: true
-    dependencies:
-      cssstyle: 4.0.1
-      data-urls: 5.0.0
-      decimal.js: 10.4.3
-      form-data: 4.0.0
-      html-encoding-sniffer: 4.0.0
-      http-proxy-agent: 7.0.2
-      https-proxy-agent: 7.0.4
-      is-potential-custom-element-name: 1.0.1
-      nwsapi: 2.2.7
-      parse5: 7.1.2
-      rrweb-cssom: 0.6.0
-      saxes: 6.0.0
-      symbol-tree: 3.2.4
-      tough-cookie: 4.1.3
-      w3c-xmlserializer: 5.0.0
-      webidl-conversions: 7.0.0
-      whatwg-encoding: 3.1.1
-      whatwg-mimetype: 4.0.0
-      whatwg-url: 14.0.0
-      ws: 8.16.0
-      xml-name-validator: 5.0.0
-    transitivePeerDependencies:
-      - bufferutil
-      - supports-color
-      - utf-8-validate
-    dev: true
-
-  /jsesc@2.5.2:
-    resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
-    engines: {node: '>=4'}
-    hasBin: true
-    dev: true
-
-  /json-buffer@3.0.1:
-    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
-    dev: true
-
-  /json-schema-traverse@0.4.1:
-    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
-    dev: true
-
-  /json-stable-stringify-without-jsonify@1.0.1:
-    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
-    dev: true
-
-  /json5@1.0.2:
-    resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
-    hasBin: true
-    dependencies:
-      minimist: 1.2.8
-    dev: true
-
-  /json5@2.2.3:
-    resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
-    engines: {node: '>=6'}
-    hasBin: true
-    dev: true
-
-  /jsonc-parser@3.2.1:
-    resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==}
-    dev: true
-
-  /keyv@4.5.4:
-    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
-    dependencies:
-      json-buffer: 3.0.1
-    dev: true
-
-  /levn@0.4.1:
-    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
-    engines: {node: '>= 0.8.0'}
-    dependencies:
-      prelude-ls: 1.2.1
-      type-check: 0.4.0
-    dev: true
-
-  /local-pkg@0.5.0:
-    resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
-    engines: {node: '>=14'}
-    dependencies:
-      mlly: 1.6.1
-      pkg-types: 1.0.3
-    dev: true
-
-  /locate-path@6.0.0:
-    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
-    engines: {node: '>=10'}
-    dependencies:
-      p-locate: 5.0.0
-    dev: true
-
-  /lodash.merge@4.6.2:
-    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
-    dev: true
-
-  /lodash@4.17.21:
-    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
-    dev: true
-
-  /loupe@2.3.7:
-    resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
-    dependencies:
-      get-func-name: 2.0.2
-    dev: true
-
-  /lru-cache@10.2.0:
-    resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
-    engines: {node: 14 || >=16.14}
-    dev: true
-
-  /lru-cache@5.1.1:
-    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
-    dependencies:
-      yallist: 3.1.1
-    dev: true
-
-  /lru-cache@6.0.0:
-    resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
-    engines: {node: '>=10'}
-    dependencies:
-      yallist: 4.0.0
-    dev: true
-
-  /magic-string@0.30.7:
-    resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==}
-    engines: {node: '>=12'}
-    dependencies:
-      '@jridgewell/sourcemap-codec': 1.4.15
-
-  /magicast@0.3.3:
-    resolution: {integrity: sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==}
-    dependencies:
-      '@babel/parser': 7.24.0
-      '@babel/types': 7.24.0
-      source-map-js: 1.0.2
-    dev: true
-
-  /make-dir@4.0.0:
-    resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
-    engines: {node: '>=10'}
-    dependencies:
-      semver: 7.6.0
-    dev: true
-
-  /merge-stream@2.0.0:
-    resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
-    dev: true
-
-  /merge2@1.4.1:
-    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
-    engines: {node: '>= 8'}
-    dev: true
-
-  /micromatch@4.0.5:
-    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
-    engines: {node: '>=8.6'}
-    dependencies:
-      braces: 3.0.2
-      picomatch: 2.3.1
-    dev: true
-
-  /mime-db@1.52.0:
-    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
-    engines: {node: '>= 0.6'}
-    dev: true
-
-  /mime-types@2.1.35:
-    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
-    engines: {node: '>= 0.6'}
-    dependencies:
-      mime-db: 1.52.0
-    dev: true
-
-  /mime@1.6.0:
-    resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
-    engines: {node: '>=4'}
-    hasBin: true
-    dev: false
-
-  /mimic-fn@4.0.0:
-    resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /minimatch@3.1.2:
-    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
-    dependencies:
-      brace-expansion: 1.1.11
-    dev: true
-
-  /minimatch@9.0.1:
-    resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    dependencies:
-      brace-expansion: 2.0.1
-    dev: true
-
-  /minimatch@9.0.3:
-    resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    dependencies:
-      brace-expansion: 2.0.1
-    dev: true
-
-  /minimist@1.2.8:
-    resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
-    dev: true
-
-  /minipass@7.0.4:
-    resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    dev: true
-
-  /mlly@1.6.1:
-    resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==}
-    dependencies:
-      acorn: 8.11.3
-      pathe: 1.1.2
-      pkg-types: 1.0.3
-      ufo: 1.4.0
-    dev: true
-
-  /ms@2.0.0:
-    resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
-    dev: false
-
-  /ms@2.1.2:
-    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
-    dev: true
-
-  /ms@2.1.3:
-    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
-
-  /nanoid@3.3.7:
-    resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
-    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
-    hasBin: true
-
-  /natural-compare@1.4.0:
-    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
-    dev: true
-
-  /node-releases@2.0.14:
-    resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
-    dev: true
-
-  /nopt@7.2.0:
-    resolution: {integrity: sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
-    hasBin: true
-    dependencies:
-      abbrev: 2.0.0
-    dev: true
-
-  /npm-run-path@5.3.0:
-    resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-    dependencies:
-      path-key: 4.0.0
-    dev: true
-
-  /nth-check@2.1.1:
-    resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
-    dependencies:
-      boolbase: 1.0.0
-    dev: true
-
-  /nwsapi@2.2.7:
-    resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==}
-    dev: true
-
-  /object-inspect@1.13.1:
-    resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
-    dev: true
-
-  /object-keys@1.1.1:
-    resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
-    engines: {node: '>= 0.4'}
-    dev: true
-
-  /object.assign@4.1.5:
-    resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      has-symbols: 1.0.3
-      object-keys: 1.1.1
-    dev: true
-
-  /object.fromentries@2.0.7:
-    resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-    dev: true
-
-  /object.groupby@1.0.2:
-    resolution: {integrity: sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==}
-    dependencies:
-      array.prototype.filter: 1.0.3
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-      es-errors: 1.3.0
-    dev: true
-
-  /object.values@1.1.7:
-    resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-    dev: true
-
-  /on-finished@2.4.1:
-    resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
-    engines: {node: '>= 0.8'}
-    dependencies:
-      ee-first: 1.1.1
-    dev: false
-
-  /once@1.4.0:
-    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
-    dependencies:
-      wrappy: 1.0.2
-    dev: true
-
-  /onetime@6.0.0:
-    resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
-    engines: {node: '>=12'}
-    dependencies:
-      mimic-fn: 4.0.0
-    dev: true
-
-  /optionator@0.9.3:
-    resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
-    engines: {node: '>= 0.8.0'}
-    dependencies:
-      '@aashutoshrathi/word-wrap': 1.2.6
-      deep-is: 0.1.4
-      fast-levenshtein: 2.0.6
-      levn: 0.4.1
-      prelude-ls: 1.2.1
-      type-check: 0.4.0
-    dev: true
-
-  /p-limit@3.1.0:
-    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      yocto-queue: 0.1.0
-    dev: true
-
-  /p-limit@5.0.0:
-    resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==}
-    engines: {node: '>=18'}
-    dependencies:
-      yocto-queue: 1.0.0
-    dev: true
-
-  /p-locate@5.0.0:
-    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
-    engines: {node: '>=10'}
-    dependencies:
-      p-limit: 3.1.0
-    dev: true
-
-  /parent-module@1.0.1:
-    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
-    engines: {node: '>=6'}
-    dependencies:
-      callsites: 3.1.0
-    dev: true
-
-  /parse5@7.1.2:
-    resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
-    dependencies:
-      entities: 4.5.0
-    dev: true
-
-  /parseurl@1.3.3:
-    resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
-    engines: {node: '>= 0.8'}
-    dev: false
-
-  /path-exists@4.0.0:
-    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /path-is-absolute@1.0.1:
-    resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /path-key@3.1.1:
-    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /path-key@4.0.0:
-    resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /path-parse@1.0.7:
-    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
-    dev: true
-
-  /path-scurry@1.10.1:
-    resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    dependencies:
-      lru-cache: 10.2.0
-      minipass: 7.0.4
-    dev: true
-
-  /path-type@4.0.0:
-    resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /pathe@1.1.2:
-    resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
-    dev: true
-
-  /pathval@1.1.1:
-    resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
-    dev: true
-
-  /picocolors@1.0.0:
-    resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
-
-  /picomatch@2.3.1:
-    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
-    engines: {node: '>=8.6'}
-    dev: true
-
-  /pkg-types@1.0.3:
-    resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
-    dependencies:
-      jsonc-parser: 3.2.1
-      mlly: 1.6.1
-      pathe: 1.1.2
-    dev: true
-
-  /possible-typed-array-names@1.0.0:
-    resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
-    engines: {node: '>= 0.4'}
-    dev: true
-
-  /postcss-selector-parser@6.0.15:
-    resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==}
-    engines: {node: '>=4'}
-    dependencies:
-      cssesc: 3.0.0
-      util-deprecate: 1.0.2
-    dev: true
-
-  /postcss@8.4.35:
-    resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
-    engines: {node: ^10 || ^12 || >=14}
-    dependencies:
-      nanoid: 3.3.7
-      picocolors: 1.0.0
-      source-map-js: 1.0.2
-
-  /prelude-ls@1.2.1:
-    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
-    engines: {node: '>= 0.8.0'}
-    dev: true
-
-  /prettier-linter-helpers@1.0.0:
-    resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
-    engines: {node: '>=6.0.0'}
-    dependencies:
-      fast-diff: 1.3.0
-    dev: true
-
-  /prettier@3.2.5:
-    resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
-    engines: {node: '>=14'}
-    hasBin: true
-    dev: true
-
-  /pretty-format@29.7.0:
-    resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    dependencies:
-      '@jest/schemas': 29.6.3
-      ansi-styles: 5.2.0
-      react-is: 18.2.0
-    dev: true
-
-  /proto-list@1.2.4:
-    resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
-    dev: true
-
-  /psl@1.9.0:
-    resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
-    dev: true
-
-  /punycode@2.3.1:
-    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /querystringify@2.2.0:
-    resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
-    dev: true
-
-  /queue-microtask@1.2.3:
-    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
-    dev: true
-
-  /range-parser@1.2.1:
-    resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
-    engines: {node: '>= 0.6'}
-    dev: false
-
-  /react-is@18.2.0:
-    resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
-    dev: true
-
-  /regexp.prototype.flags@1.5.2:
-    resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-errors: 1.3.0
-      set-function-name: 2.0.2
-    dev: true
-
-  /requires-port@1.0.0:
-    resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
-    dev: true
-
-  /resolve-from@4.0.0:
-    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /resolve-pkg-maps@1.0.0:
-    resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
-    dev: true
-
-  /resolve@1.22.8:
-    resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
-    hasBin: true
-    dependencies:
-      is-core-module: 2.13.1
-      path-parse: 1.0.7
-      supports-preserve-symlinks-flag: 1.0.0
-    dev: true
-
-  /reusify@1.0.4:
-    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
-    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
-    dev: true
-
-  /rimraf@3.0.2:
-    resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
-    hasBin: true
-    dependencies:
-      glob: 7.2.3
-    dev: true
-
-  /rimraf@5.0.5:
-    resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==}
-    engines: {node: '>=14'}
-    hasBin: true
-    dependencies:
-      glob: 10.3.10
-    dev: true
-
-  /rollup@4.12.0:
-    resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==}
-    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
-    hasBin: true
-    dependencies:
-      '@types/estree': 1.0.5
-    optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.12.0
-      '@rollup/rollup-android-arm64': 4.12.0
-      '@rollup/rollup-darwin-arm64': 4.12.0
-      '@rollup/rollup-darwin-x64': 4.12.0
-      '@rollup/rollup-linux-arm-gnueabihf': 4.12.0
-      '@rollup/rollup-linux-arm64-gnu': 4.12.0
-      '@rollup/rollup-linux-arm64-musl': 4.12.0
-      '@rollup/rollup-linux-riscv64-gnu': 4.12.0
-      '@rollup/rollup-linux-x64-gnu': 4.12.0
-      '@rollup/rollup-linux-x64-musl': 4.12.0
-      '@rollup/rollup-win32-arm64-msvc': 4.12.0
-      '@rollup/rollup-win32-ia32-msvc': 4.12.0
-      '@rollup/rollup-win32-x64-msvc': 4.12.0
-      fsevents: 2.3.3
-    dev: true
-
-  /rrweb-cssom@0.6.0:
-    resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
-    dev: true
-
-  /run-parallel@1.2.0:
-    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
-    dependencies:
-      queue-microtask: 1.2.3
-    dev: true
-
-  /safe-array-concat@1.1.0:
-    resolution: {integrity: sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==}
-    engines: {node: '>=0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      get-intrinsic: 1.2.4
-      has-symbols: 1.0.3
-      isarray: 2.0.5
-    dev: true
-
-  /safe-regex-test@1.0.3:
-    resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      es-errors: 1.3.0
-      is-regex: 1.1.4
-    dev: true
-
-  /safer-buffer@2.1.2:
-    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
-    dev: true
-
-  /saxes@6.0.0:
-    resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
-    engines: {node: '>=v12.22.7'}
-    dependencies:
-      xmlchars: 2.2.0
-    dev: true
-
-  /semver@7.6.0:
-    resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dependencies:
-      lru-cache: 6.0.0
-    dev: true
-
-  /send@0.18.0:
-    resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
-    engines: {node: '>= 0.8.0'}
-    dependencies:
-      debug: 2.6.9
-      depd: 2.0.0
-      destroy: 1.2.0
-      encodeurl: 1.0.2
-      escape-html: 1.0.3
-      etag: 1.8.1
-      fresh: 0.5.2
-      http-errors: 2.0.0
-      mime: 1.6.0
-      ms: 2.1.3
-      on-finished: 2.4.1
-      range-parser: 1.2.1
-      statuses: 2.0.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: false
-
-  /serve-static@1.15.0:
-    resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==}
-    engines: {node: '>= 0.8.0'}
-    dependencies:
-      encodeurl: 1.0.2
-      escape-html: 1.0.3
-      parseurl: 1.3.3
-      send: 0.18.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: false
-
-  /set-function-length@1.2.1:
-    resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      define-data-property: 1.1.4
-      es-errors: 1.3.0
-      function-bind: 1.1.2
-      get-intrinsic: 1.2.4
-      gopd: 1.0.1
-      has-property-descriptors: 1.0.2
-    dev: true
-
-  /set-function-name@2.0.2:
-    resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      define-data-property: 1.1.4
-      es-errors: 1.3.0
-      functions-have-names: 1.2.3
-      has-property-descriptors: 1.0.2
-    dev: true
-
-  /setprototypeof@1.2.0:
-    resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
-    dev: false
-
-  /shebang-command@2.0.0:
-    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
-    engines: {node: '>=8'}
-    dependencies:
-      shebang-regex: 3.0.0
-    dev: true
-
-  /shebang-regex@3.0.0:
-    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /side-channel@1.0.5:
-    resolution: {integrity: sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      es-errors: 1.3.0
-      get-intrinsic: 1.2.4
-      object-inspect: 1.13.1
-    dev: true
-
-  /siginfo@2.0.0:
-    resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
-    dev: true
-
-  /signal-exit@4.1.0:
-    resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
-    engines: {node: '>=14'}
-    dev: true
-
-  /slash@3.0.0:
-    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /source-map-js@1.0.2:
-    resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
-    engines: {node: '>=0.10.0'}
-
-  /source-map@0.6.1:
-    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /stackback@0.0.2:
-    resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
-    dev: true
-
-  /statuses@2.0.1:
-    resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
-    engines: {node: '>= 0.8'}
-    dev: false
-
-  /std-env@3.7.0:
-    resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==}
-    dev: true
-
-  /string-width@4.2.3:
-    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
-    engines: {node: '>=8'}
-    dependencies:
-      emoji-regex: 8.0.0
-      is-fullwidth-code-point: 3.0.0
-      strip-ansi: 6.0.1
-    dev: true
-
-  /string-width@5.1.2:
-    resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
-    engines: {node: '>=12'}
-    dependencies:
-      eastasianwidth: 0.2.0
-      emoji-regex: 9.2.2
-      strip-ansi: 7.1.0
-    dev: true
-
-  /string.prototype.trim@1.2.8:
-    resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-    dev: true
-
-  /string.prototype.trimend@1.0.7:
-    resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-    dev: true
-
-  /string.prototype.trimstart@1.0.7:
-    resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==}
-    dependencies:
-      call-bind: 1.0.7
-      define-properties: 1.2.1
-      es-abstract: 1.22.5
-    dev: true
-
-  /strip-ansi@6.0.1:
-    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
-    engines: {node: '>=8'}
-    dependencies:
-      ansi-regex: 5.0.1
-    dev: true
-
-  /strip-ansi@7.1.0:
-    resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
-    engines: {node: '>=12'}
-    dependencies:
-      ansi-regex: 6.0.1
-    dev: true
-
-  /strip-bom@3.0.0:
-    resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /strip-final-newline@3.0.0:
-    resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /strip-json-comments@3.1.1:
-    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /strip-literal@2.0.0:
-    resolution: {integrity: sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==}
-    dependencies:
-      js-tokens: 8.0.3
-    dev: true
-
-  /supports-color@5.5.0:
-    resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
-    engines: {node: '>=4'}
-    dependencies:
-      has-flag: 3.0.0
-    dev: true
-
-  /supports-color@7.2.0:
-    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
-    engines: {node: '>=8'}
-    dependencies:
-      has-flag: 4.0.0
-    dev: true
-
-  /supports-preserve-symlinks-flag@1.0.0:
-    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
-    engines: {node: '>= 0.4'}
-    dev: true
-
-  /svg-tags@1.0.0:
-    resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
-    dev: true
-
-  /symbol-tree@3.2.4:
-    resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
-    dev: true
-
-  /synckit@0.8.8:
-    resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==}
-    engines: {node: ^14.18.0 || >=16.0.0}
-    dependencies:
-      '@pkgr/core': 0.1.1
-      tslib: 2.6.2
-    dev: true
-
-  /tapable@2.2.1:
-    resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /test-exclude@6.0.0:
-    resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
-    engines: {node: '>=8'}
-    dependencies:
-      '@istanbuljs/schema': 0.1.3
-      glob: 7.2.3
-      minimatch: 3.1.2
-    dev: true
-
-  /text-table@0.2.0:
-    resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
-    dev: true
-
-  /tinybench@2.6.0:
-    resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==}
-    dev: true
-
-  /tinypool@0.8.2:
-    resolution: {integrity: sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==}
-    engines: {node: '>=14.0.0'}
-    dev: true
-
-  /tinyspy@2.2.1:
-    resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==}
-    engines: {node: '>=14.0.0'}
-    dev: true
-
-  /to-fast-properties@2.0.0:
-    resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
-    engines: {node: '>=4'}
-
-  /to-regex-range@5.0.1:
-    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
-    engines: {node: '>=8.0'}
-    dependencies:
-      is-number: 7.0.0
-    dev: true
-
-  /toidentifier@1.0.1:
-    resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
-    engines: {node: '>=0.6'}
-    dev: false
-
-  /tough-cookie@4.1.3:
-    resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
-    engines: {node: '>=6'}
-    dependencies:
-      psl: 1.9.0
-      punycode: 2.3.1
-      universalify: 0.2.0
-      url-parse: 1.5.10
-    dev: true
-
-  /tr46@5.0.0:
-    resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
-    engines: {node: '>=18'}
-    dependencies:
-      punycode: 2.3.1
-    dev: true
-
-  /ts-api-utils@1.2.1(typescript@5.3.3):
-    resolution: {integrity: sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==}
-    engines: {node: '>=16'}
-    peerDependencies:
-      typescript: '>=4.2.0'
-    dependencies:
-      typescript: 5.3.3
-    dev: true
-
-  /tsconfig-paths@3.15.0:
-    resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
-    dependencies:
-      '@types/json5': 0.0.29
-      json5: 1.0.2
-      minimist: 1.2.8
-      strip-bom: 3.0.0
-    dev: true
-
-  /tslib@2.6.2:
-    resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
-    dev: true
-
-  /type-check@0.4.0:
-    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
-    engines: {node: '>= 0.8.0'}
-    dependencies:
-      prelude-ls: 1.2.1
-    dev: true
-
-  /type-detect@4.0.8:
-    resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /type-fest@0.20.2:
-    resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /typed-array-buffer@1.0.2:
-    resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      es-errors: 1.3.0
-      is-typed-array: 1.1.13
-    dev: true
-
-  /typed-array-byte-length@1.0.1:
-    resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      for-each: 0.3.3
-      gopd: 1.0.1
-      has-proto: 1.0.3
-      is-typed-array: 1.1.13
-    dev: true
-
-  /typed-array-byte-offset@1.0.2:
-    resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      available-typed-arrays: 1.0.7
-      call-bind: 1.0.7
-      for-each: 0.3.3
-      gopd: 1.0.1
-      has-proto: 1.0.3
-      is-typed-array: 1.1.13
-    dev: true
-
-  /typed-array-length@1.0.5:
-    resolution: {integrity: sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.7
-      for-each: 0.3.3
-      gopd: 1.0.1
-      has-proto: 1.0.3
-      is-typed-array: 1.1.13
-      possible-typed-array-names: 1.0.0
-    dev: true
-
-  /typescript@5.3.3:
-    resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
-    engines: {node: '>=14.17'}
-    hasBin: true
-
-  /ufo@1.4.0:
-    resolution: {integrity: sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==}
-    dev: true
-
-  /unbox-primitive@1.0.2:
-    resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
-    dependencies:
-      call-bind: 1.0.7
-      has-bigints: 1.0.2
-      has-symbols: 1.0.3
-      which-boxed-primitive: 1.0.2
-    dev: true
-
-  /undici-types@5.26.5:
-    resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
-    dev: true
-
-  /universalify@0.2.0:
-    resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
-    engines: {node: '>= 4.0.0'}
-    dev: true
-
-  /unpipe@1.0.0:
-    resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
-    engines: {node: '>= 0.8'}
-    dev: false
-
-  /update-browserslist-db@1.0.13(browserslist@4.23.0):
-    resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
-    hasBin: true
-    peerDependencies:
-      browserslist: '>= 4.21.0'
-    dependencies:
-      browserslist: 4.23.0
-      escalade: 3.1.2
-      picocolors: 1.0.0
-    dev: true
-
-  /uri-js@4.4.1:
-    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
-    dependencies:
-      punycode: 2.3.1
-    dev: true
-
-  /url-parse@1.5.10:
-    resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
-    dependencies:
-      querystringify: 2.2.0
-      requires-port: 1.0.0
-    dev: true
-
-  /util-deprecate@1.0.2:
-    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
-    dev: true
-
-  /v8-to-istanbul@9.2.0:
-    resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==}
-    engines: {node: '>=10.12.0'}
-    dependencies:
-      '@jridgewell/trace-mapping': 0.3.23
-      '@types/istanbul-lib-coverage': 2.0.6
-      convert-source-map: 2.0.0
-    dev: true
-
-  /vite-node@1.3.1(@types/node@20.11.24):
-    resolution: {integrity: sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==}
-    engines: {node: ^18.0.0 || >=20.0.0}
-    hasBin: true
-    dependencies:
-      cac: 6.7.14
-      debug: 4.3.4
-      pathe: 1.1.2
-      picocolors: 1.0.0
-      vite: 5.1.4(@types/node@20.11.24)
-    transitivePeerDependencies:
-      - '@types/node'
-      - less
-      - lightningcss
-      - sass
-      - stylus
-      - sugarss
-      - supports-color
-      - terser
-    dev: true
-
-  /vite@5.1.4(@types/node@20.11.24):
-    resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==}
-    engines: {node: ^18.0.0 || >=20.0.0}
-    hasBin: true
-    peerDependencies:
-      '@types/node': ^18.0.0 || >=20.0.0
-      less: '*'
-      lightningcss: ^1.21.0
-      sass: '*'
-      stylus: '*'
-      sugarss: '*'
-      terser: ^5.4.0
-    peerDependenciesMeta:
-      '@types/node':
-        optional: true
-      less:
-        optional: true
-      lightningcss:
-        optional: true
-      sass:
-        optional: true
-      stylus:
-        optional: true
-      sugarss:
-        optional: true
-      terser:
-        optional: true
-    dependencies:
-      '@types/node': 20.11.24
-      esbuild: 0.19.12
-      postcss: 8.4.35
-      rollup: 4.12.0
-    optionalDependencies:
-      fsevents: 2.3.3
-    dev: true
-
-  /vitest@1.3.1(@types/node@20.11.24)(jsdom@24.0.0):
-    resolution: {integrity: sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==}
-    engines: {node: ^18.0.0 || >=20.0.0}
-    hasBin: true
-    peerDependencies:
-      '@edge-runtime/vm': '*'
-      '@types/node': ^18.0.0 || >=20.0.0
-      '@vitest/browser': 1.3.1
-      '@vitest/ui': 1.3.1
-      happy-dom: '*'
-      jsdom: '*'
-    peerDependenciesMeta:
-      '@edge-runtime/vm':
-        optional: true
-      '@types/node':
-        optional: true
-      '@vitest/browser':
-        optional: true
-      '@vitest/ui':
-        optional: true
-      happy-dom:
-        optional: true
-      jsdom:
-        optional: true
-    dependencies:
-      '@types/node': 20.11.24
-      '@vitest/expect': 1.3.1
-      '@vitest/runner': 1.3.1
-      '@vitest/snapshot': 1.3.1
-      '@vitest/spy': 1.3.1
-      '@vitest/utils': 1.3.1
-      acorn-walk: 8.3.2
-      chai: 4.4.1
-      debug: 4.3.4
-      execa: 8.0.1
-      jsdom: 24.0.0
-      local-pkg: 0.5.0
-      magic-string: 0.30.7
-      pathe: 1.1.2
-      picocolors: 1.0.0
-      std-env: 3.7.0
-      strip-literal: 2.0.0
-      tinybench: 2.6.0
-      tinypool: 0.8.2
-      vite: 5.1.4(@types/node@20.11.24)
-      vite-node: 1.3.1(@types/node@20.11.24)
-      why-is-node-running: 2.2.2
-    transitivePeerDependencies:
-      - less
-      - lightningcss
-      - sass
-      - stylus
-      - sugarss
-      - supports-color
-      - terser
-    dev: true
-
-  /vue-component-type-helpers@1.8.27:
-    resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==}
-    dev: true
-
-  /vue-eslint-parser@9.4.2(eslint@8.57.0):
-    resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==}
-    engines: {node: ^14.17.0 || >=16.0.0}
-    peerDependencies:
-      eslint: '>=6.0.0'
-    dependencies:
-      debug: 4.3.4
-      eslint: 8.57.0
-      eslint-scope: 7.2.2
-      eslint-visitor-keys: 3.4.3
-      espree: 9.6.1
-      esquery: 1.5.0
-      lodash: 4.17.21
-      semver: 7.6.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /vue-router@4.3.0(vue@3.4.21):
-    resolution: {integrity: sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==}
-    peerDependencies:
-      vue: ^3.2.0
-    dependencies:
-      '@vue/devtools-api': 6.6.1
-      vue: 3.4.21(typescript@5.3.3)
-    dev: false
-
-  /vue-toast-notification@3.1.2(vue@3.4.21):
-    resolution: {integrity: sha512-oNRL/W9aaHoeScp+iTIW7k09vM16/+8aptp2maa+7qTB43JuxmAgKdXKFYtf+uvSNOYYq2BIWgLCeJ61pwom/A==}
-    engines: {node: '>=12.15.0'}
-    peerDependencies:
-      vue: ^3.0
-    dependencies:
-      vue: 3.4.21(typescript@5.3.3)
-    dev: false
-
-  /vue@3.4.21(typescript@5.3.3):
-    resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@vue/compiler-dom': 3.4.21
-      '@vue/compiler-sfc': 3.4.21
-      '@vue/runtime-dom': 3.4.21
-      '@vue/server-renderer': 3.4.21(vue@3.4.21)
-      '@vue/shared': 3.4.21
-      typescript: 5.3.3
-
-  /w3c-xmlserializer@5.0.0:
-    resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
-    engines: {node: '>=18'}
-    dependencies:
-      xml-name-validator: 5.0.0
-    dev: true
-
-  /webidl-conversions@7.0.0:
-    resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /whatwg-encoding@3.1.1:
-    resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
-    engines: {node: '>=18'}
-    dependencies:
-      iconv-lite: 0.6.3
-    dev: true
-
-  /whatwg-mimetype@4.0.0:
-    resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
-    engines: {node: '>=18'}
-    dev: true
-
-  /whatwg-url@14.0.0:
-    resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==}
-    engines: {node: '>=18'}
-    dependencies:
-      tr46: 5.0.0
-      webidl-conversions: 7.0.0
-    dev: true
-
-  /which-boxed-primitive@1.0.2:
-    resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
-    dependencies:
-      is-bigint: 1.0.4
-      is-boolean-object: 1.1.2
-      is-number-object: 1.0.7
-      is-string: 1.0.7
-      is-symbol: 1.0.4
-    dev: true
-
-  /which-typed-array@1.1.14:
-    resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      available-typed-arrays: 1.0.7
-      call-bind: 1.0.7
-      for-each: 0.3.3
-      gopd: 1.0.1
-      has-tostringtag: 1.0.2
-    dev: true
-
-  /which@2.0.2:
-    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
-    engines: {node: '>= 8'}
-    hasBin: true
-    dependencies:
-      isexe: 2.0.0
-    dev: true
-
-  /why-is-node-running@2.2.2:
-    resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==}
-    engines: {node: '>=8'}
-    hasBin: true
-    dependencies:
-      siginfo: 2.0.0
-      stackback: 0.0.2
-    dev: true
-
-  /wrap-ansi@7.0.0:
-    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
-    engines: {node: '>=10'}
-    dependencies:
-      ansi-styles: 4.3.0
-      string-width: 4.2.3
-      strip-ansi: 6.0.1
-    dev: true
-
-  /wrap-ansi@8.1.0:
-    resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
-    engines: {node: '>=12'}
-    dependencies:
-      ansi-styles: 6.2.1
-      string-width: 5.1.2
-      strip-ansi: 7.1.0
-    dev: true
-
-  /wrappy@1.0.2:
-    resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
-    dev: true
-
-  /ws@8.16.0:
-    resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
-    engines: {node: '>=10.0.0'}
-    peerDependencies:
-      bufferutil: ^4.0.1
-      utf-8-validate: '>=5.0.2'
-    peerDependenciesMeta:
-      bufferutil:
-        optional: true
-      utf-8-validate:
-        optional: true
-    dev: true
-
-  /xml-name-validator@4.0.0:
-    resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /xml-name-validator@5.0.0:
-    resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
-    engines: {node: '>=18'}
-    dev: true
-
-  /xmlchars@2.2.0:
-    resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
-    dev: true
-
-  /yallist@3.1.1:
-    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
-    dev: true
-
-  /yallist@4.0.0:
-    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
-    dev: true
-
-  /yocto-queue@0.1.0:
-    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /yocto-queue@1.0.0:
-    resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
-    engines: {node: '>=12.20'}
-    dev: true
index f614a852bf4e0d21506fb2b1af04a65146445f78..77ba547c10079e20c78068544f32c26cf6e3346a 100644 (file)
@@ -3,7 +3,7 @@ sonar.organization=sap-1
 
 # This is the name and version displayed in the SonarCloud UI.
 sonar.projectName=e-mobility-charging-stations-simulator-webui
-sonar.projectVersion=0.2.0
+sonar.projectVersion=0.3.0
 
 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
 sonar.sources=src
index 325c6bd8675d0ccc564da9b40272bff2467a7e0d..1a0b1d6428ffab304a84fa3800cb0fe57b0dfb4a 100644 (file)
@@ -1,6 +1,9 @@
 <template>
   <router-view />
-  <Container v-show="$route.path !== '/'" id="action-container">
+  <Container
+    v-show="$route.name !== 'charging-stations' && $route.name !== 'not-found'"
+    id="action-container"
+  >
     <router-view name="action" />
   </Container>
 </template>
@@ -12,8 +15,8 @@ import Container from '@/components/Container.vue'
 <style>
 #app {
   height: fit-content;
-  width: fit-content;
-  font-family: Avenir, Helvetica, Arial, sans-serif;
+  width: 100%;
+  font-family: Tahoma, 'Arial Narrow', Arial, Helvetica, sans-serif;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
   display: flex;
@@ -24,17 +27,22 @@ import Container from '@/components/Container.vue'
 
 #action-container {
   min-width: max-content;
+  height: fit-content;
   display: flex;
+  position: sticky;
+  top: 0.008%;
   flex-direction: column;
-  align-items: center;
   justify-content: center;
-  margin: 0.1%;
-  padding: 0.1%;
-  border: solid black;
+  align-items: center;
+  text-align: center;
+  margin-right: 0.2%;
+  margin-left: 0.2%;
+  padding: 0.4%;
+  border: solid 0.25px black;
 }
 
 body {
-  margin: 0.005%;
-  padding: 0.005%;
+  margin: 0.008%;
+  padding: 0.008%;
 }
 </style>
index 322b5fb416c1b86e5194564d61f8d0aacc10e743..6ee1d7ca50867f2a6ae19781b4583af63f413e86 100644 (file)
Binary files a/ui/web/src/assets/webui.png and b/ui/web/src/assets/webui.png differ
index ff9f5f61472a5c988a7183baab9c728b6edcf10a..a95cba2b07a9afdf72cf03a2be97b81def9db712 100644 (file)
@@ -1,16 +1,13 @@
 <template>
-  <h1 id="action">Action</h1>
-  <h2>Add Charging Stations</h2>
+  <h1 id="action">Add Charging Stations</h1>
   <p>Template:</p>
-  <select
-    v-show="
-      Array.isArray(app?.appContext.config.globalProperties.$templates) &&
-      app?.appContext.config.globalProperties.$templates.length > 0
-    "
-    v-model="state.template"
-  >
+  <select :key="state.renderTemplates" v-model="state.template">
     <option disabled value="">Please select a template</option>
-    <option v-for="template in app?.appContext.config.globalProperties.$templates">
+    <option
+      v-for="template in $templates.value"
+      v-show="Array.isArray($templates.value) && $templates.value.length > 0"
+      :key="template"
+    >
       {{ template }}
     </option>
   </select>
@@ -24,7 +21,7 @@
     placeholder="number of stations"
   />
   <p>Template options overrides:</p>
-  <ul>
+  <ul id="template-options">
     <li>
       Supervision url:
       <input
@@ -72,7 +69,7 @@
     id="action-button"
     @click="
       () => {
-        uiClient
+        $uiClient
           .addChargingStations(state.template, state.numberOfStations, {
             supervisionUrls: state.supervisionUrl.length > 0 ? state.supervisionUrl : undefined,
             autoStart: convertToBoolean(state.autoStart),
   >
     Add Charging Stations
   </Button>
-  <Button id="action-button" @click="$router.push({ name: 'charging-stations' })">Cancel</Button>
 </template>
 
 <script setup lang="ts">
-import { getCurrentInstance, ref } from 'vue'
-import { useToast } from 'vue-toast-notification'
+import { getCurrentInstance, ref, watch } from 'vue'
+
 import Button from '@/components/buttons/Button.vue'
-import { convertToBoolean } from '@/composables'
+import { convertToBoolean, randomUUID } from '@/composables'
 
-const state = ref({
+const state = ref<{
+  renderTemplates: `${string}-${string}-${string}-${string}-${string}`
+  template: string
+  numberOfStations: number
+  supervisionUrl: string
+  autoStart: boolean
+  persistentConfiguration: boolean
+  ocppStrictCompliance: boolean
+  enableStatistics: boolean
+}>({
+  renderTemplates: randomUUID(),
   template: '',
   numberOfStations: 1,
   supervisionUrl: '',
@@ -114,10 +120,9 @@ const state = ref({
   enableStatistics: false
 })
 
-const app = getCurrentInstance()
-const uiClient = app?.appContext.config.globalProperties.$uiClient
-
-const $toast = useToast()
+watch(getCurrentInstance()!.appContext.config.globalProperties.$templates, () => {
+  state.value.renderTemplates = randomUUID()
+})
 </script>
 
 <style>
@@ -130,4 +135,9 @@ const $toast = useToast()
   width: 90%;
   text-align: left;
 }
+
+#template-options {
+  list-style: circle inside;
+  text-align: left;
+}
 </style>
index ff2a4b809b446cb61e5d1b3118dcf4b931abe2bc..edefc8a544c8d4eafd86404a07f7b2fc7a2c98ff 100644 (file)
@@ -1,7 +1,6 @@
 <template>
-  <h1 id="action">Action</h1>
-  <h2>Set Supervision Url</h2>
-  <h3>Charging Station {{ chargingStationId }}</h3>
+  <h1 id="action">Set Supervision Url</h1>
+  <h2>{{ chargingStationId }}</h2>
   <p>Supervision Url:</p>
   <input
     id="supervision-url"
@@ -15,8 +14,8 @@
     id="action-button"
     @click="
       () => {
-        uiClient
-          .setSupervisionUrl(props.hashId, state.supervisionUrl)
+        $uiClient
+          .setSupervisionUrl(hashId, state.supervisionUrl)
           .then(() => {
             $toast.success('Supervision url successfully set')
           })
   >
     Set Supervision Url
   </Button>
-  <Button id="action-button" @click="$router.push({ name: 'charging-stations' })">Cancel</Button>
 </template>
 
 <script setup lang="ts">
-import { getCurrentInstance, ref } from 'vue'
+import { ref } from 'vue'
+
 import Button from '@/components/buttons/Button.vue'
 
-const props = defineProps<{
+defineProps<{
   hashId: string
   chargingStationId: string
 }>()
 
-const state = ref({
+const state = ref<{ supervisionUrl: string }>({
   supervisionUrl: ''
 })
-
-const uiClient = getCurrentInstance()?.appContext.config.globalProperties.$uiClient
 </script>
 
 <style>
index 22acbef3ed9fcab693e4b8e876776588c6b452ef..a0ab5ceceff52094a03e0b3ca34f1eb1fe46ab5b 100644 (file)
@@ -1,7 +1,7 @@
 <template>
-  <h1 id="action">Action</h1>
-  <h2>Start Transaction</h2>
-  <h3>Connector {{ connectorId }} on {{ chargingStationId }}</h3>
+  <h1 id="action">Start Transaction</h1>
+  <h2>{{ chargingStationId }}</h2>
+  <h3>Connector {{ connectorId }}</h3>
   <p>Scan RFID tag:</p>
   <input id="idtag" v-model.trim="state.idTag" type="text" name="idtag" placeholder="RFID tag" />
   <br />
@@ -9,8 +9,8 @@
     id="action-button"
     @click="
       () => {
-        uiClient
-          .startTransaction(props.hashId, convertToInt(props.connectorId), state.idTag)
+        $uiClient
+          .startTransaction(hashId, convertToInt(connectorId), state.idTag)
           .then(() => {
             $toast.success('Transaction successfully started')
           })
   >
     Start Transaction
   </Button>
-  <Button id="action-button" @click="$router.push({ name: 'charging-stations' })">Cancel</Button>
 </template>
 
 <script setup lang="ts">
-import { getCurrentInstance, ref } from 'vue'
+import { ref } from 'vue'
+
 import Button from '@/components/buttons/Button.vue'
 import { convertToInt } from '@/composables'
 
-const props = defineProps<{
+defineProps<{
   hashId: string
   chargingStationId: string
   connectorId: string
 }>()
 
-const state = ref({
+const state = ref<{ idTag: string }>({
   idTag: ''
 })
-
-const uiClient = getCurrentInstance()?.appContext.config.globalProperties.$uiClient
 </script>
 
 <style>
index 6d928342684a35b96da3691314b634b580da711a..9d99cdb4e8cf424bbf1a87e9763d448b7cf29319 100644 (file)
@@ -6,6 +6,10 @@
 
 <style>
 .button {
+  display: flex;
   flex: auto;
+  justify-content: center;
+  align-items: center;
+  font: small-caption;
 }
 </style>
diff --git a/ui/web/src/components/buttons/FlatButton.vue b/ui/web/src/components/buttons/FlatButton.vue
deleted file mode 100644 (file)
index 0a6d6b4..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<template>
-  <Button class="flat">
-    <slot></slot>
-  </Button>
-</template>
-
-<script setup lang="ts">
-import Button from '@/components/buttons/Button.vue'
-</script>
-
-<style>
-.flat {
-  border: none;
-}
-</style>
index bf74ec9a330b738f9a00825aa757ed95879287b2..385e145462d369744e1757b04f9c48abca4a82c0 100644 (file)
@@ -1,11 +1,11 @@
 <template>
-  <FlatButton>
+  <Button>
     <span :class="{ spin: loading }"> &#8635; </span>
-  </FlatButton>
+  </Button>
 </template>
 
 <script setup lang="ts">
-import FlatButton from '@/components/buttons/FlatButton.vue'
+import Button from '@/components/buttons/Button.vue'
 
 defineProps<{
   loading: boolean
@@ -23,9 +23,6 @@ defineProps<{
 }
 
 .spin {
-  animation-name: rotation;
-  animation-duration: 2s;
-  animation-iteration-count: infinite;
-  animation-timing-function: linear;
+  animation: rotation 2s linear infinite;
 }
 </style>
diff --git a/ui/web/src/components/buttons/ToggleButton.vue b/ui/web/src/components/buttons/ToggleButton.vue
new file mode 100644 (file)
index 0000000..537a198
--- /dev/null
@@ -0,0 +1,54 @@
+<template>
+  <Button :class="{ on: state.status }" @click="click()">
+    <slot></slot>
+  </Button>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+
+import Button from '@/components/buttons/Button.vue'
+import { getFromLocalStorage, setToLocalStorage } from '@/composables'
+
+const props = defineProps<{
+  id: string
+  status?: boolean
+  shared?: boolean
+  on?: () => void
+  off?: () => void
+}>()
+
+const $emit = defineEmits(['clicked'])
+
+const id = props.shared === true ? `shared-toggle-button-${props.id}` : `toggle-button-${props.id}`
+
+const state = ref<{ status: boolean }>({
+  status: getFromLocalStorage<boolean>(id, props.status ?? false)
+})
+
+const click = (): void => {
+  if (props.shared === true) {
+    for (const key in localStorage) {
+      if (key !== id && key.startsWith('shared-toggle-button-')) {
+        setToLocalStorage<boolean>(key, false)
+        state.value.status = getFromLocalStorage<boolean>(key, false)
+      }
+    }
+  }
+  setToLocalStorage<boolean>(id, !getFromLocalStorage<boolean>(id, props.status ?? false))
+  state.value.status = getFromLocalStorage<boolean>(id, props.status ?? false)
+  if (getFromLocalStorage<boolean>(id, props.status ?? false)) {
+    props.on?.()
+  } else {
+    props.off?.()
+  }
+  $emit('clicked', getFromLocalStorage<boolean>(id, props.status ?? false))
+}
+</script>
+
+<style>
+.on {
+  background-color: lightgrey;
+  border-style: inset;
+}
+</style>
index 7c31ae988e1f64b6363361ab7b870c44219b4b8e..9bfeae6de04620968bfd037e21ba4b1d5c9b8d9a 100644 (file)
@@ -9,16 +9,30 @@
       {{ atgStatus?.start === true ? 'Yes' : 'No' }}
     </td>
     <td class="connectors-table__column">
-      <Button
-        @click="
-          $router.push({
-            name: 'start-transaction',
-            params: { hashId, chargingStationId, connectorId }
-          })
+      <ToggleButton
+        :id="`${hashId}-${connectorId}-start-transaction`"
+        :shared="true"
+        :on="
+          () => {
+            $router.push({
+              name: 'start-transaction',
+              params: { hashId, chargingStationId, connectorId }
+            })
+          }
+        "
+        :off="
+          () => {
+            $router.push({ name: 'charging-stations' })
+          }
+        "
+        @clicked="
+          () => {
+            $emit('need-refresh')
+          }
         "
       >
         Start Transaction
-      </Button>
+      </ToggleButton>
       <Button @click="stopTransaction()">Stop Transaction</Button>
       <Button @click="startAutomaticTransactionGenerator()">Start ATG</Button>
       <Button @click="stopAutomaticTransactionGenerator()">Stop ATG</Button>
 </template>
 
 <script setup lang="ts">
-import { getCurrentInstance } from 'vue'
 import { useToast } from 'vue-toast-notification'
+
 import Button from '@/components/buttons/Button.vue'
+import ToggleButton from '@/components/buttons/ToggleButton.vue'
+import { useUIClient } from '@/composables'
 import type { ConnectorStatus, Status } from '@/types'
 
 const props = defineProps<{
@@ -40,7 +56,9 @@ const props = defineProps<{
   atgStatus?: Status
 }>()
 
-const uiClient = getCurrentInstance()?.appContext.config.globalProperties.$uiClient
+const $emit = defineEmits(['need-refresh'])
+
+const uiClient = useUIClient()
 
 const $toast = useToast()
 
index 2462e6d6172daeba545bbec7f03add81eae16bf2..48edd609b022ae81d6a8d03f6f4b2696baa5fd7a 100644 (file)
@@ -1,40 +1,54 @@
 <template>
   <tr class="cs-table__row">
     <td class="cs-table__column">
-      {{ props.chargingStation.stationInfo.chargingStationId }}
+      {{ chargingStation.stationInfo.chargingStationId }}
     </td>
-    <td class="cs-table__column">{{ props.chargingStation.started === true ? 'Yes' : 'No' }}</td>
+    <td class="cs-table__column">{{ chargingStation.started === true ? 'Yes' : 'No' }}</td>
     <td class="cs-table__column">
       {{ getSupervisionUrl() }}
     </td>
     <td class="cs-table__column">{{ getWSState() }}</td>
     <td class="cs-table__column">
-      {{ props.chargingStation?.bootNotificationResponse?.status ?? 'Ø' }}
+      {{ chargingStation.bootNotificationResponse?.status ?? 'Ø' }}
     </td>
     <td class="cs-table__column">
-      {{ props.chargingStation.stationInfo.templateName }}
+      {{ chargingStation.stationInfo.templateName }}
     </td>
-    <td class="cs-table__column">{{ props.chargingStation.stationInfo.chargePointVendor }}</td>
-    <td class="cs-table__column">{{ props.chargingStation.stationInfo.chargePointModel }}</td>
+    <td class="cs-table__column">{{ chargingStation.stationInfo.chargePointVendor }}</td>
+    <td class="cs-table__column">{{ chargingStation.stationInfo.chargePointModel }}</td>
     <td class="cs-table__column">
-      {{ props.chargingStation.stationInfo.firmwareVersion ?? 'Ø' }}
+      {{ chargingStation.stationInfo.firmwareVersion ?? 'Ø' }}
     </td>
     <td class="cs-table__column">
       <Button @click="startChargingStation()">Start Charging Station</Button>
       <Button @click="stopChargingStation()">Stop Charging Station</Button>
-      <Button
-        @click="
-          $router.push({
-            name: 'set-supervision-url',
-            params: {
-              hashId: props.chargingStation.stationInfo.hashId,
-              chargingStationId: props.chargingStation.stationInfo.chargingStationId
-            }
-          })
+      <ToggleButton
+        :id="`${chargingStation.stationInfo.hashId}-set-supervision-url`"
+        :shared="true"
+        :on="
+          () => {
+            $router.push({
+              name: 'set-supervision-url',
+              params: {
+                hashId: chargingStation.stationInfo.hashId,
+                chargingStationId: chargingStation.stationInfo.chargingStationId
+              }
+            })
+          }
+        "
+        :off="
+          () => {
+            $router.push({ name: 'charging-stations' })
+          }
+        "
+        @clicked="
+          () => {
+            $emit('need-refresh')
+          }
         "
       >
         Set Supervision Url
-      </Button>
+      </ToggleButton>
       <Button @click="openConnection()">Open Connection</Button>
       <Button @click="closeConnection()">Close Connection</Button>
       <Button @click="deleteChargingStation()">Delete Charging Station</Button>
           </tr>
         </thead>
         <tbody id="connectors-table__body">
-          <!-- eslint-disable-next-line vue/valid-v-for -->
           <CSConnector
             v-for="(connector, index) in getConnectorStatuses()"
-            :hash-id="props.chargingStation.stationInfo.hashId"
-            :charging-station-id="props.chargingStation.stationInfo.chargingStationId"
+            :key="index + 1"
+            :hash-id="chargingStation.stationInfo.hashId"
+            :charging-station-id="chargingStation.stationInfo.chargingStationId"
             :connector-id="index + 1"
             :connector="connector"
             :atg-status="getATGStatus(index + 1)"
+            @need-refresh="$emit('need-refresh')"
           />
         </tbody>
       </table>
 </template>
 
 <script setup lang="ts">
-import { getCurrentInstance } from 'vue'
 import { useToast } from 'vue-toast-notification'
-import CSConnector from '@/components/charging-stations/CSConnector.vue'
+
 import Button from '@/components/buttons/Button.vue'
+import ToggleButton from '@/components/buttons/ToggleButton.vue'
+import CSConnector from '@/components/charging-stations/CSConnector.vue'
+import { useUIClient } from '@/composables'
 import type { ChargingStationData, ConnectorStatus, Status } from '@/types'
 
 const props = defineProps<{
   chargingStation: ChargingStationData
 }>()
 
+const $emit = defineEmits(['need-refresh'])
+
 const getConnectorStatuses = (): ConnectorStatus[] => {
   if (Array.isArray(props.chargingStation.evses) && props.chargingStation.evses.length > 0) {
     const connectorStatuses: ConnectorStatus[] = []
@@ -115,7 +134,7 @@ const getWSState = (): string => {
   }
 }
 
-const uiClient = getCurrentInstance()?.appContext.config.globalProperties.$uiClient
+const uiClient = useUIClient()
 
 const $toast = useToast()
 
@@ -127,7 +146,7 @@ const startChargingStation = (): void => {
     })
     .catch((error: Error) => {
       $toast.error('Error at starting charging station')
-      console.error('Error at starting charging station', error)
+      console.error('Error at starting charging station:', error)
     })
 }
 const stopChargingStation = (): void => {
@@ -138,7 +157,7 @@ const stopChargingStation = (): void => {
     })
     .catch((error: Error) => {
       $toast.error('Error at stopping charging station')
-      console.error('Error at stopping charging station', error)
+      console.error('Error at stopping charging station:', error)
     })
 }
 const openConnection = (): void => {
@@ -149,7 +168,7 @@ const openConnection = (): void => {
     })
     .catch((error: Error) => {
       $toast.error('Error at opening connection')
-      console.error('Error at opening connection', error)
+      console.error('Error at opening connection:', error)
     })
 }
 const closeConnection = (): void => {
@@ -160,7 +179,7 @@ const closeConnection = (): void => {
     })
     .catch((error: Error) => {
       $toast.error('Error at closing connection')
-      console.error('Error at closing connection', error)
+      console.error('Error at closing connection:', error)
     })
 }
 const deleteChargingStation = (): void => {
@@ -171,7 +190,7 @@ const deleteChargingStation = (): void => {
     })
     .catch((error: Error) => {
       $toast.error('Error at deleting charging station')
-      console.error('Error at deleting charging station', error)
+      console.error('Error at deleting charging station:', error)
     })
 }
 </script>
@@ -209,6 +228,8 @@ const deleteChargingStation = (): void => {
 
 .connectors-table__column {
   width: calc(100% / 5);
+  display: flex;
+  flex-direction: column;
   text-align: center;
 }
 </style>
index b098c0e5558a61728e92030164071df9471a39fd..3175d6501974b2b3c7afa5251a957e452a5a7c59 100644 (file)
@@ -21,8 +21,9 @@
     <tbody id="cs-table__body">
       <CSData
         v-for="chargingStation in chargingStations"
-        :key="chargingStation.stationInfo?.chargingStationId"
+        :key="chargingStation.stationInfo.hashId"
         :charging-station="chargingStation"
+        @need-refresh="$emit('need-refresh')"
       />
     </tbody>
   </table>
@@ -35,6 +36,8 @@ import type { ChargingStationData } from '@/types'
 defineProps<{
   chargingStations: ChargingStationData[]
 }>()
+
+const $emit = defineEmits(['need-refresh'])
 </script>
 
 <style>
@@ -45,6 +48,7 @@ defineProps<{
   display: flex;
   flex-direction: column;
   overflow: auto hidden;
+  border: solid 0.25px black;
   border-collapse: collapse;
   empty-cells: show;
 }
@@ -57,7 +61,7 @@ defineProps<{
 }
 
 #cs-table__caption {
-  color: white;
+  color: ivory;
   background-color: black;
   font-size: 1.5rem;
   font-weight: bold;
@@ -71,7 +75,7 @@ defineProps<{
   flex-direction: row;
   justify-content: center;
   align-items: center;
-  border: solid 0.5px black;
+  border: solid 0.25px black;
 }
 
 .cs-table__row:nth-of-type(even) {
@@ -80,7 +84,9 @@ defineProps<{
 
 .cs-table__column {
   height: fit-content;
-  width: calc(60% / 10);
+  width: calc((100% - calc(100% / 3)) / 10);
+  display: flex;
+  flex-direction: column;
   text-align: center;
 }
 
@@ -90,6 +96,8 @@ defineProps<{
 
 .cs-table__connectors-column {
   height: fit-content;
-  width: 40%;
+  width: calc(100% / 3);
+  display: flex;
+  flex-direction: column;
 }
 </style>
index d7a5eb3df6b7d270b733438171a27bdfb5f10faf..9522c59213c666a526d48927447024f3e8fc065a 100644 (file)
@@ -1,4 +1,5 @@
 import { useToast } from 'vue-toast-notification'
+
 import {
   ApplicationProtocol,
   AuthenticationType,
@@ -11,6 +12,8 @@ import {
   type UIServerConfigurationSection
 } from '@/types'
 
+import { randomUUID, validateUUID } from './Utils'
+
 type ResponseHandler = {
   procedureName: ProcedureName
   resolve: (value: ResponsePayload | PromiseLike<ResponsePayload>) => void
@@ -21,15 +24,24 @@ export class UIClient {
   private static instance: UIClient | null = null
 
   private ws?: WebSocket
-  private responseHandlers: Map<string, ResponseHandler>
+  private responseHandlers: Map<
+    `${string}-${string}-${string}-${string}-${string}`,
+    ResponseHandler
+  >
 
   private constructor(private uiServerConfiguration: UIServerConfigurationSection) {
     this.openWS()
-    this.responseHandlers = new Map<string, ResponseHandler>()
+    this.responseHandlers = new Map<
+      `${string}-${string}-${string}-${string}-${string}`,
+      ResponseHandler
+    >()
   }
 
-  public static getInstance(uiServerConfiguration: UIServerConfigurationSection): UIClient {
+  public static getInstance(uiServerConfiguration?: UIServerConfigurationSection): UIClient {
     if (UIClient.instance === null) {
+      if (uiServerConfiguration == null) {
+        throw new Error('Cannot initialize UIClient if no configuration is provided')
+      }
       UIClient.instance = new UIClient(uiServerConfiguration)
     }
     return UIClient.instance
@@ -52,6 +64,18 @@ export class UIClient {
     this.ws?.addEventListener(event, listener, options)
   }
 
+  public unregisterWSEventListener<K extends keyof WebSocketEventMap>(
+    event: K,
+    listener: (event: WebSocketEventMap[K]) => void,
+    options?: boolean | AddEventListenerOptions
+  ) {
+    this.ws?.removeEventListener(event, listener, options)
+  }
+
+  public async simulatorState(): Promise<ResponsePayload> {
+    return this.sendRequest(ProcedureName.SIMULATOR_STATE, {})
+  }
+
   public async startSimulator(): Promise<ResponsePayload> {
     return this.sendRequest(ProcedureName.START_SIMULATOR, {})
   }
@@ -190,7 +214,7 @@ export class UIClient {
   ): Promise<ResponsePayload> {
     return new Promise<ResponsePayload>((resolve, reject) => {
       if (this.ws?.readyState === WebSocket.OPEN) {
-        const uuid = crypto.randomUUID()
+        const uuid = randomUUID()
         const msg = JSON.stringify([uuid, procedureName, payload])
         const sendTimeout = setTimeout(() => {
           this.responseHandlers.delete(uuid)
@@ -212,15 +236,30 @@ export class UIClient {
   }
 
   private responseHandler(messageEvent: MessageEvent<string>): void {
-    const response = JSON.parse(messageEvent.data) as ProtocolResponse
+    let response: ProtocolResponse
+    try {
+      response = JSON.parse(messageEvent.data)
+    } catch (error) {
+      useToast().error('Invalid response JSON format')
+      console.error('Invalid response JSON format', error)
+      return
+    }
 
-    if (Array.isArray(response) === false) {
-      throw new Error(`Response not an array: ${JSON.stringify(response, undefined, 2)}`)
+    if (!Array.isArray(response)) {
+      useToast().error('Response not an array')
+      console.error('Response not an array:', response)
+      return
     }
 
     const [uuid, responsePayload] = response
 
-    if (this.responseHandlers.has(uuid) === true) {
+    if (!validateUUID(uuid)) {
+      useToast().error('Response UUID field is invalid')
+      console.error('Response UUID field is invalid:', response)
+      return
+    }
+
+    if (this.responseHandlers.has(uuid)) {
       const { procedureName, resolve, reject } = this.responseHandlers.get(uuid)!
       switch (responsePayload.status) {
         case ResponseStatus.SUCCESS:
index fffd7a54966a54a2e092d4eef2a4d5360b45036b..0c8a83996e905c7e046d624ad9a8bd64124d1df1 100644 (file)
@@ -1,3 +1,5 @@
+import { UIClient } from './UIClient'
+
 export const convertToBoolean = (value: unknown): boolean => {
   let result = false
   if (value != null) {
@@ -33,11 +35,33 @@ export const convertToInt = (value: unknown): number => {
   return changedValue
 }
 
+export const getFromLocalStorage = <T>(key: string, defaultValue: T): T => {
+  const item = localStorage.getItem(key)
+  return item != null ? (JSON.parse(item) as T) : defaultValue
+}
+
 export const setToLocalStorage = <T>(key: string, value: T): void => {
   localStorage.setItem(key, JSON.stringify(value))
 }
 
-export const getFromLocalStorage = <T>(key: string, defaultValue: T): T => {
-  const item = localStorage.getItem(key)
-  return item != null ? (JSON.parse(item) as T) : defaultValue
+export const deleteFromLocalStorage = (key: string): void => {
+  localStorage.removeItem(key)
+}
+
+export const getLocalStorage = (): Storage => {
+  return localStorage
+}
+
+export const randomUUID = (): `${string}-${string}-${string}-${string}-${string}` => {
+  return crypto.randomUUID()
+}
+
+export const validateUUID = (
+  uuid: `${string}-${string}-${string}-${string}-${string}`
+): uuid is `${string}-${string}-${string}-${string}-${string}` => {
+  return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(uuid)
+}
+
+export const useUIClient = (): UIClient => {
+  return UIClient.getInstance()
 }
index bc62dcb5abe4c9245c069b7f1c8e1c3d27ee71fb..6810a971b0689d9d21089aa51770860e398ea1ce 100644 (file)
@@ -1,2 +1,11 @@
 export { UIClient } from './UIClient'
-export { convertToBoolean, convertToInt, getFromLocalStorage, setToLocalStorage } from './Utils'
+export {
+  convertToBoolean,
+  convertToInt,
+  deleteFromLocalStorage,
+  getFromLocalStorage,
+  getLocalStorage,
+  randomUUID,
+  setToLocalStorage,
+  useUIClient
+} from './Utils'
index 5d34ae912e8ecb44f36c276b9542e6c0dc2c6bdc..a39c049ba99052c4e48a2898fe3039508f1aab15 100644 (file)
@@ -1,10 +1,12 @@
-import { type App as AppType, createApp } from 'vue'
+import 'vue-toast-notification/dist/theme-bootstrap.css'
+
+import { type App as AppType, createApp, ref } from 'vue'
 import ToastPlugin from 'vue-toast-notification'
-import type { ConfigurationData } from '@/types'
-import { router } from '@/router'
-import { UIClient, getFromLocalStorage, setToLocalStorage } from '@/composables'
+
 import App from '@/App.vue'
-import 'vue-toast-notification/dist/theme-bootstrap.css'
+import { getFromLocalStorage, setToLocalStorage, UIClient } from '@/composables'
+import { router } from '@/router'
+import type { ChargingStationData, ConfigurationData, UIServerConfigurationSection } from '@/types'
 
 const app = createApp(App)
 
@@ -19,24 +21,26 @@ const initializeApp = (app: AppType, config: ConfigurationData) => {
     config.uiServer = [config.uiServer]
   }
   if (app.config.globalProperties.$configuration == null) {
-    app.config.globalProperties.$configuration = config
+    app.config.globalProperties.$configuration = ref<ConfigurationData>(config)
   }
-  if (!Array.isArray(app.config.globalProperties.$templates)) {
-    app.config.globalProperties.$templates = []
+  if (!Array.isArray(app.config.globalProperties.$templates?.value)) {
+    app.config.globalProperties.$templates = ref<string[]>([])
   }
-  if (!Array.isArray(app.config.globalProperties.$chargingStations)) {
-    app.config.globalProperties.$chargingStations = []
+  if (!Array.isArray(app.config.globalProperties.$chargingStations?.value)) {
+    app.config.globalProperties.$chargingStations = ref<ChargingStationData[]>([])
   }
   if (
     getFromLocalStorage<number | undefined>('uiServerConfigurationIndex', undefined) == null ||
     getFromLocalStorage<number>('uiServerConfigurationIndex', 0) >
-      app.config.globalProperties.$configuration.uiServer.length - 1
+      (app.config.globalProperties.$configuration.value.uiServer as UIServerConfigurationSection[])
+        .length -
+        1
   ) {
     setToLocalStorage<number>('uiServerConfigurationIndex', 0)
   }
   if (app.config.globalProperties.$uiClient == null) {
     app.config.globalProperties.$uiClient = UIClient.getInstance(
-      app.config.globalProperties.$configuration.uiServer[
+      (app.config.globalProperties.$configuration.value.uiServer as UIServerConfigurationSection[])[
         getFromLocalStorage<number>('uiServerConfigurationIndex', 0)
       ]
     )
index bdb22503198337c01c2786bea3dc72c3156be686..cee4f416eb51cf9c5659bcd150bed263a7bfef7f 100644 (file)
@@ -1,8 +1,10 @@
 import { createRouter, createWebHistory } from 'vue-router'
-import ChargingStationsView from '@/views/ChargingStationsView.vue'
-import StartTransaction from '@/components/actions/StartTransaction.vue'
+
 import AddChargingStations from '@/components/actions/AddChargingStations.vue'
 import SetSupervisionUrl from '@/components/actions/SetSupervisionUrl.vue'
+import StartTransaction from '@/components/actions/StartTransaction.vue'
+import ChargingStationsView from '@/views/ChargingStationsView.vue'
+import NotFoundView from '@/views/NotFoundView.vue'
 
 export const router = createRouter({
   history: createWebHistory(),
@@ -39,6 +41,13 @@ export const router = createRouter({
         action: StartTransaction
       },
       props: { default: false, action: true }
+    },
+    {
+      name: 'not-found',
+      path: '/:pathMatch(.*)*',
+      components: {
+        default: NotFoundView
+      }
     }
   ]
 })
index c9d5581c0257bb7069ffe47954c9a36a384fb512..6f0d76e38bac14a980a967753b9db65609cc0a46 100644 (file)
@@ -1,6 +1,14 @@
-declare module '*.vue' {
-  import type { DefineComponent } from 'vue'
-  // eslint-disable-next-line @typescript-eslint/ban-types
-  const component: DefineComponent<{}, {}, unknown>
-  export default component
+export {}
+
+declare module 'vue' {
+  export interface GlobalComponents {
+    RouterLink: (typeof import('vue-router'))['RouterLink']
+    RouterView: (typeof import('vue-router'))['RouterView']
+  }
+  interface ComponentCustomProperties {
+    $configuration: import('vue').Ref<import('@/types').ConfigurationData>
+    $templates: import('vue').Ref<string[]>
+    $chargingStations: import('vue').Ref<import('@/types').ChargingStationData[]>
+    $uiClient: import('@/composables').UIClient
+  }
 }
index 6814737e811106b70d9212e61cff24a9b007500a..167e3a1a8498b79c272bd3792be6ced370e32199 100644 (file)
@@ -6,7 +6,7 @@ export enum IdTagDistribution {
   CONNECTOR_AFFINITY = 'connector-affinity'
 }
 
-export interface AutomaticTransactionGeneratorConfiguration {
+export interface AutomaticTransactionGeneratorConfiguration extends JsonObject {
   enable: boolean
   minDuration: number
   maxDuration: number
@@ -19,12 +19,12 @@ export interface AutomaticTransactionGeneratorConfiguration {
   idTagDistribution?: IdTagDistribution
 }
 
-export interface ChargingStationAutomaticTransactionGeneratorConfiguration {
+export interface ChargingStationAutomaticTransactionGeneratorConfiguration extends JsonObject {
   automaticTransactionGenerator?: AutomaticTransactionGeneratorConfiguration
   automaticTransactionGeneratorStatuses?: Status[]
 }
 
-export type ChargingStationData = {
+export interface ChargingStationData extends JsonObject {
   started: boolean
   stationInfo: ChargingStationInfo
   connectors: ConnectorStatus[]
@@ -50,7 +50,7 @@ export enum OCPP16FirmwareStatus {
   Installed = 'Installed'
 }
 
-export interface FirmwareUpgrade {
+export interface FirmwareUpgrade extends JsonObject {
   versionUpgrade?: {
     patternGroup?: number
     step?: number
@@ -74,7 +74,7 @@ export interface ChargingStationOptions extends JsonObject {
   stopTransactionsOnStopped?: boolean
 }
 
-export type ChargingStationInfo = {
+export interface ChargingStationInfo extends JsonObject {
   hashId: string
   templateIndex: number
   templateName: string
@@ -137,20 +137,20 @@ export type ChargingStationInfo = {
   messageTriggerSupport?: Record<MessageTrigger, boolean>
 }
 
-export interface ChargingStationOcppConfiguration {
+export interface ChargingStationOcppConfiguration extends JsonObject {
   configurationKey?: ConfigurationKey[]
 }
 
-export type ConfigurationKey = OCPPConfigurationKey & {
+export interface ConfigurationKey extends OCPPConfigurationKey {
   visible?: boolean
   reboot?: boolean
 }
 
-export type OCPPConfigurationKey = {
+export interface OCPPConfigurationKey extends JsonObject {
   key: string
   readonly: boolean
   value?: string
-} & JsonObject
+}
 
 export enum OCPP16IncomingRequestCommand {
   RESET = 'Reset',
@@ -216,7 +216,7 @@ export const MessageTrigger = {
 } as const
 export type MessageTrigger = OCPP16MessageTrigger
 
-type CommandsSupport = {
+interface CommandsSupport extends JsonObject {
   incomingCommands: Record<IncomingRequestCommand, boolean>
   outgoingCommands?: Record<RequestCommand, boolean>
 }
@@ -250,7 +250,7 @@ export enum AmpereUnits {
   AMPERE = 'A'
 }
 
-export type ConnectorStatus = {
+export interface ConnectorStatus extends JsonObject {
   availability: AvailabilityType
   bootStatus?: ChargePointStatus
   status?: ChargePointStatus
@@ -266,7 +266,7 @@ export type ConnectorStatus = {
   transactionEnergyActiveImportRegisterValue?: number // In Wh
 }
 
-export type EvseStatus = {
+export interface EvseStatus extends JsonObject {
   availability: AvailabilityType
   connectors?: ConnectorStatus[]
 }
@@ -291,7 +291,7 @@ export enum OCPP16ChargePointStatus {
 }
 export type ChargePointStatus = OCPP16ChargePointStatus
 
-export type Status = {
+export interface Status extends JsonObject {
   start?: boolean
   startDate?: Date
   lastRunDate?: Date
index 18ea267a5e16052c1e58ff4afd9ee5d5c826ba46..1c8de85bc20c623f1656104b01bebf1397f22407 100644 (file)
@@ -1,10 +1,10 @@
 import type { AuthenticationType, Protocol, ProtocolVersion } from './UIProtocol'
 
-export type ConfigurationData = {
+export interface ConfigurationData {
   uiServer: UIServerConfigurationSection | UIServerConfigurationSection[]
 }
 
-export type UIServerConfigurationSection = {
+export interface UIServerConfigurationSection {
   name?: string
   host: string
   port: number
index 3a2765f6120552337b53fd1499d9b2a2b35214b4..8f597383a4bba8d98e727288dd987a579c1c0328 100644 (file)
@@ -1,3 +1,3 @@
-export type JsonType = JsonPrimitive | JsonType[] | JsonObject
+type JsonPrimitive = string | number | boolean | Date | null
 export type JsonObject = { [key in string]?: JsonType }
-export type JsonPrimitive = string | number | boolean | Date | null
+export type JsonType = JsonPrimitive | JsonType[] | JsonObject
index afc55b03069540e6f7a48b7e86dec88f4cea1291..8522aa11dcd8c4a10ac40a7e95ec94523bad1992 100644 (file)
@@ -17,14 +17,22 @@ export enum AuthenticationType {
   PROTOCOL_BASIC_AUTH = 'protocol-basic-auth'
 }
 
-export type ProtocolRequest = [string, ProcedureName, RequestPayload]
-export type ProtocolResponse = [string, ResponsePayload]
+export type ProtocolRequest = [
+  `${string}-${string}-${string}-${string}-${string}`,
+  ProcedureName,
+  RequestPayload
+]
+export type ProtocolResponse = [
+  `${string}-${string}-${string}-${string}-${string}`,
+  ResponsePayload
+]
 
 export type ProtocolRequestHandler = (
   payload: RequestPayload
 ) => ResponsePayload | Promise<ResponsePayload>
 
 export enum ProcedureName {
+  SIMULATOR_STATE = 'simulatorState',
   START_SIMULATOR = 'startSimulator',
   STOP_SIMULATOR = 'stopSimulator',
   LIST_TEMPLATES = 'listTemplates',
@@ -56,3 +64,16 @@ export interface ResponsePayload extends JsonObject {
   status: ResponseStatus
   hashIds?: string[]
 }
+
+interface TemplateStatistics extends JsonObject {
+  configured: number
+  added: number
+  started: number
+  indexes: number[]
+}
+
+export interface SimulatorState extends JsonObject {
+  version: string
+  started: boolean
+  templateStatistics: Record<string, TemplateStatistics>
+}
index a201ca7b88e0ee92e48635abf8ef63285a0fe136..d730f0d634160a7642660d90251e0c4fdae6d198 100644 (file)
@@ -15,5 +15,6 @@ export {
   ProtocolVersion,
   type RequestPayload,
   type ResponsePayload,
-  ResponseStatus
+  ResponseStatus,
+  type SimulatorState
 } from './UIProtocol'
index a19c14c32283c7d35591552b9486c6a8f048b85a..695b88608b4df60c0e90ff848167057ddcbcdfb7 100644 (file)
 <template>
   <Container id="charging-stations-container">
-    <Container
-      v-show="Array.isArray(uiServerConfigurations) && uiServerConfigurations.length > 1"
-      id="ui-server-container"
-    >
-      <select
-        id="ui-server-selector"
-        v-model="state.uiServerIndex"
-        @change="
-          () => {
-            if (
-              getFromLocalStorage<number>('uiServerConfigurationIndex', 0) !== state.uiServerIndex
-            ) {
-              app?.appContext.config.globalProperties.$uiClient.setConfiguration(
-                app?.appContext.config.globalProperties.$configuration.uiServer[state.uiServerIndex]
-              )
-              initializeWSEventListeners()
-              app?.appContext.config.globalProperties.$uiClient.registerWSEventListener(
-                'open',
-                () => {
-                  setToLocalStorage<number>('uiServerConfigurationIndex', state.uiServerIndex)
-                  $router.currentRoute.value.name !== 'charging-stations' &&
-                    $router.push({ name: 'charging-stations' })
-                },
-                { once: true }
-              )
-              app?.appContext.config.globalProperties.$uiClient.registerWSEventListener(
-                'error',
-                () => {
-                  state.uiServerIndex = getFromLocalStorage<number>('uiServerConfigurationIndex', 0)
-                  app?.appContext.config.globalProperties.$uiClient.setConfiguration(
-                    app?.appContext.config.globalProperties.$configuration.uiServer[
-                      getFromLocalStorage<number>('uiServerConfigurationIndex', 0)
-                    ]
-                  )
-                  initializeWSEventListeners()
-                },
-                { once: true }
-              )
+    <Container id="buttons-container">
+      <Container
+        v-show="Array.isArray(uiServerConfigurations) && uiServerConfigurations.length > 1"
+        id="ui-server-container"
+      >
+        <select
+          id="ui-server-selector"
+          v-model="state.uiServerIndex"
+          @change="
+            () => {
+              if (
+                getFromLocalStorage<number>('uiServerConfigurationIndex', 0) !== state.uiServerIndex
+              ) {
+                $uiClient.setConfiguration(
+                  ($configuration.value.uiServer as UIServerConfigurationSection[])[
+                    state.uiServerIndex
+                  ]
+                )
+                registerWSEventListeners()
+                $uiClient.registerWSEventListener(
+                  'open',
+                  () => {
+                    setToLocalStorage<number>('uiServerConfigurationIndex', state.uiServerIndex)
+                    clearToggleButtons()
+                    $route.name !== 'charging-stations' &&
+                      $router.push({ name: 'charging-stations' })
+                  },
+                  { once: true }
+                )
+                $uiClient.registerWSEventListener(
+                  'error',
+                  () => {
+                    state.uiServerIndex = getFromLocalStorage<number>(
+                      'uiServerConfigurationIndex',
+                      0
+                    )
+                    $uiClient.setConfiguration(
+                      ($configuration.value.uiServer as UIServerConfigurationSection[])[
+                        getFromLocalStorage<number>('uiServerConfigurationIndex', 0)
+                      ]
+                    )
+                    registerWSEventListeners()
+                  },
+                  { once: true }
+                )
+              }
             }
+          "
+        >
+          <option
+            v-for="uiServerConfiguration in uiServerConfigurations"
+            :key="uiServerConfiguration.index"
+            :value="uiServerConfiguration.index"
+          >
+            {{
+              uiServerConfiguration.configuration.name ?? uiServerConfiguration.configuration.host
+            }}
+          </option>
+        </select>
+      </Container>
+      <ToggleButton
+        :id="'simulator'"
+        :key="state.renderSimulator"
+        :status="simulatorState?.started"
+        :on="() => startSimulator()"
+        :off="() => stopSimulator()"
+        :class="simulatorButtonClass"
+      >
+        {{ simulatorButtonMessage }}
+      </ToggleButton>
+      <ToggleButton
+        :id="'add-charging-stations'"
+        :key="state.renderAddChargingStations"
+        :shared="true"
+        :on="
+          () => {
+            $router.push({ name: 'add-charging-stations' })
+          }
+        "
+        :off="
+          () => {
+            $router.push({ name: 'charging-stations' })
+          }
+        "
+        @clicked="
+          () => {
+            state.renderChargingStations = randomUUID()
           }
         "
       >
-        <option
-          v-for="uiServerConfiguration in uiServerConfigurations"
-          :value="uiServerConfiguration.index"
-        >
-          {{ uiServerConfiguration.configuration.name ?? uiServerConfiguration.configuration.host }}
-        </option>
-      </select>
-    </Container>
-    <Container id="buttons-container">
-      <Button @click="startSimulator()">Start Simulator</Button>
-      <Button @click="stopSimulator()">Stop Simulator</Button>
-      <Button @click="$router.push({ name: 'add-charging-stations' })">
         Add Charging Stations
-      </Button>
+      </ToggleButton>
       <ReloadButton
         id="reload-button"
-        :loading="state.loading"
-        @click="loadChargingStations(() => (state.renderChargingStationsList = randomUUID()))"
+        :loading="state.gettingChargingStations"
+        @click="getChargingStations()"
       />
     </Container>
     <CSTable
-      v-show="
-        Array.isArray(app?.appContext.config.globalProperties.$chargingStations) &&
-        app?.appContext.config.globalProperties.$chargingStations.length > 0
+      v-show="Array.isArray($chargingStations.value) && $chargingStations.value.length > 0"
+      :key="state.renderChargingStations"
+      :charging-stations="$chargingStations.value"
+      @need-refresh="
+        () => {
+          state.renderAddChargingStations = randomUUID()
+          state.renderChargingStations = randomUUID()
+        }
       "
-      :key="state.renderChargingStationsList"
-      :charging-stations="app?.appContext.config.globalProperties.$chargingStations"
     />
   </Container>
 </template>
 
 <script setup lang="ts">
-import { getCurrentInstance, onMounted, ref } from 'vue'
+import { computed, getCurrentInstance, onMounted, onUnmounted, ref, watch } from 'vue'
 import { useToast } from 'vue-toast-notification'
+
+import ReloadButton from '@/components/buttons/ReloadButton.vue'
+import ToggleButton from '@/components/buttons/ToggleButton.vue'
 import CSTable from '@/components/charging-stations/CSTable.vue'
-import type { ResponsePayload, UIServerConfigurationSection } from '@/types'
 import Container from '@/components/Container.vue'
-import ReloadButton from '@/components/buttons/ReloadButton.vue'
-import Button from '@/components/buttons/Button.vue'
-import { getFromLocalStorage, setToLocalStorage } from '@/composables'
+import {
+  deleteFromLocalStorage,
+  getFromLocalStorage,
+  getLocalStorage,
+  randomUUID,
+  setToLocalStorage,
+  useUIClient
+} from '@/composables'
+import type {
+  ChargingStationData,
+  ResponsePayload,
+  SimulatorState,
+  UIServerConfigurationSection
+} from '@/types'
+
+const simulatorState = ref<SimulatorState | undefined>(undefined)
+
+const simulatorButtonClass = computed<string>(() =>
+  simulatorState.value?.started === true ? 'simulator-stop-button' : 'simulator-start-button'
+)
+const simulatorButtonMessage = computed<string>(
+  () =>
+    `${simulatorState.value?.started === true ? 'Stop' : 'Start'} Simulator${simulatorState.value?.version != null ? ` (${simulatorState.value.version})` : ''}`
+)
+
+const state = ref<{
+  renderSimulator: `${string}-${string}-${string}-${string}-${string}`
+  renderAddChargingStations: `${string}-${string}-${string}-${string}-${string}`
+  renderChargingStations: `${string}-${string}-${string}-${string}-${string}`
+  gettingSimulatorState: boolean
+  gettingTemplates: boolean
+  gettingChargingStations: boolean
+  uiServerIndex: number
+}>({
+  renderSimulator: randomUUID(),
+  renderAddChargingStations: randomUUID(),
+  renderChargingStations: randomUUID(),
+  gettingSimulatorState: false,
+  gettingTemplates: false,
+  gettingChargingStations: false,
+  uiServerIndex: getFromLocalStorage<number>('uiServerConfigurationIndex', 0)
+})
 
-const randomUUID = (): `${string}-${string}-${string}-${string}-${string}` => {
-  return crypto.randomUUID()
+const clearToggleButtons = (): void => {
+  for (const key in getLocalStorage()) {
+    if (key.includes('toggle-button')) {
+      deleteFromLocalStorage(key)
+    }
+  }
+  state.value.renderChargingStations = randomUUID()
+  state.value.renderAddChargingStations = randomUUID()
 }
 
 const app = getCurrentInstance()
 
-const initializeWSEventListeners = () => {
-  app?.appContext.config.globalProperties.$uiClient.registerWSEventListener('open', () => {
+watch(app!.appContext.config.globalProperties.$chargingStations, () => {
+  state.value.renderChargingStations = randomUUID()
+})
+
+watch(simulatorState, () => {
+  state.value.renderSimulator = randomUUID()
+})
+
+const clearTemplates = (): void => {
+  if (app != null) {
+    app.appContext.config.globalProperties.$templates.value = []
+  }
+}
+
+const clearChargingStations = (): void => {
+  if (app != null) {
+    app.appContext.config.globalProperties.$chargingStations.value = []
+  }
+}
+
+const uiClient = useUIClient()
+
+const $toast = useToast()
+
+const getSimulatorState = (): void => {
+  if (state.value.gettingSimulatorState === false) {
+    state.value.gettingSimulatorState = true
+    uiClient
+      .simulatorState()
+      .then((response: ResponsePayload) => {
+        simulatorState.value = response.state as SimulatorState
+      })
+      .catch((error: Error) => {
+        $toast.error('Error at fetching simulator state')
+        console.error('Error at fetching simulator state:', error)
+      })
+      .finally(() => {
+        state.value.gettingSimulatorState = false
+      })
+  }
+}
+
+const getTemplates = (): void => {
+  if (state.value.gettingTemplates === false) {
+    state.value.gettingTemplates = true
     uiClient
       .listTemplates()
       .then((response: ResponsePayload) => {
         if (app != null) {
-          app.appContext.config.globalProperties.$templates = response.templates
+          app.appContext.config.globalProperties.$templates.value = response.templates as string[]
         }
       })
       .catch((error: Error) => {
-        if (app != null) {
-          app.appContext.config.globalProperties.$templates = []
-        }
+        clearTemplates()
         $toast.error('Error at fetching charging station templates')
         console.error('Error at fetching charging station templates:', error)
       })
-    loadChargingStations(() => (state.value.renderChargingStationsList = randomUUID()))
-  })
-  app?.appContext.config.globalProperties.$uiClient.registerWSEventListener('error', () => {
-    app.appContext.config.globalProperties.$chargingStations = []
-    state.value.renderChargingStationsList = randomUUID()
-  })
-  app?.appContext.config.globalProperties.$uiClient.registerWSEventListener('close', () => {
-    app.appContext.config.globalProperties.$chargingStations = []
-    state.value.renderChargingStationsList = randomUUID()
-  })
+      .finally(() => {
+        state.value.gettingTemplates = false
+      })
+  }
 }
 
-onMounted(() => {
-  initializeWSEventListeners()
-})
-
-const state = ref({
-  renderChargingStationsList: randomUUID(),
-  loading: false,
-  uiServerIndex: getFromLocalStorage<number>('uiServerConfigurationIndex', 0)
-})
-
-const uiClient = app?.appContext.config.globalProperties.$uiClient
-const uiServerConfigurations: { configuration: UIServerConfigurationSection; index: number }[] =
-  app?.appContext.config.globalProperties.$configuration.uiServer.map(
-    (configuration: UIServerConfigurationSection, index: number) => ({
-      configuration,
-      index
-    })
-  )
-
-const $toast = useToast()
-
-const loadChargingStations = (renderCallback?: () => void): void => {
-  if (state.value.loading === false) {
-    state.value.loading = true
+const getChargingStations = (): void => {
+  if (state.value.gettingChargingStations === false) {
+    state.value.gettingChargingStations = true
     uiClient
       .listChargingStations()
       .then((response: ResponsePayload) => {
         if (app != null) {
-          app.appContext.config.globalProperties.$chargingStations = response.chargingStations
+          app.appContext.config.globalProperties.$chargingStations.value =
+            response.chargingStations as ChargingStationData[]
         }
       })
       .catch((error: Error) => {
-        if (app != null) {
-          app.appContext.config.globalProperties.$chargingStations = []
-        }
+        clearChargingStations()
         $toast.error('Error at fetching charging stations')
         console.error('Error at fetching charging stations:', error)
       })
       .finally(() => {
-        if (renderCallback != null) {
-          renderCallback()
-        }
-        state.value.loading = false
+        state.value.gettingChargingStations = false
       })
   }
 }
 
+const getData = (): void => {
+  getSimulatorState()
+  getTemplates()
+  getChargingStations()
+}
+
+const registerWSEventListeners = () => {
+  uiClient.registerWSEventListener('open', getData)
+  uiClient.registerWSEventListener('error', clearChargingStations)
+  uiClient.registerWSEventListener('close', clearChargingStations)
+}
+
+const unregisterWSEventListeners = () => {
+  uiClient.unregisterWSEventListener('open', getData)
+  uiClient.unregisterWSEventListener('error', clearChargingStations)
+  uiClient.unregisterWSEventListener('close', clearChargingStations)
+}
+
+onMounted(() => {
+  registerWSEventListeners()
+})
+
+onUnmounted(() => {
+  unregisterWSEventListeners()
+})
+
+const uiServerConfigurations: { index: number; configuration: UIServerConfigurationSection }[] = (
+  app?.appContext.config.globalProperties.$configuration.value
+    .uiServer as UIServerConfigurationSection[]
+).map((configuration: UIServerConfigurationSection, index: number) => ({
+  index,
+  configuration
+}))
+
 const startSimulator = (): void => {
   uiClient
     .startSimulator()
@@ -174,20 +304,24 @@ const startSimulator = (): void => {
       $toast.error('Error at starting simulator')
       console.error('Error at starting simulator:', error)
     })
+    .finally(() => {
+      getSimulatorState()
+    })
 }
 const stopSimulator = (): void => {
   uiClient
     .stopSimulator()
     .then(() => {
-      if (app != null) {
-        app.appContext.config.globalProperties.$chargingStations = []
-      }
+      clearChargingStations()
       $toast.success('Simulator successfully stopped')
     })
     .catch((error: Error) => {
       $toast.error('Error at stopping simulator')
       console.error('Error at stopping simulator:', error)
     })
+    .finally(() => {
+      getSimulatorState()
+    })
 }
 </script>
 
@@ -201,17 +335,44 @@ const stopSimulator = (): void => {
 
 #ui-server-container {
   display: flex;
-  flex-direction: row;
+  justify-content: center;
+  border-style: outset;
 }
 
 #ui-server-selector {
   width: 100%;
+  background-color: rgb(239, 239, 239);
+  font: small-caption;
   text-align: center;
 }
 
+#ui-server-selector:hover {
+  background-color: rgb(229, 229, 229);
+}
+
 #buttons-container {
   display: flex;
   flex-direction: row;
+  position: sticky;
+  top: 0;
+}
+
+.simulator-start-button {
+  color: ivory;
+  background-color: green;
+}
+
+.simulator-start-button:hover {
+  background-color: rgb(0, 98, 0);
+}
+
+.simulator-stop-button {
+  color: ivory;
+  background-color: red;
+}
+
+.simulator-stop-button:hover {
+  background-color: rgb(225, 0, 0);
 }
 
 #action-button {
@@ -219,13 +380,9 @@ const stopSimulator = (): void => {
 }
 
 #reload-button {
-  flex: auto;
-  color: white;
+  color: ivory;
   background-color: blue;
   font-size: 1.5rem;
-  font-weight: bold;
-  align-items: center;
-  justify-content: center;
 }
 
 #reload-button:hover {
@@ -233,12 +390,13 @@ const stopSimulator = (): void => {
 }
 
 #reload-button:active {
-  background-color: red;
+  background-color: darkblue;
 }
 
 #action {
-  color: white;
+  min-width: max-content;
+  color: ivory;
   background-color: black;
-  padding: 1%;
+  padding: 0.8%;
 }
 </style>
diff --git a/ui/web/src/views/NotFoundView.vue b/ui/web/src/views/NotFoundView.vue
new file mode 100644 (file)
index 0000000..a87072a
--- /dev/null
@@ -0,0 +1,15 @@
+<template><Container id="not-found">404 - Not found</Container></template>
+
+<script setup lang="ts">
+import Container from '@/components/Container.vue'
+</script>
+
+<style>
+#not-found {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 2rem;
+  font-weight: bold;
+}
+</style>
index c620c4681ddc5d73fe051ffb5846ca3bbec8b1a9..46a8bb75884f67da0c4b2ecfc9138bff44fb7ab2 100644 (file)
@@ -2,6 +2,7 @@ import { createServer } from 'node:http'
 import { dirname, join } from 'node:path'
 import { env } from 'node:process'
 import { fileURLToPath } from 'node:url'
+
 import finalhandler from 'finalhandler'
 import serveStatic from 'serve-static'
 
@@ -11,8 +12,6 @@ const uiPath = join(dirname(fileURLToPath(import.meta.url)), './dist')
 
 const serve = serveStatic(uiPath)
 
-const server = createServer(function onRequest(req, res) {
-  serve(req, res, finalhandler(req, res))
-})
+const server = createServer((req, res) => serve(req, res, finalhandler(req, res)))
 
 server.listen(PORT, () => console.info(`Web UI running at: http://localhost:${PORT}`))
index 0a364a6cf62fc3d184fd0dac8b35c6efbcb29b83..8a417af8619544233f04c94705de0e4738c8d641 100644 (file)
@@ -1,5 +1,6 @@
-import { expect, test } from 'vitest'
 import { shallowMount } from '@vue/test-utils'
+import { expect, test } from 'vitest'
+
 import CSTable from '@/components/charging-stations/CSTable.vue'
 import type { ChargingStationData } from '@/types'
 
index a5a1d53342af3729580add99ae03ef4cf7394aa2..6d5f08a34a04b179e7e53e5e3d57dc33caf0f3cb 100644 (file)
@@ -1,7 +1,8 @@
-import { URL, fileURLToPath } from 'node:url'
-import { defineConfig } from 'vite'
+import { fileURLToPath, URL } from 'node:url'
+
 import vue from '@vitejs/plugin-vue'
 import vueJsx from '@vitejs/plugin-vue-jsx'
+import { defineConfig } from 'vite'
 
 export default defineConfig({
   plugins: [vue(), vueJsx()],
index 39905b29c5bf4f670d2ab327e584209f0a81aafb..c5276e5caa0b040e1b00d0d8de6e5e3b6f53335b 100644 (file)
@@ -1,6 +1,8 @@
 import { fileURLToPath } from 'node:url'
-import { configDefaults, defineConfig } from 'vitest/config'
+
 import { mergeConfig } from 'vite'
+import { configDefaults, defineConfig } from 'vitest/config'
+
 import viteConfig from './vite.config'
 
 export default mergeConfig(