diff --git a/.github/workflows/desktop-packaging.yml b/.github/workflows/desktop-packaging.yml new file mode 100644 index 0000000000..5dc63e6dfd --- /dev/null +++ b/.github/workflows/desktop-packaging.yml @@ -0,0 +1,178 @@ +name: Desktop Packaging + +on: + pull_request: + paths: + - .github/workflows/desktop-packaging.yml + - gitnexus-desktop/** + - gitnexus/** + - gitnexus-web/** + - gitnexus-shared/** + workflow_dispatch: + +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + validate: + name: Validate desktop package + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version: 22 + cache: npm + cache-dependency-path: gitnexus-desktop/package-lock.json + + - name: Install desktop dependencies + run: npm ci + working-directory: gitnexus-desktop + + - name: Typecheck desktop package + run: npm run typecheck + working-directory: gitnexus-desktop + + - name: Run desktop unit tests + run: npm test + working-directory: gitnexus-desktop + + package: + name: Build ${{ matrix.label }} + runs-on: ${{ matrix.os }} + timeout-minutes: 90 + needs: validate + permissions: + contents: read + id-token: write + attestations: write + strategy: + fail-fast: false + matrix: + include: + - os: windows-latest + label: Windows NSIS + command: npm run build:win + artifact_name: gitnexus-desktop-windows + installer_glob: gitnexus-desktop/release/*/GitNexus Desktop Setup *.exe + artifact_paths: | + gitnexus-desktop/release/*/GitNexus Desktop Setup *.exe + gitnexus-desktop/release/*/win-unpacked/** + - os: macos-latest + label: macOS DMG + command: npm run build:mac + artifact_name: gitnexus-desktop-macos + installer_glob: gitnexus-desktop/release/*/*.dmg + artifact_paths: | + gitnexus-desktop/release/*/*.dmg + gitnexus-desktop/release/*/mac*/** + - os: ubuntu-latest + label: Linux AppImage + command: npm run build:linux + artifact_name: gitnexus-desktop-linux + installer_glob: gitnexus-desktop/release/*/*.AppImage + artifact_paths: | + gitnexus-desktop/release/*/*.AppImage + gitnexus-desktop/release/*/linux-unpacked/** + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version: 22 + cache: npm + cache-dependency-path: | + gitnexus-shared/package-lock.json + gitnexus/package-lock.json + gitnexus-web/package-lock.json + gitnexus-desktop/package-lock.json + + - name: Install shared dependencies + run: npm ci + working-directory: gitnexus-shared + + - name: Build shared package + run: npm run build + working-directory: gitnexus-shared + + - name: Install GitNexus dependencies + run: npm ci + working-directory: gitnexus + + - name: Install web dependencies + run: npm ci + working-directory: gitnexus-web + + - name: Install desktop dependencies + run: npm ci + working-directory: gitnexus-desktop + + - name: Build unpacked desktop bundle + run: npm run build:dir + working-directory: gitnexus-desktop + env: + CSC_IDENTITY_AUTO_DISCOVERY: 'false' + + - name: Inspect rebuilt native modules (Windows) + if: matrix.os == 'windows-latest' + shell: pwsh + run: | + $releaseDir = Get-ChildItem gitnexus-desktop/release -Directory | Sort-Object LastWriteTime -Descending | Select-Object -First 1 + + if (-not $releaseDir) { + Write-Host "No release directory found." + exit 0 + } + + $runtimeNodeModules = Join-Path $releaseDir.FullName 'win-unpacked\resources\gitnexus\node_modules' + Write-Host "Inspecting native modules under $runtimeNodeModules" + + if (-not (Test-Path $runtimeNodeModules)) { + Write-Host "Runtime node_modules directory not found." + exit 0 + } + + Get-ChildItem $runtimeNodeModules -Recurse -Filter '*.node' | + Select-Object FullName, LastWriteTime | + Format-Table -AutoSize + + - name: Start Xvfb + if: matrix.os == 'ubuntu-latest' + run: | + Xvfb :99 -screen 0 1024x768x24 & + echo "DISPLAY=:99" >> "$GITHUB_ENV" + + - name: Smoke test unpacked desktop bundle + run: node scripts/smoke-unpacked.mjs + working-directory: gitnexus-desktop + + - name: Build desktop package + run: ${{ matrix.command }} + working-directory: gitnexus-desktop + env: + CSC_IDENTITY_AUTO_DISCOVERY: 'false' + + - name: Upload desktop artifacts + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ matrix.artifact_name }} + path: ${{ matrix.artifact_paths }} + if-no-files-found: error + retention-days: 14 + + - name: Attest desktop artifacts + if: github.event_name != 'pull_request' + uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0 + with: + subject-path: ${{ matrix.installer_glob }} diff --git a/.gitignore b/.gitignore index 11f2743c72..f1d94e8713 100644 --- a/.gitignore +++ b/.gitignore @@ -105,8 +105,11 @@ gitnexus/vendor/**/node_modules/ local_docs/ +undefined/ +_bmad/ # Local agent scratch / review prompts (never commit) .tmp/ .agents/ .context/ gitnexus/web/ +.pi/ diff --git a/.husky/pre-commit b/.husky/pre-commit index 2a04a77f02..60cc2d09a1 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -20,7 +20,7 @@ fi if [ -n "$CLI_CHANGED" ]; then echo "pre-commit: typechecking gitnexus..." - cd "$ROOT/gitnexus" && ./node_modules/.bin/tsc --noEmit || exit 1 + cd "$ROOT" && ./node_modules/.bin/tsc -p gitnexus/tsconfig.json --noEmit || exit 1 fi echo "pre-commit: all checks passed" diff --git a/eslint.config.mjs b/eslint.config.mjs index e0779b71c7..cc07c7f162 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -50,6 +50,7 @@ export default [ { ignores: [ '**/dist/**', + 'gitnexus-desktop/release/**', '**/node_modules/**', '**/coverage/**', 'gitnexus/vendor/**', diff --git a/gitnexus-desktop/.gitignore b/gitnexus-desktop/.gitignore new file mode 100644 index 0000000000..4bb7eef0e7 --- /dev/null +++ b/gitnexus-desktop/.gitignore @@ -0,0 +1,2 @@ +dist/ +release/ \ No newline at end of file diff --git a/gitnexus-desktop/README.md b/gitnexus-desktop/README.md new file mode 100644 index 0000000000..e1a39f258c --- /dev/null +++ b/gitnexus-desktop/README.md @@ -0,0 +1,32 @@ +# GitNexus Desktop + +Electron desktop shell for GitNexus. The app starts the local backend and loads the real `gitnexus-web` UI inside the desktop window. + +## Commands + +```bash +npm run dev +npm run build:dir +npm run build:win +npm run build:mac +npm run build:linux +``` + +- `npm run dev` starts the Electron shell plus the local GitNexus backend. +- `npm run build:dir` creates an unpacked desktop app directory. +- `npm run build:win` creates a Windows NSIS installer `.exe`. +- `npm run build:mac` creates a macOS `.dmg`. +- `npm run build:linux` creates a Linux `.AppImage`. +- `npm run smoke:unpacked` launches the latest unpacked desktop build, verifies startup, and exits. + +Build output is written under `gitnexus-desktop/release//`. + +## PR Artifacts + +The `Desktop Packaging` GitHub Actions workflow builds an unpacked desktop app, smoke-tests startup, then uploads preview desktop artifacts for pull requests that touch `.github/workflows/desktop-packaging.yml`, `gitnexus-desktop/**`, `gitnexus/**`, `gitnexus-web/**`, or `gitnexus-shared/**`. + +- `gitnexus-desktop-windows` contains the Windows NSIS installer `.exe` and `win-unpacked/`. +- `gitnexus-desktop-macos` contains the macOS `.dmg` and `mac*/` output. +- `gitnexus-desktop-linux` contains the Linux `.AppImage` and `linux-unpacked/`. + +GitHub artifact URLs are run-specific and expire, so use the artifact names above from the latest successful PR workflow run. diff --git a/gitnexus-desktop/build/icon.ico b/gitnexus-desktop/build/icon.ico new file mode 100644 index 0000000000..7eb1e52135 Binary files /dev/null and b/gitnexus-desktop/build/icon.ico differ diff --git a/gitnexus-desktop/build/icon.png b/gitnexus-desktop/build/icon.png new file mode 100644 index 0000000000..9b388fa39f Binary files /dev/null and b/gitnexus-desktop/build/icon.png differ diff --git a/gitnexus-desktop/electron-builder.yml b/gitnexus-desktop/electron-builder.yml new file mode 100644 index 0000000000..e04c87ab1d --- /dev/null +++ b/gitnexus-desktop/electron-builder.yml @@ -0,0 +1,59 @@ +appId: io.github.abhigyanpatwari.gitnexus.desktop +productName: GitNexus Desktop +directories: + buildResources: build +files: + - dist/**/* + - package.json +extraResources: + - from: '${env.GITNEXUS_DESKTOP_WEB_DIST}' + to: gitnexus-web + filter: + - '**/*' + - from: '${env.GITNEXUS_DESKTOP_GITNEXUS_DIST}' + to: gitnexus/dist + filter: + - '**/*' + - from: '${env.GITNEXUS_DESKTOP_GITNEXUS_NODE_MODULES}' + to: gitnexus/node_modules + filter: + - '**/*' + - from: '${env.GITNEXUS_DESKTOP_GITNEXUS_HOOKS}' + to: gitnexus/hooks + filter: + - '**/*' + - from: '${env.GITNEXUS_DESKTOP_GITNEXUS_SKILLS}' + to: gitnexus/skills + filter: + - '**/*' + - from: '${env.GITNEXUS_DESKTOP_GITNEXUS_VENDOR}' + to: gitnexus/vendor + filter: + - '**/*' + - from: '${env.GITNEXUS_DESKTOP_GITNEXUS_PACKAGE_JSON}' + to: gitnexus/package.json + - from: 'build/icon.png' + to: icon.png + - from: 'build/icon.ico' + to: icon.ico +asar: false +npmRebuild: false +afterPack: scripts/after-pack.mjs +win: + signAndEditExecutable: false + icon: build/icon.ico + target: + - nsis +mac: + category: public.app-category.developer-tools + icon: build/icon.png + target: + - dmg +linux: + category: Development + icon: build/icon.png + target: + - AppImage +nsis: + oneClick: false + allowToChangeInstallationDirectory: true diff --git a/gitnexus-desktop/electron.vite.config.ts b/gitnexus-desktop/electron.vite.config.ts new file mode 100644 index 0000000000..ce3e9f54dd --- /dev/null +++ b/gitnexus-desktop/electron.vite.config.ts @@ -0,0 +1,46 @@ +import { resolve } from 'node:path'; + +export default { + main: { + build: { + outDir: 'dist/main', + rollupOptions: { + input: { + main: resolve(__dirname, 'src/main/main.ts'), + }, + }, + }, + }, + preload: { + build: { + outDir: 'dist/preload', + rollupOptions: { + input: { + preload: resolve(__dirname, 'src/main/preload.ts'), + }, + output: { + format: 'cjs', + entryFileNames: 'preload.js', + }, + }, + }, + }, + renderer: { + server: { + host: 'localhost', + port: 5174, + strictPort: true, + watch: { + ignored: ['**/release/**'], + }, + }, + build: { + outDir: 'dist/renderer', + rollupOptions: { + input: { + index: resolve(__dirname, 'src/renderer/index.html'), + }, + }, + }, + }, +}; diff --git a/gitnexus-desktop/package-lock.json b/gitnexus-desktop/package-lock.json new file mode 100644 index 0000000000..ae3574f332 --- /dev/null +++ b/gitnexus-desktop/package-lock.json @@ -0,0 +1,6690 @@ +{ + "name": "gitnexus-desktop", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gitnexus-desktop", + "version": "0.1.0", + "license": "PolyForm-Noncommercial-1.0.0", + "devDependencies": { + "@types/node": "^25.6.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "electron": "41.2.1", + "electron-builder": "26.8.1", + "electron-vite": "^5.0.0", + "typescript": "^6.0.3", + "vite": "^7.0.0", + "vitest": "^4.0.18" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@develar/schema-utils": { + "version": "2.6.5", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@electron/asar": { + "version": "3.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/asar/node_modules/brace-expansion": { + "version": "1.1.14", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@electron/fuses": { + "version": "1.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.1", + "fs-extra": "^9.0.1", + "minimist": "^1.2.5" + }, + "bin": { + "electron-fuses": "dist/bin.js" + } + }, + "node_modules/@electron/fuses/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/fuses/node_modules/jsonfile": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/fuses/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/get": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@electron/notarize": { + "version": "2.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/notarize/node_modules/jsonfile": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/notarize/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.3.3", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/osx-sign/node_modules/jsonfile": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/osx-sign/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/rebuild": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "got": "^11.7.0", + "graceful-fs": "^4.2.11", + "node-abi": "^4.2.0", + "node-api-version": "^0.2.1", + "node-gyp": "^11.2.0", + "ora": "^5.1.0", + "read-binary-file-arch": "^1.0.6", + "semver": "^7.3.5", + "tar": "^7.5.6", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@electron/rebuild/node_modules/semver": { + "version": "7.7.4", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/universal": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.3.1", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" + }, + "engines": { + "node": ">=16.4" + } + }, + "node_modules/@electron/universal/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "11.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/universal/node_modules/jsonfile": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/universal/node_modules/minimatch": { + "version": "9.0.9", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@electron/universal/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/windows-sign": { + "version": "1.2.2", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/windows-sign/node_modules/fs-extra": { + "version": "11.3.4", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/windows-sign/node_modules/jsonfile": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/windows-sign/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "2.0.0", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@malept/flatpak-bundler/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@npmcli/agent": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/fs": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.7.4", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.2", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.19.0" + } + }, + "node_modules/@types/plist": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*", + "xmlbuilder": ">=11.0.1" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/verror": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", + "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", + "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", + "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", + "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.5", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", + "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "@vitest/utils": "4.1.5", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", + "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.13", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/7zip-bin": { + "version": "5.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/abbrev": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/app-builder-lib": { + "version": "26.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@develar/schema-utils": "~2.6.5", + "@electron/asar": "3.4.1", + "@electron/fuses": "^1.8.0", + "@electron/get": "^3.0.0", + "@electron/notarize": "2.5.0", + "@electron/osx-sign": "1.3.3", + "@electron/rebuild": "^4.0.3", + "@electron/universal": "2.0.3", + "@malept/flatpak-bundler": "^0.4.0", + "@types/fs-extra": "9.0.13", + "async-exit-hook": "^2.0.1", + "builder-util": "26.8.1", + "builder-util-runtime": "9.5.1", + "chromium-pickle-js": "^0.2.0", + "ci-info": "4.3.1", + "debug": "^4.3.4", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "ejs": "^3.1.8", + "electron-publish": "26.8.1", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "isbinaryfile": "^5.0.0", + "jiti": "^2.4.2", + "js-yaml": "^4.1.0", + "json5": "^2.2.3", + "lazy-val": "^1.0.5", + "minimatch": "^10.0.3", + "plist": "3.1.0", + "proper-lockfile": "^4.1.2", + "resedit": "^1.7.0", + "semver": "~7.7.3", + "tar": "^7.5.7", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0", + "which": "^5.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "26.8.1", + "electron-builder-squirrel-windows": "26.8.1" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/app-builder-lib/node_modules/ci-info": { + "version": "4.3.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/app-builder-lib/node_modules/fs-extra": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/app-builder-lib/node_modules/fs-extra/node_modules/jsonfile": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/semver": { + "version": "7.7.4", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "dev": true, + "license": "MIT" + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.20", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/browserslist/node_modules/caniuse-lite": { + "version": "1.0.30001788", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", + "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/buffer": { + "version": "5.7.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/builder-util": { + "version": "26.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.6", + "7zip-bin": "~5.2.0", + "app-builder-bin": "5.0.0-alpha.12", + "builder-util-runtime": "9.5.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.6", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "js-yaml": "^4.1.0", + "sanitize-filename": "^1.6.3", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0" + } + }, + "node_modules/builder-util-runtime": { + "version": "9.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/builder-util/node_modules/app-builder-bin": { + "version": "5.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz", + "integrity": "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/builder-util/node_modules/fs-extra": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/builder-util/node_modules/jsonfile": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/builder-util/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cacache": { + "version": "19.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/cacache/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "10.5.0", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.9", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/chownr": { + "version": "3.0.0", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "4.4.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-version": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.1.0" + } + }, + "node_modules/cross-dirname": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/dir-compare": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " + } + }, + "node_modules/dir-compare/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-compare/node_modules/brace-expansion": { + "version": "1.1.14", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/dir-compare/node_modules/minimatch": { + "version": "3.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dmg-builder": { + "version": "26.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.8.1", + "builder-util": "26.8.1", + "fs-extra": "^10.1.0", + "iconv-lite": "^0.6.2", + "js-yaml": "^4.1.0" + }, + "optionalDependencies": { + "dmg-license": "^1.0.11" + } + }, + "node_modules/dmg-builder/node_modules/fs-extra": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dmg-builder/node_modules/jsonfile": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/dmg-builder/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/dmg-license": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", + "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "@types/plist": "^3.0.1", + "@types/verror": "^1.10.3", + "ajv": "^6.10.0", + "crc": "^3.8.0", + "iconv-corefoundation": "^1.1.7", + "plist": "^3.0.4", + "smart-buffer": "^4.0.2", + "verror": "^1.10.0" + }, + "bin": { + "dmg-license": "bin/dmg-license.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "11.0.7", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron": { + "version": "41.2.1", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^24.9.0", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/electron-builder": { + "version": "26.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.8.1", + "builder-util": "26.8.1", + "builder-util-runtime": "9.5.1", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "dmg-builder": "26.8.1", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/electron-builder-squirrel-windows": { + "version": "26.8.1", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "app-builder-lib": "26.8.1", + "builder-util": "26.8.1", + "electron-winstaller": "5.4.0" + } + }, + "node_modules/electron-builder/node_modules/fs-extra": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder/node_modules/jsonfile": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-publish": { + "version": "26.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "builder-util": "26.8.1", + "builder-util-runtime": "9.5.1", + "chalk": "^4.1.2", + "form-data": "^4.0.5", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, + "node_modules/electron-publish/node_modules/fs-extra": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-publish/node_modules/jsonfile": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-publish/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.340", + "dev": true, + "license": "ISC" + }, + "node_modules/electron-vite": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.4", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "cac": "^6.7.14", + "esbuild": "^0.25.11", + "magic-string": "^0.30.19", + "picocolors": "^1.1.1" + }, + "bin": { + "electron-vite": "bin/electron-vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@swc/core": "^1.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + } + } + }, + "node_modules/electron-winstaller": { + "version": "5.4.0", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@electron/asar": "^3.2.1", + "debug": "^4.1.1", + "fs-extra": "^7.0.1", + "lodash": "^4.17.21", + "temp": "^0.9.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "@electron/windows-sign": "^1.1.2" + } + }, + "node_modules/electron-winstaller/node_modules/fs-extra": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/electron-winstaller/node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/electron/node_modules/@types/node": { + "version": "24.12.2", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/electron/node_modules/undici-types": { + "version": "7.16.0", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/esbuild": { + "version": "0.25.12", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "optional": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.14", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.7.4", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "cli-truncate": "^2.1.0", + "node-addon-api": "^1.6.3" + }, + "engines": { + "node": "^8.11.2 || >=10" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isbinaryfile": { + "version": "5.0.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "3.1.5", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-fetch-happen": { + "version": "14.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.7", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/minizlib": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "4.28.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.6.3" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.7.4", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-api-version": { + "version": "0.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + } + }, + "node_modules/node-api-version/node_modules/semver": { + "version": "7.7.4", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp": { + "version": "11.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "tinyglobby": "^0.2.12", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.7.4", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-releases": { + "version": "2.0.37", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "8.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "dev": true, + "license": "MIT", + "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/p-cancelable": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pe-library": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/plist": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/postcss": { + "version": "8.5.10", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/postject/node_modules/commander": { + "version": "9.5.0", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/proc-log": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-binary-file-arch": { + "version": "1.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "bin": { + "read-binary-file-arch": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resedit": { + "version": "1.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^0.4.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "2.6.3", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/rollup": { + "version": "4.60.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.2", + "@rollup/rollup-android-arm64": "4.60.2", + "@rollup/rollup-darwin-arm64": "4.60.2", + "@rollup/rollup-darwin-x64": "4.60.2", + "@rollup/rollup-freebsd-arm64": "4.60.2", + "@rollup/rollup-freebsd-x64": "4.60.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", + "@rollup/rollup-linux-arm-musleabihf": "4.60.2", + "@rollup/rollup-linux-arm64-gnu": "4.60.2", + "@rollup/rollup-linux-arm64-musl": "4.60.2", + "@rollup/rollup-linux-loong64-gnu": "4.60.2", + "@rollup/rollup-linux-loong64-musl": "4.60.2", + "@rollup/rollup-linux-ppc64-gnu": "4.60.2", + "@rollup/rollup-linux-ppc64-musl": "4.60.2", + "@rollup/rollup-linux-riscv64-gnu": "4.60.2", + "@rollup/rollup-linux-riscv64-musl": "4.60.2", + "@rollup/rollup-linux-s390x-gnu": "4.60.2", + "@rollup/rollup-linux-x64-gnu": "4.60.2", + "@rollup/rollup-linux-x64-musl": "4.60.2", + "@rollup/rollup-openbsd-x64": "4.60.2", + "@rollup/rollup-openharmony-arm64": "4.60.2", + "@rollup/rollup-win32-arm64-msvc": "4.60.2", + "@rollup/rollup-win32-ia32-msvc": "4.60.2", + "@rollup/rollup-win32-x64-gnu": "4.60.2", + "@rollup/rollup-win32-x64-msvc": "4.60.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/sanitize-filename": { + "version": "1.6.4", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.6.0", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.7.4", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/ssri": { + "version": "12.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/stat-mode": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "7.5.13", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/temp": { + "version": "0.9.4", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/temp-file": { + "version": "3.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } + }, + "node_modules/temp-file/node_modules/fs-extra": { + "version": "10.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/temp-file/node_modules/jsonfile": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/temp-file/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/tiny-async-pool": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.5.0" + } + }, + "node_modules/tiny-async-pool/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", + "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tmp": { + "version": "0.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "dev": true, + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.19.2", + "dev": true, + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/unique-slug": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/vite": { + "version": "7.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.27.7", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/vitest": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", + "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.5", + "@vitest/mocker": "4.1.5", + "@vitest/pretty-format": "4.1.5", + "@vitest/runner": "4.1.5", + "@vitest/snapshot": "4.1.5", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.5", + "@vitest/browser-preview": "4.1.5", + "@vitest/browser-webdriverio": "4.1.5", + "@vitest/coverage-istanbul": "4.1.5", + "@vitest/coverage-v8": "4.1.5", + "@vitest/ui": "4.1.5", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/which": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/gitnexus-desktop/package.json b/gitnexus-desktop/package.json new file mode 100644 index 0000000000..fa40212a3e --- /dev/null +++ b/gitnexus-desktop/package.json @@ -0,0 +1,39 @@ +{ + "name": "gitnexus-desktop", + "version": "0.1.0", + "private": true, + "description": "Electron desktop shell for GitNexus", + "author": "Abhigyan Patwari", + "license": "PolyForm-Noncommercial-1.0.0", + "type": "module", + "main": "dist/main/main.js", + "engines": { + "node": ">=20.0.0" + }, + "scripts": { + "predev": "node scripts/ensure-gitnexus-runtime.mjs --cleanup-dev-port", + "dev": "npx --yes -p electron@41.2.1 -p electron-vite@5.0.0 electron-vite dev --outDir=dist", + "start": "npx --yes -p electron@41.2.1 -p electron-vite@5.0.0 electron-vite preview --outDir=dist", + "bundle": "npx --yes -p electron@41.2.1 -p electron-vite@5.0.0 electron-vite build --outDir=dist", + "build": "node scripts/package.mjs --dir", + "build:dir": "node scripts/package.mjs --dir", + "build:win": "node scripts/package.mjs --win nsis", + "build:mac": "node scripts/package.mjs --mac dmg", + "build:linux": "node scripts/package.mjs --linux AppImage", + "build:all": "node scripts/package.mjs --win nsis --mac dmg --linux AppImage", + "typecheck": "tsc --noEmit -p tsconfig.node.json && tsc --noEmit -p tsconfig.renderer.json && tsc --noEmit -p tsconfig.vitest.json", + "test": "vitest run" + }, + "devDependencies": { + "@types/node": "^25.6.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "electron": "41.2.1", + "electron-builder": "26.8.1", + "electron-vite": "^5.0.0", + "typescript": "^6.0.3", + "vite": "^7.0.0", + "vitest": "^4.0.18" + } +} diff --git a/gitnexus-desktop/scripts/after-pack.mjs b/gitnexus-desktop/scripts/after-pack.mjs new file mode 100644 index 0000000000..04cded19b5 --- /dev/null +++ b/gitnexus-desktop/scripts/after-pack.mjs @@ -0,0 +1,21 @@ +import { copyFileSync, mkdirSync } from 'node:fs'; +import path from 'node:path'; + +/** + * afterPack hook — runs after electron-builder assembles the app directory, + * before any installer (NSIS, DMG, AppImage) is sealed. + * + * On Windows, lbugjs.node PE-imports "node.exe" by name. Electron's binary + * is not named node.exe, so LoadLibrary fails. We copy the real node.exe into + * resources/runtime/node.exe so main.ts can use it as the subprocess host. + * This fires for both --dir and --win builds, fixing the smoke test and installer. + */ +export default async function afterPack(context) { + if (context.electronPlatformName !== 'win32') return; + + const runtimeDir = path.join(context.appOutDir, 'resources', 'runtime'); + mkdirSync(runtimeDir, { recursive: true }); + copyFileSync(process.execPath, path.join(runtimeDir, 'node.exe')); + + console.log(`[after-pack] Copied node.exe → ${path.join(runtimeDir, 'node.exe')}`); +} diff --git a/gitnexus-desktop/scripts/ensure-gitnexus-runtime.mjs b/gitnexus-desktop/scripts/ensure-gitnexus-runtime.mjs new file mode 100644 index 0000000000..1573ea6eee --- /dev/null +++ b/gitnexus-desktop/scripts/ensure-gitnexus-runtime.mjs @@ -0,0 +1,471 @@ +import { spawnSync } from 'node:child_process'; +import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const workspaceRoot = path.resolve(__dirname, '..', '..'); +const sharedRoot = path.join(workspaceRoot, 'gitnexus-shared'); +const gitnexusRoot = path.join(workspaceRoot, 'gitnexus'); +const gitnexusWebRoot = path.join(workspaceRoot, 'gitnexus-web'); +const gitnexusLockfile = JSON.parse( + readFileSync(path.join(gitnexusRoot, 'package-lock.json'), 'utf8'), +); +const gitnexusPackageJson = JSON.parse( + readFileSync(path.join(gitnexusRoot, 'package.json'), 'utf8'), +); +const gitnexusModuleResolver = createRequire(path.join(gitnexusRoot, 'package.json')); +const gitnexusServerEntry = path.join(gitnexusRoot, 'dist', 'server', 'api.js'); +const gitnexusCliEntry = path.join(gitnexusRoot, 'dist', 'cli', 'index.js'); +const gitnexusServerPort = 4747; +const gitnexusWebDevPort = 5173; +const desktopRendererPort = 5174; +const shouldCleanupDevPort = process.argv.includes('--cleanup-dev-port'); + +const getNodeModulePackageJsonPath = (packageRoot, packageName) => { + return path.join(packageRoot, 'node_modules', ...packageName.split('/'), 'package.json'); +}; + +const getLockedPackageInstallSpecifier = (packageName) => { + const lockedPackage = gitnexusLockfile.packages?.[`node_modules/${packageName}`]; + const lockedVersion = lockedPackage?.version; + + if (!lockedVersion || String(lockedVersion).startsWith('file:')) { + return null; + } + + return lockedVersion; +}; + +const getDependencySubset = (dependencyMap, packageNames, overrides = {}) => { + return Object.fromEntries( + packageNames.map((packageName) => { + const versionSpecifier = + overrides[packageName] ?? + getLockedPackageInstallSpecifier(packageName) ?? + dependencyMap?.[packageName]; + + if (!versionSpecifier) { + throw new Error(`Missing package specifier for ${packageName}.`); + } + + return [packageName, versionSpecifier]; + }), + ); +}; + +const getEntryMtimeMs = (targetPath) => { + if (!existsSync(targetPath)) { + return 0; + } + + return statSync(targetPath).mtimeMs; +}; + +const getPathMtimeMs = (targetPath) => { + if (!existsSync(targetPath)) { + return 0; + } + + const stats = statSync(targetPath); + + if (!stats.isDirectory()) { + return stats.mtimeMs; + } + + let newestMtimeMs = stats.mtimeMs; + + for (const entry of readdirSync(targetPath, { withFileTypes: true })) { + if (entry.name === '.git' || entry.name === 'dist' || entry.name === 'node_modules') { + continue; + } + + newestMtimeMs = Math.max(newestMtimeMs, getPathMtimeMs(path.join(targetPath, entry.name))); + } + + return newestMtimeMs; +}; + +const getNewestMtimeMs = (paths) => { + return paths.reduce((latestMtimeMs, targetPath) => { + return Math.max(latestMtimeMs, getPathMtimeMs(targetPath)); + }, 0); +}; + +const getOldestOutputMtimeMs = (paths) => { + let oldestMtimeMs = Number.POSITIVE_INFINITY; + + for (const targetPath of paths) { + const mtimeMs = getPathMtimeMs(targetPath); + + if (mtimeMs === 0) { + return 0; + } + + oldestMtimeMs = Math.min(oldestMtimeMs, mtimeMs); + } + + return Number.isFinite(oldestMtimeMs) ? oldestMtimeMs : 0; +}; + +const gitnexusSharedSourceInputs = [ + path.join(sharedRoot, 'src'), + path.join(sharedRoot, 'package.json'), + path.join(sharedRoot, 'package-lock.json'), + path.join(sharedRoot, 'tsconfig.json'), +]; + +const gitnexusSharedInstallInputs = [ + path.join(sharedRoot, 'package.json'), + path.join(sharedRoot, 'package-lock.json'), + path.join(sharedRoot, 'tsconfig.json'), +]; + +const gitnexusSharedInstallMarkerPaths = [path.join(sharedRoot, 'node_modules', 'typescript')]; + +const gitnexusInstallInputs = [ + path.join(gitnexusRoot, 'package.json'), + path.join(gitnexusRoot, 'package-lock.json'), + path.join(sharedRoot, 'package.json'), + path.join(sharedRoot, 'package-lock.json'), +]; + +const gitnexusDesktopRuntimeDependencyNames = [ + '@ladybugdb/core', + '@modelcontextprotocol/sdk', + 'cli-progress', + 'commander', + 'cors', + 'express', + 'glob', + 'graphology', + 'graphology-indices', + 'graphology-utils', + 'ignore', + 'js-yaml', + 'jsonc-parser', + 'lru-cache', + 'mnemonist', + 'pandemonium', + 'uuid', +]; +const unmanagedGitNexusRuntimeDependencyNames = Object.keys(gitnexusPackageJson.dependencies ?? {}) + .filter((packageName) => !gitnexusDesktopRuntimeDependencyNames.includes(packageName)) + .sort(); + +const gitnexusDesktopBuildDependencyNames = ['@types/node', 'gitnexus-shared', 'typescript']; + +const gitnexusDesktopRuntimeDependencies = getDependencySubset( + gitnexusPackageJson.dependencies ?? {}, + gitnexusDesktopRuntimeDependencyNames, +); + +const gitnexusDesktopBuildDependencies = getDependencySubset( + gitnexusPackageJson.devDependencies ?? {}, + gitnexusDesktopBuildDependencyNames, + { 'gitnexus-shared': `file:${sharedRoot}` }, +); + +const gitnexusDesktopRuntimeInstallMarkerPaths = [ + ...gitnexusDesktopRuntimeDependencyNames.map((packageName) => + getNodeModulePackageJsonPath(gitnexusRoot, packageName), + ), + path.join(gitnexusRoot, 'node_modules', 'commander', 'index.js'), + path.join(gitnexusRoot, 'node_modules', '@ladybugdb', 'core', 'lbugjs.node'), +]; + +const gitnexusDesktopRuntimeResolutionChecks = [ + 'commander', + '@modelcontextprotocol/sdk/server/index.js', + '@modelcontextprotocol/sdk/server/streamableHttp.js', + 'ignore', + 'js-yaml', +]; + +const gitnexusDesktopBuildInstallMarkerPaths = gitnexusDesktopBuildDependencyNames.map( + (packageName) => getNodeModulePackageJsonPath(gitnexusRoot, packageName), +); + +const gitnexusRuntimeInputs = [ + path.join(gitnexusRoot, 'src'), + path.join(gitnexusRoot, 'scripts'), + path.join(gitnexusRoot, 'package.json'), + path.join(gitnexusRoot, 'package-lock.json'), + path.join(gitnexusRoot, 'tsconfig.json'), + ...gitnexusSharedSourceInputs, +]; + +const gitnexusRuntimeOutputs = [gitnexusServerEntry, gitnexusCliEntry]; + +const isInstallStale = (inputPaths, installMarkerPaths) => { + if (installMarkerPaths.some((targetPath) => !existsSync(targetPath))) { + return true; + } + + const installMarkerMtimeMs = Math.min(...installMarkerPaths.map(getEntryMtimeMs)); + + return getNewestMtimeMs(inputPaths) > installMarkerMtimeMs; +}; + +const isGitNexusSharedInstallStale = () => { + return isInstallStale(gitnexusSharedInstallInputs, gitnexusSharedInstallMarkerPaths); +}; + +const canResolveGitNexusSpecifier = (specifier) => { + try { + gitnexusModuleResolver.resolve(specifier); + return true; + } catch { + return false; + } +}; + +const isGitNexusRuntimeInstallStale = () => { + return ( + isInstallStale(gitnexusInstallInputs, gitnexusDesktopRuntimeInstallMarkerPaths) || + gitnexusDesktopRuntimeResolutionChecks.some( + (specifier) => !canResolveGitNexusSpecifier(specifier), + ) + ); +}; + +const isGitNexusBuildInstallStale = () => { + return isInstallStale(gitnexusInstallInputs, gitnexusDesktopBuildInstallMarkerPaths); +}; + +const isGitNexusBuildStale = () => { + return getNewestMtimeMs(gitnexusRuntimeInputs) > getOldestOutputMtimeMs(gitnexusRuntimeOutputs); +}; + +const runNpmAttempt = (args, cwd) => { + const result = spawnSync('npm', args, { + cwd, + stdio: 'inherit', + shell: process.platform === 'win32', + }); + + if (result.error) { + throw result.error; + } + + return result.status ?? 1; +}; + +const runNpm = (args, cwd) => { + const status = runNpmAttempt(args, cwd); + + if (status !== 0) { + process.exit(status); + } +}; + +const repairGitNexusPackages = (_dependencyMap, label) => { + console.info(`[gitnexus-desktop] ${label}.`); + runNpm(['ci'], gitnexusRoot); +}; + +const runCommand = (command, args) => { + return spawnSync(command, args, { + encoding: 'utf8', + shell: false, + stdio: ['ignore', 'pipe', 'pipe'], + windowsHide: true, + }); +}; + +const getPosixCommandLine = (pid) => { + if (!pid) { + return ''; + } + + const lookup = runCommand('ps', ['-p', String(pid), '-o', 'args=']); + + if (lookup.status !== 0) { + return ''; + } + + return lookup.stdout.trim(); +}; + +const findPortOwner = (port) => { + if (process.platform === 'win32') { + const lookup = runCommand('powershell.exe', [ + '-NoProfile', + '-Command', + [ + `$connection = Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | Where-Object { $_.State -eq 'Listen' } | Select-Object -First 1;`, + 'if (-not $connection) { return }', + `$process = Get-CimInstance Win32_Process -Filter \"ProcessId = $($connection.OwningProcess)\";`, + '$payload = [PSCustomObject]@{', + ' pid = $connection.OwningProcess;', + ' name = $process.Name;', + ' executablePath = $process.ExecutablePath;', + ' commandLine = $process.CommandLine', + '};', + '$payload | ConvertTo-Json -Compress', + ].join(' '), + ]); + + if (lookup.status !== 0 || !lookup.stdout.trim()) { + return null; + } + + return JSON.parse(lookup.stdout.trim()); + } + + const lookup = runCommand('lsof', ['-nP', `-iTCP:${port}`, '-sTCP:LISTEN', '-Fpc']); + + if (lookup.status !== 0 || !lookup.stdout.trim()) { + return null; + } + + const owner = { pid: null, name: '', commandLine: '' }; + + for (const line of lookup.stdout.split(/\r?\n/)) { + if (line.startsWith('p')) { + owner.pid = Number.parseInt(line.slice(1), 10); + } else if (line.startsWith('c')) { + owner.name = line.slice(1); + } + } + + owner.commandLine = getPosixCommandLine(owner.pid); + + return owner.pid ? owner : null; +}; + +const killProcessTree = (pid) => { + if (!pid) { + return; + } + + if (process.platform === 'win32') { + const result = spawnSync('taskkill', ['/pid', String(pid), '/t', '/f'], { + stdio: 'inherit', + windowsHide: true, + }); + + if ((result.status ?? 1) !== 0) { + process.exit(result.status ?? 1); + } + + return; + } + + process.kill(pid, 'SIGTERM'); +}; + +const ensureDesktopRendererPortAvailable = () => { + const owner = findPortOwner(desktopRendererPort); + + if (!owner) { + return; + } + + const signature = `${owner.name ?? ''} ${owner.commandLine ?? ''}`.toLowerCase(); + const isStaleElectronViteProcess = signature.includes('electron-vite'); + + if (!isStaleElectronViteProcess) { + console.error( + `Port ${desktopRendererPort} is already in use by ${owner.name ?? 'another process'} (PID ${owner.pid}). Stop that process and try again.`, + ); + process.exit(1); + } + + console.info( + `Stopping stale desktop renderer process on port ${desktopRendererPort} (PID ${owner.pid}).`, + ); + killProcessTree(owner.pid); +}; + +const ensureGitNexusWebDevPortAvailable = () => { + const owner = findPortOwner(gitnexusWebDevPort); + + if (!owner) { + return; + } + + const signature = `${owner.name ?? ''} ${owner.commandLine ?? ''}`.toLowerCase(); + const isGitNexusWebViteProcess = signature.includes('vite') && signature.includes('gitnexus-web'); + + if (!isGitNexusWebViteProcess) { + console.error( + `Port ${gitnexusWebDevPort} is already in use by ${owner.name ?? 'another process'} (PID ${owner.pid}). Stop that process and try again.`, + ); + process.exit(1); + } + + console.info( + `Stopping stale GitNexus web dev server on port ${gitnexusWebDevPort} (PID ${owner.pid}).`, + ); + killProcessTree(owner.pid); +}; + +const ensureGitNexusServerPortAvailable = () => { + const owner = findPortOwner(gitnexusServerPort); + + if (!owner) { + return; + } + + const signature = `${owner.name ?? ''} ${owner.commandLine ?? ''}`.toLowerCase(); + const isGitNexusServerProcess = + (signature.includes('dist\\cli\\index.js') || signature.includes('dist/cli/index.js')) && + signature.includes('serve'); + + if (!isGitNexusServerProcess) { + console.error( + `Port ${gitnexusServerPort} is already in use by ${owner.name ?? 'another process'} (PID ${owner.pid}). Stop that process and try again.`, + ); + process.exit(1); + } + + console.info(`Stopping stale GitNexus backend on port ${gitnexusServerPort} (PID ${owner.pid}).`); + killProcessTree(owner.pid); +}; + +if (shouldCleanupDevPort) { + ensureGitNexusServerPortAvailable(); + ensureDesktopRendererPortAvailable(); + ensureGitNexusWebDevPortAvailable(); +} + +if ( + !existsSync(path.join(sharedRoot, 'node_modules', 'typescript')) || + isGitNexusSharedInstallStale() +) { + console.info('[gitnexus-desktop] Refreshing gitnexus-shared dependencies.'); + runNpm(['ci'], sharedRoot); +} + +if (unmanagedGitNexusRuntimeDependencyNames.length > 0) { + console.warn( + '[gitnexus-desktop] Runtime repair does not yet manage these direct GitNexus dependencies:\n' + + unmanagedGitNexusRuntimeDependencyNames.map((packageName) => ` - ${packageName}`).join('\n'), + ); +} + +if (isGitNexusRuntimeInstallStale()) { + repairGitNexusPackages(gitnexusDesktopRuntimeDependencies, 'Repairing GitNexus runtime packages'); +} + +if (!existsSync(path.join(gitnexusWebRoot, 'node_modules', 'vite'))) { + runNpm(['ci'], gitnexusWebRoot); +} + +if (getOldestOutputMtimeMs(gitnexusRuntimeOutputs) === 0 || isGitNexusBuildStale()) { + if (isGitNexusBuildInstallStale()) { + repairGitNexusPackages( + { + ...gitnexusDesktopRuntimeDependencies, + ...gitnexusDesktopBuildDependencies, + }, + 'Repairing GitNexus build tooling', + ); + } + + console.info('[gitnexus-desktop] Rebuilding GitNexus runtime artifacts.'); + runNpm(['run', 'build'], gitnexusRoot); +} diff --git a/gitnexus-desktop/scripts/package.mjs b/gitnexus-desktop/scripts/package.mjs new file mode 100644 index 0000000000..996a7f3646 --- /dev/null +++ b/gitnexus-desktop/scripts/package.mjs @@ -0,0 +1,474 @@ +import { execFileSync } from 'node:child_process'; +import fs from 'node:fs'; +import { createRequire } from 'node:module'; +import os from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const packageRoot = path.resolve(__dirname, '..'); +const workspaceRoot = path.resolve(packageRoot, '..'); +const gitnexusRoot = path.join(workspaceRoot, 'gitnexus'); +const gitnexusWebRoot = path.join(workspaceRoot, 'gitnexus-web'); +const releaseRoot = path.join(packageRoot, 'release'); +const gitnexusPackageJson = JSON.parse( + fs.readFileSync(path.join(gitnexusRoot, 'package.json'), 'utf8'), +); +const gitnexusPackageLock = JSON.parse( + fs.readFileSync(path.join(gitnexusRoot, 'package-lock.json'), 'utf8'), +); +const desktopPackageJson = JSON.parse( + fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'), +); +const desktopPackageLock = JSON.parse( + fs.readFileSync(path.join(packageRoot, 'package-lock.json'), 'utf8'), +); +const electronVersion = + desktopPackageJson.devDependencies?.electron?.replace(/^[^\d]*/, '') ?? '41.2.1'; +const electronBuilderVersion = + desktopPackageJson.devDependencies?.['electron-builder']?.replace(/^[^\d]*/, '') ?? '26.8.1'; +const electronBuilderCliPath = path.join(packageRoot, 'node_modules', 'electron-builder', 'cli.js'); +const builderUtilRequire = createRequire( + path.join(packageRoot, 'node_modules', 'builder-util', 'out', 'util.js'), +); +const requiredBuilderRuntimeModules = ['app-builder-bin']; +const appBuilderLibVersion = + desktopPackageLock.packages?.['node_modules/app-builder-lib']?.version ?? + electronBuilderVersion.replace(/^[^\d]*/, ''); +// Validate version is safe semver before embedding in a CLI argument. +if (!/^\d+\.\d+\.\d+(?:[.-][a-zA-Z0-9._-]*)?$/.test(appBuilderLibVersion)) { + throw new Error( + `Invalid app-builder-lib version in lockfile: ${JSON.stringify(appBuilderLibVersion)}`, + ); +} +const stamp = new Date().toISOString().replace(/[:.]/g, '-'); +const outputDir = path.join(releaseRoot, stamp); +const latestReleasePointerPath = path.join(releaseRoot, '.latest-unpacked-release'); +const electronBuilderConfigPath = path.join(packageRoot, 'electron-builder.yml'); +const requiredNsisTemplateFiles = [ + path.join(packageRoot, 'node_modules', 'app-builder-lib', 'templates', 'nsis', 'messages.yml'), + path.join( + packageRoot, + 'node_modules', + 'app-builder-lib', + 'templates', + 'nsis', + 'assistedMessages.yml', + ), +]; +const supportedTargetHosts = { + '--linux': 'linux', + '--mac': 'darwin', + '--win': 'win32', +}; +const allowedBuilderArgs = new Set([ + '--dir', + '--linux', + '--mac', + '--win', + 'AppImage', + 'dmg', + 'nsis', +]); +const artifactFileExtensions = new Set(['.AppImage', '.dmg', '.exe', '.msi', '.zip']); +const gitnexusRuntimeDependencyNames = Object.keys(gitnexusPackageJson.dependencies ?? {}); +const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm'; +const packagedResourceEntries = [ + { + from: path.join(gitnexusWebRoot, 'dist'), + to: 'gitnexus-web', + }, + { + from: path.join(gitnexusRoot, 'dist'), + to: path.join('gitnexus', 'dist'), + }, + { + from: path.join(gitnexusRoot, 'hooks'), + to: path.join('gitnexus', 'hooks'), + }, + { + from: path.join(gitnexusRoot, 'skills'), + to: path.join('gitnexus', 'skills'), + }, + { + from: path.join(gitnexusRoot, 'vendor'), + to: path.join('gitnexus', 'vendor'), + }, + { + from: path.join(gitnexusRoot, 'package.json'), + to: path.join('gitnexus', 'package.json'), + }, +]; + +const toBuilderRelativePath = (targetPath) => { + return path.relative(packageRoot, targetPath) || '.'; +}; + +const getNodeModulePath = (packageName) => { + return path.join(gitnexusRoot, 'node_modules', ...packageName.split('/')); +}; + +const getRuntimeDependencyClosure = () => { + const visited = new Set(); + const queue = [...gitnexusRuntimeDependencyNames]; + + while (queue.length > 0) { + const packageName = queue.shift(); + + if (!packageName || visited.has(packageName)) { + continue; + } + + visited.add(packageName); + + const lockEntry = gitnexusPackageLock.packages?.[`node_modules/${packageName}`]; + const dependencyNames = Object.keys({ + ...(lockEntry?.dependencies ?? {}), + ...(lockEntry?.optionalDependencies ?? {}), + }); + + for (const dependencyName of dependencyNames) { + if (!visited.has(dependencyName)) { + queue.push(dependencyName); + } + } + } + + return [...visited].filter((packageName) => fs.existsSync(getNodeModulePath(packageName))).sort(); +}; + +const packagedRuntimeNodeModules = getRuntimeDependencyClosure(); + +const builderArgs = process.argv.slice(2); +const unsupportedBuilderArgs = builderArgs.filter((argument) => !allowedBuilderArgs.has(argument)); + +if (unsupportedBuilderArgs.length > 0) { + throw new Error(`Unsupported electron-builder arguments: ${unsupportedBuilderArgs.join(', ')}`); +} + +const requestedTargets = builderArgs.filter((argument) => argument in supportedTargetHosts); + +const builderEnvironment = { + GITNEXUS_DESKTOP_GITNEXUS_DIST: toBuilderRelativePath(path.join(gitnexusRoot, 'dist')), + GITNEXUS_DESKTOP_GITNEXUS_HOOKS: toBuilderRelativePath(path.join(gitnexusRoot, 'hooks')), + GITNEXUS_DESKTOP_GITNEXUS_NODE_MODULES: toBuilderRelativePath( + path.join(gitnexusRoot, 'node_modules'), + ), + GITNEXUS_DESKTOP_GITNEXUS_PACKAGE_JSON: toBuilderRelativePath( + path.join(gitnexusRoot, 'package.json'), + ), + GITNEXUS_DESKTOP_GITNEXUS_SKILLS: toBuilderRelativePath(path.join(gitnexusRoot, 'skills')), + GITNEXUS_DESKTOP_GITNEXUS_VENDOR: toBuilderRelativePath(path.join(gitnexusRoot, 'vendor')), + GITNEXUS_DESKTOP_WEB_DIST: toBuilderRelativePath(path.join(gitnexusWebRoot, 'dist')), +}; + +const builderCliArgs = [ + electronBuilderCliPath, + '--config', + electronBuilderConfigPath, + `-c.electronVersion=${electronVersion}`, + `-c.directories.output=${outputDir}`, + '--publish', + 'never', + ...builderArgs, +]; + +const runCommand = (command, args, cwd, extraEnv = {}) => { + // On Windows, only .cmd files (e.g. npm.cmd) require shell:true to execute. + // Using shell:true for all commands causes spaces in paths to be misinterpreted + // by cmd.exe when it joins the args array into a raw command string. + const needsShell = process.platform === 'win32' && command.endsWith('.cmd'); + execFileSync(command, args, { + cwd, + env: { + ...process.env, + ...extraEnv, + }, + stdio: 'inherit', + windowsHide: true, + shell: needsShell, + }); +}; + +const mirrorDirectory = (sourceDirectory, destinationDirectory) => { + if (process.platform === 'win32') { + fs.mkdirSync(destinationDirectory, { recursive: true }); + + try { + execFileSync( + 'robocopy', + [sourceDirectory, destinationDirectory, '/MIR', '/NFL', '/NDL', '/NJH', '/NJS', '/NP'], + { + cwd: packageRoot, + stdio: 'inherit', + windowsHide: true, + }, + ); + } catch (error) { + const exitCode = error.status ?? 16; + + if (exitCode > 7) { + throw error; + } + } + + return; + } + + fs.rmSync(destinationDirectory, { force: true, recursive: true }); + fs.cpSync(sourceDirectory, destinationDirectory, { force: true, recursive: true }); +}; + +const canResolveBuilderRuntimeModule = (moduleName) => { + try { + builderUtilRequire.resolve(moduleName); + return true; + } catch { + return false; + } +}; + +const overlayDirectoryContents = (sourceDirectory, destinationDirectory) => { + fs.mkdirSync(destinationDirectory, { recursive: true }); + + for (const entry of fs.readdirSync(sourceDirectory, { withFileTypes: true })) { + fs.cpSync(path.join(sourceDirectory, entry.name), path.join(destinationDirectory, entry.name), { + force: true, + recursive: true, + }); + } +}; + +const resolvePackagedResourceRoots = () => { + const candidateRoots = [ + path.join(outputDir, 'win-unpacked', 'resources'), + path.join(outputDir, 'linux-unpacked', 'resources'), + path.join( + outputDir, + `${desktopPackageJson.productName ?? 'GitNexus Desktop'}.app`, + 'Contents', + 'Resources', + ), + ]; + + return candidateRoots.filter((candidateRoot) => fs.existsSync(candidateRoot)); +}; + +const syncPackagedRuntimeResources = () => { + const resourceRoots = resolvePackagedResourceRoots(); + + if (resourceRoots.length === 0) { + return; + } + + for (const resourceRoot of resourceRoots) { + const packagedNodeModulesRoot = path.join(resourceRoot, 'gitnexus', 'node_modules'); + + fs.rmSync(packagedNodeModulesRoot, { force: true, recursive: true }); + fs.mkdirSync(packagedNodeModulesRoot, { recursive: true }); + + const sourceNodeModulesLock = path.join(gitnexusRoot, 'node_modules', '.package-lock.json'); + const destinationNodeModulesLock = path.join(packagedNodeModulesRoot, '.package-lock.json'); + + if (fs.existsSync(sourceNodeModulesLock)) { + fs.cpSync(sourceNodeModulesLock, destinationNodeModulesLock, { force: true }); + } + + for (const packageName of packagedRuntimeNodeModules) { + const sourcePackagePath = getNodeModulePath(packageName); + const destinationPackagePath = path.join(packagedNodeModulesRoot, ...packageName.split('/')); + + mirrorDirectory(sourcePackagePath, destinationPackagePath); + } + + for (const entry of packagedResourceEntries) { + const destinationPath = path.join(resourceRoot, entry.to); + + let isDirectory; + try { + isDirectory = fs.statSync(entry.from).isDirectory(); + } catch (err) { + if (err.code !== 'ENOENT') throw err; + continue; + } + if (isDirectory) { + mirrorDirectory(entry.from, destinationPath); + continue; + } + + fs.rmSync(destinationPath, { force: true, recursive: true }); + fs.mkdirSync(path.dirname(destinationPath), { recursive: true }); + fs.cpSync(entry.from, destinationPath, { force: true }); + } + } +}; + +const repairAppBuilderLibPackage = () => { + const repairDirectory = fs.mkdtempSync( + path.join(os.tmpdir(), 'gitnexus-desktop-app-builder-lib-'), + ); + + try { + const tarballName = `app-builder-lib-${appBuilderLibVersion}.tgz`; + const tarballPath = path.join(repairDirectory, tarballName); + const extractedPackageRoot = path.join(repairDirectory, 'package'); + const installedPackageRoot = path.join(packageRoot, 'node_modules', 'app-builder-lib'); + + runCommand( + npmCommand, + ['pack', `app-builder-lib@${appBuilderLibVersion}`, '--pack-destination', repairDirectory], + packageRoot, + ); + runCommand('tar', ['-xzf', tarballPath, '-C', repairDirectory], packageRoot); + overlayDirectoryContents(extractedPackageRoot, installedPackageRoot); + } finally { + fs.rmSync(repairDirectory, { force: true, recursive: true }); + } +}; + +const assertSupportedHostForRequestedTargets = () => { + const unsupportedTargets = requestedTargets.filter( + (target) => supportedTargetHosts[target] !== process.platform, + ); + + if (unsupportedTargets.length === 0) { + return; + } + + const messages = unsupportedTargets.map((target) => { + if (target === '--mac') { + return 'macOS DMG builds must run on a macOS host. Use the Desktop Packaging GitHub Actions workflow for .dmg artifacts.'; + } + + if (target === '--linux') { + return 'Linux AppImage builds should run on a Linux host. Use the Desktop Packaging GitHub Actions workflow for Linux installers.'; + } + + return 'Windows NSIS builds must run on a Windows host.'; + }); + + throw new Error(messages.join('\n')); +}; + +const ensureDesktopToolchainHealthy = () => { + const missingBuilderRuntimeModules = requiredBuilderRuntimeModules.filter( + (moduleName) => !canResolveBuilderRuntimeModule(moduleName), + ); + const missingNsisTemplates = requestedTargets.includes('--win') + ? requiredNsisTemplateFiles.filter((filePath) => !fs.existsSync(filePath)) + : []; + + if (missingBuilderRuntimeModules.length === 0 && missingNsisTemplates.length === 0) { + return; + } + + if (missingNsisTemplates.length > 0) { + console.warn( + '[build] electron-builder NSIS templates are missing. Restoring app-builder-lib package contents first...', + ); + + try { + repairAppBuilderLibPackage(); + } catch { + // Fall through to install-based repair paths below. + } + } + + const remainingMissingBuilderRuntimeModules = requiredBuilderRuntimeModules.filter( + (moduleName) => !canResolveBuilderRuntimeModule(moduleName), + ); + const remainingMissingNsisTemplates = requestedTargets.includes('--win') + ? requiredNsisTemplateFiles.filter((filePath) => !fs.existsSync(filePath)) + : []; + + if ( + remainingMissingBuilderRuntimeModules.length === 0 && + remainingMissingNsisTemplates.length === 0 + ) { + return; + } + + console.warn( + '[build] electron-builder installation is incomplete. Restoring desktop dependencies with npm ci...', + ); + runCommand(npmCommand, ['ci'], packageRoot); + + const unresolvedBuilderRuntimeModules = requiredBuilderRuntimeModules.filter( + (moduleName) => !canResolveBuilderRuntimeModule(moduleName), + ); + const unresolvedTemplates = requiredNsisTemplateFiles.filter( + (filePath) => !fs.existsSync(filePath), + ); + + if (unresolvedBuilderRuntimeModules.length > 0 || unresolvedTemplates.length > 0) { + throw new Error( + [ + 'electron-builder is missing required runtime modules after reinstall:', + ...unresolvedBuilderRuntimeModules, + ...unresolvedTemplates, + ].join('\n'), + ); + } +}; + +const listArtifacts = (directoryPath) => { + const results = []; + const entries = fs.readdirSync(directoryPath, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(directoryPath, entry.name); + + if (entry.isDirectory()) { + if (entry.name === 'win-unpacked') { + results.push(fullPath); + continue; + } + + results.push(...listArtifacts(fullPath)); + continue; + } + + if (artifactFileExtensions.has(path.extname(entry.name))) { + results.push(fullPath); + } + } + + return results; +}; + +fs.mkdirSync(outputDir, { recursive: true }); +fs.writeFileSync(latestReleasePointerPath, `${outputDir}\n`); + +assertSupportedHostForRequestedTargets(); +ensureDesktopToolchainHealthy(); + +runCommand(process.execPath, ['scripts/ensure-gitnexus-runtime.mjs'], packageRoot); + +runCommand(npmCommand, ['run', 'bundle'], packageRoot); + +const gitnexusSharedRoot = path.join(workspaceRoot, 'gitnexus-shared'); +if (!fs.existsSync(path.join(gitnexusSharedRoot, 'dist'))) { + if (!fs.existsSync(path.join(gitnexusSharedRoot, 'node_modules'))) { + runCommand(npmCommand, ['ci'], gitnexusSharedRoot); + } + runCommand(npmCommand, ['run', 'build'], gitnexusSharedRoot); +} +if (!fs.existsSync(path.join(gitnexusWebRoot, 'node_modules'))) { + runCommand(npmCommand, ['ci'], gitnexusWebRoot); +} +runCommand(npmCommand, ['run', 'build'], gitnexusWebRoot); + +runCommand(process.execPath, builderCliArgs, packageRoot, builderEnvironment); +syncPackagedRuntimeResources(); + +const artifacts = listArtifacts(outputDir); + +console.log(`[build] artifacts written to release/${stamp}`); + +if (artifacts.length > 0) { + console.log('[build] artifacts:'); + for (const artifact of artifacts) { + console.log(` - ${artifact}`); + } +} diff --git a/gitnexus-desktop/scripts/smoke-unpacked.mjs b/gitnexus-desktop/scripts/smoke-unpacked.mjs new file mode 100644 index 0000000000..9c18bf5c2c --- /dev/null +++ b/gitnexus-desktop/scripts/smoke-unpacked.mjs @@ -0,0 +1,185 @@ +import { spawn, spawnSync } from 'node:child_process'; +import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const packageRoot = path.resolve(__dirname, '..'); +const releaseRoot = path.join(packageRoot, 'release'); +const latestReleasePointerPath = path.join(releaseRoot, '.latest-unpacked-release'); +const productName = 'GitNexus Desktop'; +const smokeTimeoutMs = 120_000; +const releaseDirectoryPattern = /^\d{4}-\d{2}-\d{2}T.*Z$/; + +const listTimestampedReleaseDirs = () => { + if (!existsSync(releaseRoot)) { + return []; + } + + return readdirSync(releaseRoot, { withFileTypes: true }) + .filter((entry) => entry.isDirectory() && releaseDirectoryPattern.test(entry.name)) + .map((entry) => path.join(releaseRoot, entry.name)) + .sort((leftPath, rightPath) => statSync(rightPath).mtimeMs - statSync(leftPath).mtimeMs); +}; + +const findFirstMatch = (rootDir, predicate) => { + for (const entry of readdirSync(rootDir, { withFileTypes: true })) { + const entryPath = path.join(rootDir, entry.name); + + if (predicate(entry, entryPath)) { + return entryPath; + } + + if (entry.isDirectory()) { + const nestedMatch = findFirstMatch(entryPath, predicate); + + if (nestedMatch) { + return nestedMatch; + } + } + } + + return null; +}; + +const resolveExecutablePath = (releaseDir) => { + if (process.platform === 'win32') { + return path.join(releaseDir, 'win-unpacked', `${productName}.exe`); + } + + if (process.platform === 'darwin') { + return findFirstMatch(releaseDir, (entry, entryPath) => { + return ( + entry.isFile() && + entry.name === productName && + entryPath.includes(`${productName}.app${path.sep}Contents${path.sep}MacOS${path.sep}`) + ); + }); + } + + const linuxUnpackedDir = path.join(releaseDir, 'linux-unpacked'); + + if (!existsSync(linuxUnpackedDir)) { + return null; + } + + const candidateNames = [productName, 'gitnexus-desktop']; + + for (const candidateName of candidateNames) { + const candidatePath = path.join(linuxUnpackedDir, candidateName); + + if (existsSync(candidatePath) && statSync(candidatePath).isFile()) { + return candidatePath; + } + } + + return findFirstMatch(linuxUnpackedDir, (entry, entryPath) => { + return entry.isFile() && path.extname(entryPath) === ''; + }); +}; + +const resolveLatestExecutable = () => { + if (existsSync(latestReleasePointerPath)) { + const releaseDir = readFileSync(latestReleasePointerPath, 'utf8').trim(); + const executablePath = releaseDir ? resolveExecutablePath(releaseDir) : null; + + return { + executablePath: executablePath && existsSync(executablePath) ? executablePath : null, + releaseDir: releaseDir || null, + }; + } + + const releaseDirs = listTimestampedReleaseDirs(); + + for (const releaseDir of releaseDirs) { + const executablePath = resolveExecutablePath(releaseDir); + + if (executablePath && existsSync(executablePath)) { + return { executablePath, releaseDir }; + } + } + + return { + executablePath: null, + releaseDir: releaseDirs[0] ?? null, + }; +}; + +const prepareExecutableForSmokeTest = (executablePath) => { + if (process.platform !== 'darwin') { + return; + } + + const result = spawnSync('xattr', ['-cr', executablePath], { stdio: 'inherit' }); + + if (result.error) { + throw result.error; + } + + if ((result.status ?? 0) !== 0) { + throw new Error(`Failed to clear macOS quarantine attributes for ${executablePath}.`); + } +}; + +const getSmokeTestArguments = () => { + return process.platform === 'linux' ? ['--no-sandbox'] : []; +}; + +const runSmokeTest = async () => { + const { executablePath, releaseDir: latestReleaseDir } = resolveLatestExecutable(); + + if (!latestReleaseDir) { + throw new Error( + 'No timestamped desktop release directory was found. Run npm run build:dir first.', + ); + } + + if (!executablePath || !existsSync(executablePath)) { + throw new Error(`Unable to locate the unpacked desktop executable under ${latestReleaseDir}.`); + } + + prepareExecutableForSmokeTest(executablePath); + console.info(`[gitnexus-desktop] Smoke testing unpacked app: ${executablePath}`); + + await new Promise((resolve, reject) => { + const childProcess = spawn(executablePath, getSmokeTestArguments(), { + cwd: path.dirname(executablePath), + env: { + ...process.env, + GITNEXUS_DESKTOP_SMOKE_TEST: '1', + }, + stdio: 'inherit', + windowsHide: true, + }); + + const timeoutHandle = setTimeout(() => { + childProcess.kill(process.platform === 'win32' ? undefined : 'SIGTERM'); + reject( + new Error(`Timed out waiting for unpacked desktop smoke test after ${smokeTimeoutMs}ms.`), + ); + }, smokeTimeoutMs); + + childProcess.once('error', (error) => { + clearTimeout(timeoutHandle); + reject(error); + }); + + childProcess.once('exit', (code, signal) => { + clearTimeout(timeoutHandle); + + if (code === 0) { + resolve(undefined); + return; + } + + reject( + new Error( + `Unpacked desktop smoke test exited with code ${code ?? 'null'}${signal ? ` (signal: ${signal})` : ''}.`, + ), + ); + }); + }); +}; + +await runSmokeTest(); diff --git a/gitnexus-desktop/src/main/main.ts b/gitnexus-desktop/src/main/main.ts new file mode 100644 index 0000000000..f478caa50d --- /dev/null +++ b/gitnexus-desktop/src/main/main.ts @@ -0,0 +1,860 @@ +import { app, BrowserView, BrowserWindow, dialog, ipcMain, shell } from 'electron'; +import { spawn, spawnSync, type ChildProcess } from 'node:child_process'; +import { createReadStream, existsSync, statSync } from 'node:fs'; +import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + getPackagedRendererEntry, + getRequestedPath, + normalizeStaticPath, +} from './runtime-paths.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const DESKTOP_APP_NAME = 'GitNexus Desktop'; +const DESKTOP_SHELL_TITLEBAR_HEIGHT = 38; +const DESKTOP_BACKGROUND_COLOR = '#0d0d0d'; +const DESKTOP_APP_ICON_PATH = app.isPackaged + ? path.join(process.resourcesPath, process.platform === 'win32' ? 'icon.ico' : 'icon.png') + : path.resolve( + __dirname, + process.platform === 'win32' ? '../../build/icon.ico' : '../../build/icon.png', + ); +const DESKTOP_GET_SHELL_STATE_CHANNEL = 'gitnexus-desktop:get-shell-state'; +const DESKTOP_WINDOW_ACTION_CHANNEL = 'gitnexus-desktop:window-action'; +const DESKTOP_WINDOW_STATE_CHANGED_CHANNEL = 'gitnexus-desktop:window-state-changed'; +const GITNEXUS_HOST = 'localhost'; +let gitNexusPort = 0; +const GITNEXUS_DEV_RUNTIME_DIR = path.resolve(__dirname, '../../../gitnexus'); +const GITNEXUS_WEB_DEV_HOST = 'localhost'; +const GITNEXUS_WEB_DEV_PORT = 5173; +const GITNEXUS_WEB_DEV_URL = `http://${GITNEXUS_WEB_DEV_HOST}:${GITNEXUS_WEB_DEV_PORT}`; +const GITNEXUS_WEB_DEV_ROOT = path.resolve(__dirname, '../../gitnexus-web'); +const GITNEXUS_WEB_DEV_ROOT_FALLBACK = path.resolve(__dirname, '../../../gitnexus-web'); +const GITNEXUS_PACKAGED_RUNTIME_DIR = path.join(process.resourcesPath, 'gitnexus'); +const GITNEXUS_WEB_PACKAGED_DIR = path.join(process.resourcesPath, 'gitnexus-web'); +// On Windows, native modules (e.g. lbugjs.node) PE-import node.exe by name. +// Electron's binary is not node.exe, so LoadLibrary fails with an access violation. +// Bundling a real node.exe and using it as the subprocess host avoids the crash. +const GITNEXUS_PACKAGED_NODE_BINARY = path.join(process.resourcesPath, 'runtime', 'node.exe'); +const GITNEXUS_WEB_EXPECTED_MARKERS = ['GitNexus', '
']; +const GITNEXUS_SERVER_READY_TIMEOUT_MS = 120_000; +const GITNEXUS_WEB_READY_TIMEOUT_MS = 60_000; +const GITNEXUS_WEB_READY_POLL_MS = 500; +const PACKAGED_WEB_SERVER_HOST = '127.0.0.1'; +const IS_DESKTOP_SMOKE_TEST = process.env.GITNEXUS_DESKTOP_SMOKE_TEST === '1'; + +const MIME_TYPES: Record = { + '.css': 'text/css; charset=utf-8', + '.gif': 'image/gif', + '.html': 'text/html; charset=utf-8', + '.ico': 'image/x-icon', + '.jpeg': 'image/jpeg', + '.jpg': 'image/jpeg', + '.js': 'text/javascript; charset=utf-8', + '.json': 'application/json; charset=utf-8', + '.map': 'application/json; charset=utf-8', + '.mjs': 'text/javascript; charset=utf-8', + '.png': 'image/png', + '.svg': 'image/svg+xml', + '.ttf': 'font/ttf', + '.txt': 'text/plain; charset=utf-8', + '.wasm': 'application/wasm', + '.woff': 'font/woff', + '.woff2': 'font/woff2', +}; + +let serverReadyPromise: Promise | null = null; +let gitNexusServerProcess: ChildProcess | null = null; +let gitNexusServerOutput = ''; +let webDevServerProcess: ChildProcess | null = null; +let webDevServerOutput = ''; +let packagedWebServer: Server | null = null; +let packagedWebServerUrl: string | null = null; +const openWindows = new Set(); +const contentViews = new WeakMap(); + +const getDesktopShellTitlebarHeight = (): number => { + return process.platform === 'darwin' ? DESKTOP_SHELL_TITLEBAR_HEIGHT : 0; +}; + +type DesktopShellState = { + appName: string; + isAlwaysOnTop: boolean; + isMaximized: boolean; + platform: NodeJS.Platform; + titleBarHeight: number; +}; + +type DesktopWindowAction = 'close' | 'minimize' | 'toggle-always-on-top' | 'toggle-maximize'; + +const getErrorMessage = (error: unknown): string => { + if (error instanceof Error) { + return error.message; + } + + return String(error); +}; + +const isAddressInUseError = (error: unknown): boolean => { + return (error as NodeJS.ErrnoException | undefined)?.code === 'EADDRINUSE'; +}; + +const isAllowedExternalUrl = (value: string): boolean => { + try { + const url = new URL(value); + + return url.protocol === 'http:' || url.protocol === 'https:' || url.protocol === 'mailto:'; + } catch { + return false; + } +}; + +const openExternalUrlIfSafe = (value: string): void => { + if (!isAllowedExternalUrl(value)) { + return; + } + + void shell.openExternal(value); +}; + +const getUrlOrigin = (value: string): string | null => { + try { + return new URL(value).origin; + } catch { + return null; + } +}; + +const appendProcessOutput = (chunk: Buffer | string): void => { + webDevServerOutput = `${webDevServerOutput}${chunk.toString()}`.slice(-8_192); +}; + +const appendGitNexusServerOutput = (chunk: Buffer | string): void => { + gitNexusServerOutput = `${gitNexusServerOutput}${chunk.toString()}`.slice(-8_192); +}; + +const sleep = async (ms: number): Promise => { + await new Promise((resolve) => { + setTimeout(resolve, ms); + }); +}; + +const fetchUrlText = async (url: string, timeoutMs: number): Promise => { + const controller = new AbortController(); + const timeoutHandle = setTimeout(() => { + controller.abort(); + }, timeoutMs); + + try { + const response = await fetch(url, { + cache: 'no-store', + method: 'GET', + signal: controller.signal, + }); + + if (!response.ok) { + return null; + } + + return await response.text(); + } catch { + return null; + } finally { + clearTimeout(timeoutHandle); + } +}; + +const isHttpUrlReady = async (url: string, expectedMarkers?: string[]): Promise => { + const responseText = await fetchUrlText(url, 2_000); + + if (!responseText) { + return false; + } + + if (!expectedMarkers || expectedMarkers.length === 0) { + return true; + } + + return expectedMarkers.every((marker) => responseText.includes(marker)); +}; + +const isAnyHttpUrlReady = async (urls: string[], expectedMarkers?: string[]): Promise => { + for (const url of urls) { + if (await isHttpUrlReady(url, expectedMarkers)) { + return true; + } + } + + return false; +}; + +const waitForUrlReady = async ( + url: string, + timeoutMs: number, + expectedMarkers?: string[], +): Promise => { + const deadline = Date.now() + timeoutMs; + + while (Date.now() < deadline) { + if (await isHttpUrlReady(url, expectedMarkers)) { + return; + } + + if (webDevServerProcess?.exitCode !== null && webDevServerProcess?.exitCode !== undefined) { + const output = webDevServerOutput.trim(); + throw new Error( + output + ? `GitNexus web dev server exited before it became ready.\n\n${output}` + : 'GitNexus web dev server exited before it became ready.', + ); + } + + await sleep(GITNEXUS_WEB_READY_POLL_MS); + } + + const output = webDevServerOutput.trim(); + throw new Error( + output + ? `Timed out waiting for GitNexus web dev server at ${url}.\n\n${output}` + : `Timed out waiting for GitNexus web dev server at ${url}.`, + ); +}; + +const getGitNexusRuntimeDir = (): string => { + return app.isPackaged ? GITNEXUS_PACKAGED_RUNTIME_DIR : GITNEXUS_DEV_RUNTIME_DIR; +}; + +const getGitNexusCliEntry = (): string => { + return path.join(getGitNexusRuntimeDir(), 'dist', 'cli', 'index.js'); +}; + +const getGitNexusWebDevRoot = (): string => { + return existsSync(GITNEXUS_WEB_DEV_ROOT) ? GITNEXUS_WEB_DEV_ROOT : GITNEXUS_WEB_DEV_ROOT_FALLBACK; +}; + +const getGitNexusWebViteCliEntry = (): string => { + return path.join(getGitNexusWebDevRoot(), 'node_modules', 'vite', 'bin', 'vite.js'); +}; + +const getNodeCommand = (): string => { + if (app.isPackaged) { + if (process.platform === 'win32' && existsSync(GITNEXUS_PACKAGED_NODE_BINARY)) { + return GITNEXUS_PACKAGED_NODE_BINARY; + } + + return process.execPath; + } + + return ( + process.env.npm_node_execpath || + process.env.NODE || + (process.platform === 'win32' ? 'node.exe' : 'node') + ); +}; + +const getNodeProcessEnvironment = (overrides: NodeJS.ProcessEnv = {}): NodeJS.ProcessEnv => { + const environment = { + ...process.env, + ...overrides, + }; + + if (app.isPackaged) { + // Only set ELECTRON_RUN_AS_NODE when falling back to the Electron binary as the node host. + // When using the bundled node.exe, the host is already a real node process — no flag needed. + const usingBundledNode = + process.platform === 'win32' && existsSync(GITNEXUS_PACKAGED_NODE_BINARY); + + if (!usingBundledNode) { + environment.ELECTRON_RUN_AS_NODE = '1'; + } + } + + return environment; +}; + +const spawnGitNexusServer = (): ChildProcess => { + const childProcess = spawn( + getNodeCommand(), + [getGitNexusCliEntry(), 'serve', '--host', GITNEXUS_HOST, '--port', '0'], + { + cwd: getGitNexusRuntimeDir(), + env: getNodeProcessEnvironment(app.isPackaged ? { GITNEXUS_DISABLE_MCP_HTTP: '1' } : {}), + stdio: ['ignore', 'pipe', 'pipe'], + windowsHide: true, + }, + ); + + childProcess.stdout?.on('data', (chunk) => { + appendGitNexusServerOutput(chunk); + const str = chunk.toString(); + const match = str.match(/GITNEXUS_PORT=(\d+)/); + if (match) { + gitNexusPort = parseInt(match[1], 10); + } + process.stdout.write(`[gitnexus-server] ${str}`); + }); + + childProcess.stderr?.on('data', (chunk) => { + appendGitNexusServerOutput(chunk); + process.stderr.write(`[gitnexus-server] ${chunk.toString()}`); + }); + + childProcess.once('error', (error) => { + appendGitNexusServerOutput(getErrorMessage(error)); + }); + + childProcess.once('exit', () => { + serverReadyPromise = null; + }); + + return childProcess; +}; + +const waitForGitNexusServerReady = async (): Promise => { + const deadline = Date.now() + GITNEXUS_SERVER_READY_TIMEOUT_MS; + + while (Date.now() < deadline) { + if (gitNexusPort > 0) { + const healthUrl = `http://${GITNEXUS_HOST}:${gitNexusPort}/api/info`; + const responseText = await fetchUrlText(healthUrl, 2_000); + + if (responseText) { + try { + const data = JSON.parse(responseText); + // Identity check: /api/info must return { version: string } + if (typeof data.version === 'string') { + return gitNexusPort; + } + } catch { + // Retry — response may be incomplete or not yet the API server + } + } + } + + if (gitNexusServerProcess?.exitCode !== null && gitNexusServerProcess?.exitCode !== undefined) { + const output = gitNexusServerOutput.trim(); + throw new Error( + output + ? `GitNexus backend exited before it became ready.\n\n${output}` + : 'GitNexus backend exited before it became ready.', + ); + } + + await sleep(GITNEXUS_WEB_READY_POLL_MS); + } + + const output = gitNexusServerOutput.trim(); + throw new Error( + output + ? `Timed out waiting for GitNexus backend.\n\n${output}` + : 'Timed out waiting for GitNexus backend.', + ); +}; + +const spawnWebDevServer = (): ChildProcess => { + const childProcess = spawn( + getNodeCommand(), + [ + getGitNexusWebViteCliEntry(), + '--host', + GITNEXUS_WEB_DEV_HOST, + '--port', + String(GITNEXUS_WEB_DEV_PORT), + '--strictPort', + ], + { + cwd: getGitNexusWebDevRoot(), + env: getNodeProcessEnvironment({ BROWSER: 'none' }), + stdio: ['ignore', 'pipe', 'pipe'], + windowsHide: true, + }, + ); + + childProcess.stdout?.on('data', (chunk) => { + appendProcessOutput(chunk); + process.stdout.write(`[gitnexus-web] ${chunk.toString()}`); + }); + + childProcess.stderr?.on('data', (chunk) => { + appendProcessOutput(chunk); + process.stderr.write(`[gitnexus-web] ${chunk.toString()}`); + }); + + childProcess.once('error', (error) => { + appendProcessOutput(getErrorMessage(error)); + }); + + return childProcess; +}; + +const ensureWebDevServerStarted = async (): Promise => { + if (await isHttpUrlReady(GITNEXUS_WEB_DEV_URL, GITNEXUS_WEB_EXPECTED_MARKERS)) { + return; + } + + if (!webDevServerProcess || webDevServerProcess.exitCode !== null) { + webDevServerOutput = ''; + webDevServerProcess = spawnWebDevServer(); + } + + await waitForUrlReady( + GITNEXUS_WEB_DEV_URL, + GITNEXUS_WEB_READY_TIMEOUT_MS, + GITNEXUS_WEB_EXPECTED_MARKERS, + ); +}; + +export const sendStaticResponse = (assetPath: string | null, response: ServerResponse): void => { + if (!assetPath) { + response.writeHead(403, { 'Content-Type': 'text/plain; charset=utf-8' }); + response.end('Forbidden'); + return; + } + + try { + if (!existsSync(assetPath) || !statSync(assetPath).isFile()) { + response.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }); + response.end('Not found'); + return; + } + } catch { + response.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }); + response.end('Not found'); + return; + } + + const contentType = MIME_TYPES[path.extname(assetPath)] ?? 'application/octet-stream'; + const stream = createReadStream(assetPath); + + stream.once('error', () => { + if (!response.headersSent) { + response.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' }); + response.end('Internal server error'); + return; + } + + response.destroy(); + }); + + response.once('close', () => { + if (!stream.destroyed) { + stream.destroy(); + } + }); + + stream.once('open', () => { + if (response.destroyed) { + stream.destroy(); + return; + } + + response.writeHead(200, { + 'Cache-Control': 'no-store', + 'Content-Type': contentType, + }); + stream.pipe(response); + }); +}; + +const handlePackagedWebRequest = (request: IncomingMessage, response: ServerResponse): void => { + const requestUrl = request.url ?? '/'; + const requestedPath = getRequestedPath(requestUrl); + + if (!requestedPath) { + response.writeHead(400, { 'Content-Type': 'text/plain; charset=utf-8' }); + response.end('Bad request'); + return; + } + + sendStaticResponse(normalizeStaticPath(GITNEXUS_WEB_PACKAGED_DIR, requestedPath), response); +}; + +const ensurePackagedWebServerStarted = async (): Promise => { + if (packagedWebServerUrl) { + return packagedWebServerUrl; + } + + const packagedIndex = path.join(GITNEXUS_WEB_PACKAGED_DIR, 'index.html'); + + if (!existsSync(packagedIndex)) { + throw new Error( + `GitNexus web bundle was not found at ${packagedIndex}. Run the desktop packaging flow to stage gitnexus-web/dist.`, + ); + } + + packagedWebServer = createServer(handlePackagedWebRequest); + + await new Promise((resolve, reject) => { + packagedWebServer?.once('error', reject); + packagedWebServer?.listen(0, PACKAGED_WEB_SERVER_HOST, () => { + resolve(); + }); + }); + + const address = packagedWebServer.address(); + + if (!address || typeof address === 'string') { + throw new Error('Failed to resolve the packaged GitNexus web server address.'); + } + + packagedWebServerUrl = `http://${PACKAGED_WEB_SERVER_HOST}:${address.port}`; + return packagedWebServerUrl; +}; + +const stopWebDevServer = (): void => { + if (!webDevServerProcess || !webDevServerProcess.pid || webDevServerProcess.exitCode !== null) { + return; + } + + if (process.platform === 'win32') { + spawnSync('taskkill', ['/pid', String(webDevServerProcess.pid), '/t', '/f'], { + stdio: 'ignore', + windowsHide: true, + }); + } else { + webDevServerProcess.kill('SIGTERM'); + } + + webDevServerProcess = null; +}; + +const stopGitNexusServer = (): void => { + if ( + !gitNexusServerProcess || + !gitNexusServerProcess.pid || + gitNexusServerProcess.exitCode !== null + ) { + return; + } + + if (process.platform === 'win32') { + spawnSync('taskkill', ['/pid', String(gitNexusServerProcess.pid), '/t', '/f'], { + stdio: 'ignore', + windowsHide: true, + }); + } else { + gitNexusServerProcess.kill('SIGTERM'); + } + + gitNexusServerProcess = null; +}; + +const stopPackagedWebServer = (): void => { + if (!packagedWebServer) { + return; + } + + packagedWebServer.close(); + packagedWebServer = null; + packagedWebServerUrl = null; +}; + +const exitStartupFailure = (): void => { + stopGitNexusServer(); + stopPackagedWebServer(); + stopWebDevServer(); + process.exit(1); +}; + +const getDesktopShellState = (window: BrowserWindow): DesktopShellState => { + return { + appName: DESKTOP_APP_NAME, + isAlwaysOnTop: window.isAlwaysOnTop(), + isMaximized: window.isMaximized() || window.isFullScreen(), + platform: process.platform, + titleBarHeight: getDesktopShellTitlebarHeight(), + }; +}; + +const emitDesktopShellState = (window: BrowserWindow): void => { + if (window.isDestroyed()) { + return; + } + + window.webContents.send(DESKTOP_WINDOW_STATE_CHANGED_CHANNEL, getDesktopShellState(window)); +}; + +const getWindowFromSender = (sender: Electron.WebContents): BrowserWindow => { + const window = BrowserWindow.fromWebContents(sender); + + if (!window) { + throw new Error('Unable to resolve the active desktop window.'); + } + + return window; +}; + +const updateContentViewBounds = (window: BrowserWindow): void => { + const contentView = contentViews.get(window); + + if (!contentView) { + return; + } + + const [width, height] = window.getContentSize(); + const titlebarHeight = getDesktopShellTitlebarHeight(); + + contentView.setBounds({ + x: 0, + y: titlebarHeight, + width: Math.max(width, 1), + height: Math.max(height - titlebarHeight, 1), + }); +}; + +const createEmbeddedContentView = (contentUrl: string): BrowserView => { + // BrowserView is deprecated in Electron; keep this isolated until we migrate to WebContentsView. + const contentView = new BrowserView({ + webPreferences: { + contextIsolation: true, + nodeIntegration: false, + sandbox: true, + }, + }); + const allowedOrigin = getUrlOrigin(contentUrl); + + contentView.webContents.setWindowOpenHandler(({ url }) => { + if (!allowedOrigin || getUrlOrigin(url) !== allowedOrigin) { + openExternalUrlIfSafe(url); + } + + return { action: 'deny' }; + }); + + contentView.webContents.on('will-navigate', (event, navigationUrl) => { + if (!allowedOrigin || getUrlOrigin(navigationUrl) === allowedOrigin) { + return; + } + + event.preventDefault(); + openExternalUrlIfSafe(navigationUrl); + }); + + return contentView; +}; + +const loadShellRenderer = async (window: BrowserWindow): Promise => { + const rendererUrl = process.env.ELECTRON_RENDERER_URL; + + if (rendererUrl) { + console.info(`[gitnexus-desktop] Loading shell renderer: ${rendererUrl}`); + await window.loadURL(rendererUrl); + return; + } + + const rendererEntry = getPackagedRendererEntry(__dirname); + console.info(`[gitnexus-desktop] Loading shell renderer file: ${rendererEntry}`); + await window.loadFile(rendererEntry); +}; + +const registerWindowIpcHandlers = (): void => { + ipcMain.removeHandler(DESKTOP_GET_SHELL_STATE_CHANNEL); + ipcMain.removeHandler(DESKTOP_WINDOW_ACTION_CHANNEL); + + ipcMain.handle(DESKTOP_GET_SHELL_STATE_CHANNEL, (event) => { + const window = getWindowFromSender(event.sender); + + return getDesktopShellState(window); + }); + + ipcMain.handle(DESKTOP_WINDOW_ACTION_CHANNEL, (event, action: DesktopWindowAction) => { + const window = getWindowFromSender(event.sender); + + switch (action) { + case 'close': + window.close(); + return null; + case 'minimize': + window.minimize(); + return getDesktopShellState(window); + case 'toggle-always-on-top': + window.setAlwaysOnTop(!window.isAlwaysOnTop()); + return getDesktopShellState(window); + case 'toggle-maximize': + if (window.isMaximized()) { + window.unmaximize(); + } else { + window.maximize(); + } + + return getDesktopShellState(window); + default: + throw new Error(`Unsupported desktop window action: ${String(action)}`); + } + }); +}; + +const showStartupError = (error: unknown): void => { + console.error('[gitnexus-desktop] Startup failed.', error); + + // In CI smoke tests, dialog.showErrorBox() blocks synchronously forever (no display). + // Log the error above and let process.exit() handle the failure signal instead. + if (IS_DESKTOP_SMOKE_TEST) { + return; + } + + if (isAddressInUseError(error)) { + dialog.showErrorBox( + 'GitNexus Desktop Startup Failed', + 'Another process is already using the port GitNexus needs.\n\nClose the other application and try again.', + ); + return; + } + + dialog.showErrorBox( + 'GitNexus Desktop Startup Failed', + `Failed to start GitNexus Desktop.\n\n${getErrorMessage(error)}`, + ); +}; + +const ensureGitNexusServerStarted = async (): Promise => { + if (!serverReadyPromise) { + serverReadyPromise = (async () => { + if (gitNexusPort > 0) { + const healthUrl = `http://${GITNEXUS_HOST}:${gitNexusPort}/api/info`; + const responseText = await fetchUrlText(healthUrl, 2_000); + if (responseText) { + try { + const data = JSON.parse(responseText); + if (typeof data.version === 'string') { + return gitNexusPort; + } + } catch {} + } + } + + if (!gitNexusServerProcess || gitNexusServerProcess.exitCode !== null) { + gitNexusPort = 0; + gitNexusServerOutput = ''; + gitNexusServerProcess = spawnGitNexusServer(); + } + + return await waitForGitNexusServerReady(); + })().catch((error) => { + serverReadyPromise = null; + throw error; + }); + } + + return serverReadyPromise; +}; + +async function createWindow(): Promise { + const serverPort = await ensureGitNexusServerStarted(); + let embeddedAppUrl: string; + + if (app.isPackaged) { + embeddedAppUrl = await ensurePackagedWebServerStarted(); + } else { + await ensureWebDevServerStarted(); + embeddedAppUrl = GITNEXUS_WEB_DEV_URL; + } + + // Pass server URL to web UI via query param so it auto-connects (App.tsx reads ?server=) + const serverUrl = `http://${GITNEXUS_HOST}:${serverPort}`; + const separator = embeddedAppUrl.includes('?') ? '&' : '?'; + embeddedAppUrl += `${separator}server=${encodeURIComponent(serverUrl)}`; + + const window = new BrowserWindow({ + width: 1280, + height: 800, + minWidth: 960, + minHeight: 640, + title: DESKTOP_APP_NAME, + icon: DESKTOP_APP_ICON_PATH, + backgroundColor: DESKTOP_BACKGROUND_COLOR, + show: false, + autoHideMenuBar: true, + titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : 'default', + webPreferences: { + preload: path.join(__dirname, '../preload/preload.js'), + contextIsolation: true, + nodeIntegration: false, + }, + }); + const contentView = createEmbeddedContentView(embeddedAppUrl); + + window.setMenuBarVisibility(false); + window.setBrowserView(contentView); + contentViews.set(window, contentView); + updateContentViewBounds(window); + + openWindows.add(window); + + window.on('resize', () => { + updateContentViewBounds(window); + }); + + window.on('maximize', () => { + emitDesktopShellState(window); + }); + + window.on('unmaximize', () => { + emitDesktopShellState(window); + }); + + window.on('enter-full-screen', () => { + emitDesktopShellState(window); + }); + + window.on('leave-full-screen', () => { + emitDesktopShellState(window); + }); + + window.on('always-on-top-changed', () => { + emitDesktopShellState(window); + }); + + window.once('closed', () => { + openWindows.delete(window); + contentViews.delete(window); + }); + + console.info(`[gitnexus-desktop] Loading embedded app URL: ${embeddedAppUrl}`); + await Promise.all([loadShellRenderer(window), contentView.webContents.loadURL(embeddedAppUrl)]); + emitDesktopShellState(window); + + if (!window.isDestroyed()) { + window.show(); + + if (IS_DESKTOP_SMOKE_TEST) { + setTimeout(() => { + app.quit(); + }, 1_000); + } + } +} + +app.whenReady().then(async () => { + try { + registerWindowIpcHandlers(); + await createWindow(); + } catch (error) { + showStartupError(error); + exitStartupFailure(); + } + + app.on('activate', () => { + if (openWindows.size === 0) { + void createWindow().catch((error) => { + showStartupError(error); + exitStartupFailure(); + }); + } + }); +}); + +app.on('before-quit', () => { + stopGitNexusServer(); + stopPackagedWebServer(); + stopWebDevServer(); +}); + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit(); + } +}); diff --git a/gitnexus-desktop/src/main/preload.ts b/gitnexus-desktop/src/main/preload.ts new file mode 100644 index 0000000000..3e24dc14c7 --- /dev/null +++ b/gitnexus-desktop/src/main/preload.ts @@ -0,0 +1,56 @@ +import { contextBridge, ipcRenderer, type IpcRendererEvent } from 'electron'; + +const DESKTOP_APP_NAME = 'GitNexus Desktop'; +const DESKTOP_GET_SHELL_STATE_CHANNEL = 'gitnexus-desktop:get-shell-state'; +const DESKTOP_WINDOW_ACTION_CHANNEL = 'gitnexus-desktop:window-action'; +const DESKTOP_WINDOW_STATE_CHANGED_CHANNEL = 'gitnexus-desktop:window-state-changed'; +const DESKTOP_SHELL_TITLEBAR_HEIGHT = process.platform === 'darwin' ? 38 : 0; + +type DesktopShellState = { + appName: string; + isAlwaysOnTop: boolean; + isMaximized: boolean; + platform: NodeJS.Platform; + titleBarHeight: number; +}; + +type DesktopWindowAction = 'close' | 'minimize' | 'toggle-always-on-top' | 'toggle-maximize'; + +const invokeWindowAction = (action: DesktopWindowAction): Promise => { + return ipcRenderer.invoke( + DESKTOP_WINDOW_ACTION_CHANNEL, + action, + ) as Promise; +}; + +contextBridge.exposeInMainWorld('gitnexusDesktop', { + appName: DESKTOP_APP_NAME, + platform: process.platform, + titleBarHeight: DESKTOP_SHELL_TITLEBAR_HEIGHT, + close: async (): Promise => { + await invokeWindowAction('close'); + }, + getShellState: (): Promise => { + return ipcRenderer.invoke(DESKTOP_GET_SHELL_STATE_CHANNEL) as Promise; + }, + minimize: (): Promise => { + return invokeWindowAction('minimize'); + }, + onWindowStateChanged: (callback: (state: DesktopShellState) => void): (() => void) => { + const listener = (_event: IpcRendererEvent, state: DesktopShellState): void => { + callback(state); + }; + + ipcRenderer.on(DESKTOP_WINDOW_STATE_CHANGED_CHANNEL, listener); + + return () => { + ipcRenderer.removeListener(DESKTOP_WINDOW_STATE_CHANGED_CHANNEL, listener); + }; + }, + toggleAlwaysOnTop: (): Promise => { + return invokeWindowAction('toggle-always-on-top'); + }, + toggleMaximize: (): Promise => { + return invokeWindowAction('toggle-maximize'); + }, +}); diff --git a/gitnexus-desktop/src/main/runtime-paths.ts b/gitnexus-desktop/src/main/runtime-paths.ts new file mode 100644 index 0000000000..8a929c7ea4 --- /dev/null +++ b/gitnexus-desktop/src/main/runtime-paths.ts @@ -0,0 +1,46 @@ +import { existsSync, statSync } from 'node:fs'; +import path from 'node:path'; + +export const normalizeStaticPath = (rootDir: string, requestPath: string): string | null => { + if (requestPath.includes('\0')) { + return null; + } + + const normalizedRoot = path.resolve(rootDir); + const requestedFile = requestPath === '/' ? '/index.html' : requestPath; + const resolvedPath = path.resolve(normalizedRoot, `.${requestedFile}`); + const isInsideRoot = + resolvedPath === normalizedRoot || resolvedPath.startsWith(`${normalizedRoot}${path.sep}`); + + if (!isInsideRoot) { + return null; + } + + try { + if (existsSync(resolvedPath) && statSync(resolvedPath).isFile()) { + return resolvedPath; + } + } catch { + return null; + } + + if (requestPath === '/' || path.extname(requestedFile) === '') { + return path.join(normalizedRoot, 'index.html'); + } + + return resolvedPath; +}; + +export const getRequestedPath = (requestUrl: string): string | null => { + try { + const requestedPath = decodeURIComponent(new URL(requestUrl, 'http://127.0.0.1').pathname); + + return requestedPath.includes('\0') ? null : requestedPath; + } catch { + return null; + } +}; + +export const getPackagedRendererEntry = (currentDir: string): string => { + return path.join(currentDir, '../renderer/index.html'); +}; diff --git a/gitnexus-desktop/src/renderer/index.html b/gitnexus-desktop/src/renderer/index.html new file mode 100644 index 0000000000..a079748af1 --- /dev/null +++ b/gitnexus-desktop/src/renderer/index.html @@ -0,0 +1,12 @@ + + + + + + GitNexus Desktop + + +
+ + + diff --git a/gitnexus-desktop/src/renderer/src/main.tsx b/gitnexus-desktop/src/renderer/src/main.tsx new file mode 100644 index 0000000000..2da67de25b --- /dev/null +++ b/gitnexus-desktop/src/renderer/src/main.tsx @@ -0,0 +1,207 @@ +type DesktopShellState = { + appName: string; + isAlwaysOnTop: boolean; + isMaximized: boolean; + platform: string; + titleBarHeight: number; +}; + +type DesktopApi = { + appName: string; + close: () => Promise; + getShellState: () => Promise; + minimize: () => Promise; + onWindowStateChanged: (callback: (state: DesktopShellState) => void) => () => void; + platform: string; + titleBarHeight: number; + toggleAlwaysOnTop: () => Promise; + toggleMaximize: () => Promise; +}; + +const rootElement = document.getElementById('root'); + +if (!rootElement) { + throw new Error('Renderer root element was not found.'); +} + +const defaultDesktopState: DesktopShellState = { + appName: 'GitNexus Desktop', + isAlwaysOnTop: false, + isMaximized: false, + platform: 'win32', + titleBarHeight: 38, +}; + +const fallbackDesktopApi: DesktopApi = { + appName: defaultDesktopState.appName, + close: async () => {}, + getShellState: async () => defaultDesktopState, + minimize: async () => defaultDesktopState, + onWindowStateChanged: () => () => {}, + platform: defaultDesktopState.platform, + titleBarHeight: defaultDesktopState.titleBarHeight, + toggleAlwaysOnTop: async () => defaultDesktopState, + toggleMaximize: async () => defaultDesktopState, +}; + +const desktopApi = + (window as Window & { gitnexusDesktop?: DesktopApi }).gitnexusDesktop ?? fallbackDesktopApi; + +const shellState: DesktopShellState = { + ...defaultDesktopState, + appName: desktopApi.appName, + platform: desktopApi.platform, + titleBarHeight: desktopApi.titleBarHeight, +}; + +const styles = ` + :root { + color-scheme: dark; + } + + html, + body, + #root { + width: 100%; + height: 100%; + margin: 0; + overflow: hidden; + background: #0d0d0d; + color: #f4f4f5; + } + + body { + font-family: 'Segoe UI Variable', 'Segoe UI', 'SF Pro Text', system-ui, sans-serif; + } + + .desktop-shell { + --titlebar-height: 38px; + height: 100%; + display: grid; + grid-template-rows: var(--titlebar-height) 1fr; + background: #0d0d0d; + overflow: hidden; + } + + .desktop-shell--native-frame { + grid-template-rows: 1fr; + } + + .desktop-shell--native-frame .desktop-shell__titlebar { + display: none; + } + + .desktop-shell__titlebar { + display: grid; + grid-template-columns: minmax(144px, 1fr) auto minmax(144px, 1fr); + align-items: center; + gap: 12px; + height: var(--titlebar-height); + padding: 0 10px; + box-sizing: border-box; + background: #0d0d0d; + border-bottom: 1px solid rgba(255, 255, 255, 0.06); + -webkit-app-region: drag; + user-select: none; + } + + .desktop-shell__side { + display: flex; + align-items: center; + min-width: 144px; + min-height: 100%; + } + + .desktop-shell__side--end { + justify-content: flex-end; + } + + .desktop-shell__title { + justify-self: center; + font-size: 12px; + font-weight: 600; + letter-spacing: 0.06em; + color: rgba(244, 244, 245, 0.92); + white-space: nowrap; + } + + .desktop-shell__spacer { + min-width: 118px; + min-height: 1px; + } + + .desktop-shell__content { + background: #0d0d0d; + } + + @media (max-width: 720px) { + .desktop-shell__titlebar { + grid-template-columns: minmax(116px, 1fr) auto minmax(116px, 1fr); + gap: 8px; + padding: 0 8px; + } + + .desktop-shell__side { + min-width: 116px; + } + } +`; + +const styleElement = document.createElement('style'); +styleElement.textContent = styles; +document.head.append(styleElement); + +const shell = document.createElement('div'); +shell.className = 'desktop-shell'; + +const titlebar = document.createElement('header'); +titlebar.className = 'desktop-shell__titlebar'; + +const leadingSide = document.createElement('div'); +leadingSide.className = 'desktop-shell__side'; + +const titleElement = document.createElement('div'); +titleElement.className = 'desktop-shell__title'; + +const trailingSide = document.createElement('div'); +trailingSide.className = 'desktop-shell__side desktop-shell__side--end'; + +const content = document.createElement('main'); +content.className = 'desktop-shell__content'; +content.setAttribute('aria-hidden', 'true'); + +const spacer = (): HTMLDivElement => { + const element = document.createElement('div'); + element.className = 'desktop-shell__spacer'; + element.setAttribute('aria-hidden', 'true'); + return element; +}; + +leadingSide.append(spacer()); +trailingSide.append(spacer()); + +titlebar.append(leadingSide, titleElement, trailingSide); +shell.append(titlebar, content); +rootElement.replaceChildren(shell); + +const applyShellState = (state: DesktopShellState): void => { + shellState.appName = state.appName; + shellState.isAlwaysOnTop = state.isAlwaysOnTop; + shellState.isMaximized = state.isMaximized; + shellState.platform = state.platform; + shellState.titleBarHeight = state.titleBarHeight; + + shell.style.setProperty('--titlebar-height', `${state.titleBarHeight}px`); + shell.classList.toggle('desktop-shell--native-frame', state.titleBarHeight === 0); + titleElement.textContent = state.appName; +}; + +applyShellState(shellState); + +void desktopApi.getShellState().then((state) => { + applyShellState(state); +}); + +desktopApi.onWindowStateChanged((state) => { + applyShellState(state); +}); diff --git a/gitnexus-desktop/test/main-utils.test.ts b/gitnexus-desktop/test/main-utils.test.ts new file mode 100644 index 0000000000..f34b6114c1 --- /dev/null +++ b/gitnexus-desktop/test/main-utils.test.ts @@ -0,0 +1,61 @@ +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; + +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; + +import { + getPackagedRendererEntry, + getRequestedPath, + normalizeStaticPath, +} from '../src/main/runtime-paths.js'; + +describe('normalizeStaticPath', () => { + let rootDir: string; + + beforeEach(() => { + rootDir = mkdtempSync(path.join(os.tmpdir(), 'gitnexus-desktop-static-')); + writeFileSync(path.join(rootDir, 'index.html'), ''); + writeFileSync(path.join(rootDir, 'asset.js'), 'console.log("ok");'); + }); + + afterEach(() => { + rmSync(rootDir, { force: true, recursive: true }); + }); + + it('rejects directory traversal attempts', () => { + expect(normalizeStaticPath(rootDir, '/../../etc/passwd')).toBeNull(); + }); + + it('rejects null-byte paths', () => { + expect(normalizeStaticPath(rootDir, '/\0asset.js')).toBeNull(); + }); + + it('returns a real asset path when the file exists', () => { + expect(normalizeStaticPath(rootDir, '/asset.js')).toBe(path.join(rootDir, 'asset.js')); + }); + + it('falls back to index.html for extensionless routes', () => { + expect(normalizeStaticPath(rootDir, '/desktop')).toBe(path.join(rootDir, 'index.html')); + }); +}); + +describe('getRequestedPath', () => { + it('returns null for malformed percent-encoding', () => { + expect(getRequestedPath('/%GG')).toBeNull(); + }); + + it('returns null for decoded null bytes', () => { + expect(getRequestedPath('/%00')).toBeNull(); + }); +}); + +describe('getPackagedRendererEntry', () => { + it('resolves the packaged renderer relative to dist/main', () => { + const rendererEntry = getPackagedRendererEntry(path.join('dist', 'main')); + + expect(path.normalize(rendererEntry)).toBe( + path.normalize(path.join('dist', 'renderer', 'index.html')), + ); + }); +}); diff --git a/gitnexus-desktop/tsconfig.json b/gitnexus-desktop/tsconfig.json new file mode 100644 index 0000000000..7ad3df4a47 --- /dev/null +++ b/gitnexus-desktop/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "strict": true, + "forceConsistentCasingInFileNames": true + }, + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.renderer.json" + } + ] +} diff --git a/gitnexus-desktop/tsconfig.node.json b/gitnexus-desktop/tsconfig.node.json new file mode 100644 index 0000000000..ddd5f3a971 --- /dev/null +++ b/gitnexus-desktop/tsconfig.node.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "composite": true, + "target": "ES2022", + "lib": ["ES2022"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "types": ["node"], + "noEmit": true + }, + "include": ["electron.vite.config.ts", "src/main/**/*.ts"] +} diff --git a/gitnexus-desktop/tsconfig.renderer.json b/gitnexus-desktop/tsconfig.renderer.json new file mode 100644 index 0000000000..3b41876f84 --- /dev/null +++ b/gitnexus-desktop/tsconfig.renderer.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "composite": true, + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "types": ["vite/client"] + }, + "include": ["src/renderer/src/main.tsx"] +} diff --git a/gitnexus-desktop/tsconfig.vitest.json b/gitnexus-desktop/tsconfig.vitest.json new file mode 100644 index 0000000000..fa26603fe6 --- /dev/null +++ b/gitnexus-desktop/tsconfig.vitest.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.node.json", + "compilerOptions": { + "types": ["node", "vitest/globals"] + }, + "include": ["vitest.config.ts", "test/**/*.ts", "src/main/runtime-paths.ts"] +} diff --git a/gitnexus-desktop/vitest.config.ts b/gitnexus-desktop/vitest.config.ts new file mode 100644 index 0000000000..6d7b158563 --- /dev/null +++ b/gitnexus-desktop/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'node', + globals: true, + include: ['test/**/*.test.ts'], + }, +}); diff --git a/gitnexus-web/src/components/CodeReferencesPanel.tsx b/gitnexus-web/src/components/CodeReferencesPanel.tsx index bd416f9fec..2fc877c555 100644 --- a/gitnexus-web/src/components/CodeReferencesPanel.tsx +++ b/gitnexus-web/src/components/CodeReferencesPanel.tsx @@ -381,7 +381,7 @@ export const CodeReferencesPanel = ({ onFocusNode }: CodeReferencesPanelProps) = -
+
{isLoadingFile ? (
@@ -454,7 +454,7 @@ export const CodeReferencesPanel = ({ onFocusNode }: CodeReferencesPanelProps) = {t('graph:codePanel.references', { count: aiReferences.length })}
-
+
{refsWithSnippets.map( ({ ref, content, start, highlightStart, highlightEnd, totalLines }) => { const nodeColor = ref.label diff --git a/gitnexus-web/src/components/FileTreePanel.tsx b/gitnexus-web/src/components/FileTreePanel.tsx index a3c7487a99..57aff50ab1 100644 --- a/gitnexus-web/src/components/FileTreePanel.tsx +++ b/gitnexus-web/src/components/FileTreePanel.tsx @@ -389,7 +389,7 @@ export const FileTreePanel = ({ onFocusNode }: FileTreePanelProps) => {
{/* File tree */} -
+
{fileTree.length === 0 ? (
{t('graph:fileTree.noFilesLoaded')} @@ -413,7 +413,7 @@ export const FileTreePanel = ({ onFocusNode }: FileTreePanelProps) => { )} {activeTab === 'filters' && ( -
+

{t('graph:fileTree.nodeTypes')} diff --git a/gitnexus-web/src/components/ProcessesPanel.tsx b/gitnexus-web/src/components/ProcessesPanel.tsx index 5ff9c54d4b..73839678cb 100644 --- a/gitnexus-web/src/components/ProcessesPanel.tsx +++ b/gitnexus-web/src/components/ProcessesPanel.tsx @@ -364,7 +364,7 @@ export const ProcessesPanel = () => {

{/* Process list */} -
+
{/* View All Processes Card */}
{showResults && queryResult.rows.length > 0 && ( -
+
diff --git a/gitnexus-web/src/components/RightPanel.tsx b/gitnexus-web/src/components/RightPanel.tsx index 193d2c1e62..100392ce22 100644 --- a/gitnexus-web/src/components/RightPanel.tsx +++ b/gitnexus-web/src/components/RightPanel.tsx @@ -292,7 +292,7 @@ export const RightPanel = () => { )} {/* Messages */} -
+
{chatMessages.length === 0 ? (
@@ -417,7 +417,7 @@ export const RightPanel = () => { onKeyDown={handleKeyDown} placeholder={t('chat:input.placeholder')} rows={1} - className="scrollbar-thin min-h-[36px] flex-1 resize-none border-none bg-transparent text-sm text-text-primary outline-none placeholder:text-text-muted" + className="min-h-[36px] flex-1 resize-none border-none bg-transparent text-sm text-text-primary outline-none placeholder:text-text-muted" style={{ height: '36px', overflowY: 'hidden' }} />