Repository: OWASP/threat-dragon
Pull request #1016 (2024-07-11 15:32:54)

Merging from Main


Patch:
diff --git a/.github/workflows/browserstack.yaml b/.github/workflows/browserstack.yaml
index 8ccf887b..6c5aae02 100644
--- a/.github/workflows/browserstack.yaml
+++ b/.github/workflows/browserstack.yaml
@@ -25,7 +25,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
diff --git a/.github/workflows/housekeeping.yaml b/.github/workflows/housekeeping.yaml
index fc2c5dbe..06d4bd2c 100644
--- a/.github/workflows/housekeeping.yaml
+++ b/.github/workflows/housekeeping.yaml
@@ -65,7 +65,7 @@ jobs:
           ref: main
 
       - name: Run vulnerability scanner
-        uses: aquasecurity/trivy-action@0.22.0
+        uses: aquasecurity/trivy-action@0.23.0
         with:
           image-ref: '${{ env.IMAGE_NAME }}:latest'
           format: 'template'
diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml
index a9862556..3896c99a 100644
--- a/.github/workflows/pull_request.yaml
+++ b/.github/workflows/pull_request.yaml
@@ -8,7 +8,7 @@ on:
 env:
   # threatdragon is the working area on docker hub so use this area
   # owasp/threat-dragon is the final release area so DO NOT use that
-  IMAGE_NAME: "threatdragon/owasp-threat-dragon:PR-${{ github.event.number }}"
+  IMAGE_NAME: "pr-${{ github.event.number }}"
 
 # for security reasons the github actions are pinned to specific release versions
 jobs:
@@ -29,7 +29,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -62,7 +62,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -95,7 +95,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -106,7 +106,7 @@ jobs:
         run: npm clean-install
 
       - name: Desktop lint
-        run: npm run lint
+        run: npm run lint:desktop
 
       - name: Run unit tests
         run: npm run test:desktop
@@ -152,38 +152,50 @@ jobs:
 
       - name: Set up Docker Buildx
         id: buildx
-        uses: docker/setup-buildx-action@v3.3.0
+        uses: docker/setup-buildx-action@v3.4.0
         with:
           install: true
 
-      - name: Setup dockerx cache
-        uses: actions/cache@v4.0.0
+      - name: Cache Docker layers
+        uses: actions/cache@v4.0.2
         with:
           path: /tmp/.buildx-cache
           key: ${{ runner.os }}-buildx-${{ hashFiles('Dockerfile') }}
           restore-keys: |
             ${{ runner.os }}-buildx-
 
-      - name: Login to Docker Hub
-        uses: docker/login-action@v3.2.0
-        with:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
-
-      - name: Build and push
+      - name: Build for amd64
         id: docker_build
-        uses: docker/build-push-action@v5.4.0
+        uses: docker/build-push-action@v6.3.0
         with:
           context: ./
           file: ./Dockerfile
           builder: ${{ steps.buildx.outputs.name }}
-          push: true
           tags:  ${{ env.IMAGE_NAME }}
+          outputs: type=docker,dest=/tmp/${{ env.IMAGE_NAME }}.tar
           cache-from: type=local,src=/tmp/.buildx-cache
-          cache-to: type=local,dest=/tmp/.buildx-cache
+          cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
           platforms: linux/amd64
           load: true
 
+      - name: Upload docker local image
+        uses: actions/upload-artifact@v4.3.4
+        with:
+          name: ${{ env.IMAGE_NAME }}
+          path: /tmp/${{ env.IMAGE_NAME }}.tar
+
+      - name: Check docker local image
+        run: |
+          docker load --input /tmp/${{ env.IMAGE_NAME }}.tar
+          docker image ls -a 
+
+      - # Temp fix for large cache bug
+        # https://github.com/docker/build-push-action/issues/252
+        name: Move cache
+        run: |
+          rm -rf /tmp/.buildx-cache
+          mv /tmp/.buildx-cache-new /tmp/.buildx-cache
+
   e2e_smokes:
     name: Site e2e smokes
     runs-on: ubuntu-24.04
@@ -196,6 +208,16 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v4.1.1
 
+      - name: Download docker local image
+        uses: actions/download-artifact@v4.1.8
+        with:
+          name: ${{ env.IMAGE_NAME }}
+          path: /tmp
+      
+      - name: Load docker local image
+        run: |
+          docker load --input /tmp/${{ env.IMAGE_NAME }}.tar
+
       - name: Run Threat Dragon
         run: |
           docker run -d \
@@ -215,7 +237,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -229,11 +251,11 @@ jobs:
         run: npm run test:e2e-ci-smokes
 
       - name: Upload e2e videos
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: e2e_vids.zip
           path: td.vue/tests/e2e/videos
-        if: ${{ always() }}
+        if: ${{ failure() && hashFiles('td.vue/tests/e2e/videos/') != '' }}
 
   e2e_tests:
     name: Site e2e tests
@@ -247,6 +269,16 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v4.1.1
 
+      - name: Download docker local image
+        uses: actions/download-artifact@v4.1.8
+        with:
+          name: ${{ env.IMAGE_NAME }}
+          path: /tmp
+      
+      - name: Load docker local image
+        run: |
+          docker load --input /tmp/${{ env.IMAGE_NAME }}.tar
+
       - name: Run Threat Dragon
         run: |
           docker run -d \
@@ -266,7 +298,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -280,18 +312,28 @@ jobs:
         run: npm run test:e2e-ci
 
       - name: Upload e2e videos
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: e2e_vids.zip
           path: td.vue/tests/e2e/videos
-        if: ${{ always() }}
+        if: ${{ failure() && hashFiles('td.vue/tests/e2e/videos/') != '' }}
 
-  zap_scan_web:
-    name: Site zap scan
+  zap_scan_web_app:
+    name: Local site zap scan
     runs-on: ubuntu-24.04
     needs: build_docker_image
 
     steps:
+      - name: Download docker local image
+        uses: actions/download-artifact@v4.1.8
+        with:
+          name: ${{ env.IMAGE_NAME }}
+          path: /tmp
+      
+      - name: Load docker local image
+        run: |
+          docker load --input /tmp/${{ env.IMAGE_NAME }}.tar
+
       - name: Run Threat Dragon
         run: |
           docker run -d \
@@ -330,8 +372,18 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v4.1.1
 
+      - name: Download docker local image
+        uses: actions/download-artifact@v4.1.8
+        with:
+          name: ${{ env.IMAGE_NAME }}
+          path: /tmp
+      
+      - name: Load docker local image
+        run: |
+          docker load --input /tmp/${{ env.IMAGE_NAME }}.tar
+
       - name: Run Trivy vulnerability scanner
-        uses: aquasecurity/trivy-action@0.22.0
+        uses: aquasecurity/trivy-action@0.23.0
         with:
           image-ref: '${{ env.IMAGE_NAME }}'
           format: 'table'
diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml
index c3cd87f1..b28716ff 100644
--- a/.github/workflows/push.yaml
+++ b/.github/workflows/push.yaml
@@ -32,7 +32,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -52,7 +52,7 @@ jobs:
         run: npm run make-sbom
 
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms-server
           path: './td.server/sbom.*'
@@ -74,7 +74,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -107,7 +107,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -118,7 +118,7 @@ jobs:
         run: npm clean-install
 
       - name: lint
-        run: npm run lint
+        run: npm run lint:desktop
 
       - name: Unit test
         run: npm run test:desktop
@@ -162,14 +162,17 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v4.1.1
 
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v3
+
       - name: Set up Docker Buildx
         id: buildx
-        uses: docker/setup-buildx-action@v3.3.0
+        uses: docker/setup-buildx-action@v3.4.0
         with:
           install: true
 
-      - name: Setup dockerx cache
-        uses: actions/cache@v4.0.0
+      - name: Cache Docker layers
+        uses: actions/cache@v4.0.2
         with:
           path: /tmp/.buildx-cache
           key: ${{ runner.os }}-buildx-${{ hashFiles('Dockerfile') }}
@@ -182,9 +185,10 @@ jobs:
           username: ${{ secrets.DOCKERHUB_USERNAME }}
           password: ${{ secrets.DOCKERHUB_TOKEN }}
 
-      - name: Build and push
-        id: docker_build
-        uses: docker/build-push-action@v5.4.0
+      # platform manifests not (yet) supported, so split out architectures
+      - name: Build  for amd64 and push latest
+        id: docker_build_amd64
+        uses: docker/build-push-action@v6.3.0
         with:
           context: ./
           file: ./Dockerfile
@@ -192,22 +196,43 @@ jobs:
           push: true
           tags:  ${{ env.IMAGE_NAME }}
           cache-from: type=local,src=/tmp/.buildx-cache
-          cache-to: type=local,dest=/tmp/.buildx-cache
+          cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
           platforms: linux/amd64
           load: true
 
+      - name: Build for arm64 and push latest-arm64
+        id: docker_build_arm64
+        uses: docker/build-push-action@v6.3.0
+        with:
+          context: ./
+          file: ./Dockerfile
+          builder: ${{ steps.buildx.outputs.name }}
+          push: true
+          tags:  ${{ env.IMAGE_NAME }}-arm64
+          cache-from: type=local,src=/tmp/.buildx-cache
+          cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
+          platforms: linux/arm64
+          load: true
+
       - name: fetch app SBOMs
         run: docker run --rm --entrypoint tar "$IMAGE_ID" -c boms | tar -xv
         env:
-          IMAGE_ID: ${{ steps.docker_build.outputs.imageid }}
+          IMAGE_ID: ${{ steps.docker_build_amd64.outputs.imageid }}
 
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms-container-image-app
           path: './boms/*'
           if-no-files-found: error
 
+      - # Temp fix for large cache bug
+        # https://github.com/docker/build-push-action/issues/252
+        name: Move cache
+        run: |
+          rm -rf /tmp/.buildx-cache
+          mv /tmp/.buildx-cache-new /tmp/.buildx-cache
+
   heroku_deploy:
     name: Upload to Heroku
     runs-on: ubuntu-22.04      # ubuntu-24.04 does not have heroku cli, yet
@@ -242,7 +267,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -292,7 +317,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -306,11 +331,11 @@ jobs:
         run: npm run test:e2e-ci-smokes
 
       - name: Upload e2e videos
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: e2e_vids.zip
           path: td.vue/tests/e2e/videos
-        if: ${{ always() }}
+        if: ${{ failure() && hashFiles('td.vue/tests/e2e/videos/') != '' }}
 
   e2e_tests:
     name: Site e2e tests
@@ -343,7 +368,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -357,14 +382,14 @@ jobs:
         run: npm run test:e2e-ci
 
       - name: Upload e2e videos
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: e2e_vids.zip
           path: td.vue/tests/e2e/videos
-        if: ${{ always() }}
+        if: ${{ failure() && hashFiles('td.vue/tests/e2e/videos/') != '' }}
 
-  zap_scan_web:
-    name: Site zap scan
+  zap_scan_web_app:
+    name: Local site zap scan
     runs-on: ubuntu-24.04
     needs: build_docker_image
 
@@ -408,7 +433,7 @@ jobs:
         uses: actions/checkout@v4.1.1
 
       - name: Run Trivy vulnerability scanner
-        uses: aquasecurity/trivy-action@0.22.0
+        uses: aquasecurity/trivy-action@0.23.0
         with:
           image-ref: '${{ env.IMAGE_NAME }}'
           format: 'table'
@@ -432,7 +457,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -442,23 +467,11 @@ jobs:
       - name: Install packages
         run: npm install
 
-      # Build Windows installer
       - name: Build Windows executable
-        # follow Comodo signing instructions
-        # comodosslstore.com/resources/comodo-code-signing-certificate-instructions
-        env:
-          # Windows signing certificate and password
-          CSC_KEY_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD}}
-          CSC_LINK:  ${{ secrets.WINDOWS_CERT }}
-          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         run: npm run build:desktop -- --windows --publish never
 
-      - name: Print logs on error
-        if: ${{ failure() }}
-        run: find . -name "*.log" -exec cat '{}' \;
-
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms-desktop-windows-site
           path: './td.vue/dist-desktop/bundled/.sbom/*'
@@ -482,7 +495,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -492,33 +505,13 @@ jobs:
       - name: Install packages
         run: npm install
 
-      - name: Prepare for MacOS notarization
-        # Import Apple API key for app notarization on macOS
-        # see github.com/samuelmeuli/action-electron-builder#notarization
-        run: |
-          mkdir -p ~/private_keys/
-          echo '${{ secrets.API_KEY }}' > ~/private_keys/AuthKey_${{ secrets.API_KEY_ID }}.p8
-
       - name: Build MacOS disk image
         env:
-          # MacOS signing certificate and password
-          # see github.com/samuelmeuli/action-electron-builder#code-signing
-          CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
-          CSC_LINK:  ${{ secrets.MAC_CERTS }}
-          # MacOS notarization API IDs
-          # see github.com/samuelmeuli/action-electron-builder#notarization
-          API_KEY_ID: ${{ secrets.API_KEY_ID }}
-          API_KEY_ISSUER_ID: ${{ secrets.API_KEY_ISSUER_ID }}
-          # github token is automatically provided to the action
           GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         run: npm run build:desktop -- --mac --publish never
 
-      - name: Print logs on error
-        if: ${{ failure() }}
-        run: find . -name "*.log" -exec cat '{}' \; -print
-
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms-desktop-macos-site
           path: './td.vue/dist-desktop/bundled/.sbom/*'
@@ -542,7 +535,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -563,7 +556,7 @@ jobs:
         run: find . -name "*.log" -exec cat '{}' \; -print
 
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms-desktop-linux-site
           path: './td.vue/dist-desktop/bundled/.sbom/*'
@@ -587,7 +580,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -608,7 +601,7 @@ jobs:
         run: find . -name "*.log" -exec cat '{}' \; -print
 
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms-desktop-linux-snap-site
           path: './td.vue/dist-desktop/bundled/.sbom/*'
