diff --git a/.github/workflows/checks.build.yaml b/.github/workflows/checks.build.yaml index c7d3bef..0df3d9d 100644 --- a/.github/workflows/checks.build.yaml +++ b/.github/workflows/checks.build.yaml @@ -32,12 +32,15 @@ jobs: name: Build run: npm run build -- --mode ${{ matrix.mode }} - # A new job is used due to environments/modes different from Vue CLI, https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/1626 build-desktop: strategy: matrix: os: [ macos, ubuntu, windows ] - mode: [ development, production ] # "test" is not supported https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/1627 + mode: [ + # electron-vite modes: https://electron-vite.org/guide/env-and-mode.html#global-env-variables + development, # Used by `dev` command + production, # Used by `build` and `preview` commands + ] fail-fast: false # Allows to see results from other combinations runs-on: ${{ matrix.os }}-latest steps: @@ -51,14 +54,11 @@ jobs: name: Install dependencies run: npm ci - - name: Install cross-env - # Used to set NODE_ENV due to https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/1626 - run: npm install --global cross-env + name: Prebuild + run: npm run electron:prebuild -- --mode ${{ matrix.mode }} - name: Build - run: |- - cross-env-shell NODE_ENV=${{ matrix.mode }} - npm run electron:build -- --publish never --mode ${{ matrix.mode }} + run: npm run electron:build -- --publish never create-icons: strategy: diff --git a/.github/workflows/release.desktop.yaml b/.github/workflows/release.desktop.yaml index 247b145..b113b1b 100644 --- a/.github/workflows/release.desktop.yaml +++ b/.github/workflows/release.desktop.yaml @@ -13,20 +13,29 @@ jobs: fail-fast: false # So publish runs for other OSes if one fails runs-on: ${{ matrix.os }}-latest steps: - - uses: actions/checkout@v2 + - + uses: actions/checkout@v2 with: ref: master # otherwise it defaults to the version tag missing bump commit fetch-depth: 0 # fetch all history - - name: Checkout to bump commit + - + name: Checkout to bump commit run: git checkout "$(git rev-list "${{ github.event.release.tag_name }}"..master | tail -1)" - - name: Setup node + - + name: Setup node uses: ./.github/actions/setup-node - - name: Install dependencies + - + name: Install dependencies run: npm ci - - name: Run unit tests + - + name: Run unit tests run: npm run test:unit - - name: Publish desktop app - run: npm run electron:build -- -p always # https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/recipes.html#upload-release-to-github + - + name: Prebuild + run: npm run electron:prebuild + - + name: Build and publish + run: npm run electron:build -- --publish always env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} EP_GH_IGNORE_TIME: true # Otherwise publishing fails if GitHub release is more than 2 hours old https://github.com/electron-userland/electron-builder/issues/2074 diff --git a/docs/development.md b/docs/development.md index 53519e5..6dde6df 100644 --- a/docs/development.md +++ b/docs/development.md @@ -43,6 +43,13 @@ You could run other types of tests as well, but they may take longer time and ov - Start a local web server that serves the built solution from `./dist`. - 💡 Run `npm run build` before `npm run preview`. +**Desktop apps:** + +- `npm run electron:dev`: The command will build the main process and preload scripts source code, and start a dev server for the renderer, and start the Electron app. +- `npm run electron:preview`: The command will build the main process, preload scripts and renderer source code, and start the Electron app to preview. +- `npm run electron:prebuild`: The command will build the main process, preload scripts and renderer source code. Usually before packaging the Electron application, you need to execute this command. +- `npm run electron:build`: Prebuilds the Electron application, packages and publishes it through `electron-builder`. + **Docker:** 1. Build: `docker build -t undergroundwires/privacy.sexy:latest .` diff --git a/docs/presentation.md b/docs/presentation.md index 4677cfb..95f8303 100644 --- a/docs/presentation.md +++ b/docs/presentation.md @@ -24,8 +24,10 @@ The presentation layer uses an event-driven architecture for bidirectional react - [**`main.scss`**](./../src/presentation/assets/styles/main.scss): Main Sass file, imported by other components as single entrypoint. - [**`main.ts`**](./../src/presentation/main.ts): Starts Vue app. - [**`electron/`**](./../src/presentation/electron/): Contains Electron code. - - [**`main.ts`**](./../src/presentation/main.ts): Starts Electron app. + - [`/main/` **`index.ts`**](./../src/presentation/main.ts): Main entry for Electron, managing application windows and lifecycle events. + - [`/preload/` **`index.ts`**](./../src/presentation/main.ts): Script executed before the renderer, securing Node.js features for renderer use. - [**`/vite.config.ts`**](./../vite.config.ts): Contains Vite configurations for building web application. +- [**`/electron.vite.config.ts`**](./../electron.vite.config.ts): Contains Vite configurations for building desktop applications. - [**`/postcss.config.cjs`**](./../postcss.config.cjs): Contains PostCSS configurations for Vite. ## Visual design best-practices diff --git a/electron-builder.yml b/electron-builder.yml new file mode 100644 index 0000000..a4037b7 --- /dev/null +++ b/electron-builder.yml @@ -0,0 +1,31 @@ +# ------- +# Windows +# ------- +win: + target: nsis +nsis: + artifactName: ${name}-${version}-Setup.${ext} + +# ----- +# Linux +# ----- +linux: + target: AppImage +appImage: + artifactName: ${name}-${version}.${ext} + +# ----- +# macOS +# ----- +mac: + target: dmg +dmg: + artifactName: ${name}-${version}.${ext} + +# ---------------- +# Publish options +# ---------------- +publish: + provider: 'github' + vPrefixedTagName: false # default: true + releaseType: release # default: draft diff --git a/electron.vite.config.ts b/electron.vite.config.ts new file mode 100644 index 0000000..bf77aec --- /dev/null +++ b/electron.vite.config.ts @@ -0,0 +1,69 @@ +import { resolve } from 'path'; +import { mergeConfig, UserConfig } from 'vite'; +import { defineConfig, externalizeDepsPlugin } from 'electron-vite'; +import { getAliasesFromTsConfig, getClientEnvironmentVariables } from './vite-config-helper'; +import { createVueConfig } from './vite.config'; + +const MAIN_ENTRY_FILE = resolvePathFromProjectRoot('src/presentation/electron/main/index.ts'); +const PRELOAD_ENTRY_FILE = resolvePathFromProjectRoot('src/presentation/electron/preload/index.ts'); +const WEB_INDEX_HTML_PATH = resolvePathFromProjectRoot('src/presentation/index.html'); +const DIST_DIR = resolvePathFromProjectRoot('dist_electron/'); + +export default defineConfig({ + main: getSharedElectronConfig({ + distDirSubfolder: 'main', + entryFilePath: MAIN_ENTRY_FILE, + }), + preload: getSharedElectronConfig({ + distDirSubfolder: 'preload', + entryFilePath: PRELOAD_ENTRY_FILE, + }), + renderer: mergeConfig( + createVueConfig({ + supportLegacyBrowsers: false, + }), + { + build: { + outDir: resolve(DIST_DIR, 'renderer'), + rollupOptions: { + input: { + index: WEB_INDEX_HTML_PATH, + }, + external: ['os', 'child_process', 'fs', 'path'], + }, + }, + }, + ), +}); + +function getSharedElectronConfig(options: { + readonly distDirSubfolder: string; + readonly entryFilePath: string; +}): UserConfig { + return { + build: { + outDir: resolve(DIST_DIR, options.distDirSubfolder), + lib: { + entry: options.entryFilePath, + }, + rollupOptions: { + output: { + entryFileNames: '[name].cjs', // This is needed so `type="module"` works + }, + }, + }, + plugins: [externalizeDepsPlugin()], + define: { + ...getClientEnvironmentVariables(), + }, + resolve: { + alias: { + ...getAliasesFromTsConfig(), + }, + }, + }; +} + +function resolvePathFromProjectRoot(pathSegment: string) { + return resolve(__dirname, pathSegment); +} diff --git a/package-lock.json b/package-lock.json index 1c99ff4..1d9f1db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,7 +67,6 @@ "typescript": "~4.6.2", "vite": "^4.4.9", "vitest": "^0.34.2", - "vue-cli-plugin-electron-builder": "^3.0.0-alpha.4", "vue-tsc": "^1.8.8", "yaml-lint": "^1.7.0" } @@ -81,20 +80,6 @@ "node": ">=0.10.0" } }, - "node_modules/@achrinza/node-ipc": { - "version": "9.2.7", - "resolved": "https://registry.npmjs.org/@achrinza/node-ipc/-/node-ipc-9.2.7.tgz", - "integrity": "sha512-/EvNkqB4HNxPWCZASmgrjqG8gIdPOolD67LGASvGMp/FY5ne0rbvpYg5o9x8RmgjAl8KdmNQ4YlV1et9DYiW8g==", - "dev": true, - "dependencies": { - "@node-ipc/js-queue": "2.0.3", - "event-pubsub": "4.3.0", - "js-message": "1.0.7" - }, - "engines": { - "node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20" - } - }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -3368,18 +3353,6 @@ "vite": "^2.6.0 || ^3.0.0 || ^4.0.0" } }, - "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", - "integrity": "sha512-fL1wpr8hhD5gT2dA1qifeVaoDFlQR5es8tFuKqjHX+kdOtdNHnxkVZbtIrR2rxnMFvehkjaZRNV2H/gPXlb0hw==", - "dev": true, - "dependencies": { - "easy-stack": "1.0.1" - }, - "engines": { - "node": ">=1.0.0" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3611,91 +3584,6 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@soda/friendly-errors-webpack-plugin": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz", - "integrity": "sha512-h2ooWqP8XuFqTXT+NyAFbrArzfQA7R6HTezADrvD9Re8fxMLTPPniLdqVTdDaO0eIoLaAwKT+d6w+5GeTk7Vbg==", - "dev": true, - "dependencies": { - "chalk": "^3.0.0", - "error-stack-parser": "^2.0.6", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8.0.0" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/@soda/friendly-errors-webpack-plugin/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@soda/friendly-errors-webpack-plugin/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@soda/friendly-errors-webpack-plugin/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@soda/friendly-errors-webpack-plugin/node_modules/color-name": { - "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 - }, - "node_modules/@soda/friendly-errors-webpack-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@soda/friendly-errors-webpack-plugin/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -3774,26 +3662,6 @@ "@types/ms": "*" } }, - "node_modules/@types/eslint": { - "version": "8.44.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz", - "integrity": "sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", @@ -3887,12 +3755,6 @@ "integrity": "sha512-PZXtT+wqSMHnLPVExTh+tMt1VK+GvjRLsGZMbcQ4Mb/cG63xJig/TUmgrDa9aborl2i22UnpIzHYNu7s97NbBQ==", "dev": true }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, "node_modules/@types/plist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", @@ -4314,232 +4176,6 @@ "@volar/language-core": "1.10.1" } }, - "node_modules/@vue/cli-shared-utils": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.8.tgz", - "integrity": "sha512-uK2YB7bBVuQhjOJF+O52P9yFMXeJVj7ozqJkwYE9PlMHL1LMHjtCYm4cSdOebuPzyP+/9p0BimM/OqxsevIopQ==", - "dev": true, - "dependencies": { - "@achrinza/node-ipc": "^9.2.5", - "chalk": "^4.1.2", - "execa": "^1.0.0", - "joi": "^17.4.0", - "launch-editor": "^2.2.1", - "lru-cache": "^6.0.0", - "node-fetch": "^2.6.7", - "open": "^8.0.2", - "ora": "^5.3.0", - "read-pkg": "^5.1.1", - "semver": "^7.3.4", - "strip-ansi": "^6.0.0" - } - }, - "node_modules/@vue/cli-shared-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@vue/cli-shared-utils/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-shared-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@vue/cli-shared-utils/node_modules/color-name": { - "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 - }, - "node_modules/@vue/cli-shared-utils/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/@vue/cli-shared-utils/node_modules/cross-spawn/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@vue/cli-shared-utils/node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@vue/cli-shared-utils/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@vue/cli-shared-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@vue/cli-shared-utils/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@vue/cli-shared-utils/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/@vue/cli-shared-utils/node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@vue/cli-shared-utils/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@vue/cli-shared-utils/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@vue/cli-shared-utils/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@vue/cli-shared-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@vue/cli-shared-utils/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/@vue/cli-shared-utils/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/@vue/compiler-core": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz", @@ -4717,152 +4353,6 @@ "@vue/language-core": "1.8.8" } }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", - "dev": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dev": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, "node_modules/@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", @@ -4872,18 +4362,6 @@ "node": ">=10.0.0" } }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, "node_modules/7zip-bin": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz", @@ -4919,15 +4397,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -6267,15 +5736,6 @@ "node": ">=10" } }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, "node_modules/chromium-pickle-js": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", @@ -6318,18 +5778,6 @@ "node": ">=8" } }, - "node_modules/cli-spinners": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", - "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-table3": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", @@ -6375,38 +5823,6 @@ "node": ">=12" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone-deep/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -6991,27 +6407,6 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/deepmerge": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", - "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", @@ -7021,15 +6416,6 @@ "node": ">=10" } }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", @@ -7314,15 +6700,6 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "node_modules/easy-stack": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", - "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -7827,15 +7204,6 @@ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "dev": true, - "dependencies": { - "stackframe": "^1.3.4" - } - }, "node_modules/es-abstract": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", @@ -7911,12 +7279,6 @@ "safe-array-concat": "^1.0.0" } }, - "node_modules/es-module-lexer": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", - "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", - "dev": true - }, "node_modules/es-set-tostringtag": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", @@ -8787,15 +8149,6 @@ "node": ">=0.10.0" } }, - "node_modules/event-pubsub": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", - "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/event-stream": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", @@ -8817,15 +8170,6 @@ "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/execa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", @@ -9494,12 +8838,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -10268,21 +9606,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-empty": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-empty/-/is-empty-1.2.0.tgz", @@ -10377,15 +9700,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-ip": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", @@ -10642,18 +9956,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -10833,18 +10135,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -10869,15 +10159,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -11025,50 +10306,6 @@ "node": ">=8" } }, - "node_modules/javascript-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz", - "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", - "dev": true - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/jimp": { "version": "0.16.13", "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.16.13.tgz", @@ -11140,15 +10377,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/js-message": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", - "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", - "dev": true, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -11459,16 +10687,6 @@ "language-subtag-registry": "~0.3.2" } }, - "node_modules/launch-editor": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", - "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", - "dev": true, - "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.7.3" - } - }, "node_modules/lazy-ass": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", @@ -11676,15 +10894,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "engines": { - "node": ">=6.11.5" - } - }, "node_modules/local-pkg": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", @@ -13019,18 +12228,6 @@ "node": ">=10" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "node_modules/node-abi": { "version": "3.47.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.47.0.tgz", @@ -16238,23 +15435,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dev": true, - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -16272,99 +15452,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ora/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/ora/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ora/node_modules/color-name": { - "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 - }, - "node_modules/ora/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/os-locale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", @@ -16988,18 +16075,6 @@ "pathe": "^1.1.0" } }, - "node_modules/playwright-core": { - "version": "1.37.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz", - "integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==", - "dev": true, - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", @@ -17588,15 +16663,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -17672,21 +16738,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/read-pkg-up": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", @@ -17753,39 +16804,6 @@ "node": ">=0.10.0" } }, - "node_modules/read-pkg/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/read-pkg/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -19343,24 +18361,6 @@ "node": ">=v12.22.7" } }, - "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/secure-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz", @@ -19436,15 +18436,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -19457,27 +18448,6 @@ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "dev": true }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shallow-clone/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/sharp": { "version": "0.30.7", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.30.7.tgz", @@ -19519,12 +18489,6 @@ "node": ">=8" } }, - "node_modules/shebang-loader": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/shebang-loader/-/shebang-loader-0.0.1.tgz", - "integrity": "sha512-nQvhUHvKyzGK5aqPxHfHB5nlAN2EZ2U61S2G0YrxAuCRU5iGhFcxxRiaAdb18UoRS1zVMhRz4gdQ1xFEg3AOyA==", - "dev": true - }, "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", @@ -19534,15 +18498,6 @@ "node": ">=8" } }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -19816,12 +18771,6 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "dev": true - }, "node_modules/start-server-and-test": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.0.tgz", @@ -20062,15 +19011,6 @@ "node": ">=4" } }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -20427,40 +19367,6 @@ "node": ">=10" } }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -21819,127 +20725,6 @@ "csstype": "^3.1.0" } }, - "node_modules/vue-cli-plugin-electron-builder": { - "version": "3.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/vue-cli-plugin-electron-builder/-/vue-cli-plugin-electron-builder-3.0.0-alpha.4.tgz", - "integrity": "sha512-YHt0w3onxE6F5OJJTkArm2gIpJi0QW8QEp7uMV+vg+Ze8C3To/Qt8K5eIPkVs72uxqjiBhmE2Xapl3rHz9kEJg==", - "dev": true, - "dependencies": { - "@soda/friendly-errors-webpack-plugin": "^1.8.1", - "@vue/cli-shared-utils": "^5.0.0-rc.1", - "chokidar": "^3.0.2", - "electron-builder": "^22.2.0", - "execa": "^5.0.0", - "fs-extra": "^10.0.0", - "lodash.merge": "^4.6.1", - "playwright-core": "^1.17.1", - "shebang-loader": "^0.0.1", - "terser-webpack-plugin": "^5.3.0", - "webpack": "^5.65.0", - "webpack-chain": "^6.0.0", - "webpack-merge": "^5.8.0", - "yargs": "^16.2.0" - }, - "funding": { - "url": "https://github.com/sponsors/nklayman" - } - }, - "node_modules/vue-cli-plugin-electron-builder/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "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", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/vue-cli-plugin-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/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vue-cli-plugin-electron-builder/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/vue-cli-plugin-electron-builder/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/vue-cli-plugin-electron-builder/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/vue-eslint-parser": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.1.tgz", @@ -22055,28 +20840,6 @@ "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==", "dev": true }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dev": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -22086,88 +20849,6 @@ "node": ">=12" } }, - "node_modules/webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-chain": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-6.5.1.tgz", - "integrity": "sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA==", - "dev": true, - "dependencies": { - "deepmerge": "^1.5.2", - "javascript-stringify": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-merge": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", - "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", @@ -22315,12 +20996,6 @@ "node": ">=8" } }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -22644,17 +21319,6 @@ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "dev": true }, - "@achrinza/node-ipc": { - "version": "9.2.7", - "resolved": "https://registry.npmjs.org/@achrinza/node-ipc/-/node-ipc-9.2.7.tgz", - "integrity": "sha512-/EvNkqB4HNxPWCZASmgrjqG8gIdPOolD67LGASvGMp/FY5ne0rbvpYg5o9x8RmgjAl8KdmNQ4YlV1et9DYiW8g==", - "dev": true, - "requires": { - "@node-ipc/js-queue": "2.0.3", - "event-pubsub": "4.3.0", - "js-message": "1.0.7" - } - }, "@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -24931,15 +23595,6 @@ "tosource": "2.0.0-alpha.3" } }, - "@node-ipc/js-queue": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@node-ipc/js-queue/-/js-queue-2.0.3.tgz", - "integrity": "sha512-fL1wpr8hhD5gT2dA1qifeVaoDFlQR5es8tFuKqjHX+kdOtdNHnxkVZbtIrR2rxnMFvehkjaZRNV2H/gPXlb0hw==", - "dev": true, - "requires": { - "easy-stack": "1.0.1" - } - }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -25110,69 +23765,6 @@ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true }, - "@soda/friendly-errors-webpack-plugin": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz", - "integrity": "sha512-h2ooWqP8XuFqTXT+NyAFbrArzfQA7R6HTezADrvD9Re8fxMLTPPniLdqVTdDaO0eIoLaAwKT+d6w+5GeTk7Vbg==", - "dev": true, - "requires": { - "chalk": "^3.0.0", - "error-stack-parser": "^2.0.6", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "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 - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -25245,26 +23837,6 @@ "@types/ms": "*" } }, - "@types/eslint": { - "version": "8.44.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz", - "integrity": "sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "@types/estree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", @@ -25358,12 +23930,6 @@ "integrity": "sha512-PZXtT+wqSMHnLPVExTh+tMt1VK+GvjRLsGZMbcQ4Mb/cG63xJig/TUmgrDa9aborl2i22UnpIzHYNu7s97NbBQ==", "dev": true }, - "@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, "@types/plist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", @@ -25655,182 +24221,6 @@ "@volar/language-core": "1.10.1" } }, - "@vue/cli-shared-utils": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.8.tgz", - "integrity": "sha512-uK2YB7bBVuQhjOJF+O52P9yFMXeJVj7ozqJkwYE9PlMHL1LMHjtCYm4cSdOebuPzyP+/9p0BimM/OqxsevIopQ==", - "dev": true, - "requires": { - "@achrinza/node-ipc": "^9.2.5", - "chalk": "^4.1.2", - "execa": "^1.0.0", - "joi": "^17.4.0", - "launch-editor": "^2.2.1", - "lru-cache": "^6.0.0", - "node-fetch": "^2.6.7", - "open": "^8.0.2", - "ora": "^5.3.0", - "read-pkg": "^5.1.1", - "semver": "^7.3.4", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "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 - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true - }, - "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, - "requires": { - "yallist": "^4.0.0" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, "@vue/compiler-core": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz", @@ -25970,170 +24360,12 @@ "@vue/language-core": "1.8.8" } }, - "@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", - "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", - "dev": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, "@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", "dev": true }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, "7zip-bin": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz", @@ -26163,13 +24395,6 @@ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true }, - "acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "requires": {} - }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -27165,12 +25390,6 @@ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true - }, "chromium-pickle-js": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", @@ -27198,12 +25417,6 @@ "restore-cursor": "^3.1.0" } }, - "cli-spinners": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", - "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", - "dev": true - }, "cli-table3": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", @@ -27235,31 +25448,6 @@ "wrap-ansi": "^7.0.0" } }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true - }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, "clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -27730,33 +25918,12 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "deepmerge": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", - "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", - "dev": true - }, - "defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, "defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true - }, "define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", @@ -27983,12 +26150,6 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "easy-stack": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", - "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", - "dev": true - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -28379,15 +26540,6 @@ } } }, - "error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "dev": true, - "requires": { - "stackframe": "^1.3.4" - } - }, "es-abstract": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", @@ -28457,12 +26609,6 @@ "safe-array-concat": "^1.0.0" } }, - "es-module-lexer": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", - "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", - "dev": true - }, "es-set-tostringtag": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", @@ -29120,12 +27266,6 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "event-pubsub": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", - "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", - "dev": true - }, "event-stream": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", @@ -29147,12 +27287,6 @@ "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", "dev": true }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true - }, "execa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", @@ -29673,12 +27807,6 @@ "is-glob": "^4.0.3" } }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, "global": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", @@ -30222,12 +28350,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, "is-empty": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-empty/-/is-empty-1.2.0.tgz", @@ -30295,12 +28417,6 @@ "is-path-inside": "^3.0.2" } }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, "is-ip": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", @@ -30494,15 +28610,6 @@ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, "is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -30625,15 +28732,6 @@ "integrity": "sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==", "dev": true }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, "isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -30652,12 +28750,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -30769,40 +28861,6 @@ } } }, - "javascript-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz", - "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", - "dev": true - }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "jimp": { "version": "0.16.13", "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.16.13.tgz", @@ -30862,12 +28920,6 @@ } } }, - "js-message": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", - "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -31138,16 +29190,6 @@ "language-subtag-registry": "~0.3.2" } }, - "launch-editor": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", - "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", - "dev": true, - "requires": { - "picocolors": "^1.0.0", - "shell-quote": "^1.7.3" - } - }, "lazy-ass": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", @@ -31308,12 +29350,6 @@ "import-meta-resolve": "^2.0.0" } }, - "loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true - }, "local-pkg": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", @@ -32223,18 +30259,6 @@ } } }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "node-abi": { "version": "3.47.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.47.0.tgz", @@ -34317,17 +32341,6 @@ "mimic-fn": "^2.1.0" } }, - "open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dev": true, - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, "optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -34342,74 +32355,6 @@ "type-check": "^0.4.0" } }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "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 - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "os-locale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", @@ -34904,12 +32849,6 @@ "pathe": "^1.1.0" } }, - "playwright-core": { - "version": "1.37.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz", - "integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==", - "dev": true - }, "plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", @@ -35339,15 +33278,6 @@ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -35412,44 +33342,6 @@ } } }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, "read-pkg-up": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", @@ -36691,17 +34583,6 @@ "xmlchars": "^2.2.0" } }, - "schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, "secure-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz", @@ -36760,15 +34641,6 @@ } } }, - "serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -36781,23 +34653,6 @@ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "dev": true }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, "sharp": { "version": "0.30.7", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.30.7.tgz", @@ -36831,24 +34686,12 @@ "shebang-regex": "^3.0.0" } }, - "shebang-loader": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/shebang-loader/-/shebang-loader-0.0.1.tgz", - "integrity": "sha512-nQvhUHvKyzGK5aqPxHfHB5nlAN2EZ2U61S2G0YrxAuCRU5iGhFcxxRiaAdb18UoRS1zVMhRz4gdQ1xFEg3AOyA==", - "dev": true - }, "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true - }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -37054,12 +34897,6 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true }, - "stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "dev": true - }, "start-server-and-test": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.0.tgz", @@ -37248,12 +35085,6 @@ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true - }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -37553,19 +35384,6 @@ } } }, - "terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.17", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" - } - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -38496,102 +36314,6 @@ "csstype": "^3.1.0" } }, - "vue-cli-plugin-electron-builder": { - "version": "3.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/vue-cli-plugin-electron-builder/-/vue-cli-plugin-electron-builder-3.0.0-alpha.4.tgz", - "integrity": "sha512-YHt0w3onxE6F5OJJTkArm2gIpJi0QW8QEp7uMV+vg+Ze8C3To/Qt8K5eIPkVs72uxqjiBhmE2Xapl3rHz9kEJg==", - "dev": true, - "requires": { - "@soda/friendly-errors-webpack-plugin": "^1.8.1", - "@vue/cli-shared-utils": "^5.0.0-rc.1", - "chokidar": "^3.0.2", - "electron-builder": "^24.6.3", - "execa": "^5.0.0", - "fs-extra": "^10.0.0", - "lodash.merge": "^4.6.1", - "playwright-core": "^1.17.1", - "shebang-loader": "^0.0.1", - "terser-webpack-plugin": "^5.3.0", - "webpack": "^5.65.0", - "webpack-chain": "^6.0.0", - "webpack-merge": "^5.8.0", - "yargs": "^16.2.0" - }, - "dependencies": { - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "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, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } - } - }, "vue-eslint-parser": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.1.tgz", @@ -38676,89 +36398,12 @@ "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==", "dev": true }, - "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dev": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, "webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", "dev": true }, - "webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", - "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - } - }, - "webpack-chain": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-6.5.1.tgz", - "integrity": "sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA==", - "dev": true, - "requires": { - "deepmerge": "^1.5.2", - "javascript-stringify": "^2.0.1" - } - }, - "webpack-merge": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", - "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - } - }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true - }, "whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", @@ -38867,12 +36512,6 @@ "stackback": "0.0.2" } }, - "wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index 4edc7ec..088c1fc 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,10 @@ "test:cy:open": "start-server-and-test \"vite --port 7070 --mode production\" http://localhost:7070 \"cypress open --config baseUrl=http://localhost:7070\"", "lint": "npm run lint:md && npm run lint:md:consistency && npm run lint:md:relative-urls && npm run lint:eslint && npm run lint:yaml", "icons:build": "node scripts/logo-update.js", - "electron:build": "vue-cli-service electron:build", - "electron:serve": "vue-cli-service electron:serve", + "electron:dev": "electron-vite dev", + "electron:preview": "electron-vite preview", + "electron:prebuild": "electron-vite build", + "electron:build": "electron-builder", "lint:eslint": "eslint .", "lint:md": "markdownlint **/*.md --ignore node_modules", "lint:md:consistency": "remark . --frail --use remark-preset-lint-consistent", @@ -27,7 +29,7 @@ "postinstall": "electron-builder install-app-deps", "postuninstall": "electron-builder install-app-deps" }, - "main": "index.js", + "main": "./dist_electron/main/index.cjs", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/free-brands-svg-icons": "^6.4.0", @@ -87,15 +89,9 @@ "typescript": "~4.6.2", "vite": "^4.4.9", "vitest": "^0.34.2", - "vue-cli-plugin-electron-builder": "^3.0.0-alpha.4", "vue-tsc": "^1.8.8", "yaml-lint": "^1.7.0" }, - "overrides": { - "vue-cli-plugin-electron-builder": { - "electron-builder": "^24.6.3" - } - }, "//devDependencies": { "terser": "Used by @vitejs/plugin-legacy for minification", "typescript": [ diff --git a/scripts/check-desktop-runtime-errors/app/check-for-errors.js b/scripts/check-desktop-runtime-errors/app/check-for-errors.js index 744e0b1..7db6f66 100644 --- a/scripts/check-desktop-runtime-errors/app/check-for-errors.js +++ b/scripts/check-desktop-runtime-errors/app/check-for-errors.js @@ -2,9 +2,13 @@ import { splitTextIntoLines, indentText } from '../utils/text.js'; import { die } from '../utils/log.js'; import { readAppLogFile } from './app-logs.js'; -const LOG_ERROR_MARKER = '[error]'; // from electron-log const ELECTRON_CRASH_TITLE = 'Error'; // Used by electron for early crashes -const APP_INITIALIZED_MARKER = '[APP_INIT_SUCCESS]'; // Logged by application on successful initialization +const LOG_ERROR_MARKER = '[error]'; // from electron-log +const EXPECTED_LOG_MARKERS = [ + '[WINDOW_INIT]', + '[PRELOAD_INIT]', + '[APP_MOUNT_INIT]', +]; export async function checkForErrors(stderr, windowTitles, projectDir) { if (!projectDir) { throw new Error('missing project directory'); } @@ -19,7 +23,8 @@ async function gatherErrors(stderr, windowTitles, projectDir) { const logContent = await readAppLogFile(projectDir); return [ verifyStdErr(stderr), - verifyApplicationInitializationLog(logContent), + verifyApplicationLogsExist(logContent), + ...EXPECTED_LOG_MARKERS.map((marker) => verifyLogMarkerExistsInLogs(logContent, marker)), verifyWindowTitle(windowTitles), verifyErrorsInLogs(logContent), ].filter(Boolean); @@ -45,17 +50,24 @@ function formatError(error) { return message; } -function verifyApplicationInitializationLog(logContent) { +function verifyApplicationLogsExist(logContent) { if (!logContent || !logContent.length) { return describeError( 'Missing application logs', 'Application logs are empty not were not found.', ); } - if (!logContent.includes(APP_INITIALIZED_MARKER)) { + return undefined; +} + +function verifyLogMarkerExistsInLogs(logContent, marker) { + if (!marker) { + throw new Error('missing marker'); + } + if (!logContent?.includes(marker)) { return describeError( - 'Unexpected application logs', - `Missing identifier "${APP_INITIALIZED_MARKER}" in application logs.`, + 'Incomplete application logs', + `Missing identifier "${marker}" in application logs.`, ); } return undefined; @@ -104,7 +116,7 @@ function verifyErrorsInLogs(logContent) { function describeError(reason, description) { return { reason, - description: `${description}\nThis might indicate an early crash or significant runtime issue.`, + description: `${description}\n\nThis might indicate an early crash or significant runtime issue.`, }; } diff --git a/scripts/check-desktop-runtime-errors/config.js b/scripts/check-desktop-runtime-errors/config.js index 428f10e..6e520b2 100644 --- a/scripts/check-desktop-runtime-errors/config.js +++ b/scripts/check-desktop-runtime-errors/config.js @@ -1,7 +1,7 @@ import { join } from 'path'; -export const DESKTOP_BUILD_COMMAND = 'npm run electron:build -- -p never'; +export const DESKTOP_BUILD_COMMAND = 'npm run electron:prebuild && npm run electron:build -- --publish never'; export const PROJECT_DIR = process.cwd(); -export const DESKTOP_DIST_PATH = join(PROJECT_DIR, 'dist_electron'); +export const DESKTOP_DIST_PATH = join(PROJECT_DIR, 'dist'); export const APP_EXECUTION_DURATION_IN_SECONDS = 60; // Long enough for CI runners export const SCREENSHOT_PATH = join(PROJECT_DIR, 'screenshot.png'); diff --git a/scripts/check-desktop-runtime-errors/utils/npm.js b/scripts/check-desktop-runtime-errors/utils/npm.js index 803515d..b74e600 100644 --- a/scripts/check-desktop-runtime-errors/utils/npm.js +++ b/scripts/check-desktop-runtime-errors/utils/npm.js @@ -15,7 +15,7 @@ export async function npmInstall(projectDir) { if (!projectDir) { throw new Error('missing project directory'); } const npmModulesPath = join(projectDir, 'node_modules'); if (!await isDirMissingOrEmpty(npmModulesPath)) { - log(`Directory "${npmModulesPath}" exists and has content. Skipping 'npm install'.`); + log(`Directory "${npmModulesPath}" exists and has content. Skipping \`npm install\`.`); return; } log('Starting dependency installation...'); diff --git a/tests/shared/TypeHelpers.ts b/src/TypeHelpers.ts similarity index 72% rename from tests/shared/TypeHelpers.ts rename to src/TypeHelpers.ts index 1f8f6a8..cfac3f4 100644 --- a/tests/shared/TypeHelpers.ts +++ b/src/TypeHelpers.ts @@ -6,3 +6,6 @@ export type Constructible = { export type PropertyKeys = { [K in keyof T]: T[K] extends (...args: unknown[]) => unknown ? never : K; }[keyof T]; + +export type ConstructorArguments = + T extends new (...args: infer U) => unknown ? U : never; diff --git a/src/application/Parser/ApplicationParser.ts b/src/application/Parser/ApplicationParser.ts index 660ee20..53884ca 100644 --- a/src/application/Parser/ApplicationParser.ts +++ b/src/application/Parser/ApplicationParser.ts @@ -8,17 +8,18 @@ import LinuxData from '@/application/collections/linux.yaml'; import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser'; import { Application } from '@/domain/Application'; import { IAppMetadata } from '@/infrastructure/Metadata/IAppMetadata'; -import { ViteAppMetadata } from '@/infrastructure/Metadata/Vite/ViteAppMetadata'; +import { AppMetadataFactory } from '@/infrastructure/Metadata/AppMetadataFactory'; import { parseCategoryCollection } from './CategoryCollectionParser'; export function parseApplication( - parser = parseCategoryCollection, - metadata: IAppMetadata = new ViteAppMetadata(), + categoryParser = parseCategoryCollection, + informationParser = parseProjectInformation, + metadata: IAppMetadata = AppMetadataFactory.Current, collectionsData = PreParsedCollections, ): IApplication { validateCollectionsData(collectionsData); - const information = parseProjectInformation(metadata); - const collections = collectionsData.map((collection) => parser(collection, information)); + const information = informationParser(metadata); + const collections = collectionsData.map((collection) => categoryParser(collection, information)); const app = new Application(information, collections); return app; } diff --git a/src/application/Parser/ProjectInformationParser.ts b/src/application/Parser/ProjectInformationParser.ts index e379813..133bcae 100644 --- a/src/application/Parser/ProjectInformationParser.ts +++ b/src/application/Parser/ProjectInformationParser.ts @@ -2,14 +2,20 @@ import { IProjectInformation } from '@/domain/IProjectInformation'; import { ProjectInformation } from '@/domain/ProjectInformation'; import { IAppMetadata } from '@/infrastructure/Metadata/IAppMetadata'; import { Version } from '@/domain/Version'; +import { AppMetadataFactory } from '@/infrastructure/Metadata/AppMetadataFactory'; +import { ConstructorArguments } from '@/TypeHelpers'; -export function parseProjectInformation( - metadata: IAppMetadata, +export function +parseProjectInformation( + metadata: IAppMetadata = AppMetadataFactory.Current, + createProjectInformation: ProjectInformationFactory = ( + ...args + ) => new ProjectInformation(...args), ): IProjectInformation { const version = new Version( metadata.version, ); - return new ProjectInformation( + return createProjectInformation( metadata.name, version, metadata.slogan, @@ -17,3 +23,7 @@ export function parseProjectInformation( metadata.homepageUrl, ); } + +export type ProjectInformationFactory = ( + ...args: ConstructorArguments +) => IProjectInformation; diff --git a/src/infrastructure/Metadata/AppMetadataFactory.ts b/src/infrastructure/Metadata/AppMetadataFactory.ts new file mode 100644 index 0000000..70b088f --- /dev/null +++ b/src/infrastructure/Metadata/AppMetadataFactory.ts @@ -0,0 +1,16 @@ +import { IAppMetadata } from './IAppMetadata'; +import { ViteAppMetadata } from './Vite/ViteAppMetadata'; + +export class AppMetadataFactory { + public static get Current(): IAppMetadata { + if (!this.instance) { + this.instance = new ViteAppMetadata(); + } + return this.instance; + } + + private static instance: IAppMetadata; + + // eslint-disable-next-line @typescript-eslint/no-empty-function + private constructor() {} +} diff --git a/src/infrastructure/RuntimeSanity/ISanityCheckOptions.ts b/src/infrastructure/RuntimeSanity/ISanityCheckOptions.ts new file mode 100644 index 0000000..7dcb25c --- /dev/null +++ b/src/infrastructure/RuntimeSanity/ISanityCheckOptions.ts @@ -0,0 +1,3 @@ +export interface ISanityCheckOptions { + readonly validateMetadata: boolean; +} diff --git a/src/infrastructure/RuntimeSanity/ISanityValidator.ts b/src/infrastructure/RuntimeSanity/ISanityValidator.ts new file mode 100644 index 0000000..3b10d3d --- /dev/null +++ b/src/infrastructure/RuntimeSanity/ISanityValidator.ts @@ -0,0 +1,6 @@ +import { ISanityCheckOptions } from './ISanityCheckOptions'; + +export interface ISanityValidator { + shouldValidate(options: ISanityCheckOptions): boolean; + collectErrors(): Iterable; +} diff --git a/src/infrastructure/RuntimeSanity/SanityChecks.ts b/src/infrastructure/RuntimeSanity/SanityChecks.ts new file mode 100644 index 0000000..cf0d208 --- /dev/null +++ b/src/infrastructure/RuntimeSanity/SanityChecks.ts @@ -0,0 +1,28 @@ +import { ISanityCheckOptions } from './ISanityCheckOptions'; +import { ISanityValidator } from './ISanityValidator'; +import { MetadataValidator } from './Validators/MetadataValidator'; + +const SanityValidators: ISanityValidator[] = [ + new MetadataValidator(), +]; + +export function validateRuntimeSanity( + options: ISanityCheckOptions, + validators: readonly ISanityValidator[] = SanityValidators, +): void { + if (!options) { + throw new Error('missing options'); + } + if (!validators?.length) { + throw new Error('missing validators'); + } + const errorMessages = validators.reduce((errors, validator) => { + if (validator.shouldValidate(options)) { + errors.push(...validator.collectErrors()); + } + return errors; + }, new Array()); + if (errorMessages.length > 0) { + throw new Error(`Sanity check failed.\n${errorMessages.join('\n---\n')}`); + } +} diff --git a/src/infrastructure/RuntimeSanity/Validators/MetadataValidator.ts b/src/infrastructure/RuntimeSanity/Validators/MetadataValidator.ts new file mode 100644 index 0000000..a46b291 --- /dev/null +++ b/src/infrastructure/RuntimeSanity/Validators/MetadataValidator.ts @@ -0,0 +1,66 @@ +import { IAppMetadata } from '@/infrastructure/Metadata/IAppMetadata'; +import { AppMetadataFactory } from '@/infrastructure/Metadata/AppMetadataFactory'; +import { ISanityCheckOptions } from '../ISanityCheckOptions'; +import { ISanityValidator } from '../ISanityValidator'; + +export class MetadataValidator implements ISanityValidator { + private readonly metadata: IAppMetadata; + + constructor(metadataFactory: () => IAppMetadata = () => AppMetadataFactory.Current) { + this.metadata = metadataFactory(); + } + + public shouldValidate(options: ISanityCheckOptions): boolean { + return options.validateMetadata; + } + + public* collectErrors(): Iterable { + if (!this.metadata) { + yield 'missing metadata'; + return; + } + const keyValues = capturePropertyValues(this.metadata); + if (!Object.keys(keyValues).length) { + yield 'Unable to capture metadata key/value pairs'; + return; + } + const keysMissingValue = getMissingMetadataKeys(keyValues); + if (keysMissingValue.length > 0) { + yield `Metadata keys missing: ${keysMissingValue.join(', ')}`; + } + } +} + +function getMissingMetadataKeys(keyValuePairs: Record): string[] { + return Object.entries(keyValuePairs) + .reduce((acc, [key, value]) => { + if (!value) { + acc.push(key); + } + return acc; + }, new Array()); +} + +/** + * Captures values of properties and getters from the provided instance. + * Necessary because code transformations can make class getters non-enumerable during bundling. + * This ensures that even if getters are non-enumerable, their values are still captured and used. + */ +function capturePropertyValues(instance: unknown): Record { + const obj: Record = {}; + const descriptors = Object.getOwnPropertyDescriptors(instance.constructor.prototype); + + // Capture regular properties from the instance + for (const [key, value] of Object.entries(instance)) { + obj[key] = value; + } + + // Capture getter properties from the instance's prototype + for (const [key, descriptor] of Object.entries(descriptors)) { + if (typeof descriptor.get === 'function') { + obj[key] = descriptor.get.call(instance); + } + } + + return obj; +} diff --git a/src/presentation/components/App.vue b/src/presentation/components/App.vue index b8a87bc..309396a 100644 --- a/src/presentation/components/App.vue +++ b/src/presentation/components/App.vue @@ -18,6 +18,7 @@ import TheCodeButtons from '@/presentation/components/Code/CodeButtons/TheCodeBu import TheScriptArea from '@/presentation/components/Scripts/TheScriptArea.vue'; import TheSearchBar from '@/presentation/components/TheSearchBar.vue'; import { buildContext } from '@/application/Context/ApplicationContextFactory'; +import { validateRuntimeSanity } from '@/infrastructure/RuntimeSanity/SanityChecks'; import { provideDependencies } from '../bootstrapping/DependencyProvider'; const singletonAppContext = await buildContext(); @@ -32,6 +33,9 @@ export default defineComponent({ }, setup() { provideDependencies(singletonAppContext); // In Vue 3.0 we can move it to main.ts + validateRuntimeSanity({ + validateMetadata: true, + }); }, }); diff --git a/src/presentation/electron/main/ElectronConfig.ts b/src/presentation/electron/main/ElectronConfig.ts new file mode 100644 index 0000000..d9a8e4c --- /dev/null +++ b/src/presentation/electron/main/ElectronConfig.ts @@ -0,0 +1,16 @@ +/** + * Abstraction for electron-vite specific logic and other Electron CLI helpers/wrappers. + * Allows for agnostic application design and centralizes adjustments when switching wrappers. + */ + +/// +import { join } from 'path'; +import appIcon from '@/presentation/public/icon.png?asset'; + +export const APP_ICON_PATH = appIcon; + +export const RENDERER_URL = process.env.ELECTRON_RENDERER_URL; + +export const RENDERER_HTML_PATH = join('file://', __dirname, '../renderer/index.html'); + +export const PRELOADER_SCRIPT_PATH = join(__dirname, '../preload/index.cjs'); diff --git a/src/presentation/electron/Update/AutoUpdater.ts b/src/presentation/electron/main/Update/AutoUpdater.ts similarity index 100% rename from src/presentation/electron/Update/AutoUpdater.ts rename to src/presentation/electron/main/Update/AutoUpdater.ts diff --git a/src/presentation/electron/Update/ManualUpdater.ts b/src/presentation/electron/main/Update/ManualUpdater.ts similarity index 100% rename from src/presentation/electron/Update/ManualUpdater.ts rename to src/presentation/electron/main/Update/ManualUpdater.ts index e8edef2..3f71bd1 100644 --- a/src/presentation/electron/Update/ManualUpdater.ts +++ b/src/presentation/electron/main/Update/ManualUpdater.ts @@ -6,8 +6,8 @@ import log from 'electron-log'; import fetch from 'cross-fetch'; import { ProjectInformation } from '@/domain/ProjectInformation'; import { OperatingSystem } from '@/domain/OperatingSystem'; -import { Version } from '@/domain/Version'; import { ViteAppMetadata } from '@/infrastructure/Metadata/Vite/ViteAppMetadata'; +import { Version } from '@/domain/Version'; import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser'; import { UpdateProgressBar } from './UpdateProgressBar'; diff --git a/src/presentation/electron/Update/UpdateProgressBar.ts b/src/presentation/electron/main/Update/UpdateProgressBar.ts similarity index 100% rename from src/presentation/electron/Update/UpdateProgressBar.ts rename to src/presentation/electron/main/Update/UpdateProgressBar.ts diff --git a/src/presentation/electron/Update/Updater.ts b/src/presentation/electron/main/Update/Updater.ts similarity index 100% rename from src/presentation/electron/Update/Updater.ts rename to src/presentation/electron/main/Update/Updater.ts diff --git a/src/presentation/electron/main.ts b/src/presentation/electron/main/index.ts similarity index 61% rename from src/presentation/electron/main.ts rename to src/presentation/electron/main/index.ts index e70377a..c3aee27 100644 --- a/src/presentation/electron/main.ts +++ b/src/presentation/electron/main/index.ts @@ -1,21 +1,17 @@ -// This is main process of Electron, started as first thing when app starts. -// This script is running through entire life of the application. -// It doesn't have any windows which you can see on screen, opens the main window from here. +// Initializes Electron's main process, always runs in the background, and manages the main window. -import path from 'path'; import { app, protocol, BrowserWindow, shell, screen, } from 'electron'; -import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'; import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'; import log from 'electron-log'; +import { validateRuntimeSanity } from '@/infrastructure/RuntimeSanity/SanityChecks'; import { setupAutoUpdater } from './Update/Updater'; +import { + APP_ICON_PATH, PRELOADER_SCRIPT_PATH, RENDERER_HTML_PATH, RENDERER_URL, +} from './ElectronConfig'; -const isDevelopment = process.env.NODE_ENV !== 'production'; - -// Path of static assets, magic variable populated by electron -// eslint-disable-next-line @typescript-eslint/naming-convention, no-underscore-dangle -declare const __static: string; // https://github.com/electron-userland/electron-webpack/issues/172 +const isDevelopment = !app.isPackaged; // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. @@ -27,6 +23,9 @@ protocol.registerSchemesAsPrivileged([ ]); setupLogger(); +validateRuntimeSanity({ + validateMetadata: true, +}); function createWindow() { // Create the browser window. @@ -35,14 +34,11 @@ function createWindow() { width: size.width, height: size.height, webPreferences: { - contextIsolation: false, // To reach node https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/1285 - // Use pluginOptions.nodeIntegration, leave this alone - // See https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration - nodeIntegration: (process.env - .ELECTRON_NODE_INTEGRATION as unknown) as boolean, + nodeIntegration: true, + contextIsolation: false, + preload: PRELOADER_SCRIPT_PATH, }, - // https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/recipes.html#set-tray-icon - icon: path.join(__static, 'icon.png'), + icon: APP_ICON_PATH, }); win.setMenuBarVisibility(false); @@ -83,17 +79,12 @@ app.on('activate', () => { } }); -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. app.on('ready', async () => { - if (isDevelopment && !process.env.IS_TEST) { - // Install Vue Devtools + if (isDevelopment) { try { await installExtension(VUEJS_DEVTOOLS); } catch (e) { - // eslint-disable-next-line no-console - console.error('Vue Devtools failed to install:', e.toString()); + log.error('Vue Devtools failed to install:', e.toString()); } } createWindow(); @@ -115,22 +106,19 @@ if (isDevelopment) { } function loadApplication(window: BrowserWindow) { - if (process.env.WEBPACK_DEV_SERVER_URL) { - // Load the url of the dev server if in development mode - loadUrlWithNodeWorkaround(win, process.env.WEBPACK_DEV_SERVER_URL as string); - if (!process.env.IS_TEST) { - window.webContents.openDevTools(); - } + if (RENDERER_URL) { // Populated in a dev server during development + loadUrlWithNodeWorkaround(win, RENDERER_URL); + } else { + loadUrlWithNodeWorkaround(win, RENDERER_HTML_PATH); + } + if (isDevelopment) { + window.webContents.openDevTools(); } else { - createProtocol('app'); - // Load the index.html when not in development - loadUrlWithNodeWorkaround(win, 'app://./index.html'); const updater = setupAutoUpdater(); updater.checkForUpdates(); } - // Do not remove [APP_INIT_SUCCESS]; it's a marker used in tests to verify - // app initialization. - log.info('[APP_INIT_SUCCESS] Main window initialized and content loading.'); + // Do not remove [WINDOW_INIT]; it's a marker used in tests. + log.info('[WINDOW_INIT] Main window initialized and content loading.'); } function configureExternalsUrlsOpenBrowser(window: BrowserWindow) { @@ -158,7 +146,7 @@ function getWindowSize(idealWidth: number, idealHeight: number) { function setupLogger(): void { log.transports.file.level = 'silly'; - if (!process.env.IS_TEST) { + if (!isDevelopment) { Object.assign(console, log.functions); // override console.log, console.warn etc. } } diff --git a/src/presentation/electron/preload/index.ts b/src/presentation/electron/preload/index.ts new file mode 100644 index 0000000..e560579 --- /dev/null +++ b/src/presentation/electron/preload/index.ts @@ -0,0 +1,11 @@ +// This preload script serves as a placeholder to securely expose Electron APIs to the application. +// As of now, the application does not utilize any specific Electron APIs through this script. +import log from 'electron-log'; +import { validateRuntimeSanity } from '@/infrastructure/RuntimeSanity/SanityChecks'; + +validateRuntimeSanity({ + validateMetadata: true, +}); + +// Do not remove [PRELOAD_INIT]; it's a marker used in tests. +log.info('[PRELOAD_INIT] Preload script successfully initialized and executed.'); diff --git a/tests/integration/infrastructure/Metadata/Vite/ViteAppMetadata.spec.ts b/tests/integration/infrastructure/Metadata/Vite/ViteAppMetadata.spec.ts index 3c11466..657b5f7 100644 --- a/tests/integration/infrastructure/Metadata/Vite/ViteAppMetadata.spec.ts +++ b/tests/integration/infrastructure/Metadata/Vite/ViteAppMetadata.spec.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; import { ViteAppMetadata } from '@/infrastructure/Metadata/Vite/ViteAppMetadata'; import packageJson from '@/../package.json' assert { type: 'json' }; -import { PropertyKeys } from '@tests/shared/TypeHelpers'; +import { PropertyKeys } from '@/TypeHelpers'; describe('ViteAppMetadata', () => { describe('populates from package.json', () => { diff --git a/tests/unit/application/Parser/ApplicationParser.spec.ts b/tests/unit/application/Parser/ApplicationParser.spec.ts index f4e9e00..99cec2d 100644 --- a/tests/unit/application/Parser/ApplicationParser.spec.ts +++ b/tests/unit/application/Parser/ApplicationParser.spec.ts @@ -1,4 +1,3 @@ -/* eslint-disable max-classes-per-file */ import { describe, it, expect } from 'vitest'; import type { CollectionData } from '@/application/collections/'; import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser'; @@ -7,28 +6,28 @@ import { IAppMetadata } from '@/infrastructure/Metadata/IAppMetadata'; import WindowsData from '@/application/collections/windows.yaml'; import MacOsData from '@/application/collections/macos.yaml'; import LinuxData from '@/application/collections/linux.yaml'; -import { IProjectInformation } from '@/domain/IProjectInformation'; -import { ProjectInformation } from '@/domain/ProjectInformation'; -import { ICategoryCollection } from '@/domain/ICategoryCollection'; import { OperatingSystem } from '@/domain/OperatingSystem'; -import { getEnumValues } from '@/application/Common/Enum'; import { CategoryCollectionStub } from '@tests/unit/shared/Stubs/CategoryCollectionStub'; import { CollectionDataStub } from '@tests/unit/shared/Stubs/CollectionDataStub'; import { getAbsentCollectionTestCases, AbsentObjectTestCases } from '@tests/unit/shared/TestCases/AbsentTests'; import { AppMetadataStub } from '@tests/unit/shared/Stubs/AppMetadataStub'; +import { AppMetadataFactory } from '@/infrastructure/Metadata/AppMetadataFactory'; +import { CategoryCollectionParserStub } from '@tests/unit/shared/Stubs/CategoryCollectionParserStub'; +import { ProjectInformationParserStub } from '@tests/unit/shared/Stubs/ProjectInformationParserStub'; +import { ProjectInformationStub } from '@tests/unit/shared/Stubs/ProjectInformationStub'; describe('ApplicationParser', () => { describe('parseApplication', () => { - describe('parser', () => { + describe('categoryParser', () => { it('returns result from the parser', () => { // arrange const os = OperatingSystem.macOS; const data = new CollectionDataStub(); const expected = new CategoryCollectionStub() .withOs(os); - const parser = new CategoryCollectionParserSpy() - .setUpReturnValue(data, expected) - .mockParser(); + const parser = new CategoryCollectionParserStub() + .withReturnValue(data, expected) + .getStub(); const sut = new ApplicationParserBuilder() .withCategoryCollectionParser(parser) .withCollectionsData([data]); @@ -39,20 +38,63 @@ describe('ApplicationParser', () => { expect(expected).to.equal(actual); }); }); - describe('processEnv', () => { - it('used to parse expected project information', () => { + describe('project information', () => { + it('informationParser is used to create application info', () => { // arrange - const env = new AppMetadataStub(); - const expected = parseProjectInformation(env); - const parserSpy = new CategoryCollectionParserSpy(); - const parserMock = parserSpy.mockParser(); + const expectedInformation = new ProjectInformationStub(); + const informationParserStub = new ProjectInformationParserStub() + .withReturnValue(expectedInformation); const sut = new ApplicationParserBuilder() - .withCategoryCollectionParser(parserMock); + .withProjectInformationParser(informationParserStub.getStub()); // act const app = sut.parseApplication(); // assert - expect(expected).to.deep.equal(app.info); - expect(parserSpy.arguments.map((arg) => arg.info).every((info) => info === expected)); + const actualInformation = app.info; + expect(expectedInformation).to.deep.equal(actualInformation); + }); + it('informationParser is used to parse collection info', () => { + // arrange + const expectedInformation = new ProjectInformationStub(); + const informationParserStub = new ProjectInformationParserStub() + .withReturnValue(expectedInformation); + const collectionParserStub = new CategoryCollectionParserStub(); + const sut = new ApplicationParserBuilder() + .withProjectInformationParser(informationParserStub.getStub()) + .withCategoryCollectionParser(collectionParserStub.getStub()); + // act + sut.parseApplication(); + // assert + expect(collectionParserStub.arguments).to.have.length.above(0); + const actualyUsedInfos = collectionParserStub.arguments.map((arg) => arg.info); + expect(actualyUsedInfos.every((info) => info === expectedInformation)); + }); + }); + describe('metadata', () => { + it('used to parse expected metadata', () => { + // arrange + const expectedMetadata = new AppMetadataStub(); + const infoParserStub = new ProjectInformationParserStub(); + // act + new ApplicationParserBuilder() + .withMetadata(expectedMetadata) + .withProjectInformationParser(infoParserStub.getStub()) + .parseApplication(); + // assert + expect(infoParserStub.arguments).to.have.lengthOf(1); + expect(infoParserStub.arguments[0]).to.equal(expectedMetadata); + }); + it('defaults to metadata from factory', () => { + // arrange + const expectedMetadata = AppMetadataFactory.Current; + const infoParserStub = new ProjectInformationParserStub(); + // act + new ApplicationParserBuilder() + .withMetadata(undefined) // force using default + .withProjectInformationParser(infoParserStub.getStub()) + .parseApplication(); + // assert + expect(infoParserStub.arguments).to.have.lengthOf(1); + expect(infoParserStub.arguments[0]).to.equal(expectedMetadata); }); }); describe('collectionsData', () => { @@ -79,12 +121,13 @@ describe('ApplicationParser', () => { // act for (const testCase of testCases) { it(testCase.name, () => { - let parserSpy = new CategoryCollectionParserSpy(); + let categoryParserStub = new CategoryCollectionParserStub(); for (let i = 0; i < testCase.input.length; i++) { - parserSpy = parserSpy.setUpReturnValue(testCase.input[i], testCase.output[i]); + categoryParserStub = categoryParserStub + .withReturnValue(testCase.input[i], testCase.output[i]); } const sut = new ApplicationParserBuilder() - .withCategoryCollectionParser(parserSpy.mockParser()) + .withCategoryCollectionParser(categoryParserStub.getStub()) .withCollectionsData(testCase.input); // act const app = sut.parseApplication(); @@ -96,14 +139,14 @@ describe('ApplicationParser', () => { it('defaults to expected data', () => { // arrange const expected = [WindowsData, MacOsData, LinuxData]; - const parserSpy = new CategoryCollectionParserSpy(); + const categoryParserStub = new CategoryCollectionParserStub(); const sut = new ApplicationParserBuilder() .withCollectionsData(undefined) - .withCategoryCollectionParser(parserSpy.mockParser()); + .withCategoryCollectionParser(categoryParserStub.getStub()); // act sut.parseApplication(); // assert - const actual = parserSpy.arguments.map((args) => args.data); + const actual = categoryParserStub.arguments.map((args) => args.data); expect(actual).to.deep.equal(expected); }); describe('throws when data is invalid', () => { @@ -136,10 +179,13 @@ describe('ApplicationParser', () => { }); class ApplicationParserBuilder { - private categoryCollectionParser: CategoryCollectionParserType = new CategoryCollectionParserSpy() - .mockParser(); + private categoryCollectionParser + : CategoryCollectionParserType = new CategoryCollectionParserStub().getStub(); + + private projectInformationParser + : typeof parseProjectInformation = new ProjectInformationParserStub().getStub(); - private environment: IAppMetadata = new AppMetadataStub(); + private metadata: IAppMetadata = new AppMetadataStub(); private collectionsData: CollectionData[] = [new CollectionDataStub()]; @@ -150,10 +196,17 @@ class ApplicationParserBuilder { return this; } - public withEnvironment( + public withProjectInformationParser( + projectInformationParser: typeof parseProjectInformation, + ): this { + this.projectInformationParser = projectInformationParser; + return this; + } + + public withMetadata( environment: IAppMetadata, ): this { - this.environment = environment; + this.metadata = environment; return this; } @@ -165,39 +218,9 @@ class ApplicationParserBuilder { public parseApplication(): ReturnType { return parseApplication( this.categoryCollectionParser, - this.environment, + this.projectInformationParser, + this.metadata, this.collectionsData, ); } } - -class CategoryCollectionParserSpy { - public arguments = new Array<{ - data: CollectionData, - info: ProjectInformation, - }>(); - - private returnValues = new Map(); - - public setUpReturnValue( - data: CollectionData, - collection: ICategoryCollection, - ): CategoryCollectionParserSpy { - this.returnValues.set(data, collection); - return this; - } - - public mockParser(): CategoryCollectionParserType { - return (data: CollectionData, info: IProjectInformation) => { - this.arguments.push({ data, info }); - if (this.returnValues.has(data)) { - return this.returnValues.get(data); - } - // Get next OS with a unique OS so mock does not result in an invalid app due to duplicated OS - // collections. - const currentRun = this.arguments.length - 1; - const nextOs = getEnumValues(OperatingSystem)[currentRun]; - return new CategoryCollectionStub().withOs(nextOs); - }; - } -} diff --git a/tests/unit/application/Parser/ProjectInformationParser.spec.ts b/tests/unit/application/Parser/ProjectInformationParser.spec.ts index b7dc5ec..744fd6f 100644 --- a/tests/unit/application/Parser/ProjectInformationParser.spec.ts +++ b/tests/unit/application/Parser/ProjectInformationParser.spec.ts @@ -1,60 +1,111 @@ import { describe, it, expect } from 'vitest'; -import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser'; -import { IProjectInformation } from '@/domain/IProjectInformation'; +import { parseProjectInformation, ProjectInformationFactory } from '@/application/Parser/ProjectInformationParser'; import { AppMetadataStub } from '@tests/unit/shared/Stubs/AppMetadataStub'; +import { PropertyKeys } from '@/TypeHelpers'; +import { ProjectInformationStub } from '@tests/unit/shared/Stubs/ProjectInformationStub'; +import { Version } from '@/domain/Version'; describe('ProjectInformationParser', () => { describe('parseProjectInformation', () => { - interface IEnvironmentParsingTestCase { - readonly testCaseName: string; - readonly setMetadata: (appMetadataStub: AppMetadataStub, value: string) => AppMetadataStub; - readonly expectedValue: string; - readonly getActualValue: (info: IProjectInformation) => string; - } - const testCases: readonly IEnvironmentParsingTestCase[] = [ - { - testCaseName: 'version', - setMetadata: (metadata, value) => metadata.withVersion(value), - expectedValue: '0.11.3', - getActualValue: (info) => info.version.toString(), - }, - { - testCaseName: 'name', - setMetadata: (metadata, value) => metadata.witName(value), - expectedValue: 'expected-app-name', - getActualValue: (info) => info.name, - }, - { - testCaseName: 'homepage', - setMetadata: (metadata, value) => metadata.withHomepageUrl(value), - expectedValue: 'https://expected.sexy', - getActualValue: (info) => info.homepage, - }, - { - testCaseName: 'repositoryUrl', - setMetadata: (metadata, value) => metadata.withRepositoryUrl(value), - expectedValue: 'https://expected-repository.url', - getActualValue: (info) => info.repositoryUrl, - }, - { - testCaseName: 'slogan', - setMetadata: (metadata, value) => metadata.withSlogan(value), - expectedValue: 'expected-slogan', - getActualValue: (info) => info.slogan, - }, - ]; - for (const { - expectedValue, testCaseName, setMetadata, getActualValue, - } of testCases) { - it(testCaseName, () => { + it('returns expected information', () => { + // arrange + const expectedInformation = new ProjectInformationStub(); + const factoryMock = () => expectedInformation; + // act + const actualInformation = parseProjectInformation(new AppMetadataStub(), factoryMock); + // assert + expect(expectedInformation).to.equal(actualInformation); + }); + describe('default behavior does not throw', () => { + it('without metadataFactory', () => { + // arrange + const metadataFactory = undefined; + const informationFactory = new ProjectInformationFactoryStub().getStub(); // act - const metadata = setMetadata(new AppMetadataStub(), expectedValue); + const act = () => parseProjectInformation(metadataFactory, informationFactory); + // expectS + expect(act).to.not.throw(); + }); + it('without projectInformationFactory', () => { + // arrange + const metadataFactory = new AppMetadataStub(); + const informationFactory = undefined; // act - const info = parseProjectInformation(metadata); - // assert - const actual = getActualValue(info); - expect(actual).to.be.equal(expectedValue); + const act = () => parseProjectInformation(metadataFactory, informationFactory); + // expect + expect(act).to.not.throw(); + }); + }); + describe('parses metadata to project information', () => { + interface IMetadataTestCase { + readonly setMetadata: (appMetadataStub: AppMetadataStub, value: string) => AppMetadataStub; + readonly expectedValue: string; + readonly getActualValue: (info: ProjectInformationFactoryStub) => string; + } + const testCases: { [K in PropertyKeys]: IMetadataTestCase } = { + name: { + setMetadata: (metadata, value) => metadata.witName(value), + expectedValue: 'expected-app-name', + getActualValue: (info) => info.name, + }, + version: { + setMetadata: (metadata, value) => metadata.withVersion(value), + expectedValue: '0.11.3', + getActualValue: (info) => info.version.toString(), + }, + slogan: { + setMetadata: (metadata, value) => metadata.withSlogan(value), + expectedValue: 'expected-slogan', + getActualValue: (info) => info.slogan, + }, + repositoryUrl: { + setMetadata: (metadata, value) => metadata.withRepositoryUrl(value), + expectedValue: 'https://expected-repository.url', + getActualValue: (info) => info.repositoryUrl, + }, + homepage: { + setMetadata: (metadata, value) => metadata.withHomepageUrl(value), + expectedValue: 'https://expected.sexy', + getActualValue: (info) => info.homepage, + }, + }; + Object.entries(testCases).forEach(([propertyName, { + expectedValue, setMetadata, getActualValue, + }]) => { + it(propertyName, () => { + // act + const metadata = setMetadata(new AppMetadataStub(), expectedValue); + const factoryStub = new ProjectInformationFactoryStub(); + // act + parseProjectInformation(metadata, factoryStub.getStub()); + // assert + const actual = getActualValue(factoryStub); + expect(actual).to.be.equal(expectedValue); + }); }); - } + }); }); }); + +class ProjectInformationFactoryStub { + public name: string; + + public version: Version; + + public slogan: string; + + public repositoryUrl: string; + + public homepage: string; + + public getStub(): ProjectInformationFactory { + return (name, version, slogan, repositoryUrl, homepage) => { + this.name = name; + this.version = version; + this.slogan = slogan; + this.repositoryUrl = repositoryUrl; + this.homepage = homepage; + return new ProjectInformationStub(); + }; + } +} diff --git a/tests/unit/domain/ProjectInformation.spec.ts b/tests/unit/domain/ProjectInformation.spec.ts index 30aa06b..591492d 100644 --- a/tests/unit/domain/ProjectInformation.spec.ts +++ b/tests/unit/domain/ProjectInformation.spec.ts @@ -4,7 +4,7 @@ import { OperatingSystem } from '@/domain/OperatingSystem'; import { EnumRangeTestRunner } from '@tests/unit/application/Common/EnumRangeTestRunner'; import { VersionStub } from '@tests/unit/shared/Stubs/VersionStub'; import { Version } from '@/domain/Version'; -import { PropertyKeys } from '@tests/shared/TypeHelpers'; +import { PropertyKeys } from '@/TypeHelpers'; describe('ProjectInformation', () => { describe('retrieval of property values', () => { diff --git a/tests/unit/infrastructure/Metadata/AppMetadataFactory.spec.ts b/tests/unit/infrastructure/Metadata/AppMetadataFactory.spec.ts new file mode 100644 index 0000000..2471afe --- /dev/null +++ b/tests/unit/infrastructure/Metadata/AppMetadataFactory.spec.ts @@ -0,0 +1,15 @@ +import { + describe, +} from 'vitest'; +import { itIsSingleton } from '@tests/unit/shared/TestCases/SingletonTests'; +import { AppMetadataFactory } from '@/infrastructure/Metadata/AppMetadataFactory'; +import { ViteAppMetadata } from '@/infrastructure/Metadata/Vite/ViteAppMetadata'; + +describe('AppMetadataFactory', () => { + describe('instance', () => { + itIsSingleton({ + getter: () => AppMetadataFactory.Current, + expectedType: ViteAppMetadata, + }); + }); +}); diff --git a/tests/unit/infrastructure/Metadata/ViteAppMetadata.spec.ts b/tests/unit/infrastructure/Metadata/ViteAppMetadata.spec.ts index f9cdca9..2e3fa19 100644 --- a/tests/unit/infrastructure/Metadata/ViteAppMetadata.spec.ts +++ b/tests/unit/infrastructure/Metadata/ViteAppMetadata.spec.ts @@ -3,7 +3,7 @@ import { } from 'vitest'; import { ViteAppMetadata } from '@/infrastructure/Metadata/Vite/ViteAppMetadata'; import { VITE_ENVIRONMENT_KEYS } from '@/infrastructure/Metadata/Vite/ViteEnvironmentKeys'; -import { PropertyKeys } from '@tests/shared/TypeHelpers'; +import { PropertyKeys } from '@/TypeHelpers'; describe('ViteAppMetadata', () => { describe('reads values from import.meta.env', () => { diff --git a/tests/unit/infrastructure/RuntimeSanity/SanityChecks.spec.ts b/tests/unit/infrastructure/RuntimeSanity/SanityChecks.spec.ts new file mode 100644 index 0000000..4d212df --- /dev/null +++ b/tests/unit/infrastructure/RuntimeSanity/SanityChecks.spec.ts @@ -0,0 +1,155 @@ +import { describe, it, expect } from 'vitest'; +import { validateRuntimeSanity } from '@/infrastructure/RuntimeSanity/SanityChecks'; +import { ISanityCheckOptions } from '@/infrastructure/RuntimeSanity/ISanityCheckOptions'; +import { SanityCheckOptionsStub } from '@tests/unit/shared/Stubs/SanityCheckOptionsStub'; +import { ISanityValidator } from '@/infrastructure/RuntimeSanity/ISanityValidator'; +import { SanityValidatorStub } from '@tests/unit/shared/Stubs/SanityValidatorStub'; +import { itEachAbsentObjectValue } from '@tests/unit/shared/TestCases/AbsentTests'; + +describe('SanityChecks', () => { + describe('validateRuntimeSanity', () => { + describe('parameter validation', () => { + describe('options', () => { + itEachAbsentObjectValue((absentValue) => { + // arrange + const expectedError = 'missing options'; + const context = new TestContext() + .withOptions(absentValue); + // act + const act = () => context.validateRuntimeSanity(); + // assert + expect(act).to.throw(expectedError); + }); + }); + it('throws when validators are empty', () => { + // arrange + const expectedError = 'missing validators'; + const context = new TestContext() + .withValidators([]); + // act + const act = () => context.validateRuntimeSanity(); + // assert + expect(act).to.throw(expectedError); + }); + }); + + describe('aggregates validators', () => { + it('does not throw if all validators pass', () => { + // arrange + const context = new TestContext() + .withValidators([ + new SanityValidatorStub() + .withShouldValidateResult(false), + new SanityValidatorStub() + .withShouldValidateResult(false), + ]); + // act + const act = () => context.validateRuntimeSanity(); + // assert + expect(act).to.not.throw(); + }); + it('does not throw if a validator return errors but pass', () => { + // arrange + const context = new TestContext() + .withValidators([ + new SanityValidatorStub() + .withErrorsResult(['should be ignored']) + .withShouldValidateResult(false), + ]); + // act + const act = () => context.validateRuntimeSanity(); + // assert + expect(act).to.not.throw(); + }); + it('does not throw if validators return no errors', () => { + // arrange + const context = new TestContext() + .withValidators([ + new SanityValidatorStub() + .withShouldValidateResult(true) + .withErrorsResult([]), + new SanityValidatorStub() + .withShouldValidateResult(true) + .withErrorsResult([]), + ]); + // act + const act = () => context.validateRuntimeSanity(); + // assert + expect(act).to.not.throw(); + }); + it('throws if single validator has errors', () => { + // arrange + const firstError = 'first-error'; + const secondError = 'second-error'; + let actualError = ''; + const context = new TestContext() + .withValidators([ + new SanityValidatorStub() + .withShouldValidateResult(true) + .withErrorsResult([firstError, secondError]), + ]); + // act + try { + context.validateRuntimeSanity(); + } catch (err) { + actualError = err.toString(); + } + // assert + expect(actualError).to.have.length.above(0); + expect(actualError).to.include(firstError); + expect(actualError).to.include(secondError); + }); + it('accumulates error messages from validators', () => { + // arrange + const errorFromFirstValidator = 'first-error'; + const errorFromSecondValidator = 'second-error'; + let actualError = ''; + const context = new TestContext() + .withValidators([ + new SanityValidatorStub() + .withShouldValidateResult(true) + .withErrorsResult([errorFromFirstValidator]), + new SanityValidatorStub() + .withShouldValidateResult(true) + .withErrorsResult([errorFromSecondValidator]), + ]); + // act + try { + context.validateRuntimeSanity(); + } catch (err) { + actualError = err.toString(); + } + // assert + expect(actualError).to.have.length.above(0); + expect(actualError).to.include(errorFromFirstValidator); + expect(actualError).to.include(errorFromSecondValidator); + }); + }); + }); +}); + +class TestContext { + private options: ISanityCheckOptions = new SanityCheckOptionsStub(); + + private validators: ISanityValidator[] = [new SanityValidatorStub()]; + + public withOptionsSetup( + setup: (stub: SanityCheckOptionsStub) => SanityCheckOptionsStub, + ) { + return this.withOptions(setup(new SanityCheckOptionsStub())); + } + + public withOptions(options: ISanityCheckOptions): this { + this.options = options; + return this; + } + + public withValidators(validators: ISanityValidator[]): this { + this.validators = validators; + return this; + } + + public validateRuntimeSanity(): ReturnType { + return validateRuntimeSanity(this.options, this.validators); + } +} diff --git a/tests/unit/infrastructure/RuntimeSanity/Validators/MetadataValidator.spec.ts b/tests/unit/infrastructure/RuntimeSanity/Validators/MetadataValidator.spec.ts new file mode 100644 index 0000000..a280932 --- /dev/null +++ b/tests/unit/infrastructure/RuntimeSanity/Validators/MetadataValidator.spec.ts @@ -0,0 +1,133 @@ +import { describe, it, expect } from 'vitest'; +import { MetadataValidator } from '@/infrastructure/RuntimeSanity/Validators/MetadataValidator'; +import { SanityCheckOptionsStub } from '@tests/unit/shared/Stubs/SanityCheckOptionsStub'; +import { itEachAbsentObjectValue } from '@tests/unit/shared/TestCases/AbsentTests'; +import { AppMetadataStub } from '@tests/unit/shared/Stubs/AppMetadataStub'; +import { IAppMetadata } from '@/infrastructure/Metadata/IAppMetadata'; + +describe('MetadataValidator', () => { + describe('shouldValidate', () => { + it('returns true when validateMetadata is true', () => { + // arrange + const expectedValue = true; + const options = new SanityCheckOptionsStub() + .withValidateMetadata(true); + const validator = new TestContext() + .createSut(); + // act + const actualValue = validator.shouldValidate(options); + // assert + expect(actualValue).to.equal(expectedValue); + }); + + it('returns false when validateMetadata is false', () => { + // arrange + const expectedValue = false; + const options = new SanityCheckOptionsStub() + .withValidateMetadata(false); + const validator = new TestContext() + .createSut(); + // act + const actualValue = validator.shouldValidate(options); + // assert + expect(actualValue).to.equal(expectedValue); + }); + }); + describe('collectErrors', () => { + describe('yields "missing metadata" if metadata is not provided', () => { + itEachAbsentObjectValue((absentValue) => { + // arrange + const expectedError = 'missing metadata'; + const validator = new TestContext() + .withMetadata(absentValue) + .createSut(); + // act + const errors = [...validator.collectErrors()]; + // assert + expect(errors).to.have.lengthOf(1); + expect(errors[0]).to.equal(expectedError); + }); + }); + it('yields missing keys if metadata has keys without values', () => { + // arrange + const expectedError = 'Metadata keys missing: name, homepageUrl'; + const metadata = new AppMetadataStub() + .witName(undefined) + .withHomepageUrl(undefined); + const validator = new TestContext() + .withMetadata(metadata) + .createSut(); + // act + const errors = [...validator.collectErrors()]; + // assert + expect(errors).to.have.lengthOf(1); + expect(errors[0]).to.equal(expectedError); + }); + it('yields missing keys if metadata has getters instead of properties', () => { + /* + This test may behave differently in unit testing vs. production due to how code + is transformed, especially around class getters and their enumerability during bundling. + */ + // arrange + const expectedError = 'Metadata keys missing: name, homepageUrl'; + const stubWithGetters: Partial = { + get name() { + return undefined; + }, + get homepageUrl() { + return undefined; + }, + }; + const stub: IAppMetadata = { + ...new AppMetadataStub(), + ...stubWithGetters, + }; + const validator = new TestContext() + .withMetadata(stub) + .createSut(); + // act + const errors = [...validator.collectErrors()]; + // assert + expect(errors).to.have.lengthOf(1); + expect(errors[0]).to.equal(expectedError); + }); + it('yields unable to capture metadata if metadata has no getter values', () => { + // arrange + const expectedError = 'Unable to capture metadata key/value pairs'; + const stub = {} as IAppMetadata; + const validator = new TestContext() + .withMetadata(stub) + .createSut(); + // act + const errors = [...validator.collectErrors()]; + // assert + expect(errors).to.have.lengthOf(1); + expect(errors[0]).to.equal(expectedError); + }); + it('does not yield errors if all metadata keys have values', () => { + // arrange + const metadata = new AppMetadataStub(); + const validator = new TestContext() + .withMetadata(metadata) + .createSut(); + // act + const errors = [...validator.collectErrors()]; + // assert + expect(errors).to.have.lengthOf(0); + }); + }); +}); + +class TestContext { + public metadata: IAppMetadata = new AppMetadataStub(); + + public withMetadata(metadata: IAppMetadata): this { + this.metadata = metadata; + return this; + } + + public createSut(): MetadataValidator { + const mockFactory = () => this.metadata; + return new MetadataValidator(mockFactory); + } +} diff --git a/tests/unit/presentation/components/App.spec.ts b/tests/unit/presentation/components/App.spec.ts new file mode 100644 index 0000000..279fc20 --- /dev/null +++ b/tests/unit/presentation/components/App.spec.ts @@ -0,0 +1,14 @@ +import { describe, it, expect } from 'vitest'; +import { shallowMount } from '@vue/test-utils'; +import App from '@/presentation/components/App.vue'; + +describe('App.vue', () => { + it('should be successfully mounted', () => { + // arrange + const component = App; + // act + const act = () => shallowMount(component); + // assert + expect(act).to.not.throw(); + }); +}); diff --git a/tests/unit/shared/Stubs/CategoryCollectionParserStub.ts b/tests/unit/shared/Stubs/CategoryCollectionParserStub.ts new file mode 100644 index 0000000..d643398 --- /dev/null +++ b/tests/unit/shared/Stubs/CategoryCollectionParserStub.ts @@ -0,0 +1,40 @@ +import { IProjectInformation } from '@/domain/IProjectInformation'; +import { ProjectInformation } from '@/domain/ProjectInformation'; +import { ICategoryCollection } from '@/domain/ICategoryCollection'; +import { getEnumValues } from '@/application/Common/Enum'; +import type { CollectionData } from '@/application/collections/'; +import { CategoryCollectionParserType } from '@/application/Parser/ApplicationParser'; +import { OperatingSystem } from '@/domain/OperatingSystem'; +import { CategoryCollectionStub } from './CategoryCollectionStub'; + +export class CategoryCollectionParserStub { + public readonly arguments = new Array<{ + data: CollectionData, + info: ProjectInformation, + }>(); + + private readonly returnValues = new Map(); + + public withReturnValue( + data: CollectionData, + collection: ICategoryCollection, + ): this { + this.returnValues.set(data, collection); + return this; + } + + public getStub(): CategoryCollectionParserType { + return (data: CollectionData, info: IProjectInformation) => { + this.arguments.push({ data, info }); + if (this.returnValues.has(data)) { + return this.returnValues.get(data); + } + // Get next OS with a unique OS so mock does not result in an invalid app due to duplicated OS + // collections. + const currentRun = this.arguments.length - 1; + const nextOs = getEnumValues(OperatingSystem)[currentRun]; + return new CategoryCollectionStub() + .withOs(nextOs); + }; + } +} diff --git a/tests/unit/shared/Stubs/CodeValidatorStub.ts b/tests/unit/shared/Stubs/CodeValidatorStub.ts index d66f734..ea08497 100644 --- a/tests/unit/shared/Stubs/CodeValidatorStub.ts +++ b/tests/unit/shared/Stubs/CodeValidatorStub.ts @@ -1,5 +1,5 @@ import { expect } from 'vitest'; -import { Constructible } from '@tests/shared/TypeHelpers'; +import { Constructible } from '@/TypeHelpers'; import { ICodeValidationRule } from '@/application/Parser/Script/Validation/ICodeValidationRule'; import { ICodeValidator } from '@/application/Parser/Script/Validation/ICodeValidator'; diff --git a/tests/unit/shared/Stubs/ProjectInformationParserStub.ts b/tests/unit/shared/Stubs/ProjectInformationParserStub.ts new file mode 100644 index 0000000..a102f8b --- /dev/null +++ b/tests/unit/shared/Stubs/ProjectInformationParserStub.ts @@ -0,0 +1,22 @@ +import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser'; +import { IAppMetadata } from '@/infrastructure/Metadata/IAppMetadata'; +import { IProjectInformation } from '@/domain/IProjectInformation'; +import { ProjectInformationStub } from './ProjectInformationStub'; + +export class ProjectInformationParserStub { + public readonly arguments = new Array(); + + private returnValue: IProjectInformation = new ProjectInformationStub(); + + public withReturnValue(value: IProjectInformation): this { + this.returnValue = value; + return this; + } + + public getStub(): typeof parseProjectInformation { + return (metadata) => { + this.arguments.push(metadata); + return this.returnValue; + }; + } +} diff --git a/tests/unit/shared/Stubs/SanityCheckOptionsStub.ts b/tests/unit/shared/Stubs/SanityCheckOptionsStub.ts new file mode 100644 index 0000000..97d0f23 --- /dev/null +++ b/tests/unit/shared/Stubs/SanityCheckOptionsStub.ts @@ -0,0 +1,10 @@ +import { ISanityCheckOptions } from '@/infrastructure/RuntimeSanity/ISanityCheckOptions'; + +export class SanityCheckOptionsStub implements ISanityCheckOptions { + public validateMetadata = false; + + public withValidateMetadata(value: boolean): this { + this.validateMetadata = value; + return this; + } +} diff --git a/tests/unit/shared/Stubs/SanityValidatorStub.ts b/tests/unit/shared/Stubs/SanityValidatorStub.ts new file mode 100644 index 0000000..3ced2e6 --- /dev/null +++ b/tests/unit/shared/Stubs/SanityValidatorStub.ts @@ -0,0 +1,29 @@ +import { ISanityCheckOptions } from '@/infrastructure/RuntimeSanity/ISanityCheckOptions'; +import { ISanityValidator } from '@/infrastructure/RuntimeSanity/ISanityValidator'; + +export class SanityValidatorStub implements ISanityValidator { + public shouldValidateArgs = new Array(); + + private errors: readonly string[] = []; + + private shouldValidateResult = true; + + public shouldValidate(options: ISanityCheckOptions): boolean { + this.shouldValidateArgs.push(options); + return this.shouldValidateResult; + } + + public collectErrors(): Iterable { + return this.errors; + } + + public withErrorsResult(errors: readonly string[]): this { + this.errors = errors; + return this; + } + + public withShouldValidateResult(shouldValidate: boolean): this { + this.shouldValidateResult = shouldValidate; + return this; + } +} diff --git a/tests/unit/shared/TestCases/SingletonTests.ts b/tests/unit/shared/TestCases/SingletonTests.ts index 253d79e..5848170 100644 --- a/tests/unit/shared/TestCases/SingletonTests.ts +++ b/tests/unit/shared/TestCases/SingletonTests.ts @@ -1,5 +1,5 @@ import { it, expect } from 'vitest'; -import { Constructible } from '@tests/shared/TypeHelpers'; +import { Constructible } from '@/TypeHelpers'; interface ISingletonTestData { getter: () => T; diff --git a/vite.config.ts b/vite.config.ts index 5729a28..c7d249a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,6 +1,6 @@ /// import { resolve } from 'path'; -import { defineConfig } from 'vite'; +import { defineConfig, UserConfig } from 'vite'; import legacy from '@vitejs/plugin-legacy'; import vue from '@vitejs/plugin-vue2'; import ViteYaml from '@modyfi/vite-plugin-yaml'; @@ -10,45 +10,53 @@ const WEB_DIRECTORY = resolve(getSelfDirectoryAbsolutePath(), 'src/presentation' const TEST_INITIALIZATION_FILE = resolve(getSelfDirectoryAbsolutePath(), 'tests/shared/bootstrap/setup.ts'); const NODE_CORE_MODULES = ['os', 'child_process', 'fs', 'path']; -export default defineConfig({ - root: WEB_DIRECTORY, - plugins: [ - vue(), - ViteYaml(), - legacy(), - ], - esbuild: { - supported: { - 'top-level-await': true, // Exclude browsers not supporting top-level-await +export function createVueConfig(options?: { + readonly supportLegacyBrowsers: boolean, +}): UserConfig { + return { + root: WEB_DIRECTORY, + plugins: [ + vue(), + ViteYaml(), + ...[options?.supportLegacyBrowsers ? legacy() : undefined], + ], + esbuild: { + supported: { + 'top-level-await': true, // Exclude browsers not supporting top-level-await + }, }, - }, - define: { - ...getClientEnvironmentVariables(), - }, - resolve: { - alias: { - ...getAliasesFromTsConfig(), + define: { + ...getClientEnvironmentVariables(), }, - }, - build: { - rollupOptions: { - // Ensure Node core modules are externalized and don't trigger warnings in browser builds - external: { - ...NODE_CORE_MODULES, + resolve: { + alias: { + ...getAliasesFromTsConfig(), }, }, - }, - server: { - port: 3169, - }, - test: { - globals: true, - environment: 'jsdom', - alias: { - ...getAliasesFromTsConfig(), + build: { + rollupOptions: { + // Ensure Node core modules are externalized and don't trigger warnings in browser builds + external: { + ...NODE_CORE_MODULES, + }, + }, }, - setupFiles: [ - TEST_INITIALIZATION_FILE, - ], - }, -}); + server: { + port: 3169, + }, + test: { + globals: true, + environment: 'jsdom', + alias: { + ...getAliasesFromTsConfig(), + }, + setupFiles: [ + TEST_INITIALIZATION_FILE, + ], + }, + }; +} + +export default defineConfig(createVueConfig({ + supportLegacyBrowsers: true, +}));