@@ -647,7 +640,7 @@ jobs:
           cp raw/sboms-desktop-linux-snap-site/bom.xml  sboms/threat-dragon-desktop-linux-snap-site-bom.xml
           cp raw/sboms-container-image-app/*            sboms/threat-dragon-container-image/app/
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms
           path: 'sboms/'
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index d49f8aa8..8efacf34 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -31,7 +31,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -64,7 +64,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -84,7 +84,7 @@ jobs:
         run: npm run make-sbom
 
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms-server
           path: './td.server/sbom.*'
@@ -106,7 +106,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -117,12 +117,13 @@ jobs:
         run: npm clean-install
 
       - name: lint
-        run: npm run lint
+        run: npm run lint:desktop
 
       - name: Unit test
         run: npm run test:desktop
 
   desktop_windows:
+    # Build and publish Windows installer to github Release Draft
     name: Windows installer
     runs-on: windows-latest
     needs: [desktop_unit_tests, site_unit_tests]
@@ -140,7 +141,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -150,10 +151,7 @@ jobs:
       - name: Install clean packages
         run: npm clean-install
 
-      # Build and publish Windows installer to github Release Draft
       - name: Publish Windows executable
-        # follow Comodo signing instructions
-        # comodosslstore.com/resources/comodo-code-signing-certificate-instructions
         env:
           # Windows signing certificate and password
           CSC_KEY_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD}}
@@ -166,13 +164,15 @@ jobs:
         run: find . -name "*.log" -exec cat '{}' \;
 
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms-desktop-windows-site
           path: './td.vue/dist-desktop/bundled/.sbom/*'
           if-no-files-found: error
 
   desktop_macos:
+    # Build and publish MacOS installer to github Release Draft
+    # the draft name uses version and is created if it does not already exist
     name: MacOS installer
     runs-on: macos-latest
     needs: [desktop_unit_tests, site_unit_tests]
@@ -190,7 +190,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -200,27 +200,22 @@ jobs:
       - name: Install clean packages
         run: npm clean-install
 
-      # Build and publish MacOS installer to github Release Draft
-      # the draft name uses version and is created if it does not already exist
       - name: Prepare for MacOS notarization
         # Import Apple API key for app notarization on macOS
-        # see github.com/samuelmeuli/action-electron-builder#notarization
         run: |
           mkdir -p ~/private_keys/
           echo '${{ secrets.API_KEY }}' > ~/private_keys/AuthKey_${{ secrets.API_KEY_ID }}.p8
 
       - name: Publish MacOS disk image
         env:
-          # MacOS signing certificate and password
-          # see github.com/samuelmeuli/action-electron-builder#code-signing
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          # MacOS signing certificate and password, see electron.build/code-signing
           CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
           CSC_LINK:  ${{ secrets.MAC_CERTS }}
-          # MacOS notarization API IDs
-          # see github.com/samuelmeuli/action-electron-builder#notarization
+          # MacOS notarization, see electron.build/configuration/mac.html
+          # see also github.com/samuelmeuli/action-electron-builder#notarization
           API_KEY_ID: ${{ secrets.API_KEY_ID }}
           API_KEY_ISSUER_ID: ${{ secrets.API_KEY_ISSUER_ID }}
-          # github token is automatically provided to the action
-          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         run: npm run build:desktop -- --mac --publish always
 
       - name: Print logs on error
@@ -228,7 +223,7 @@ jobs:
         run: find . -name "*.log" -exec cat '{}' \; -print
 
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms-desktop-macos-site
           path: './td.vue/dist-desktop/bundled/.sbom/*'
@@ -252,7 +247,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -276,7 +271,7 @@ jobs:
         run: find . -name "*.log" -exec cat '{}' \; -print
 
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms-desktop-linux-site
           path: './td.vue/dist-desktop/bundled/.sbom/*'
@@ -300,7 +295,7 @@ jobs:
           node-version: '20.14.0'
 
       - name: Cache NPM dir
-        uses: actions/cache@v4.0.0
+        uses: actions/cache@v4.0.2
         with:
           path: ~/.npm
           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
@@ -331,7 +326,7 @@ jobs:
         run: find . -name "*.log" -exec cat '{}' \; -print
 
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms-desktop-linux-snap-site
           path: './td.vue/dist-desktop/bundled/.sbom/*'
@@ -351,12 +346,12 @@ jobs:
 
       - name: Set up Docker Buildx
         id: buildx
-        uses: docker/setup-buildx-action@v3.3.0
+        uses: docker/setup-buildx-action@v3.4.0
         with:
           install: true
 
-      - name: Setup dockerx cache
-        uses: actions/cache@v4.0.0
+      - name: Cache Docker layers
+        uses: actions/cache@v4.0.2
         with:
           path: /tmp/.buildx-cache
           key: ${{ runner.os }}-buildx-${{ hashFiles('Dockerfile') }}
@@ -369,9 +364,10 @@ jobs:
           username: ${{ secrets.DOCKERHUB_USERNAME }}
           password: ${{ secrets.DOCKERHUB_TOKEN }}
 
-      - name: Build and push to Docker Hub
-        id: docker_build
-        uses: docker/build-push-action@v5.4.0
+      # platform manifests not (yet) supported, so split out architectures
+      - name: Build for amd64 and push to Docker Hub
+        id: docker_build_amd64
+        uses: docker/build-push-action@v6.3.0
         with:
           context: ./
           file: ./Dockerfile
@@ -379,22 +375,43 @@ jobs:
           push: ${{ startsWith(github.ref, 'refs/tags/v') }}
           tags: ${{ env.IMAGE_NAME }}:${{ github.ref_name }},${{ env.IMAGE_NAME }}:stable
           cache-from: type=local,src=/tmp/.buildx-cache
-          cache-to: type=local,dest=/tmp/.buildx-cache
-          platforms: linux/amd64,linux/arm64
+          cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
+          platforms: linux/amd64
+          load: true
+
+      - name: Build for arm64 and push to Docker Hub
+        id: docker_build_arm64
+        uses: docker/build-push-action@v6.3.0
+        with:
+          context: ./
+          file: ./Dockerfile
+          builder: ${{ steps.buildx.outputs.name }}
+          push: ${{ startsWith(github.ref, 'refs/tags/v') }}
+          tags: ${{ env.IMAGE_NAME }}:${{ github.ref_name }}-arm64
+          cache-from: type=local,src=/tmp/.buildx-cache
+          cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
+          platforms: linux/arm64
           load: true
 
       - name: fetch app SBOM
         run: docker run --rm --entrypoint tar "$IMAGE_ID" -c boms | tar -xv
         env:
-          IMAGE_ID: ${{ steps.docker_build.outputs.imageid }}
+          IMAGE_ID: ${{ steps.docker_build_amd64.outputs.imageid }}
 
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms-container-image-app
           path: './boms/*'
           if-no-files-found: error
 
+      - # Temp fix for large cache bug
+        # https://github.com/docker/build-push-action/issues/252
+        name: Move cache
+        run: |
+          rm -rf /tmp/.buildx-cache
+          mv /tmp/.buildx-cache-new /tmp/.buildx-cache
+
   sbom_combiner:
     name: SBOM combiner
     runs-on: ubuntu-24.04
@@ -429,7 +446,7 @@ jobs:
           cp raw/sboms-desktop-linux-snap-site/bom.xml  sboms/threat-dragon-desktop-linux-snap-site-bom.xml
           cp raw/sboms-container-image-app/*            sboms/threat-dragon-container-image/app/
       - name: Save SBOM artifact
-        uses: actions/upload-artifact@v4.3.0
+        uses: actions/upload-artifact@v4.3.4
         with:
           name: sboms
           path: 'sboms/'
@@ -448,7 +465,7 @@ jobs:
         uses: actions/checkout@v4.1.1
 
       - name: Fetch prepared SBOM artifacts
-        uses: actions/download-artifact@v4.1.7
+        uses: actions/download-artifact@v4.1.8
         with:
           name: 'sboms'
           path: 'sboms/'
diff --git a/Dockerfile b/Dockerfile
index b0847752..4ade0828 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
 ARG         NODE_VERSION=20.14
 
 # The base image with updates applied
-FROM        node:$NODE_VERSION-alpine as base-node
+FROM        node:$NODE_VERSION-alpine AS base-node
 RUN         apk -U upgrade
 WORKDIR     /app
 RUN         npm i -g npm@latest
@@ -12,7 +12,7 @@ USER        node
 
 # Build the front and back-end.  This needs devDependencies which do not
 # need to be included in the final image
-FROM        base-node as build
+FROM        base-node AS build
 RUN         mkdir boms
 
 COPY        package-lock.json package.json /app/
@@ -36,7 +36,7 @@ RUN         cp td.server/sbom.json        boms/threat-dragon-server-bom.json &&
             cp td.vue/dist/.sbom/bom.xml  boms/threat-dragon-site-bom.xml
 
 # Builds the docs
-FROM        imoshtokill/jekyll-bundler as build-docs
+FROM        imoshtokill/jekyll-bundler AS build-docs
 WORKDIR     /td.docs
 COPY        ./docs/Gemfile* ./
 RUN         bundle install
diff --git a/td.server/src/controllers/auth.js b/td.server/src/controllers/auth.js
index f474180d..90b97c4a 100644
--- a/td.server/src/controllers/auth.js
+++ b/td.server/src/controllers/auth.js
@@ -9,7 +9,7 @@ import tokenRepo from '../repositories/token.js';
 const logger = loggerHelper.get('controllers/auth.js');
 
 const login = (req, res) => {
-    logger.debug('API login request: ' + req);
+    logger.debug(`API login request: ${logger.transformToString(req)}`);
 
     try {
         const provider = providers.get(req.params.provider);
@@ -20,7 +20,7 @@ const login = (req, res) => {
 };
 
 const oauthReturn = (req, res) => {
-    logger.debug('API oauthReturn request: ' + req);
+    logger.debug(`API oauthReturn request: ${logger.transformToString(req)}`);
 
     let returnUrl = `/#/oauth-return?code=${req.query.code}`;
     if (env.get().config.NODE_ENV === 'development') {
@@ -30,7 +30,7 @@ const oauthReturn = (req, res) => {
 };
 
 const completeLogin = (req, res) => {
-    logger.debug('API completeLogin request: ' + req);
+    logger.debug(`API completeLogin request: ${logger.transformToString(req)}`);
 
     try {
         const provider = providers.get(req.params.provider);
@@ -51,7 +51,7 @@ const completeLogin = (req, res) => {
 };
 
 const logout = (req, res) => responseWrapper.sendResponse(() => {
-    logger.debug('API logout request: ' + req);
+    logger.debug(`API logout request: ${logger.transformToString(req)}`);
 
     try {
         const refreshToken = req.body.refreshToken;
@@ -71,7 +71,7 @@ const logout = (req, res) => responseWrapper.sendResponse(() => {
 }, req, res, logger);
 
 const refresh = (req, res) => {
-    logger.debug('API refresh request: ' + req);
+    logger.debug(`API refresh request: ${logger.transformToString(req)}`);
 
     const tokenBody = tokenRepo.verify(req.body.refreshToken);
     if (!tokenBody) {
diff --git a/td.server/src/controllers/threatmodelcontroller.js b/td.server/src/controllers/threatmodelcontroller.js
index e8f01082..29e9b53b 100644
--- a/td.server/src/controllers/threatmodelcontroller.js
+++ b/td.server/src/controllers/threatmodelcontroller.js
@@ -12,9 +12,9 @@ const repos = (req, res) => responseWrapper.sendResponseAsync(async () => {
     const page = req.query.page || 1;
     let reposResp;
     let repos;
-    if (env.get().config.GITHUB_USE_SEARCH === 'true') {
+    if (env.get().config.REPO_USE_SEARCH === 'true') {
         logger.debug('Using searchAsync');
-        const searchQuery = env.get().config.GITHUB_SEARCH_QUERY;
+        const searchQuery = env.get().config.REPO_SEARCH_QUERY;
         reposResp = await repository.searchAsync(page, req.provider.access_token, searchQuery);
         repos = reposResp[0].items;
     } else {
@@ -24,7 +24,7 @@ const repos = (req, res) => responseWrapper.sendResponseAsync(async () => {
     }
     const headers = reposResp[1];
     const pageLinks = reposResp[2];
-    logger.debug('API repos request: ' + req);
+    logger.debug(`API repos request: ${logger.transformToString(req)}`);
 
     const pagination = getPagination(headers, pageLinks, page);
 
@@ -45,7 +45,7 @@ const branches = (req, res) => responseWrapper.sendResponseAsync(async () => {
         repo: req.params.repo,
         page: req.query.page || 1
     };
-    logger.debug('API branches request: ' + req);
+    logger.debug(`API branches request: ${logger.transformToString(req)}`);
 
     const branchesResp = await repository.branchesAsync(repoInfo, req.provider.access_token);
     const branches = branchesResp[0];
@@ -70,7 +70,7 @@ const models = (req, res) => responseWrapper.sendResponseAsync(async () => {
         repo: req.params.repo,
         branch: req.params.branch
     };
-    logger.debug('API models request: ' + req);
+    logger.debug(`API models request: ${logger.transformToString(req)}`);
 
     let modelsResp;
     try {
@@ -93,7 +93,7 @@ const model = (req, res) => responseWrapper.sendResponseAsync(async () => {
         branch: req.params.branch,
         model: req.params.model
     };
-    logger.debug('API model request: ' + req);
+    logger.debug(`API model request: ${logger.transformToString(req)}`);
 
     const modelResp = await repository.modelAsync(modelInfo, req.provider.access_token);
     return JSON.parse(Buffer.from(modelResp[0].content, 'base64').toString('utf8'));
@@ -109,7 +109,7 @@ const create = async (req, res) => {
         model: req.params.model,
         body: req.body
     };
-    logger.debug('API create request: ' + req);
+    logger.debug(`API create request: ${logger.transformToString(req)}`);
 
     try {
         const createResp = await repository.createAsync(modelBody, req.provider.access_token);
@@ -130,7 +130,7 @@ const update = async (req, res) => {
         model: req.params.model,
         body: req.body
     };
-    logger.debug('API update request: ' + req);
+    logger.debug(`API update request: ${logger.transformToString(req)}`);
 
     try {
         const updateResp = await repository.updateAsync(modelBody, req.provider.access_token);
@@ -150,7 +150,7 @@ const deleteModel = async (req, res) => {
         branch: req.params.branch,
         model: req.params.model
     };
-    logger.debug('API deleteModel request: ' + req);
+    logger.debug(`API deleteModel request: ${logger.transformToString(req)}`);
 
     try {
         const deleteResp = await repository.deleteAsync(modelInfo, req.provider.access_token);
@@ -205,7 +205,7 @@ const organisation = (req, res) => {
         hostname: env.get().config.GITHUB_ENTERPRISE_HOSTNAME || 'www.github.com',
         port: env.get().config.GITHUB_ENTERPRISE_PORT || '',
     };
-    logger.debug('API organisation request: ' + req);
+    logger.debug(`API organisation request: ${logger.transformToString(req)}`);
 
     return res.status(200).send(organisation);
 };
diff --git a/td.server/src/helpers/logger.helper.js b/td.server/src/helpers/logger.helper.js
index be8795b9..0b527409 100644
--- a/td.server/src/helpers/logger.helper.js
+++ b/td.server/src/helpers/logger.helper.js
@@ -69,6 +69,21 @@ class Logger {
         this.logger.log(level, message);
     }
 
+    transformToString (complexObject) {
+        const cache = [];
+        const resultString = JSON.stringify(complexObject, function(key, value) {
+          if (typeof value === "object" && value !== null) {
+            if (cache.indexOf(value) !== -1) {
+              // Circular reference found
+              return "[Circular]";
+            }
+            cache.push(value);
+          }
+          return value;
+        });
+        return resultString;
+    }
+
     log (level, message) { this.logger.log(level, this._formatMessage(this.service, message)); }
 
     silly (message) { this.logger.silly(this._formatMessage(this.service, message, 'silly')); }
diff --git a/td.server/src/providers/gitlab.js b/td.server/src/providers/gitlab.js
index 52e7428f..73959354 100644
--- a/td.server/src/providers/gitlab.js
+++ b/td.server/src/providers/gitlab.js
@@ -26,7 +26,7 @@ const getGitlabUrl = () => env.get().config.GITLAB_HOST || 'https://gitlab.com';
  * @returns {String}
  */
 const getOauthRedirectUrl = () => {
-    const scope = env.get().config.GITLAB_SCOPE || 'read_user,read_repository,write_repository,profile';
+    const scope = env.get().config.GITLAB_SCOPE || 'read_user read_repository write_repository profile';
     return `${getGitlabUrl()}/oauth/authorize?scope=${scope}&redirect_uri=${env.get().config.GITLAB_REDIRECT_URI}&response_type=code&client_id=${env.get().config.GITLAB_CLIENT_ID}`;
 };
 
diff --git a/td.server/src/repositories/gitlabrepo.js b/td.server/src/repositories/gitlabrepo.js
index fb1f842f..cdde0a05 100644
--- a/td.server/src/repositories/gitlabrepo.js
+++ b/td.server/src/repositories/gitlabrepo.js
@@ -23,14 +23,7 @@ export const getClient = (accessToken) => {
     return GitlabClientWrapper.getClient(clientOptions.auth);
 };
 
-export const reposAsync = async (page, accessToken) => {
-    const repos = await getClient(accessToken).Projects.all({page: page, membership: true, showExpanded: true});
-    repos.data.map((repo) => {
-        repo.full_name = repo.path_with_namespace;
-        return repo;
-    });
-    return [repos.data, null, getPagination(repos.paginationInfo)];
-};
+export const reposAsync = (page, accessToken) => searchAsync(page, accessToken, undefined);
 
 export const getPagination = (paginationInfo, page) => {
     const pagination = {page, next: false, prev: false};
@@ -43,7 +36,14 @@ export const getPagination = (paginationInfo, page) => {
     return pagination;
 };
 
-export const searchAsync = (page, accessToken, searchQuery) => getClient(accessToken).reposAsync({page: page, q: searchQuery});
+export const searchAsync = async (page, accessToken, searchQuery) => {
+    const repos = await getClient(accessToken).Projects.all({page: page, membership: true, showExpanded: true, search: searchQuery});
+    repos.data.map((repo) => {
+        repo.full_name = repo.path_with_namespace;
+        return repo;
+    });
+    return [repos.data, null, getPagination(repos.paginationInfo)];
+};
 
 export const userAsync = (accessToken) => getClient(accessToken).Users.showCurrentUser();
 
diff --git a/td.vue/package-lock.json b/td.vue/package-lock.json
index 0111d31f..06ac935d 100644
--- a/td.vue/package-lock.json
+++ b/td.vue/package-lock.json
@@ -35,6 +35,7 @@
         "@babel/polyfill": "^7.12.1",
         "@babel/register": "^7.23.7",
         "@cyclonedx/webpack-plugin": "^3.11.0",
+        "@electron/notarize": "^2.3.2",
         "@vue/cli-plugin-babel": "~5.0.8",
         "@vue/cli-plugin-e2e-cypress": "~5.0.8",
         "@vue/cli-plugin-eslint": "~5.0.8",
@@ -58,8 +59,8 @@
         "chromedriver": "^119.0.1",
         "cypress": "^13.7.3",
         "electron": "^26.3.0",
-        "electron-builder": "^24.6.4",
-        "electron-builder-notarize": "^1.5.1",
+        "electron-builder": "^24.13.3",
+        "electron-builder-notarize": "^1.5.2",
         "electron-chromedriver": "^26.6.10",
         "electron-devtools-installer": "^3.2.0",
         "eslint": "^8.46.0",
@@ -2232,10 +2233,11 @@
       }
     },
     "node_modules/@electron/asar": {
-      "version": "3.2.8",
-      "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.8.tgz",
-      "integrity": "sha512-cmskk5M06ewHMZAplSiF4AlME3IrnnZhKnWbtwKVLRkdJkKyUVjMLhDIiPIx/+6zQWVlKX/LtmK9xDme7540Sg==",
+      "version": "3.2.10",
+      "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.10.tgz",
+      "integrity": "sha512-mvBSwIBUeiRscrCeJE1LwctAriBj65eUDm0Pc11iE5gRwzkmsdbS7FnZ1XUWjpSeQWL1L5g12Fc/SchPM9DUOw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "commander": "^5.0.0",
         "glob": "^7.1.6",
@@ -2253,6 +2255,7 @@
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -2263,6 +2266,7 @@
       "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
       "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">= 6"
       }
@@ -2271,7 +2275,9 @@
       "version": "7.2.3",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
       "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "deprecated": "Glob versions prior to v9 are no longer supported",
       "dev": true,
+      "license": "ISC",
       "dependencies": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
@@ -2292,6 +2298,7 @@
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
       "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
       "dev": true,
+      "license": "ISC",
       "dependencies": {
         "brace-expansion": "^1.1.7"
       },
@@ -2353,10 +2360,11 @@
       }
     },
     "node_modules/@electron/notarize": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.1.0.tgz",
-      "integrity": "sha512-Q02xem1D0sg4v437xHgmBLxI2iz/fc0D4K7fiVWHa/AnW8o7D751xyKNXgziA6HrTOme9ul1JfWN5ark8WH1xA==",
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.3.2.tgz",
+      "integrity": "sha512-zfayxCe19euNwRycCty1C7lF7snk9YwfRpB5M8GLr1a4ICH63znxaPNAubrMvj0yDvVozqfgsdYpXVUnpWBDpg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "debug": "^4.1.1",
         "fs-extra": "^9.0.1",
@@ -2371,6 +2379,7 @@
       "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz",
       "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==",
       "dev": true,
+      "license": "BSD-2-Clause",
       "dependencies": {
         "compare-version": "^0.1.2",
         "debug": "^4.3.4",
@@ -2392,6 +2401,7 @@
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
       "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "graceful-fs": "^4.2.0",
         "jsonfile": "^6.0.1",
@@ -2406,6 +2416,7 @@
       "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
       "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">= 8.0.0"
       },
@@ -2414,10 +2425,11 @@
       }
     },
     "node_modules/@electron/universal": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.4.1.tgz",
-      "integrity": "sha512-lE/U3UNw1YHuowNbTmKNs9UlS3En3cPgwM5MI+agIgr/B1hSze9NdOP0qn7boZaI9Lph8IDv3/24g9IxnJP7aQ==",
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz",
+      "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "@electron/asar": "^3.2.1",
         "@malept/cross-spawn-promise": "^1.1.0",
@@ -2436,6 +2448,7 @@
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -2446,6 +2459,7 @@
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
       "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
       "dev": true,
+      "license": "ISC",
       "dependencies": {
         "brace-expansion": "^1.1.7"
       },
@@ -3933,89 +3947,6 @@
         "node": ">= 10.0.0"
       }
     },
-    "node_modules/@mapbox/node-pre-gyp": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
-      "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
-      "dev": true,
-      "optional": true,
-      "dependencies": {
-        "detect-libc": "^2.0.0",
-        "https-proxy-agent": "^5.0.0",
-        "make-dir": "^3.1.0",
-        "node-fetch": "^2.6.7",
-        "nopt": "^5.0.0",
-        "npmlog": "^5.0.1",
-        "rimraf": "^3.0.2",
-        "semver": "^7.3.5",
-        "tar": "^6.1.11"
-      },
-      "bin": {
-        "node-pre-gyp": "bin/node-pre-gyp"
-      }
-    },
-    "node_modules/@mapbox/node-pre-gyp/node_modules/abbrev": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
-      "dev": true,
-      "optional": true
-    },
-    "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
-      "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
-      "dev": true,
-      "optional": true,
-      "dependencies": {
-        "semver": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "dev": true,
-      "optional": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
-      "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
-      "dev": true,
-      "optional": true,
-      "dependencies": {
-        "abbrev": "1"
-      },
-      "bin": {
-        "nopt": "bin/nopt.js"
-      },
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/@mapbox/node-pre-gyp/node_modules/semver": {
-      "version": "7.6.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
-      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
-      "dev": true,
-      "optional": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/@node-ipc/js-queue": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/@node-ipc/js-queue/-/js-queue-2.0.3.tgz",
@@ -4994,17 +4925,6 @@
         "@types/node": "*"
       }
     },
-    "node_modules/@types/glob": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
-      "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
-      "dev": true,
-      "optional": true,
-      "dependencies": {
-        "@types/minimatch": "*",
-        "@types/node": "*"
-      }
-    },
     "node_modules/@types/graceful-fs": {
       "version": "4.1.9",
       "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
@@ -6876,21 +6796,6 @@
         "url": "https://dotenvx.com"
       }
     },
-    "node_modules/@wdio/cli/node_modules/ejs": {
-      "version": "3.1.10",
-      "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
-      "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
-      "dev": true,
-      "dependencies": {
-        "jake": "^10.8.5"
-      },
-      "bin": {
-        "ejs": "bin/cli.js"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/@wdio/cli/node_modules/execa": {
       "version": "8.0.1",
       "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
@@ -8235,7 +8140,8 @@
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz",
       "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==",
-      "dev": true
+      "dev": true,
+      "license": "MIT"
     },
     "node_modules/abab": {
       "version": "2.0.6",
@@ -8558,25 +8464,26 @@
       "dev": true
     },
     "node_modules/app-builder-lib": {
-      "version": "24.12.0",
-      "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.12.0.tgz",
-      "integrity": "sha512-t/xinVrMbsEhwljLDoFOtGkiZlaxY1aceZbHERGAS02EkUHJp9lgs/+L8okXLlYCaDSqYdB05Yb8Co+krvguXA==",
+      "version": "24.13.3",
+      "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz",
+      "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "@develar/schema-utils": "~2.6.5",
-        "@electron/notarize": "2.1.0",
+        "@electron/notarize": "2.2.1",
         "@electron/osx-sign": "1.0.5",
-        "@electron/universal": "1.4.1",
+        "@electron/universal": "1.5.1",
         "@malept/flatpak-bundler": "^0.4.0",
         "@types/fs-extra": "9.0.13",
         "async-exit-hook": "^2.0.1",
         "bluebird-lst": "^1.0.9",
-        "builder-util": "24.9.4",
-        "builder-util-runtime": "9.2.3",
+        "builder-util": "24.13.1",
+        "builder-util-runtime": "9.2.4",
         "chromium-pickle-js": "^0.2.0",
         "debug": "^4.3.4",
         "ejs": "^3.1.8",
-        "electron-publish": "24.9.4",
+        "electron-publish": "24.13.1",
         "form-data": "^4.0.0",
         "fs-extra": "^10.1.0",
         "hosted-git-info": "^4.1.0",
@@ -8593,34 +8500,56 @@
       },
       "engines": {
         "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "dmg-builder": "24.13.3",
+        "electron-builder-squirrel-windows": "24.13.3"
       }
     },
-    "node_modules/app-builder-lib/node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true
-    },
-    "node_modules/app-builder-lib/node_modules/ejs": {
-      "version": "3.1.10",
-      "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
-      "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
+    "node_modules/app-builder-lib/node_modules/@electron/notarize": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz",
+      "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "jake": "^10.8.5"
+        "debug": "^4.1.1",
+        "fs-extra": "^9.0.1",
+        "promise-retry": "^2.0.1"
       },
-      "bin": {
-        "ejs": "bin/cli.js"
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/app-builder-lib/node_modules/@electron/notarize/node_modules/fs-extra": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+      "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "at-least-node": "^1.0.0",
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
       },
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">=10"
       }
     },
+    "node_modules/app-builder-lib/node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true,
+      "license": "Python-2.0"
+    },
     "node_modules/app-builder-lib/node_modules/fs-extra": {
       "version": "10.1.0",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
       "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "graceful-fs": "^4.2.0",
         "jsonfile": "^6.0.1",
@@ -8635,6 +8564,7 @@
       "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
       "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "argparse": "^2.0.1"
       },
@@ -8642,26 +8572,12 @@
         "js-yaml": "bin/js-yaml.js"
       }
     },
-    "node_modules/app-builder-lib/node_modules/lru-cache": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
-      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
-      "dev": true,
-      "dependencies": {
-        "yallist": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/app-builder-lib/node_modules/semver": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
-      "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+      "version": "7.6.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
+      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
       "dev": true,
-      "dependencies": {
-        "lru-cache": "^6.0.0"
-      },
+      "license": "ISC",
       "bin": {
         "semver": "bin/semver.js"
       },
@@ -8669,19 +8585,6 @@
         "node": ">=10"
       }
     },
-    "node_modules/app-builder-lib/node_modules/yallist": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-      "dev": true
-    },
-    "node_modules/aproba": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
-      "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
-      "dev": true,
-      "optional": true
-    },
     "node_modules/arch": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
@@ -8819,21 +8722,6 @@
         "safe-buffer": "~5.1.0"
       }
     },
-    "node_modules/are-we-there-yet": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
-      "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
-      "deprecated": "This package is no longer supported.",
-      "dev": true,
-      "optional": true,
-      "dependencies": {
-        "delegates": "^1.0.0",
-        "readable-stream": "^3.6.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/argparse": {
       "version": "1.0.10",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -9013,79 +8901,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/asar": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/asar/-/asar-3.2.0.tgz",
-      "integrity": "sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg==",
-      "deprecated": "Please use @electron/asar moving forward.  There is no API change, just a package name change",
-      "dev": true,
-      "dependencies": {
-        "chromium-pickle-js": "^0.2.0",
-        "commander": "^5.0.0",
-        "glob": "^7.1.6",
-        "minimatch": "^3.0.4"
-      },
-      "bin": {
-        "asar": "bin/asar.js"
-      },
-      "engines": {
-        "node": ">=10.12.0"
-      },
-      "optionalDependencies": {
-        "@types/glob": "^7.1.1"
-      }
-    },
-    "node_modules/asar/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/asar/node_modules/commander": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
-      "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
-      "dev": true,
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/asar/node_modules/glob": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-      "dev": true,
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      },
-      "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/asar/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
     "node_modules/asn1": {
       "version": "0.2.6",
       "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
@@ -9694,16 +9509,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/bindings": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
-      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
-      "dev": true,
-      "optional": true,
-      "dependencies": {
-        "file-uri-to-path": "1.0.0"
-      }
-    },
     "node_modules/bl": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@@ -9983,12 +9788,13 @@
       }
     },
     "node_modules/braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "fill-range": "^7.0.1"
+        "fill-range": "^7.1.1"
       },
       "engines": {
         "node": ">=8"
@@ -10493,6 +10299,7 @@
       "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz",
       "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">=0.4"
       },
@@ -10531,16 +10338,17 @@
       }
     },
     "node_modules/builder-util": {
-      "version": "24.9.4",
-      "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.9.4.tgz",
-      "integrity": "sha512-YNon3rYjPSm4XDDho9wD6jq7vLRJZUy9FR+yFZnHoWvvdVCnZakL4BctTlPABP41MvIH5yk2cTZ2YfkOhGistQ==",
+      "version": "24.13.1",
+      "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz",
+      "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "@types/debug": "^4.1.6",
         "7zip-bin": "~5.2.0",
         "app-builder-bin": "4.0.0",
         "bluebird-lst": "^1.0.9",
-        "builder-util-runtime": "9.2.3",
+        "builder-util-runtime": "9.2.4",
         "chalk": "^4.1.2",
         "cross-spawn": "^7.0.3",
         "debug": "^4.3.4",
@@ -10555,10 +10363,11 @@
       }
     },
     "node_modules/builder-util-runtime": {
-      "version": "9.2.3",
-      "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz",
-      "integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==",
+      "version": "9.2.4",
+      "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
+      "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "debug": "^4.3.4",
         "sax": "^1.2.4"
@@ -10572,6 +10381,7 @@
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
       "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "color-convert": "^2.0.1"
       },
@@ -10586,13 +10396,15 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
       "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true
+      "dev": true,
+      "license": "Python-2.0"
     },
     "node_modules/builder-util/node_modules/chalk": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
       "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "ansi-styles": "^4.1.0",
         "supports-color": "^7.1.0"
@@ -10609,6 +10421,7 @@
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
       "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "color-name": "~1.1.4"
       },
@@ -10620,13 +10433,15 @@
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "dev": true
+      "dev": true,
+      "license": "MIT"
     },
     "node_modules/builder-util/node_modules/fs-extra": {
       "version": "10.1.0",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
       "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "graceful-fs": "^4.2.0",
         "jsonfile": "^6.0.1",
@@ -10641,6 +10456,7 @@
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
       "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">=8"
       }
@@ -10650,6 +10466,7 @@
       "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
       "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "argparse": "^2.0.1"
       },
@@ -10662,6 +10479,7 @@
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
       "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "has-flag": "^4.0.0"
       },
@@ -11734,16 +11552,6 @@
       "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
       "dev": true
     },
-    "node_modules/color-support": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
-      "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
-      "dev": true,
-      "optional": true,
-      "bin": {
-        "color-support": "bin.js"
-      }
-    },
     "node_modules/colord": {
       "version": "2.9.3",
       "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
@@ -11913,16 +11721,18 @@
       "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz",
       "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "glob": "^10.3.10",
         "typescript": "^5.3.3"
       }
     },
     "node_modules/config-file-ts/node_modules/typescript": {
-      "version": "5.3.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
-      "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+      "version": "5.5.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
+      "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
       "dev": true,
+      "license": "Apache-2.0",
       "bin": {
         "tsc": "bin/tsc",
         "tsserver": "bin/tsserver"
@@ -11977,13 +11787,6 @@
       "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz",
       "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw=="
     },
-    "node_modules/console-control-strings": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-      "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
-      "dev": true,
-      "optional": true
-    },
     "node_modules/consolidate": {
       "version": "0.15.1",
       "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz",
@@ -13474,13 +13277,6 @@
         "node": ">=0.4.0"
       }
     },
-    "node_modules/delegates": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-      "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
-      "dev": true,
-      "optional": true
-    },
     "node_modules/depd": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -13509,16 +13305,6 @@
         "npm": "1.2.8000 || >= 1.4.16"
       }
     },
-    "node_modules/detect-libc": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
-      "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
-      "dev": true,
-      "optional": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/detect-newline": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
@@ -13790,6 +13576,7 @@
       "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz",
       "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "buffer-equal": "^1.0.0",
         "minimatch": "^3.0.4"
@@ -13800,6 +13587,7 @@
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -13810,6 +13598,7 @@
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
       "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
       "dev": true,
+      "license": "ISC",
       "dependencies": {
         "brace-expansion": "^1.1.7"
       },
@@ -13837,14 +13626,15 @@
       "optional": true
     },
     "node_modules/dmg-builder": {
-      "version": "24.12.0",
-      "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.12.0.tgz",
-      "integrity": "sha512-nS22OyHUIYcK40UnILOtqC5Qffd1SN1Ljqy/6e+QR2H1wM3iNBrKJoEbDRfEmYYaALKNFRkKPqSbZKRsGUBdPw==",
+      "version": "24.13.3",
+      "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz",
+      "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "app-builder-lib": "24.12.0",
-        "builder-util": "24.9.4",
-        "builder-util-runtime": "9.2.3",
+        "app-builder-lib": "24.13.3",
+        "builder-util": "24.13.1",
+        "builder-util-runtime": "9.2.4",
         "fs-extra": "^10.1.0",
         "iconv-lite": "^0.6.2",
         "js-yaml": "^4.1.0"
@@ -13857,13 +13647,15 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
       "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true
+      "dev": true,
+      "license": "Python-2.0"
     },
     "node_modules/dmg-builder/node_modules/fs-extra": {
       "version": "10.1.0",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
       "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "graceful-fs": "^4.2.0",
         "jsonfile": "^6.0.1",
@@ -13878,6 +13670,7 @@
       "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
       "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "argparse": "^2.0.1"
       },
@@ -14346,10 +14139,11 @@
       "dev": true
     },
     "node_modules/ejs": {
-      "version": "3.1.9",
-      "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
-      "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==",
+      "version": "3.1.10",
+      "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
+      "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
       "dev": true,
+      "license": "Apache-2.0",
       "dependencies": {
         "jake": "^10.8.5"
       },
@@ -14379,16 +14173,17 @@
       }
     },
     "node_modules/electron-builder": {
-      "version": "24.12.0",
-      "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.12.0.tgz",
-      "integrity": "sha512-dH4O9zkxFxFbBVFobIR5FA71yJ1TZSCvjZ2maCskpg7CWjBF+SNRSQAThlDyUfRuB+jBTMwEMzwARywmap0CSw==",
+      "version": "24.13.3",
+      "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz",
+      "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "app-builder-lib": "24.12.0",
-        "builder-util": "24.9.4",
-        "builder-util-runtime": "9.2.3",
+        "app-builder-lib": "24.13.3",
+        "builder-util": "24.13.1",
+        "builder-util-runtime": "9.2.4",
         "chalk": "^4.1.2",
-        "dmg-builder": "24.12.0",
+        "dmg-builder": "24.13.3",
         "fs-extra": "^10.1.0",
         "is-ci": "^3.0.0",
         "lazy-val": "^1.0.5",
@@ -14405,10 +14200,11 @@
       }
     },
     "node_modules/electron-builder-notarize": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/electron-builder-notarize/-/electron-builder-notarize-1.5.1.tgz",
-      "integrity": "sha512-xS7s9gE+1AcJIuJ4DU/LqCrmRypE1zOR/6b66egKzgP/UVh9YSa7rINos34gF/KcueNDQU39HcXcCEKiEI5wPQ==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/electron-builder-notarize/-/electron-builder-notarize-1.5.2.tgz",
+      "integrity": "sha512-vo6RGgIFYxMk2yp59N4NsvmAYfB7ncYi6gV9Fcq2TVKxEn2tPXrSjIKB2e/pu+5iXIY6BHNZNXa75F3DHgOOLA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "dotenv": "^8.2.0",
         "electron-notarize": "^1.1.1",
@@ -14427,6 +14223,7 @@
       "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
       "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "find-up": "^4.1.0",
         "read-pkg": "^5.2.0",
@@ -14444,10 +14241,69 @@
       "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
       "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
       "dev": true,
+      "license": "(MIT OR CC0-1.0)",
       "engines": {
         "node": ">=8"
       }
     },
+    "node_modules/electron-builder-squirrel-windows": {
+      "version": "24.13.3",
+      "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz",
+      "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "app-builder-lib": "24.13.3",
+        "archiver": "^5.3.1",
+        "builder-util": "24.13.1",
+        "fs-extra": "^10.1.0"
+      }
+    },
+    "node_modules/electron-builder-squirrel-windows/node_modules/archiver": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz",
+      "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "archiver-utils": "^2.1.0",
+        "async": "^3.2.4",
+        "buffer-crc32": "^0.2.1",
+        "readable-stream": "^3.6.0",
+        "readdir-glob": "^1.1.2",
+        "tar-stream": "^2.2.0",
+        "zip-stream": "^4.1.0"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/electron-builder-squirrel-windows/node_modules/async": {
+      "version": "3.2.5",
+      "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
+      "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/electron-builder/node_modules/ansi-styles": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -14605,6 +14461,7 @@
       "integrity": "sha512-ZStVWYcWI7g87/PgjPJSIIhwQXOaw4/XeXU+pWqMMktSLHaGMLHdyPPN7Cmao7+Cr7fYufA16npdtMndYciHNw==",
       "deprecated": "Please use @electron/notarize moving forward.  There is no API change, just a package name change",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "debug": "^4.1.1",
         "fs-extra": "^9.0.1"
@@ -14613,64 +14470,16 @@
         "node": ">= 10.0.0"
       }
     },
-    "node_modules/electron-osx-sign": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.6.0.tgz",
-      "integrity": "sha512-+hiIEb2Xxk6eDKJ2FFlpofCnemCbjbT5jz+BKGpVBrRNT3kWTGs4DfNX6IzGwgi33hUcXF+kFs9JW+r6Wc1LRg==",
-      "deprecated": "Please use @electron/osx-sign moving forward. Be aware the API is slightly different",
-      "dev": true,
-      "dependencies": {
-        "bluebird": "^3.5.0",
-        "compare-version": "^0.1.2",
-        "debug": "^2.6.8",
-        "isbinaryfile": "^3.0.2",
-        "minimist": "^1.2.0",
-        "plist": "^3.0.1"
-      },
-      "bin": {
-        "electron-osx-flat": "bin/electron-osx-flat.js",
-        "electron-osx-sign": "bin/electron-osx-sign.js"
-      },
-      "engines": {
-        "node": ">=4.0.0"
-      }
-    },
-    "node_modules/electron-osx-sign/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/electron-osx-sign/node_modules/isbinaryfile": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz",
-      "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==",
-      "dev": true,
-      "dependencies": {
-        "buffer-alloc": "^1.2.0"
-      },
-      "engines": {
-        "node": ">=0.6.0"
-      }
-    },
-    "node_modules/electron-osx-sign/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "dev": true
-    },
     "node_modules/electron-publish": {
-      "version": "24.9.4",
-      "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.9.4.tgz",
-      "integrity": "sha512-FghbeVMfxHneHjsG2xUSC0NMZYWOOWhBxfZKPTbibcJ0CjPH0Ph8yb5CUO62nqywXfA5u1Otq6K8eOdOixxmNg==",
+      "version": "24.13.1",
+      "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz",
+      "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "@types/fs-extra": "^9.0.11",
-        "builder-util": "24.9.4",
-        "builder-util-runtime": "9.2.3",
+        "builder-util": "24.13.1",
+        "builder-util-runtime": "9.2.4",
         "chalk": "^4.1.2",
         "fs-extra": "^10.1.0",
         "lazy-val": "^1.0.5",
@@ -14682,6 +14491,7 @@
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
       "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "color-convert": "^2.0.1"
       },
@@ -14697,6 +14507,7 @@
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
       "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "ansi-styles": "^4.1.0",
         "supports-color": "^7.1.0"
@@ -14713,6 +14524,7 @@
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
       "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "color-name": "~1.1.4"
       },
@@ -14724,13 +14536,15 @@
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "dev": true
+      "dev": true,
+      "license": "MIT"
     },
     "node_modules/electron-publish/node_modules/fs-extra": {
       "version": "10.1.0",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
       "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "graceful-fs": "^4.2.0",
         "jsonfile": "^6.0.1",
@@ -14745,6 +14559,7 @@
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
       "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">=8"
       }
@@ -14754,6 +14569,7 @@
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
       "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "has-flag": "^4.0.0"
       },
@@ -14969,7 +14785,8 @@
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
       "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
-      "dev": true
+      "dev": true,
+      "license": "MIT"
     },
     "node_modules/error-ex": {
       "version": "1.3.2",
@@ -17162,13 +16979,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/file-uri-to-path": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
-      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
-      "dev": true,
-      "optional": true
-    },
     "node_modules/filelist": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
@@ -17179,10 +16989,11 @@
       }
     },
     "node_modules/fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "to-regex-range": "^5.0.1"
       },
@@ -17720,28 +17531,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/gauge": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
-      "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
-      "deprecated": "This package is no longer supported.",
-      "dev": true,
-      "optional": true,
-      "dependencies": {
-        "aproba": "^1.0.3 || ^2.0.0",
-        "color-support": "^1.1.2",
-        "console-control-strings": "^1.0.0",
-        "has-unicode": "^2.0.1",
-        "object-assign": "^4.1.1",
-        "signal-exit": "^3.0.0",
-        "string-width": "^4.2.3",
-        "strip-ansi": "^6.0.1",
-        "wide-align": "^1.1.2"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/gaze": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
@@ -18310,12 +18099,6 @@
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
       "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
     },
-    "node_modules/graceful-readlink": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
-      "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==",
-      "dev": true
-    },
     "node_modules/grapheme-splitter": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
@@ -18484,13 +18267,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/has-unicode": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-      "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
-      "dev": true,
-      "optional": true
-    },
     "node_modules/has-yarn": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz",
@@ -19576,6 +19352,7 @@
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
       "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">=0.12.0"
       }
@@ -19829,6 +19606,7 @@
       "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz",
       "integrity": "sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">= 18.0.0"
       },
@@ -23298,22 +23076,6 @@
         "node": ">= 0.8.0"
       }
     },
-    "node_modules/libxmljs2": {
-      "version": "0.33.0",
-      "resolved": "https://registry.npmjs.org/libxmljs2/-/libxmljs2-0.33.0.tgz",
-      "integrity": "sha512-Hw74f2/3rbpxc6tkTqe3yrs4v2Tx0rEukrYxaNkXSVKK540i2eqlQxzf1jjG+RlwMuv66WxkkuZHM/OQq6km4w==",
-      "dev": true,
-      "hasInstallScript": true,
-      "optional": true,
-      "dependencies": {
-        "@mapbox/node-pre-gyp": "^1.0.11",
-        "bindings": "~1.5.0",
-        "nan": "~2.18.0"
-      },
-      "engines": {
-        "node": ">=18"
-      }
-    },
     "node_modules/lie": {
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
@@ -24676,13 +24438,6 @@
       "integrity": "sha512-kQITb5LlO0Gk8rmbMAkfbmhs+QlXZ5SRHsx6YcG++3yc57iolbiQuo5rsfu3dkB7Qw3jKCqntsZvNNgvdfotkA==",
       "dev": true
     },
-    "node_modules/nan": {
-      "version": "2.18.0",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
-      "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==",
-      "dev": true,
-      "optional": true
-    },
     "node_modules/nanoid": {
       "version": "3.3.7",
       "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
@@ -25034,20 +24789,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/npmlog": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
-      "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
-      "deprecated": "This package is no longer supported.",
-      "dev": true,
-      "optional": true,
-      "dependencies": {
-        "are-we-there-yet": "^2.0.0",
-        "console-control-strings": "^1.1.0",
-        "gauge": "^3.0.0",
-        "set-blocking": "^2.0.0"
-      }
-    },
     "node_modules/nssocket": {
       "version": "0.6.0",
       "resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.6.0.tgz",
@@ -27291,6 +27032,7 @@
       "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
       "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "err-code": "^2.0.2",
         "retry": "^0.12.0"
@@ -27863,6 +27605,7 @@
       "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz",
       "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "config-file-ts": "^0.2.4",
         "dotenv": "^9.0.2",
@@ -27879,13 +27622,15 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
       "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true
+      "dev": true,
+      "license": "Python-2.0"
     },
     "node_modules/read-config-file/node_modules/dotenv": {
       "version": "9.0.2",
       "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz",
       "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==",
       "dev": true,
+      "license": "BSD-2-Clause",
       "engines": {
         "node": ">=10"
       }
@@ -27895,6 +27640,7 @@
       "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
       "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "argparse": "^2.0.1"
       },
@@ -28677,6 +28423,7 @@
       "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
       "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
       "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">= 4"
       }
@@ -31218,6 +30965,7 @@
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
       "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
         "is-number": "^7.0.0"
       },
@@ -32194,33 +31942,6 @@
         "url": "https://github.com/sponsors/nklayman"
       }
     },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/@electron/universal": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.2.1.tgz",
-      "integrity": "sha512-7323HyMh7KBAl/nPDppdLsC87G6RwRU02dy5FPeGB1eS7rUePh55+WNWiDPLhFQqqVPHzh77M69uhmoT8XnwMQ==",
-      "dev": true,
-      "dependencies": {
-        "@malept/cross-spawn-promise": "^1.1.0",
-        "asar": "^3.1.0",
-        "debug": "^4.3.1",
-        "dir-compare": "^2.4.0",
-        "fs-extra": "^9.0.1",
-        "minimatch": "^3.0.4",
-        "plist": "^3.0.4"
-      },
-      "engines": {
-        "node": ">=8.6"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/@types/yargs": {
-      "version": "17.0.32",
-      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
-      "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
-      "dev": true,
-      "dependencies": {
-        "@types/yargs-parser": "*"
-      }
-    },
     "node_modules/vue-cli-plugin-electron-builder/node_modules/@vue/cli-shared-utils": {
       "version": "4.5.19",
       "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.19.tgz",
@@ -32314,12 +32035,6 @@
         "semver": "bin/semver.js"
       }
     },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/7zip-bin": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz",
-      "integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==",
-      "dev": true
-    },
     "node_modules/vue-cli-plugin-electron-builder/node_modules/ansi-regex": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
@@ -32344,150 +32059,6 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/app-builder-lib": {
-      "version": "23.6.0",
-      "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-23.6.0.tgz",
-      "integrity": "sha512-dQYDuqm/rmy8GSCE6Xl/3ShJg6Ab4bZJMT8KaTKGzT436gl1DN4REP3FCWfXoh75qGTJ+u+WsdnnpO9Jl8nyMA==",
-      "dev": true,
-      "dependencies": {
-        "@develar/schema-utils": "~2.6.5",
-        "@electron/universal": "1.2.1",
-        "@malept/flatpak-bundler": "^0.4.0",
-        "7zip-bin": "~5.1.1",
-        "async-exit-hook": "^2.0.1",
-        "bluebird-lst": "^1.0.9",
-        "builder-util": "23.6.0",
-        "builder-util-runtime": "9.1.1",
-        "chromium-pickle-js": "^0.2.0",
-        "debug": "^4.3.4",
-        "ejs": "^3.1.7",
-        "electron-osx-sign": "^0.6.0",
-        "electron-publish": "23.6.0",
-        "form-data": "^4.0.0",
-        "fs-extra": "^10.1.0",
-        "hosted-git-info": "^4.1.0",
-        "is-ci": "^3.0.0",
-        "isbinaryfile": "^4.0.10",
-        "js-yaml": "^4.1.0",
-        "lazy-val": "^1.0.5",
-        "minimatch": "^3.1.2",
-        "read-config-file": "6.2.0",
-        "sanitize-filename": "^1.6.3",
-        "semver": "^7.3.7",
-        "tar": "^6.1.11",
-        "temp-file": "^3.4.0"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/app-builder-lib/node_modules/fs-extra": {
-      "version": "10.1.0",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
-      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
-      "dev": true,
-      "dependencies": {
-        "graceful-fs": "^4.2.0",
-        "jsonfile": "^6.0.1",
-        "universalify": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/argparse": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
-      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
-      "dev": true
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/buffer-equal": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz",
-      "integrity": "sha512-tcBWO2Dl4e7Asr9hTGcpVrCe+F7DubpmqWCTbj4FHLmjqO2hIaC383acQubWtRJhdceqs5uBHs6Es+Sk//RKiQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/builder-util": {
-      "version": "23.6.0",
-      "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-23.6.0.tgz",
-      "integrity": "sha512-QiQHweYsh8o+U/KNCZFSvISRnvRctb8m/2rB2I1JdByzvNKxPeFLlHFRPQRXab6aYeXc18j9LpsDLJ3sGQmWTQ==",
-      "dev": true,
-      "dependencies": {
-        "@types/debug": "^4.1.6",
-        "@types/fs-extra": "^9.0.11",
-        "7zip-bin": "~5.1.1",
-        "app-builder-bin": "4.0.0",
-        "bluebird-lst": "^1.0.9",
-        "builder-util-runtime": "9.1.1",
-        "chalk": "^4.1.1",
-        "cross-spawn": "^7.0.3",
-        "debug": "^4.3.4",
-        "fs-extra": "^10.0.0",
-        "http-proxy-agent": "^5.0.0",
-        "https-proxy-agent": "^5.0.0",
-        "is-ci": "^3.0.0",
-        "js-yaml": "^4.1.0",
-        "source-map-support": "^0.5.19",
-        "stat-mode": "^1.0.0",
-        "temp-file": "^3.4.0"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/builder-util-runtime": {
-      "version": "9.1.1",
-      "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.1.1.tgz",
-      "integrity": "sha512-azRhYLEoDvRDR8Dhis4JatELC/jUvYjm4cVSj7n9dauGTOM2eeNn9KS0z6YA6oDsjI1xphjNbY6PZZeHPzzqaw==",
-      "dev": true,
-      "dependencies": {
-        "debug": "^4.3.4",
-        "sax": "^1.2.4"
-      },
-      "engines": {
-        "node": ">=12.0.0"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/builder-util/node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/builder-util/node_modules/fs-extra": {
-      "version": "10.1.0",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
-      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
-      "dev": true,
-      "dependencies": {
-        "graceful-fs": "^4.2.0",
-        "jsonfile": "^6.0.1",
-        "universalify": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
     "node_modules/vue-cli-plugin-electron-builder/node_modules/camelcase": {
       "version": "5.3.1",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -32538,18 +32109,6 @@
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
       "dev": true
     },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/commander": {
-      "version": "2.9.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
-      "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==",
-      "dev": true,
-      "dependencies": {
-        "graceful-readlink": ">= 1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.6.x"
-      }
-    },
     "node_modules/vue-cli-plugin-electron-builder/node_modules/decamelize": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
@@ -32559,233 +32118,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/dir-compare": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-2.4.0.tgz",
-      "integrity": "sha512-l9hmu8x/rjVC9Z2zmGzkhOEowZvW7pmYws5CWHutg8u1JgvsKWMx7Q/UODeu4djLZ4FgW5besw5yvMQnBHzuCA==",
-      "dev": true,
-      "dependencies": {
-        "buffer-equal": "1.0.0",
-        "colors": "1.0.3",
-        "commander": "2.9.0",
-        "minimatch": "3.0.4"
-      },
-      "bin": {
-        "dircompare": "src/cli/dircompare.js"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/dir-compare/node_modules/minimatch": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/dmg-builder": {
-      "version": "23.6.0",
-      "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-23.6.0.tgz",
-      "integrity": "sha512-jFZvY1JohyHarIAlTbfQOk+HnceGjjAdFjVn3n8xlDWKsYNqbO4muca6qXEZTfGXeQMG7TYim6CeS5XKSfSsGA==",
-      "dev": true,
-      "dependencies": {
-        "app-builder-lib": "23.6.0",
-        "builder-util": "23.6.0",
-        "builder-util-runtime": "9.1.1",
-        "fs-extra": "^10.0.0",
-        "iconv-lite": "^0.6.2",
-        "js-yaml": "^4.1.0"
-      },
-      "optionalDependencies": {
-        "dmg-license": "^1.0.11"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/dmg-builder/node_modules/fs-extra": {
-      "version": "10.1.0",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
-      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
-      "dev": true,
-      "dependencies": {
-        "graceful-fs": "^4.2.0",
-        "jsonfile": "^6.0.1",
-        "universalify": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/dotenv": {
-      "version": "9.0.2",
-      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz",
-      "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/electron-builder": {
-      "version": "23.6.0",
-      "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-23.6.0.tgz",
-      "integrity": "sha512-y8D4zO+HXGCNxFBV/JlyhFnoQ0Y0K7/sFH+XwIbj47pqaW8S6PGYQbjoObolKBR1ddQFPt4rwp4CnwMJrW3HAw==",
-      "dev": true,
-      "dependencies": {
-        "@types/yargs": "^17.0.1",
-        "app-builder-lib": "23.6.0",
-        "builder-util": "23.6.0",
-        "builder-util-runtime": "9.1.1",
-        "chalk": "^4.1.1",
-        "dmg-builder": "23.6.0",
-        "fs-extra": "^10.0.0",
-        "is-ci": "^3.0.0",
-        "lazy-val": "^1.0.5",
-        "read-config-file": "6.2.0",
-        "simple-update-notifier": "^1.0.7",
-        "yargs": "^17.5.1"
-      },
-      "bin": {
-        "electron-builder": "cli.js",
-        "install-app-deps": "install-app-deps.js"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/electron-builder/node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/electron-builder/node_modules/cliui": {
-      "version": "8.0.1",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
-      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
-      "dev": true,
-      "dependencies": {
-        "string-width": "^4.2.0",
-        "strip-ansi": "^6.0.1",
-        "wrap-ansi": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/electron-builder/node_modules/fs-extra": {
-      "version": "10.1.0",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
-      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
-      "dev": true,
-      "dependencies": {
-        "graceful-fs": "^4.2.0",
-        "jsonfile": "^6.0.1",
-        "universalify": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/electron-builder/node_modules/wrap-ansi": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^4.0.0",
-        "string-width": "^4.1.0",
-        "strip-ansi": "^6.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/electron-builder/node_modules/yargs": {
-      "version": "17.7.2",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
-      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
-      "dev": true,
-      "dependencies": {
-        "cliui": "^8.0.1",
-        "escalade": "^3.1.1",
-        "get-caller-file": "^2.0.5",
-        "require-directory": "^2.1.1",
-        "string-width": "^4.2.3",
-        "y18n": "^5.0.5",
-        "yargs-parser": "^21.1.1"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/electron-builder/node_modules/yargs-parser": {
-      "version": "21.1.1",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
-      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
-      "dev": true,
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/electron-publish": {
-      "version": "23.6.0",
-      "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-23.6.0.tgz",
-      "integrity": "sha512-jPj3y+eIZQJF/+t5SLvsI5eS4mazCbNYqatv5JihbqOstIM13k0d1Z3vAWntvtt13Itl61SO6seicWdioOU5dg==",
-      "dev": true,
-      "dependencies": {
-        "@types/fs-extra": "^9.0.11",
-        "builder-util": "23.6.0",
-        "builder-util-runtime": "9.1.1",
-        "chalk": "^4.1.1",
-        "fs-extra": "^10.0.0",
-        "lazy-val": "^1.0.5",
-        "mime": "^2.5.2"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/electron-publish/node_modules/chalk": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/electron-publish/node_modules/fs-extra": {
-      "version": "10.1.0",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
-      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
-      "dev": true,
-      "dependencies": {
-        "graceful-fs": "^4.2.0",
-        "jsonfile": "^6.0.1",
-        "universalify": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
     "node_modules/vue-cli-plugin-electron-builder/node_modules/execa": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@@ -32889,18 +32221,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/isbinaryfile": {
-      "version": "4.0.10",
-      "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
-      "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
-      "dev": true,
-      "engines": {
-        "node": ">= 8.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/gjtorikian/"
-      }
-    },
     "node_modules/vue-cli-plugin-electron-builder/node_modules/isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -32921,18 +32241,6 @@
         "node": ">= 10.13.0"
       }
     },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/js-yaml": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
-      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
-      "dev": true,
-      "dependencies": {
-        "argparse": "^2.0.1"
-      },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
-      }
-    },
     "node_modules/vue-cli-plugin-electron-builder/node_modules/log-symbols": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
@@ -32978,18 +32286,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
     "node_modules/vue-cli-plugin-electron-builder/node_modules/open": {
       "version": "6.4.0",
       "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz",
@@ -33058,22 +32354,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/read-config-file": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.2.0.tgz",
-      "integrity": "sha512-gx7Pgr5I56JtYz+WuqEbQHj/xWo+5Vwua2jhb1VwM4Wid5PqYmZ4i00ZB0YEGIfkVBsCv9UrjgyqCiQfS/Oosg==",
-      "dev": true,
-      "dependencies": {
-        "dotenv": "^9.0.2",
-        "dotenv-expand": "^5.1.0",
-        "js-yaml": "^4.1.0",
-        "json5": "^2.2.0",
-        "lazy-val": "^1.0.4"
-      },
-      "engines": {
-        "node": ">=12.0.0"
-      }
-    },
     "node_modules/vue-cli-plugin-electron-builder/node_modules/restore-cursor": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
@@ -33156,27 +32436,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/simple-update-notifier": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz",
-      "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==",
-      "dev": true,
-      "dependencies": {
-        "semver": "~7.0.0"
-      },
-      "engines": {
-        "node": ">=8.10.0"
-      }
-    },
-    "node_modules/vue-cli-plugin-electron-builder/node_modules/simple-update-notifier/node_modules/semver": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
-      "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
-      "dev": true,
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
     "node_modules/vue-cli-plugin-electron-builder/node_modules/split2": {
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
@@ -35858,16 +35117,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/wide-align": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
-      "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
-      "dev": true,
-      "optional": true,
-      "dependencies": {
-        "string-width": "^1.0.2 || 2 || 3 || 4"
-      }
-    },
     "node_modules/widest-line": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
diff --git a/td.vue/package.json b/td.vue/package.json
index b684cb34..ebba574c 100644
--- a/td.vue/package.json
+++ b/td.vue/package.json
@@ -13,7 +13,8 @@
     "cypress:update": "npx update-browserslist-db@latest",
     "dev": "vue-cli-service serve --port 8080",
     "lint": "vue-cli-service lint",
-    "pretest": "npm run lint",
+    "lint:desktop": "vue-cli-service lint src/desktop tests/unit/desktop",
+    "pretest": "npm run lint && npm run lint:desktop",
     "serve": "vue-cli-service serve",
     "start": "echo 'Please run the start command from the root of this repository' && exit 1",
     "start:desktop": "vue-cli-service electron:serve",
@@ -76,6 +77,7 @@
     "@babel/polyfill": "^7.12.1",
     "@babel/register": "^7.23.7",
     "@cyclonedx/webpack-plugin": "^3.11.0",
+    "@electron/notarize": "^2.3.2",
     "@vue/cli-plugin-babel": "~5.0.8",
     "@vue/cli-plugin-e2e-cypress": "~5.0.8",
     "@vue/cli-plugin-eslint": "~5.0.8",
@@ -99,8 +101,8 @@
     "chromedriver": "^119.0.1",
     "cypress": "^13.7.3",
     "electron": "^26.3.0",
-    "electron-builder": "^24.6.4",
-    "electron-builder-notarize": "^1.5.1",
+    "electron-builder": "^24.13.3",
+    "electron-builder-notarize": "^1.5.2",
     "electron-chromedriver": "^26.6.10",
     "electron-devtools-installer": "^3.2.0",
     "eslint": "^8.46.0",
@@ -136,6 +138,7 @@
   "overrides": {
     "@achrinza/node-ipc": ">=10.1.10",
     "@babel/traverse": ">=7.23.2",
+    "braces": ">=3.0.3",
     "debug@<3.1.0": ">=4.3.4",
     "electron-builder@<23.0.3": ">=23.0.3",
     "ejs@<3.1.10": ">=3.1.10",
@@ -156,7 +159,7 @@
     "tough-cookie@<4.1.3": ">=4.1.3",
     "vm2@<3.9.16": ">=3.9.19",
     "vue-cli-plugin-electron-builder": {
-      "electron-builder": "^23.0.3"
+      "electron-builder": "^24.13.3"
     },
     "webpack@<5.0.0": ">=5.77.0",
     "webpack-dev-middleware": ">=5.3.4",
diff --git a/td.vue/src/App.vue b/td.vue/src/App.vue
index ed8ed8f3..3de65b4e 100644
--- a/td.vue/src/App.vue
+++ b/td.vue/src/App.vue
@@ -35,7 +35,6 @@ export default {
     }),
     mounted() {
         this.$store.dispatch(LOADER_FINISHED);
-        this.$toast.warning(this.$t('nav.v2Warning'), { timeout: false });
     }
 };
 </script>
diff --git a/td.vue/src/components/Graph.vue b/td.vue/src/components/Graph.vue
index a21498f2..3202ef06 100644
--- a/td.vue/src/components/Graph.vue
+++ b/td.vue/src/components/Graph.vue
@@ -24,12 +24,11 @@
                 </b-row>
             </b-col>
         </b-row>
-        <td-graph-meta @threatSelected="threatSelected" @threatSuggest="threatSuggest" />
+        <td-graph-meta @threatSelected="threatSelected" />
 
         <div>
             <td-keyboard-shortcuts />
             <td-threat-edit-dialog ref="threatEditDialog" />
-            <td-threat-suggest-dialog ref="threatSuggestDialog" />
         </div>
     </div>
 </template>
@@ -47,7 +46,6 @@ import TdGraphButtons from '@/components/GraphButtons.vue';
 import TdGraphMeta from '@/components/GraphMeta.vue';
 import TdKeyboardShortcuts from '@/components/KeyboardShortcuts.vue';
 import TdThreatEditDialog from '@/components/ThreatEditDialog.vue';
-import TdThreatSuggestDialog from './ThreatSuggestDialog.vue';
 
 import { getProviderType } from '@/service/provider/providers.js';
 import diagramService from '@/service/migration/diagram.js';
@@ -60,8 +58,7 @@ export default {
         TdGraphButtons,
         TdGraphMeta,
         TdKeyboardShortcuts,
-        TdThreatEditDialog,
-        TdThreatSuggestDialog
+        TdThreatEditDialog
     },
     computed: mapState({
         diagram: (state) => state.threatmodel.selectedDiagram,
@@ -89,9 +86,6 @@ export default {
         threatSelected(threatId,state) {
             this.$refs.threatEditDialog.editThreat(threatId,state);
         },
-        threatSuggest(type){
-            this.$refs.threatSuggestDialog.showModal(type);
-        },
         saved() {
             console.debug('Save diagram');
             const updated = Object.assign({}, this.diagram);
diff --git a/td.vue/src/components/GraphMeta.vue b/td.vue/src/components/GraphMeta.vue
index 135df7e1..d7b772d9 100644
--- a/td.vue/src/components/GraphMeta.vue
+++ b/td.vue/src/components/GraphMeta.vue
@@ -11,6 +11,7 @@
             <b-card header-tag="header">
                 <template #header>
                     {{ $t('threatmodel.threats') }}
+
                     <b-btn
                         :disabled="disableNewThreat"
                         @click="newThreat()"
@@ -51,20 +52,11 @@
                     </b-card-text>
                 </b-card-body>
             </b-card>
-            <a href="javascript:void(0)" v-if="!!cellRef" @click="AddThreatByType()" class="new-threat-by-type m-2">
-                <font-awesome-icon icon="plus"></font-awesome-icon>
-                {{ $t('threats.newThreatByType') }}
-            </a>
         </b-col>
     </b-row>
 </template>
 
 <style lang="scss" scoped>
-.new-threat-by-type {
-    color: $orange;
-    font-size: 16px;
-    padding: 15px;
-}
 .props-header {
     a {
         font-size: 12px;
@@ -128,12 +120,6 @@ export default {
             this.$store.dispatch(CELL_DATA_UPDATED, this.cellRef.data);
             dataChanged.updateStyleAttrs(this.cellRef);
             this.threatSelected(threat.id,'new');
-        },
-        AddThreatByType(){
-            this.$emit('threatSuggest','type');
-        },
-        AddThreatByContext(){
-            this.$emit('threatSuggest','context');
         }
     },
 };
diff --git a/td.vue/src/components/ThreatEditDialog.vue b/td.vue/src/components/ThreatEditDialog.vue
index 2922003e..e29805cc 100644
--- a/td.vue/src/components/ThreatEditDialog.vue
+++ b/td.vue/src/components/ThreatEditDialog.vue
@@ -228,19 +228,19 @@ export default {
         },
         updateThreat() {
             const threatRef = this.cellRef.data.threats.find(x => x.id === this.threat.id);
+            const objRef = this.cellRef.data;
+            if(!objRef.threatFrequency){
+                const tmpfreq = threatModels.getFrequencyMapByElement(this.threat.modelType,this.cellRef.data.type);
+                if(tmpfreq!==null)
+                    objRef.threatFrequency = tmpfreq;
+            }
+            if(objRef.threatFrequency){
+                Object.keys(objRef.threatFrequency).forEach((k)=>{
+                    if(this.$t(`threats.model.${this.threat.modelType.toLowerCase()}.${k}`)===this.threat.type)
+                        objRef.threatFrequency[k]++;
+                });
+            }
             if (threatRef) {
-                const objRef = this.cellRef.data;
-                if(!objRef.threatFrequency){
-                    const tmpfreq = threatModels.getFrequencyMapByElement(this.threat.modelType,this.cellRef.data.type);
-                    if(tmpfreq!==null)
-                        objRef.threatFrequency = tmpfreq;
-                }
-                if(objRef.threatFrequency){
-                    Object.keys(objRef.threatFrequency).forEach((k)=>{
-                        if(this.$t(`threats.model.${this.threat.modelType.toLowerCase()}.${k}`)===this.threat.type)
-                            objRef.threatFrequency[k]++;
-                    });
-                }
                 threatRef.status = this.threat.status;
                 threatRef.severity = this.threat.severity;
                 threatRef.title = this.threat.title;
diff --git a/td.vue/src/components/ThreatSuggestDialog.vue b/td.vue/src/components/ThreatSuggestDialog.vue
deleted file mode 100644
index e7a30c7c..00000000
--- a/td.vue/src/components/ThreatSuggestDialog.vue
+++ /dev/null
@@ -1,256 +0,0 @@
-<template>
-    <div>
-        <b-modal
-            v-if="!!threat"
-            id="threat-edit"
-            size="lg"
-            ok-variant="primary"
-            header-bg-variant="primary"
-            header-text-variant="light"
-            :title="modalTitle"
-            ref="editModal"
-        >
-            <b-form>
-                <b-form-row>
-                    <b-col>
-                        <b-form-group
-                            id="title-group"
-                            :label="$t('threats.properties.title')"
-                            label-for="title">
-                            <b-form-input
-                                id="title"
-                                v-model="threat.title"
-                                type="text"
-                                required
-                            ></b-form-input>
-                        </b-form-group>
-                    </b-col>
-                </b-form-row>
-
-                <b-form-row>
-                    <b-col>
-                        <b-form-group
-                            id="threat-type-group"
-                            :label="$t('threats.properties.type')"
-                            label-for="threat-type">
-                            <b-form-select
-                                id="threat-type"
-                                v-model="threat.type"
-                                :options="threatTypes">
-                            </b-form-select>
-                        </b-form-group>
-                    </b-col>
-                </b-form-row>
-
-                <b-form-row>
-                    <b-col md=5>
-                        <b-form-group
-                            id="status-group"
-                            class="float-left"
-                            :label="$t('threats.properties.status')"
-                            label-for="status">
-                            <b-form-radio-group
-                                id="status"
-                                v-model="threat.status"
-                                :options="statuses"
-                                buttons
-                            ></b-form-radio-group>
-                        </b-form-group>
-                    </b-col>
-
-                    <b-col md=2>
-                        <b-form-group
-                            id="score-group"
-                            :label="$t('threats.properties.score')"
-                            label-for="score">
-                            <b-form-input
-                                id="score"
-                                v-model="threat.score"
-                                type="text"
-                            ></b-form-input>
-                        </b-form-group>
-                    </b-col>
-
-                    <b-col md=5>
-                        <b-form-group
-                            id="priority-group"
-                            class="float-right"
-                            :label="$t('threats.properties.priority')"
-                            label-for="priority">
-                            <b-form-radio-group
-                                id="priority"
-                                v-model="threat.severity"
-                                :options="priorities"
-                                buttons
-                            ></b-form-radio-group>
-                        </b-form-group>
-                    </b-col>
-                </b-form-row>
-
-                <b-form-row>
-                    <b-col>
-                        <b-form-group
-                            id="description-group"
-                            :label="$t('threats.properties.description')"
-                            label-for="description">
-                            <b-form-textarea
-                                id="description"
-                                v-model="threat.description"
-                                rows="5">
-                            </b-form-textarea>
-                        </b-form-group>
-                    </b-col>
-                </b-form-row>
-
-                <b-form-row>
-                    <b-col>
-                        <b-form-group
-                            id="mitigation-group"
-                            :label="$t('threats.properties.mitigation')"
-                            label-for="mitigation">
-                            <b-form-textarea
-                                id="mitigation"
-                                v-model="threat.mitigation"
-                                rows="5">
-                            </b-form-textarea>
-                        </b-form-group>
-                    </b-col>
-                </b-form-row>
-            </b-form>
-
-            <template #modal-footer>
-                <div class="w-100">
-                <b-button
-                    variant="danger"
-                    class="float-right"
-                    @click="acceptSuggestion()"
-                >
-                    {{ $t('forms.apply') }}
-                </b-button>
-                <b-button
-                variant="secondary"
-                class="float-left"
-                @click="previous()"
-                >
-                {{ $t('forms.previous') }}
-            </b-button>
-            <b-button
-                variant="danger"
-                class="float-left"
-                @click="next()"
-            >
-                {{ $t('forms.next') }}
-            </b-button>
-                <b-button
-                variant="secondary"
-                class="float-right mr-2"
-                @click="hideModal()"
-            >
-                {{ $t('forms.cancel') }}
-            </b-button>
-                </div>
-            </template>
-        </b-modal>
-    </div>
-</template>
-
-
-<script>
-import {mapState} from 'vuex';
-import { createNewTypedThreat } from '@/service/threats/index.js';
-import { CELL_DATA_UPDATED} from '@/store/actions/cell.js';
-import tmActions from '@/store/actions/threatmodel.js';
-import dataChanged from '@/service/x6/graph/data-changed.js';
-import threatModels from '@/service/threats/models/index.js';
-export default {
-    name: 'TdThreatSuggest',
-    computed: {
-        ...mapState({
-            cellRef: (state) => state.cell.ref,
-            modelType: (state) => state.threatmodel.selectedDiagram.diagramType,
-            threatTop: (state) => state.threatmodel.data.detail.threatTop
-        }),
-        threatTypes(){
-            if(!this.cellRef || !this.threat || !this.modelType)
-                return [];
-            const res = [];
-            const threattypes = threatModels.getThreatTypesByElement(this.modelType, this.cellRef.data.type);
-            Object.keys(threattypes).forEach((type)=>{
-                res.push(this.$t(type));
-            }, this);
-            return res;
-        },
-        statuses() {
-            return [
-                { value: 'NotApplicable', text: this.$t('threats.status.notApplicable') },
-                { value: 'Open', text: this.$t('threats.status.open') },
-                { value: 'Mitigated', text: this.$t('threats.status.mitigated') }
-            ];
-        },
-        priorities() {
-            return [
-                { value: 'Low', text: this.$t('threats.priority.low') },
-                { value: 'Medium', text: this.$t('threats.priority.medium') },
-                { value: 'High', text: this.$t('threats.priority.high') }
-            ];
-        },
-        modalTitle() { return this.$t('threats.newThreatByType') + ' #' + (this.threatTop+1); }
-    },
-    data() {
-        return {
-            threat: {},
-            index:0
-        };
-    },
-    methods: {
-        showModal() {
-            this.modalTitle;
-            this.threat = createNewTypedThreat(this.modelType,this.cellRef.data.type,this.threatTop+1);
-            this.$refs.editModal.show();
-        },
-        hideModal(){
-            this.threat={};
-            this.index=0;
-            this.$refs.editModal.hide();
-        },
-        next(){
-            this.index++;
-            if(this.index>=this.threatTypes.length){
-                this.$refs.editModal.hide();
-                this.index=0;
-                return;
-            }
-            this.threat.type = this.threatTypes[this.index];
-        },
-        acceptSuggestion(){
-            const objRef = this.cellRef.data;
-            if(!objRef.threatFrequency){
-                const tmpfreq = threatModels.getFrequencyMapByElement(this.threat.modelType,this.cellRef.data.type);
-                if(tmpfreq!==null)
-                    objRef.threatFrequency = tmpfreq;
-            }
-            if(objRef.threatFrequency){
-                Object.keys(objRef.threatFrequency).forEach((k)=>{
-                    if(this.$t(`threats.model.${this.modelType.toLowerCase()}.${k}`)===this.threat.type)
-                        objRef.threatFrequency[k]++;
-                });
-            }
-            this.threat.new=false;
-            this.cellRef.data.threats.push(this.threat);
-            this.cellRef.data.hasOpenThreats = this.cellRef.data.threats.length > 0;
-            this.$store.dispatch(tmActions.update, {threatTop : this.threatTop+1});
-            this.$store.dispatch(tmActions.modified);
-            this.$store.dispatch(CELL_DATA_UPDATED,this.cellRef.data);
-            dataChanged.updateStyleAttrs(this.cellRef);
-            this.next();
-            
-        },
-        previous(){
-            if(this.index>0){
-                this.index--;
-                this.threat.type = this.threatTypes[this.index];
-            }
-        }
-    }
-};
-</script>
\ No newline at end of file
diff --git a/td.vue/src/i18n/en.js b/td.vue/src/i18n/en.js
index daea511b..ac35da54 100644
--- a/td.vue/src/i18n/en.js
+++ b/td.vue/src/i18n/en.js
@@ -253,9 +253,7 @@ const eng = {
         saveAs: 'Save As',
         saveModel: 'Save Model',
         saveModelAs: 'Save Model As',
-        search: 'Search',
-        next:'next',
-        previous:'previous'
+        search: 'Search'
     },
     threats: {
         model: {
diff --git a/td.vue/src/service/migration/diagram.js b/td.vue/src/service/migration/diagram.js
index df5c2cdb..cca8e76a 100644
--- a/td.vue/src/service/migration/diagram.js
+++ b/td.vue/src/service/migration/diagram.js
@@ -1,9 +1,3 @@
-/**
- * @name diagram
- * @description Reads an existing v1 diagram to create components for the v2 UI
- * This is not for persistence, and is done on a diagram by diagram basis
- */
-
 import cells from './cells.js';
 import dataChanged from '@/service/x6/graph/data-changed.js';
 import graphFactory from '@/service/x6/graph/graph.js';
@@ -42,10 +36,10 @@ const upgradeAndDraw = (diagram, graph) => {
 
 const drawGraph = (diagram, graph) => {
     if (diagram.version && diagram.version.startsWith('2.')) {
-	    console.debug('open version 2.x diagram');
+        console.debug('open version 2.x diagram');
         graph.fromJSON(diagram);
     } else {
-	    console.debug('upgrade version 1.x diagram');
+        console.debug('upgrade version 1.x diagram');
         upgradeAndDraw(diagram, graph);
     }
     return graph;
diff --git a/td.vue/src/service/schema/ajv.js b/td.vue/src/service/schema/ajv.js
index 7bf119e0..1baed285 100644
--- a/td.vue/src/service/schema/ajv.js
+++ b/td.vue/src/service/schema/ajv.js
@@ -1,4 +1,7 @@
 import Ajv from 'ajv';
+import Vue from 'vue';
+import i18n from '@/i18n/index.js';
+
 import { schema } from './threat-model-schema';
 import { schema as schemaV2 } from './threat-model-schema.V2';
 import { schema as schemaOTM } from './open-threat-model-schema';
@@ -20,6 +23,7 @@ export const isValidSchema = (jsonFile) => {
     valid = validate(jsonFile);
     if (valid) {
         console.debug('Schema validate success for V1.x model');
+        Vue.$toast.warning(i18n.get().t('nav.v2Warning'), { timeout: false });
         return true;
     }
 
diff --git a/td.vue/src/service/x6/shapes/trust-boundary-box.js b/td.vue/src/service/x6/shapes/trust-boundary-box.js
index 5eb4a0ac..20bf2c67 100644
--- a/td.vue/src/service/x6/shapes/trust-boundary-box.js
+++ b/td.vue/src/service/x6/shapes/trust-boundary-box.js
@@ -14,8 +14,8 @@ export const TrustBoundaryBox = Shape.HeaderedRect.define({
         body: {
             rx: 10,
             ry: 10,
-            strokeDasharray: '5 5',
             strokeWidth: 3,
+            strokeDasharray: '10 5',
             fill: 'transparent',
             fillOpacity: 0
         },
diff --git a/td.vue/src/service/x6/shapes/trust-boundary-curve-stencil.js b/td.vue/src/service/x6/shapes/trust-boundary-curve-stencil.js
index 89ec618d..60bb699d 100644
--- a/td.vue/src/service/x6/shapes/trust-boundary-curve-stencil.js
+++ b/td.vue/src/service/x6/shapes/trust-boundary-curve-stencil.js
@@ -27,7 +27,7 @@ export const TrustBoundaryCurveStencil = Shape.Empty.define({
             strokeWidth: 3,
             stroke: '#333333',
             fill: 'transparent',
-            strokeDasharray: '5 5',
+            strokeDasharray: '10 5',
             refD: 'M 30 20 C 70 20 70 100 110 100'
         },
         label: {
diff --git a/td.vue/src/service/x6/shapes/trust-boundary-curve.js b/td.vue/src/service/x6/shapes/trust-boundary-curve.js
index e2c5b457..5eca0fd3 100644
--- a/td.vue/src/service/x6/shapes/trust-boundary-curve.js
+++ b/td.vue/src/service/x6/shapes/trust-boundary-curve.js
@@ -13,7 +13,7 @@ export const TrustBoundaryCurve = Shape.Edge.define({
     attrs: {
         line: {
             strokeWidth: 3,
-            strokeDasharray: '5 5',
+            strokeDasharray: '10 5',
             sourceMarker: null,
             targetMarker: null
         }
diff --git a/td.vue/vue.config.js b/td.vue/vue.config.js
index d8a5b59e..b6c51eae 100644
--- a/td.vue/vue.config.js
+++ b/td.vue/vue.config.js
@@ -40,24 +40,25 @@ module.exports = {
                 publish: {
                     provider: 'github'
                 },
-                afterSign: 'electron-builder-notarize',
                 mac: {
                     category: 'public.app-category.developer-tools',
                     icon: './src/icons/icon.icns',
                     hardenedRuntime: true,
                     entitlements: './node_modules/electron-builder-notarize/entitlements.mac.inherit.plist',
                     entitlementsInherit: './node_modules/electron-builder-notarize/entitlements.mac.inherit.plist',
-                    target: 'default'
+                    target: [
+                        {
+                            target: 'default',
+                            arch: ['x64', 'arm64']
+                        }
+                    ]
                 },
                 win: {
                     icon: './src/icons/icon.ico',
                     target: [
                         {
                             target: 'nsis',
-                            arch: [
-                                'ia32',
-                                'x64'
-                            ]
+                            arch: ['arm64', 'x64']
                         }
                     ]
                 },
@@ -67,8 +68,14 @@ module.exports = {
                     icon: './src/icons/td-256.png',
                     synopsis: 'OWASP Threat Dragon',
                     target: [
-                        'AppImage',
-                        'snap',
+                        {
+                            target: 'AppImage',
+                            arch: ['arm64', 'x64']
+                        },
+                        {
+                            target: 'snap',
+                            arch: ['arm64', 'x64']
+                        },
                         'deb',
                         'rpm'
                     ]


Test patch:
diff --git a/td.server/test/helpers/logger.helper.spec.js b/td.server/test/helpers/logger.helper.spec.js
index af63397b..45ad2ada 100644
--- a/td.server/test/helpers/logger.helper.spec.js
+++ b/td.server/test/helpers/logger.helper.spec.js
@@ -82,4 +82,13 @@ describe('helpers/logger.helper.js', () => {
     it('throws an error for an unknown level', () => {
         expect(() => logger.fatal('fatal')).to.throw;
     });
+
+    it('stringifies complex objects', () => {
+        const complexObject = {a: 'a', b: { ba: 'ba', bb: 'bb'}};
+        let objectString = logger.transformToString(complexObject);
+        expect(objectString).to.equal('{"a":"a","b":{"ba":"ba","bb":"bb"}}');
+        complexObject.b.bb = complexObject.b
+        objectString = logger.transformToString(complexObject);
+        expect(objectString).to.equal('{"a":"a","b":{"ba":"ba","bb":"[Circular]"}}');
+    })
 });
\ No newline at end of file
diff --git a/td.vue/tests/e2e/support/e2e.js b/td.vue/tests/e2e/support/e2e.js
index 0c224109..1ec44d5a 100644
--- a/td.vue/tests/e2e/support/e2e.js
+++ b/td.vue/tests/e2e/support/e2e.js
@@ -6,7 +6,5 @@ beforeEach(() => {
     cy.visit('/', {
         onBeforeLoad: (win) => win.sessionStorage.clear()
     });
-    cy.get('.Vue-Toastification__toast--warning').should('be.visible');
-    cy.get('.Vue-Toastification__close-button').click();
     cy.get('#local-login-btn').should('be.visible');
 });
diff --git a/td.vue/tests/unit/app.spec.js b/td.vue/tests/unit/app.spec.js
index 2aebf1cc..57d88e91 100644
--- a/td.vue/tests/unit/app.spec.js
+++ b/td.vue/tests/unit/app.spec.js
@@ -9,13 +9,10 @@ import { LOADER_FINISHED } from '@/store/actions/loader.js';
 import Navbar from '@/components/Navbar.vue';
 
 describe('App.vue', () => {
-    let wrapper, localVue, mockStore, mockToast;
+    let wrapper, localVue, mockStore;
 
     beforeEach(() => {
         console.log = jest.fn();
-        mockToast = {
-            warning: jest.fn()
-        };
         localVue = createLocalVue();
         localVue.use(BootstrapVue);
         localVue.use(Vuex);
@@ -36,8 +33,7 @@ describe('App.vue', () => {
             stubs: ['router-view'],
             store: mockStore,
             mocks: {
-                $t: key => key,
-                $toast: mockToast
+                $t: key => key
             }
         });
     });
@@ -53,8 +49,4 @@ describe('App.vue', () => {
     it('has a b-container', () => {
         expect(wrapper.findComponent(BContainer).exists()).toBe(true);
     });
-
-    it('shows a warning toast for v2', () => {
-        expect(mockToast.warning).toHaveBeenCalled();
-    });
 });
diff --git a/td.vue/tests/unit/components/graph.spec.js b/td.vue/tests/unit/components/graph.spec.js
index 99bb25ac..bb4390b5 100644
--- a/td.vue/tests/unit/components/graph.spec.js
+++ b/td.vue/tests/unit/components/graph.spec.js
@@ -118,35 +118,6 @@ describe('components/GraphButtons.vue', () => {
         expect(threatEditStub.methods.editThreat).toHaveBeenCalledWith('asdf','new');
     });
 
-    it('saves the threat model diagram', () => {
-        wrapper.vm.saved();
-    });
-
-    it.skip('closes the diagram if there were no changes', () => {
-        wrapper.vm.closed();
-        expect(routerMock.push)
-            .toHaveBeenCalledWith({
-                name: 'gitThreatModel',
-                params: routerMock.params
-            });
-    });
-
-    it.skip('prompts the user before closing if data has changed', () => {
-        wrapper.setData({ diagram: { cells: [ 1, 2 ]}});
-        storeMock.state.threatmodel.modified = true;
-        wrapper.vm.closed();
-        expect(wrapper.vm.$bvModal.msgBoxConfirm)
-            .toHaveBeenCalled();
-    });
-
-    it.skip('does not close if the user selects no', () => {
-        wrapper.vm.$bvModal.msgBoxConfirm.mockResolvedValue(false);
-        wrapper.setData({ diagram: { cells: [ 1, 2 ]}});
-        storeMock.state.threatmodel.modified = true;
-        wrapper.vm.closed();
-        expect(routerMock.push).not.toHaveBeenCalled();
-    });
-
     it('disposes the graph', () => {
         wrapper.destroy();
         expect(diagramService.dispose).toHaveBeenCalled();
diff --git a/td.vue/tests/unit/components/graphButtons.spec.js b/td.vue/tests/unit/components/graphButtons.spec.js
index 8e8553f7..93a86b34 100644
--- a/td.vue/tests/unit/components/graphButtons.spec.js
+++ b/td.vue/tests/unit/components/graphButtons.spec.js
@@ -45,10 +45,6 @@ describe('components/GraphButtons.vue', () => {
             expect(btn.attributes('text')).toEqual('forms.save');
 
         });
-
-        it('emits the saved event', () => {
-            expect(wrapper.emitted().saved);
-        });
     });
 
     describe('close', () => {
@@ -60,10 +56,6 @@ describe('components/GraphButtons.vue', () => {
         it('has the save translation text', () => {
             expect(btn.attributes('text')).toEqual('forms.close');
         });
-
-        it('emits the closed event', () => {
-            expect(wrapper.emitted().closed);
-        });
     });
 
     describe('keyboard shortcuts', () => {
diff --git a/td.vue/tests/unit/service/debounce.spec.js b/td.vue/tests/unit/service/debounce.spec.js
deleted file mode 100644
index 97a75b6d..00000000
--- a/td.vue/tests/unit/service/debounce.spec.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import debounce from '@/service/debounce.js';
-
-describe('service/debounce.js', () => {
-    it('only calls the function once if run in under the delay time', (done) => {
-        const fn = jest.fn();
-        const debouncedFn = debounce(fn, 200);
-        for (let i = 0; i < 5; i++) {
-            debouncedFn();
-        }
-        setTimeout(() => {
-            expect(fn).toHaveBeenCalledTimes(1);
-            done();
-        }, 250);
-    });
-});
diff --git a/td.vue/tests/unit/service/x6/graph/events.spec.js b/td.vue/tests/unit/service/x6/graph/events.spec.js
index fef67aea..2b23fb8e 100644
--- a/td.vue/tests/unit/service/x6/graph/events.spec.js
+++ b/td.vue/tests/unit/service/x6/graph/events.spec.js
@@ -175,23 +175,6 @@ describe('service/x6/graph/events.js', () => {
             });
         });
 
-        describe.skip('a node without data', () => {
-            beforeEach(() => {
-                cell.convertToEdge = true;
-                cell.isNode.mockImplementation(() => true);
-                cell.constructor = { name: 'Store' };
-                cell.type = shapes.StoreShape.prototype.type;
-                if (cell.data) { delete cell.data; }
-                cell.setData.mockImplementation((data) => cell.data = data);
-                graph.on.mockImplementation((evt, fn) => fn({ isNew: false, edge, node, cell }));
-                events.listen(graph);
-            });
-
-            it('does not add an edge', () => {
-                expect(graph.addEdge).not.toHaveBeenCalled();
-            });
-        });
-
         describe('trust boundary curve', () => {
             beforeEach(() => {
                 cell.convertToEdge = true;
diff --git a/td.vue/tests/unit/service/x6/shapes/trust-boundary-box.spec.js b/td.vue/tests/unit/service/x6/shapes/trust-boundary-box.spec.js
index bf70f694..5b4a1fc6 100644
--- a/td.vue/tests/unit/service/x6/shapes/trust-boundary-box.spec.js
+++ b/td.vue/tests/unit/service/x6/shapes/trust-boundary-box.spec.js
@@ -13,6 +13,16 @@ describe('service/x6/shapes/trust-boundary-box.js', () => {
         expect(victim.constructor.name).toEqual('TrustBoundaryBox');
     });
 
+    describe('get victim attributes', () => {
+        it('sets the stroke dash array', () => {
+            expect(victim.attrs.body.strokeDasharray).toEqual('10 5');
+        });
+
+        it('sets the stroke width', () => {
+            expect(victim.attrs.body.strokeWidth).toEqual(3);
+        });
+    });
+
     describe('setName', () => {
         it('sets the name', () => {
             const name = 'tbbName';
diff --git a/td.vue/tests/unit/service/x6/shapes/trust-boundary-curve.spec.js b/td.vue/tests/unit/service/x6/shapes/trust-boundary-curve.spec.js
index 3082073f..8edb7f22 100644
--- a/td.vue/tests/unit/service/x6/shapes/trust-boundary-curve.spec.js
+++ b/td.vue/tests/unit/service/x6/shapes/trust-boundary-curve.spec.js
@@ -12,13 +12,17 @@ describe('service/x6/shapes/trust-boundary-curve.js', () => {
         expect(victim.constructor.name).toEqual('TrustBoundaryCurve');
     });
 
-    describe('get edge victim', () => {
+    describe('get edge victim attributes', () => {
         it('uses the smooth connector', () => {
             expect(victim.connector).toEqual('smooth');
         });
 
         it('sets the stroke dash array', () => {
-            expect(victim.attrs.line.strokeDasharray).toEqual('5 5');
+            expect(victim.attrs.line.strokeDasharray).toEqual('10 5');
+        });
+
+        it('sets the stroke width', () => {
+            expect(victim.attrs.line.strokeWidth).toEqual(3);
         });
 
         it('does not have a source marker', () => {
diff --git a/td.vue/tests/unit/service/x6/stencil.spec.js b/td.vue/tests/unit/service/x6/stencil.spec.js
index 931e5da2..f9389171 100644
--- a/td.vue/tests/unit/service/x6/stencil.spec.js
+++ b/td.vue/tests/unit/service/x6/stencil.spec.js
@@ -52,34 +52,6 @@ describe('service/x6/stencil.js', () => {
         });
     });
 
-    // TODO skip this test until the search function is back in, issues #194 and #454
-    describe.skip('search', () => {
-        it('returns true for a matching shape regardless of case', () => {
-            expect(cfg.search({ shape: 'MYSHAPE' }, 'mys')).toEqual(true);
-        });
-
-        it('returns false for unmatched shapes', () => {
-            expect(cfg.search({ shape: 'MYSHAPE' }, 'myyyyshape')).toEqual(false);
-        });
-
-        it('returns true for matched labels regardless of case', () => {
-            expect(cfg.search({ shape: 'MYSHAPE', label: 'MYLABEL' }, 'myl')).toEqual(true);
-        });
-
-        it('returns false for unmatched labels and shapes', () => {
-            expect(cfg.search({ shape: 'MYSHAPE', label: 'MYLABEL' }, 'foobar')).toEqual(false);
-        });
-
-        it('has a placeholder', () => {
-            expect(cfg.placeholder).toEqual('Search');
-        });
-    
-        it('has notFoundText', () => {
-            expect(cfg.notFoundText).toContain('want to open an issue?');
-        });
-
-    });
-
     it('creates an instance of TrustBoundaryBox', () => {
         expect(shapes.TrustBoundaryBox).toHaveBeenCalledTimes(1);
     });
diff --git a/td.vue/tests/unit/store/modules/branch.spec.js b/td.vue/tests/unit/store/modules/branch.spec.js
index 0f8287d1..2e42fc33 100644
--- a/td.vue/tests/unit/store/modules/branch.spec.js
+++ b/td.vue/tests/unit/store/modules/branch.spec.js
@@ -123,19 +123,6 @@ describe('store/modules/branch.js', () => {
             });
         });
 
-        describe('fetch', () => {
-            const branches = [ 'foo', 'bar' ];
-
-            beforeEach(() => {
-                branchModule.mutations[BRANCH_FETCH](branchModule.state, branches);
-            });
-
-            // TODO skip test because it stumbles over Vue.set
-            it.skip('sets the all array to the provided branches', () => {
-                expect(branchModule.state.all).toEqual(branches);
-            });
-        });
-
         describe('selected', () => {
             const branch = 'test';
 
diff --git a/td.vue/tests/unit/store/modules/cell.spec.js b/td.vue/tests/unit/store/modules/cell.spec.js
index da6b3225..80dae5fb 100644
--- a/td.vue/tests/unit/store/modules/cell.spec.js
+++ b/td.vue/tests/unit/store/modules/cell.spec.js
@@ -54,19 +54,6 @@ describe('store/modules/cell.js', () => {
             });
         });
 
-        describe('selected with threats', () => {
-
-            beforeEach(() => {
-                cell.data.threats = [{ one: 'two' }];
-                cellModule.mutations[CELL_SELECTED](cellModule.state, cell);
-            });
-
-            // TODO skip test because it stumbles over Vue.set
-            it.skip('sets the threats', () => {
-                expect(cellModule.state.threats).toEqual([{ one: 'two' }]);
-            });
-        });
-
         describe('unselected', () => {
             beforeEach(() => {
                 cellModule.state = { ref: cell };
@@ -78,26 +65,6 @@ describe('store/modules/cell.js', () => {
             });
         });
 
-        describe('data updated with threats', () => {
-            const newThreats = [{ name: 'one' }, { name: 'two' }];
-            
-            beforeEach(() => {
-                cellModule.state = { ref: cell, threats: [] };
-                cellModule.state.ref.setData = jest.fn();
-                cellModule.mutations[CELL_DATA_UPDATED](cellModule.state, { threats: newThreats });
-            });
-
-            // TODO skip test because it stumbles over Vue.set
-            it.skip('updates the data object', () => {
-                expect(cellModule.state.ref.setData).toHaveBeenCalledWith({ threats: newThreats });
-            });
-
-            // TODO skip test because it stumbles over Vue.set
-            it.skip('updates the threats', () => {
-                expect(cellModule.state.threats).toEqual(newThreats);
-            });
-        });
-
         describe('data updated without threats', () => {
             beforeEach(() => {
                 cellModule.state = { ref: cell, threats: [] };
diff --git a/td.vue/tests/unit/store/modules/provider.spec.js b/td.vue/tests/unit/store/modules/provider.spec.js
index 89fa85a1..33a0e39a 100644
--- a/td.vue/tests/unit/store/modules/provider.spec.js
+++ b/td.vue/tests/unit/store/modules/provider.spec.js
@@ -107,19 +107,6 @@ describe('store/modules/provider.js', () => {
             });
         });
 
-        describe('fetch', () => {
-            const providerNames = Object.keys(providerService.providerNames);
-
-            beforeEach(() => {
-                providerModule.mutations[PROVIDER_FETCH](providerModule.state, providerNames);
-            });
-
-            // TODO skip test because it stumbles over Vue.set
-            it.skip('sets the all array to the provided providers', () => {
-                expect(providerModule.state.all).toEqual(providerNames);
-            });
-        });
-
         describe('selected', () => {
             const provider = 'test';
             const providerUri = 'https://github.com';
diff --git a/td.vue/tests/unit/store/modules/repository.spec.js b/td.vue/tests/unit/store/modules/repository.spec.js
index 1825bb15..25209365 100644
--- a/td.vue/tests/unit/store/modules/repository.spec.js
+++ b/td.vue/tests/unit/store/modules/repository.spec.js
@@ -120,19 +120,6 @@ describe('store/modules/repository.js', () => {
             });
         });
 
-        describe('fetch', () => {
-            const repos = [ 'foo', 'bar' ];
-
-            beforeEach(() => {
-                repoModule.mutations[REPOSITORY_FETCH](repoModule.state, repos);
-            });
-
-            // TODO skip test because it stumbles over Vue.set
-            it.skip('sets the all array to the provided repos', () => {
-                expect(repoModule.state.all).toEqual(repos);
-            });
-        });
-
         describe('selected', () => {
             const repo = 'test';
 
diff --git a/td.vue/tests/unit/store/modules/threatmodel.spec.js b/td.vue/tests/unit/store/modules/threatmodel.spec.js
index 813fcff3..4b148f76 100644
--- a/td.vue/tests/unit/store/modules/threatmodel.spec.js
+++ b/td.vue/tests/unit/store/modules/threatmodel.spec.js
@@ -4,7 +4,6 @@ import save from '@/service/save.js';
 import {
     THREATMODEL_CLEAR,
     THREATMODEL_CREATE,
-    THREATMODEL_DIAGRAM_MODIFIED,
     THREATMODEL_DIAGRAM_SAVED,
     THREATMODEL_DIAGRAM_SELECTED,
     THREATMODEL_FETCH,
@@ -135,15 +134,6 @@ describe('store/modules/threatmodel.js', () => {
             });
         });
 
-        describe.skip('modifies the diagram', () => {
-	        it('sets the modified flag', () => {
-                const diagram = { foo: 'bar' };
-	            threatmodelModule.state.modified = false;
-	            threatmodelModule.mutations[THREATMODEL_DIAGRAM_MODIFIED](threatmodelModule.state, diagram);
-	            expect(threatmodelModule.state.modified).toEqual(true);
-	        });
-        });
-
         it('commits the diagram updated action', () => {
             const diagram = { foo: 'bar' };
             threatmodelModule.actions[THREATMODEL_DIAGRAM_SAVED](mocks, diagram);
@@ -351,28 +341,6 @@ describe('store/modules/threatmodel.js', () => {
             });
         });
 
-        describe.skip('diagramSaved', () => {
-            let diagram;
-            beforeEach(() => {
-                threatmodelModule.state.data.detail = {
-                    diagrams: [
-                        { id: 1 },
-                        { id: 2}
-                    ]
-                };
-                threatmodelModule.state.selectedDiagram = { id: 2, foo: 'bar' };
-                diagram = { id: 2, foo: 'baz' };
-                threatmodelModule.mutations[THREATMODEL_DIAGRAM_SAVED](threatmodelModule.state, diagram);
-            });
-
-            it('updates the selectedDiagram', () => {
-                expect(threatmodelModule.state.selectedDiagram).toEqual(diagram);
-            });
-
-            it('updates the diagrams array', () => {
-                expect(threatmodelModule.state.data.detail.diagrams[1]).toEqual(diagram);
-            });
-        });
 
         describe('diagramSelected', () => {
             let diagram;
@@ -404,18 +372,6 @@ describe('store/modules/threatmodel.js', () => {
             });
         });
 
-        describe.skip('fetch all', () => {
-            const models = [ 'foo', 'bar' ];
-
-            beforeEach(() => {
-                threatmodelModule.mutations[THREATMODEL_FETCH_ALL](threatmodelModule.state, models);
-            });
-
-            it('sets the all array to the provided models', () => {
-                expect(threatmodelModule.state.all).toEqual(models);
-            });
-        });
-
         describe('selected', () => {
             const model = 'test';
 
@@ -428,22 +384,6 @@ describe('store/modules/threatmodel.js', () => {
             });
         });
 
-        describe.skip('contributors updated', () => {
-            const contribs = [ 'test1', 'test2' ];
-            beforeEach(() => {
-                threatmodelModule.state.data = {
-                    detail: {
-                        contributors: []
-                    }
-                };
-                threatmodelModule.mutations[THREATMODEL_CONTRIBUTORS_UPDATED](threatmodelModule.state, contribs);
-            });
-
-            it('sets the contributors', () => {
-                expect(threatmodelModule.state.data.detail.contributors).toEqual(contribs.map(x => ({ name: x })));
-            });
-        });
-
         describe('modified', () => {
 	        it('sets the modified flag', () => {
 	            threatmodelModule.state.modified = false;
diff --git a/td.vue/tests/unit/views/homePage.spec.js b/td.vue/tests/unit/views/homePage.spec.js
index 248e9c07..1fccb16d 100644
--- a/td.vue/tests/unit/views/homePage.spec.js
+++ b/td.vue/tests/unit/views/homePage.spec.js
@@ -86,45 +86,4 @@ describe('HomePage.vue', () => {
             });
         });
     });
-
-    describe.skip('desktop', () => {
-        // TODO: this needs to be reinstated
-        beforeEach(() => {
-            localVue = createLocalVue();
-            localVue.use(Vuex);
-            localVue.use(BootstrapVue);
-            localVue.component('font-awesome-icon', FontAwesomeIcon);
-            mockStore = new Vuex.Store({
-                actions: {
-                    [AUTH_SET_LOCAL]: () => {},
-                    [PROVIDER_SELECTED]: () => {}
-                }
-            });
-            mockIsElectron = true;
-            jest.spyOn(loginApi, 'loginAsync').mockResolvedValue({ data: redirectUrl });
-            jest.spyOn(mockStore, 'dispatch');
-
-            // There may be a better way of doing this
-            // Source: https://remarkablemark.org/blog/2018/11/17/mock-window-location/
-            delete window.location;
-            window.location = { replace: jest.fn() };
-            router.push = jest.fn();
-
-            wrapper = shallowMount(HomePage, {
-                localVue,
-                isElectron: mockIsElectron,
-                store: mockStore,
-                mocks: {
-                    $t: key => key
-                }
-            });
-        });
-
-        describe('layout', () => {
-            it('has only the local session button', () => {
-                expect(wrapper.findAllComponents(TdProviderLoginButton)).toHaveLength(1);
-            });
-        });
-    });
-
 });
\ No newline at end of file